mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
Add detailed support for Airwell A/C protocol. (#1204)
* Add support for controlling: - Operation Mode - Power Toggle - Fan Speed - Temperature * Enforce a fan speed of Low when in Dry mode. * Common A/C API support added. * Unit tests Fixes #1202
This commit is contained in:
58
src/IRac.cpp
58
src/IRac.cpp
@@ -16,6 +16,7 @@
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
#include "ir_Airwell.h"
|
||||
#include "ir_Amcor.h"
|
||||
#include "ir_Argo.h"
|
||||
#include "ir_Carrier.h"
|
||||
@@ -132,6 +133,9 @@ stdAc::state_t IRac::getStatePrev(void) { return _prev; }
|
||||
/// @return true if the protocol is supported by this class, otherwise false.
|
||||
bool IRac::isProtocolSupported(const decode_type_t protocol) {
|
||||
switch (protocol) {
|
||||
#if SEND_AIRWELL
|
||||
case decode_type_t::AIRWELL:
|
||||
#endif
|
||||
#if SEND_AMCOR
|
||||
case decode_type_t::AMCOR:
|
||||
#endif
|
||||
@@ -269,6 +273,34 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
|
||||
}
|
||||
}
|
||||
|
||||
#if SEND_AIRWELL
|
||||
/// Send an Airwell A/C message with the supplied settings.
|
||||
/// @param[in, out] ac A Ptr to an IRAirwellAc object to use.
|
||||
/// @param[in] on The power setting.
|
||||
/// @param[in] mode The operation mode setting.
|
||||
/// @param[in] degrees The temperature setting in degrees.
|
||||
/// @param[in] fan The speed setting for the fan.
|
||||
void IRac::airwell(IRAirwellAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan) {
|
||||
ac->begin();
|
||||
ac->setPowerToggle(on);
|
||||
ac->setMode(ac->convertMode(mode));
|
||||
ac->setTemp(degrees);
|
||||
ac->setFan(ac->convertFan(fan));
|
||||
// No Swing setting available.
|
||||
// No Quiet setting available.
|
||||
// No Light setting available.
|
||||
// No Filter setting available.
|
||||
// No Turbo setting available.
|
||||
// No Economy setting available.
|
||||
// No Clean setting available.
|
||||
// No Beep setting available.
|
||||
// No Sleep setting available.
|
||||
ac->send();
|
||||
}
|
||||
#endif // SEND_AIRWELL
|
||||
|
||||
#if SEND_AMCOR
|
||||
/// Send an Amcor A/C message with the supplied settings.
|
||||
/// @param[in, out] ac A Ptr to an IRAmcorAc object to use.
|
||||
@@ -1892,6 +1924,7 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired,
|
||||
else
|
||||
result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle.
|
||||
break;
|
||||
case decode_type_t::AIRWELL:
|
||||
case decode_type_t::DAIKIN64:
|
||||
case decode_type_t::WHIRLPOOL_AC:
|
||||
result.power = desired.power ^ prev->power;
|
||||
@@ -1959,6 +1992,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
|
||||
stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev);
|
||||
// Per vendor settings & setup.
|
||||
switch (send.protocol) {
|
||||
#if SEND_AIRWELL
|
||||
case AIRWELL:
|
||||
{
|
||||
IRAirwellAc ac(_pin, _inverted, _modulation);
|
||||
airwell(&ac, send.power, send.mode, degC, send.fanspeed);
|
||||
break;
|
||||
}
|
||||
#endif // SEND_AIRWELL
|
||||
#if SEND_AMCOR
|
||||
case AMCOR:
|
||||
{
|
||||
@@ -2715,6 +2756,13 @@ namespace IRAcUtils {
|
||||
/// An empty string if we can't.
|
||||
String resultAcToString(const decode_results * const result) {
|
||||
switch (result->decode_type) {
|
||||
#if DECODE_AIRWELL
|
||||
case decode_type_t::AIRWELL: {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
ac.setRaw(result->value); // AIRWELL uses value instead of state.
|
||||
return ac.toString();
|
||||
}
|
||||
#endif // DECODE_AIRWELL
|
||||
#if DECODE_AMCOR
|
||||
case decode_type_t::AMCOR: {
|
||||
IRAmcorAc ac(0);
|
||||
@@ -2724,7 +2772,7 @@ namespace IRAcUtils {
|
||||
#endif // DECODE_AMCOR
|
||||
#if DECODE_ARGO
|
||||
case decode_type_t::ARGO: {
|
||||
IRArgoAC ac(0);
|
||||
IRArgoAC ac(kGpioUnused);
|
||||
ac.setRaw(result->state);
|
||||
return ac.toString();
|
||||
}
|
||||
@@ -3040,6 +3088,14 @@ namespace IRAcUtils {
|
||||
) {
|
||||
if (decode == NULL || result == NULL) return false; // Safety check.
|
||||
switch (decode->decode_type) {
|
||||
#if DECODE_AIRWELL
|
||||
case decode_type_t::AIRWELL: {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
ac.setRaw(decode->value); // Uses value instead of state.
|
||||
*result = ac.toCommon();
|
||||
break;
|
||||
}
|
||||
#endif // DECODE_AIRWELL
|
||||
#if DECODE_AMCOR
|
||||
case decode_type_t::AMCOR: {
|
||||
IRAmcorAc ac(kGpioUnused);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "ir_Airwell.h"
|
||||
#include "ir_Amcor.h"
|
||||
#include "ir_Argo.h"
|
||||
#include "ir_Carrier.h"
|
||||
@@ -98,6 +99,11 @@ class IRac {
|
||||
bool _inverted; ///< IR LED is lit when GPIO is LOW (true) or HIGH (false)?
|
||||
bool _modulation; ///< Is frequency modulation to be used?
|
||||
stdAc::state_t _prev; ///< The state we expect the device to currently be in.
|
||||
#if SEND_AIRWELL
|
||||
void airwell(IRAirwellAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan);
|
||||
#endif // SEND_AIRWELL
|
||||
#if SEND_AMCOR
|
||||
void amcor(IRAmcorAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
// Copyright 2020 David Conran
|
||||
|
||||
#include "ir_Airwell.h"
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
/// @file
|
||||
/// @brief Airwell "Manchester code" based protocol.
|
||||
/// Some other Airwell products use the COOLIX protocol.
|
||||
|
||||
// Supports:
|
||||
// Brand: Airwell, Model: RC08W remote
|
||||
// Brand: Airwell, Model: RC04 remote
|
||||
// Brand: Airwell, Model: DLS 21 DCI R410 AW A/C
|
||||
|
||||
const uint8_t kAirwellOverhead = 4;
|
||||
const uint16_t kAirwellHalfClockPeriod = 950; // uSeconds
|
||||
const uint16_t kAirwellHdrMark = 3 * kAirwellHalfClockPeriod; // uSeconds
|
||||
const uint16_t kAirwellHdrSpace = 3 * kAirwellHalfClockPeriod; // uSeconds
|
||||
const uint16_t kAirwellFooterMark = 5 * kAirwellHalfClockPeriod; // uSeconds
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::setBit;
|
||||
using irutils::setBits;
|
||||
|
||||
#if SEND_AIRWELL
|
||||
/// Send an Airwell Manchester Code formatted message.
|
||||
/// Status: BETA / Appears to be working.
|
||||
@@ -74,3 +79,203 @@ bool IRrecv::decodeAirwell(decode_results *results, uint16_t offset,
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// 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?
|
||||
IRAirwellAc::IRAirwellAc(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 IRAirwellAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
/// Get the raw state of the object, suitable to be sent with the appropriate
|
||||
/// IRsend object method.
|
||||
/// @return A copy of the internal state.
|
||||
uint64_t IRAirwellAc::getRaw(void) {
|
||||
return remote_state;
|
||||
}
|
||||
|
||||
/// Set the raw state of the object.
|
||||
/// @param[in] state The raw state from the native IR message.
|
||||
void IRAirwellAc::setRaw(const uint64_t state) {
|
||||
remote_state = state;
|
||||
}
|
||||
|
||||
#if SEND_AIRWELL
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRAirwellAc::send(const uint16_t repeat) {
|
||||
_irsend.sendAirwell(getRaw(), kAirwellBits, repeat);
|
||||
}
|
||||
#endif // SEND_AIRWELL
|
||||
|
||||
/// Reset the internals of the object to a known good state.
|
||||
void IRAirwellAc::stateReset(void) {
|
||||
remote_state = kAirwellKnownGoodState;
|
||||
}
|
||||
|
||||
/// Turn on/off the Power Airwell setting.
|
||||
/// @param[in] on The desired setting state.
|
||||
void IRAirwellAc::setPowerToggle(const bool on) {
|
||||
setBit(&remote_state, kAirwellPowerToggleBit, on);
|
||||
}
|
||||
|
||||
/// Get the power toggle setting from the internal state.
|
||||
/// @return A boolean indicating the setting.
|
||||
bool IRAirwellAc::getPowerToggle(void) {
|
||||
return GETBIT64(remote_state, kAirwellPowerToggleBit);
|
||||
}
|
||||
|
||||
/// Get the current operation mode setting.
|
||||
/// @return The current operation mode.
|
||||
uint8_t IRAirwellAc::getMode(void) {
|
||||
return GETBITS64(remote_state, kAirwellModeOffset, kAirwellModeSize);
|
||||
}
|
||||
|
||||
/// Set the desired operation mode.
|
||||
/// @param[in] mode The desired operation mode.
|
||||
void IRAirwellAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kAirwellFan:
|
||||
case kAirwellCool:
|
||||
case kAirwellHeat:
|
||||
case kAirwellDry:
|
||||
case kAirwellAuto:
|
||||
setBits(&remote_state, kAirwellModeOffset, kAirwellModeSize, mode);
|
||||
break;
|
||||
default:
|
||||
setMode(kAirwellAuto);
|
||||
}
|
||||
setFan(getFan()); // Ensure the fan is at the correct speed for the new mode.
|
||||
}
|
||||
|
||||
/// 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 IRAirwellAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kAirwellCool;
|
||||
case stdAc::opmode_t::kHeat: return kAirwellHeat;
|
||||
case stdAc::opmode_t::kDry: return kAirwellDry;
|
||||
case stdAc::opmode_t::kFan: return kAirwellFan;
|
||||
default: return kAirwellAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 IRAirwellAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kAirwellCool: return stdAc::opmode_t::kCool;
|
||||
case kAirwellHeat: return stdAc::opmode_t::kHeat;
|
||||
case kAirwellDry: return stdAc::opmode_t::kDry;
|
||||
case kAirwellFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
/// @note The speed is locked to Low when in Dry mode.
|
||||
void IRAirwellAc::setFan(const uint8_t speed) {
|
||||
setBits(&remote_state, kAirwellFanOffset, kAirwellFanSize,
|
||||
(getMode() == kAirwellDry) ? kAirwellFanLow
|
||||
: std::min(speed, kAirwellFanAuto));
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRAirwellAc::getFan(void) {
|
||||
return GETBITS64(remote_state, kAirwellFanOffset, kAirwellFanSize);
|
||||
}
|
||||
|
||||
/// 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 IRAirwellAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow:
|
||||
return kAirwellFanLow;
|
||||
case stdAc::fanspeed_t::kMedium:
|
||||
return kAirwellFanMedium;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax:
|
||||
return kAirwellFanHigh;
|
||||
default:
|
||||
return kAirwellFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 IRAirwellAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kAirwellFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kAirwellFanMedium: return stdAc::fanspeed_t::kMedium;
|
||||
case kAirwellFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRAirwellAc::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = std::max(kAirwellMinTemp, degrees);
|
||||
temp = std::min(kAirwellMaxTemp, temp);
|
||||
setBits(&remote_state, kAirwellTempOffset, kAirwellTempSize,
|
||||
temp - kAirwellMinTemp + 1);
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return Get current setting for temp. in degrees celsius.
|
||||
uint8_t IRAirwellAc::getTemp(void) {
|
||||
return GETBITS64(remote_state, kAirwellTempOffset,
|
||||
kAirwellTempSize) + kAirwellMinTemp - 1;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivilant.
|
||||
/// @return The stdAc equivilant of the native settings.
|
||||
stdAc::state_t IRAirwellAc::toCommon(void) {
|
||||
stdAc::state_t result;
|
||||
result.protocol = decode_type_t::AIRWELL;
|
||||
result.power = getPowerToggle();
|
||||
result.mode = toCommonMode(getMode());
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(getFan());
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.turbo = false;
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.light = false;
|
||||
result.filter = false;
|
||||
result.econo = false;
|
||||
result.quiet = false;
|
||||
result.clean = 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 IRAirwellAc::toString(void) {
|
||||
String result = "";
|
||||
result.reserve(70); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(getPowerToggle(), kPowerToggleStr, false);
|
||||
result += addModeToString(getMode(), kAirwellAuto, kAirwellCool,
|
||||
kAirwellHeat, kAirwellDry, kAirwellFan);
|
||||
result += addFanToString(getFan(), kAirwellFanHigh, kAirwellFanLow,
|
||||
kAirwellFanAuto, kAirwellFanAuto,
|
||||
kAirwellFanMedium);
|
||||
result += addTempToString(getTemp());
|
||||
return result;
|
||||
}
|
||||
|
||||
97
src/ir_Airwell.h
Normal file
97
src/ir_Airwell.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2020 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Airwell "Manchester code" based protocol.
|
||||
/// Some other Airwell products use the COOLIX protocol.
|
||||
|
||||
// Supports:
|
||||
// Brand: Airwell, Model: RC08W remote
|
||||
// Brand: Airwell, Model: RC04 remote
|
||||
// Brand: Airwell, Model: DLS 21 DCI R410 AW A/C
|
||||
|
||||
#ifndef IR_AIRWELL_H_
|
||||
#define IR_AIRWELL_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Constants
|
||||
const uint64_t kAirwellKnownGoodState = 0x140500002; // Mode Fan, Speed 1, 25C
|
||||
// Temperature
|
||||
const uint8_t kAirwellMinTemp = 16; // Celsius
|
||||
const uint8_t kAirwellMaxTemp = 30; // Celsius
|
||||
const uint8_t kAirwellTempSize = 4; // Bits
|
||||
const uint8_t kAirwellTempOffset = 19; // 0b1111 << 19
|
||||
// Fan
|
||||
const uint8_t kAirwellFanSize = 2; // Bits
|
||||
const uint8_t kAirwellFanOffset = 28; // 0b11 << 28
|
||||
const uint8_t kAirwellFanLow = 0; // 0b00
|
||||
const uint8_t kAirwellFanMedium = 1; // 0b01
|
||||
const uint8_t kAirwellFanHigh = 2; // 0b10
|
||||
const uint8_t kAirwellFanAuto = 3; // 0b11
|
||||
// Modes
|
||||
const uint8_t kAirwellModeSize = 3; // Bits
|
||||
const uint8_t kAirwellModeOffset = 30; // 0b111 << 30
|
||||
const uint8_t kAirwellCool = 1; // 0b001
|
||||
const uint8_t kAirwellHeat = 2; // 0b010
|
||||
const uint8_t kAirwellAuto = 3; // 0b011
|
||||
const uint8_t kAirwellDry = 4; // 0b100
|
||||
const uint8_t kAirwellFan = 5; // 0b101
|
||||
// Power
|
||||
const uint8_t kAirwellPowerToggleBit = 33; // 0b1 << 33
|
||||
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Airwell A/C messages.
|
||||
class IRAirwellAc {
|
||||
public:
|
||||
explicit IRAirwellAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset();
|
||||
#if SEND_AIRWELL
|
||||
void send(const uint16_t repeat = kAirwellMinRepeats);
|
||||
/// Run the calibration to calculate uSec timing offsets for this platform.
|
||||
/// @return The uSec timing offset needed per modulation of the IR Led.
|
||||
/// @note This will produce a 65ms IR signal pulse at 38kHz.
|
||||
/// Only ever needs to be run once per object instantiation, if at all.
|
||||
int8_t calibrate(void) { return _irsend.calibrate(); }
|
||||
#endif // SEND_AIRWELL
|
||||
void begin();
|
||||
void setPowerToggle(const bool on);
|
||||
bool getPowerToggle();
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp();
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan();
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode();
|
||||
uint64_t getRaw();
|
||||
void setRaw(const uint64_t state);
|
||||
uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void);
|
||||
String toString();
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
uint64_t remote_state; // The state of the IR remote in native IR code form.
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_AIRWELL_H_
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
#include <string>
|
||||
#include "ir_Airwell.h"
|
||||
#include "ir_Amcor.h"
|
||||
#include "ir_Argo.h"
|
||||
#include "ir_Carrier.h"
|
||||
@@ -39,6 +40,29 @@
|
||||
|
||||
// Tests for IRac class.
|
||||
|
||||
TEST(TestIRac, Airwell) {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
IRac irac(kGpioUnused);
|
||||
IRrecv capture(kGpioUnused);
|
||||
char expected[] =
|
||||
"Power Toggle: On, Mode: 3 (Auto), Fan: 1 (Medium), Temp: 18C";
|
||||
|
||||
ac.begin();
|
||||
irac.airwell(&ac,
|
||||
true, // Power
|
||||
stdAc::opmode_t::kAuto, // Mode
|
||||
18, // Celsius
|
||||
stdAc::fanspeed_t::kMedium); // Fan speed
|
||||
ASSERT_EQ(expected, ac.toString());
|
||||
ac._irsend.makeDecodeResult();
|
||||
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
|
||||
ASSERT_EQ(AIRWELL, ac._irsend.capture.decode_type);
|
||||
ASSERT_EQ(kAirwellBits, ac._irsend.capture.bits);
|
||||
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
|
||||
stdAc::state_t r, p;
|
||||
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
|
||||
}
|
||||
|
||||
TEST(TestIRac, Amcor) {
|
||||
IRAmcorAc ac(0);
|
||||
IRac irac(0);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright 2020 David Conran
|
||||
|
||||
#include "ir_Airwell.h"
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRrecv_test.h"
|
||||
@@ -44,6 +45,9 @@ TEST(TestDecodeAirwell, RealExample) {
|
||||
EXPECT_EQ(0x2B0D0181B, irsend.capture.value);
|
||||
EXPECT_EQ(0x0, irsend.capture.address);
|
||||
EXPECT_EQ(0x0, irsend.capture.command);
|
||||
EXPECT_EQ(
|
||||
"Power Toggle: On, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 25C",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
|
||||
const uint16_t rawData_2[175] = {
|
||||
2862, 3892,
|
||||
@@ -76,6 +80,9 @@ TEST(TestDecodeAirwell, RealExample) {
|
||||
EXPECT_EQ(0x270F8181B, irsend.capture.value);
|
||||
EXPECT_EQ(0x0, irsend.capture.address);
|
||||
EXPECT_EQ(0x0, irsend.capture.command);
|
||||
EXPECT_EQ(
|
||||
"Power Toggle: On, Mode: 1 (Cool), Fan: 3 (Auto), Temp: 30C",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
}
|
||||
|
||||
TEST(TestDecodeAirwell, SyntheticExample) {
|
||||
@@ -192,6 +199,9 @@ TEST(TestDecodeAirwell, RealExample2) {
|
||||
EXPECT_EQ(0xB0C0181B, irsend.capture.value);
|
||||
EXPECT_EQ(0x0, irsend.capture.address);
|
||||
EXPECT_EQ(0x0, irsend.capture.command);
|
||||
EXPECT_EQ(
|
||||
"Power Toggle: Off, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 23C",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
|
||||
// Resend it as a synthetic to see if it decodes to the same value.
|
||||
irsend.reset();
|
||||
@@ -210,7 +220,7 @@ TEST(TestUtils, Housekeeping) {
|
||||
ASSERT_EQ("AIRWELL", typeToString(decode_type_t::AIRWELL));
|
||||
ASSERT_EQ(decode_type_t::AIRWELL, strToDecodeType("AIRWELL"));
|
||||
ASSERT_FALSE(hasACState(decode_type_t::AIRWELL));
|
||||
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::AIRWELL));
|
||||
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::AIRWELL));
|
||||
ASSERT_EQ(kAirwellBits, IRsend::defaultBits(decode_type_t::AIRWELL));
|
||||
ASSERT_EQ(kAirwellMinRepeats, IRsend::minRepeats(decode_type_t::AIRWELL));
|
||||
}
|
||||
@@ -250,4 +260,142 @@ TEST(TestDecodeAirwell, RealExample3) {
|
||||
EXPECT_EQ(0x60080002, irsend.capture.value);
|
||||
EXPECT_EQ(0x0, irsend.capture.address);
|
||||
EXPECT_EQ(0x0, irsend.capture.command);
|
||||
EXPECT_EQ(
|
||||
"Power Toggle: Off, Mode: 1 (Cool), Fan: 2 (High), Temp: 16C",
|
||||
IRAcUtils::resultAcToString(&irsend.capture));
|
||||
}
|
||||
|
||||
// Tests for IRAirwellAc class.
|
||||
|
||||
TEST(TestAirwellAcClass, PowerToggle) {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setPowerToggle(true);
|
||||
EXPECT_TRUE(ac.getPowerToggle());
|
||||
ac.setPowerToggle(false);
|
||||
EXPECT_FALSE(ac.getPowerToggle());
|
||||
ac.setPowerToggle(true);
|
||||
EXPECT_TRUE(ac.getPowerToggle());
|
||||
}
|
||||
|
||||
TEST(TestAirwellAcClass, Temperature) {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setTemp(0);
|
||||
EXPECT_EQ(kAirwellMinTemp, ac.getTemp());
|
||||
|
||||
ac.setTemp(255);
|
||||
EXPECT_EQ(kAirwellMaxTemp, ac.getTemp());
|
||||
|
||||
ac.setTemp(kAirwellMinTemp);
|
||||
EXPECT_EQ(kAirwellMinTemp, ac.getTemp());
|
||||
|
||||
ac.setTemp(kAirwellMaxTemp);
|
||||
EXPECT_EQ(kAirwellMaxTemp, ac.getTemp());
|
||||
|
||||
ac.setTemp(kAirwellMinTemp - 1);
|
||||
EXPECT_EQ(kAirwellMinTemp, ac.getTemp());
|
||||
|
||||
ac.setTemp(kAirwellMaxTemp + 1);
|
||||
EXPECT_EQ(kAirwellMaxTemp, ac.getTemp());
|
||||
|
||||
ac.setTemp(17);
|
||||
EXPECT_EQ(17, ac.getTemp());
|
||||
|
||||
ac.setTemp(21);
|
||||
EXPECT_EQ(21, ac.getTemp());
|
||||
|
||||
ac.setTemp(25);
|
||||
EXPECT_EQ(25, ac.getTemp());
|
||||
|
||||
ac.setTemp(29);
|
||||
EXPECT_EQ(29, ac.getTemp());
|
||||
}
|
||||
|
||||
TEST(TestAirwellAcClass, OperatingMode) {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
ac.begin();
|
||||
|
||||
ac.setMode(kAirwellAuto);
|
||||
EXPECT_EQ(kAirwellAuto, ac.getMode());
|
||||
|
||||
ac.setMode(kAirwellCool);
|
||||
EXPECT_EQ(kAirwellCool, ac.getMode());
|
||||
|
||||
ac.setMode(kAirwellHeat);
|
||||
EXPECT_EQ(kAirwellHeat, ac.getMode());
|
||||
|
||||
ac.setMode(kAirwellDry);
|
||||
EXPECT_EQ(kAirwellDry, ac.getMode());
|
||||
|
||||
ac.setMode(kAirwellFan);
|
||||
EXPECT_EQ(kAirwellFan, ac.getMode());
|
||||
|
||||
ac.setMode(kAirwellFan + 1);
|
||||
EXPECT_EQ(kAirwellAuto, ac.getMode());
|
||||
|
||||
ac.setMode(255);
|
||||
EXPECT_EQ(kAirwellAuto, ac.getMode());
|
||||
}
|
||||
|
||||
TEST(TestAirwellAcClass, FanSpeed) {
|
||||
IRAirwellAc ac(0);
|
||||
ac.begin();
|
||||
|
||||
ac.setFan(0);
|
||||
EXPECT_EQ(kAirwellFanLow, ac.getFan());
|
||||
|
||||
ac.setFan(255);
|
||||
EXPECT_EQ(kAirwellFanAuto, ac.getFan());
|
||||
|
||||
ac.setFan(kAirwellFanHigh);
|
||||
EXPECT_EQ(kAirwellFanHigh, ac.getFan());
|
||||
|
||||
ac.setFan(kAirwellFanHigh + 2);
|
||||
EXPECT_EQ(kAirwellFanAuto, ac.getFan());
|
||||
|
||||
ac.setFan(kAirwellFanHigh - 1);
|
||||
EXPECT_EQ(kAirwellFanHigh - 1, ac.getFan());
|
||||
|
||||
ac.setFan(1);
|
||||
EXPECT_EQ(1, ac.getFan());
|
||||
|
||||
ac.setFan(1);
|
||||
EXPECT_EQ(1, ac.getFan());
|
||||
|
||||
ac.setFan(3);
|
||||
EXPECT_EQ(3, ac.getFan());
|
||||
}
|
||||
|
||||
// Test human readable output.
|
||||
TEST(TestAirwellAcClass, HumanReadable) {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
EXPECT_EQ(
|
||||
"Power Toggle: Off, Mode: 5 (Fan), Fan: 0 (Low), Temp: 25C",
|
||||
ac.toString());
|
||||
ac.setPowerToggle(true);
|
||||
ac.setMode(kAirwellHeat);
|
||||
ac.setTemp(30);
|
||||
ac.setFan(kAirwellFanAuto);
|
||||
EXPECT_EQ(
|
||||
"Power Toggle: On, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 30C",
|
||||
ac.toString());
|
||||
}
|
||||
|
||||
TEST(TestAirwellAcClass, ReconstructKnownState) {
|
||||
IRAirwellAc ac(kGpioUnused);
|
||||
const uint64_t expected = 0x240380002;
|
||||
ac.begin();
|
||||
ac.stateReset();
|
||||
ASSERT_NE(expected, ac.getRaw());
|
||||
ac.setPowerToggle(true);
|
||||
ac.setMode(kAirwellCool);
|
||||
ac.setTemp(22);
|
||||
ac.setFan(kAirwellFanLow);
|
||||
EXPECT_EQ(expected, ac.getRaw());
|
||||
EXPECT_EQ(
|
||||
"Power Toggle: On, Mode: 1 (Cool), Fan: 0 (Low), Temp: 22C",
|
||||
ac.toString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user