From 41ed1af7c1dbe489f58bf1576e330ae40b6f07df Mon Sep 17 00:00:00 2001 From: Takeshi Shimizu Date: Wed, 30 Aug 2023 23:34:29 +0900 Subject: [PATCH 1/2] Add Fujitsu 264 bit AC protocol --- src/IRac.cpp | 10207 +++++++++++++++++++------------------ src/IRac.h | 1172 ++--- src/IRrecv.cpp | 4168 +++++++-------- src/IRrecv.h | 1782 +++---- src/IRremoteESP8266.h | 3063 +++++------ src/IRsend.cpp | 2889 +++++------ src/IRsend.h | 1847 +++---- src/IRtext.cpp | 1124 ++-- src/IRutils.cpp | 2877 +++++------ src/ir_Fujitsu.cpp | 3192 ++++++++---- src/ir_Fujitsu.h | 810 ++- src/locale/defaults.h | 2299 ++++----- test/ir_Fujitsu_test.cpp | 3668 +++++++------ 13 files changed, 20640 insertions(+), 18458 deletions(-) diff --git a/src/IRac.cpp b/src/IRac.cpp index 67c218c1e..3ce3cb5e1 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -1,5063 +1,5144 @@ -// Copyright 2019 David Conran - -// Provide a universal/standard interface for sending A/C nessages. -// It does not provide complete and maximum granular control but tries -// to offer most common functionality across all supported devices. - -#include "IRac.h" -#ifndef UNIT_TEST -#include -#endif -#include -#ifndef ARDUINO -#include -#endif -#include -#if __cplusplus >= 201103L && defined(_GLIBCXX_USE_C99_MATH_TR1) - using std::roundf; -#else - using ::roundf; -#endif -#include "IRsend.h" -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" -#include "ir_Airton.h" -#include "ir_Airwell.h" -#include "ir_Amcor.h" -#include "ir_Argo.h" -#include "ir_Bosch.h" -#include "ir_Carrier.h" -#include "ir_Coolix.h" -#include "ir_Corona.h" -#include "ir_Daikin.h" -#include "ir_Ecoclim.h" -#include "ir_Electra.h" -#include "ir_Fujitsu.h" -#include "ir_Haier.h" -#include "ir_Hitachi.h" -#include "ir_Kelon.h" -#include "ir_Kelvinator.h" -#include "ir_LG.h" -#include "ir_Midea.h" -#include "ir_Mitsubishi.h" -#include "ir_MitsubishiHeavy.h" -#include "ir_Neoclima.h" -#include "ir_Panasonic.h" -#include "ir_Rhoss.h" -#include "ir_Samsung.h" -#include "ir_Sanyo.h" -#include "ir_Sharp.h" -#include "ir_Tcl.h" -#include "ir_Technibel.h" -#include "ir_Teco.h" -#include "ir_Toshiba.h" -#include "ir_Transcold.h" -#include "ir_Trotec.h" -#include "ir_Truma.h" -#include "ir_Vestel.h" -#include "ir_Voltas.h" -#include "ir_Whirlpool.h" - -// On the ESP8266 platform we need to use a special version of string handling -// functions to handle the strings stored in the flash address space. -#ifndef STRCASECMP -#if defined(ESP8266) -#define STRCASECMP(LHS, RHS) \ - strcasecmp_P(LHS, reinterpret_cast(RHS)) -#else // ESP8266 -#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS) -#endif // ESP8266 -#endif // STRCASECMP - -#ifndef UNIT_TEST -#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) -#else -/* NOTE: THIS IS NOT A DOXYGEN COMMENT (would require ENABLE_PREPROCESSING-YES) -/// If compiling for UT *and* a test receiver @c IRrecv is provided via the -/// @c _utReceived param, this injects an "output" gadget @c _lastDecodeResults -/// into the @c IRAc::sendAc method, so that the UT code may parse the "sent" -/// value and drive further assertions -/// -/// @note The @c decode_results "returned" is a shallow copy (empty rawbuf), -/// mostly b/c the class does not have a custom/deep copy c-tor -/// and defining it would be an overkill for this purpose -/// @note For future maintainers: If @c IRAc class is ever refactored to use -/// polymorphism (static or dynamic)... this macro should be removed -/// and replaced with proper GMock injection. -*/ -#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) \ - { \ - if (_utReceiver) { \ - _lastDecodeResults = nullptr; \ - (ac)._irsend.makeDecodeResult(); \ - if (_utReceiver->decode(&(ac)._irsend.capture)) { \ - _lastDecodeResults = std::unique_ptr( \ - new decode_results((ac)._irsend.capture)); \ - _lastDecodeResults->rawbuf = nullptr; \ - } \ - } \ - } -#endif // UNIT_TEST - -/// Class constructor -/// @param[in] pin Gpio pin to use when transmitting IR messages. -/// @param[in] inverted true, gpio output defaults to high. false, to low. -/// @param[in] use_modulation true means use frequency modulation. false, don't. -IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { - _pin = pin; - _inverted = inverted; - _modulation = use_modulation; - this->markAsSent(); -} - -/// Initialise the given state with the supplied settings. -/// @param[out] state A Ptr to where the settings will be stored. -/// @param[in] vendor The vendor/protocol type. -/// @param[in] model The A/C model if applicable. -/// @param[in] power The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. -/// Others it may be the time to enter/exit sleep mode. -/// i.e. Time in Nr. of mins since midnight. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::initState(stdAc::state_t *state, - const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, - const float degrees, const bool celsius, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep, - const int16_t clock) { - state->protocol = vendor; - state->model = model; - state->power = power; - state->mode = mode; - state->degrees = degrees; - state->celsius = celsius; - state->fanspeed = fan; - state->swingv = swingv; - state->swingh = swingh; - state->quiet = quiet; - state->turbo = turbo; - state->econo = econo; - state->light = light; - state->filter = filter; - state->clean = clean; - state->beep = beep; - state->sleep = sleep; - state->clock = clock; -} - -/// Initialise the given state with the supplied settings. -/// @param[out] state A Ptr to where the settings will be stored. -/// @note Sets all the parameters to reasonable base/automatic defaults. -void IRac::initState(stdAc::state_t *state) { - stdAc::state_t def; - *state = def; -} - -/// Get the current internal A/C climate state. -/// @return A Ptr to a state containing the current (to be sent) settings. -stdAc::state_t IRac::getState(void) { return next; } - -/// Get the previous internal A/C climate state that should have already been -/// sent to the device. i.e. What the A/C unit should already be set to. -/// @return A Ptr to a state containing the previously sent settings. -stdAc::state_t IRac::getStatePrev(void) { return _prev; } - -/// Is the given protocol supported by the IRac class? -/// @param[in] protocol The vendor/protocol type. -/// @return true if the protocol is supported by this class, otherwise false. -bool IRac::isProtocolSupported(const decode_type_t protocol) { - switch (protocol) { -#if SEND_AIRTON - case decode_type_t::AIRTON: -#endif // SEND_AIRTON -#if SEND_AIRWELL - case decode_type_t::AIRWELL: -#endif // SEND_AIRWELL -#if SEND_AMCOR - case decode_type_t::AMCOR: -#endif -#if SEND_ARGO - case decode_type_t::ARGO: -#endif -#if SEND_BOSCH144 - case decode_type_t::BOSCH144: -#endif -#if SEND_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - case decode_type_t::COOLIX: -#endif -#if SEND_CORONA_AC - case decode_type_t::CORONA_AC: -#endif -#if SEND_DAIKIN - case decode_type_t::DAIKIN: -#endif -#if SEND_DAIKIN128 - case decode_type_t::DAIKIN128: -#endif -#if SEND_DAIKIN152 - case decode_type_t::DAIKIN152: -#endif -#if SEND_DAIKIN160 - case decode_type_t::DAIKIN160: -#endif -#if SEND_DAIKIN176 - case decode_type_t::DAIKIN176: -#endif -#if SEND_DAIKIN2 - case decode_type_t::DAIKIN2: -#endif -#if SEND_DAIKIN216 - case decode_type_t::DAIKIN216: -#endif -#if SEND_DAIKIN64 - case decode_type_t::DAIKIN64: -#endif -#if SEND_DELONGHI_AC - case decode_type_t::DELONGHI_AC: -#endif -#if SEND_ECOCLIM - case decode_type_t::ECOCLIM: -#endif -#if SEND_ELECTRA_AC - case decode_type_t::ELECTRA_AC: -#endif -#if SEND_FUJITSU_AC - case decode_type_t::FUJITSU_AC: -#endif -#if SEND_GOODWEATHER - case decode_type_t::GOODWEATHER: -#endif -#if SEND_GREE - case decode_type_t::GREE: -#endif -#if SEND_HAIER_AC - case decode_type_t::HAIER_AC: -#endif -#if SEND_HAIER_AC160 - case decode_type_t::HAIER_AC160: -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - case decode_type_t::HAIER_AC176: -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: -#endif -#if SEND_HITACHI_AC - case decode_type_t::HITACHI_AC: -#endif -#if SEND_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: -#endif -#if SEND_HITACHI_AC264 - case decode_type_t::HITACHI_AC264: -#endif -#if SEND_HITACHI_AC296 - case decode_type_t::HITACHI_AC296: -#endif -#if SEND_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: -#endif -#if SEND_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: -#endif -#if SEND_KELON - case decode_type_t::KELON: -#endif -#if SEND_KELVINATOR - case decode_type_t::KELVINATOR: -#endif -#if SEND_LG - case decode_type_t::LG: - case decode_type_t::LG2: -#endif -#if SEND_MIDEA - case decode_type_t::MIDEA: -#endif // SEND_MIDEA -#if SEND_MIRAGE - case decode_type_t::MIRAGE: -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: -#endif -#if SEND_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: -#endif -#if SEND_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: -#endif -#if SEND_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: - case decode_type_t::MITSUBISHI_HEAVY_152: -#endif -#if SEND_NEOCLIMA - case decode_type_t::NEOCLIMA: -#endif -#if SEND_PANASONIC_AC - case decode_type_t::PANASONIC_AC: -#endif -#if SEND_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: -#endif -#if SEND_RHOSS - case decode_type_t::RHOSS: -#endif -#if SEND_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: -#endif -#if SEND_SANYO_AC - case decode_type_t::SANYO_AC: -#endif -#if SEND_SANYO_AC88 - case decode_type_t::SANYO_AC88: -#endif -#if SEND_SHARP_AC - case decode_type_t::SHARP_AC: -#endif -#if SEND_TCL112AC - case decode_type_t::TCL112AC: -#endif -#if SEND_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: -#endif -#if SEND_TECO - case decode_type_t::TECO: -#endif -#if SEND_TEKNOPOINT - case decode_type_t::TEKNOPOINT: -#endif // SEND_TEKNOPOINT -#if SEND_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: -#endif -#if SEND_TRANSCOLD - case decode_type_t::TRANSCOLD: -#endif -#if SEND_TROTEC - case decode_type_t::TROTEC: -#endif -#if SEND_TROTEC_3550 - case decode_type_t::TROTEC_3550: -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - case decode_type_t::TRUMA: -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case decode_type_t::VESTEL_AC: -#endif -#if SEND_VOLTAS - case decode_type_t::VOLTAS: -#endif -#if SEND_YORK - case decode_type_t::YORK: -#endif -#if SEND_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: -#endif - return true; - default: - return false; - } -} - -#if SEND_AIRTON -/// Send an Airton 56-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRAirtonAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/health/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::airton(IRAirtonAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool light, const bool econo, const bool filter, - const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Quiet setting available. - ac->setLight(light); - ac->setHealth(filter); - ac->setTurbo(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Convert to a boolean. - ac->send(); -} -#endif // SEND_AIRTON - -#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. -/// @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::amcor(IRAmcorAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setPower(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_AMCOR - -#if SEND_ARGO -/// Send an Argo A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRArgoAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::argo(IRArgoAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, - const bool turbo, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(static_cast(roundf(degrees))); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } - ac->setiFeel(iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setFlap(ac->convertSwingV(swingv)); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - ac->setMax(turbo); - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - ac->setNight(sleep >= 0); // Convert to a boolean. - ac->send(); -} - -/// Send an Argo A/C WREM-3 AC **control** message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The set temperature setting in degrees Celsius. -/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) -/// The overflow is *not* checked, though. -/// @note The value is rounded to nearest integer, rounding halfway cases -/// away from zero. E.g. 1.5 [C] becomes 2 [C]. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] night Enable night mode (raises temp by +1*C after 1h). -/// @param[in] econo Enable eco mode (limits power consumed). -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Enable filter mode -/// @param[in] light Enable device display/LEDs -void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on, - const stdAc::opmode_t mode, const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool iFeel, - const bool night, const bool econo, const bool turbo, const bool filter, - const bool light) { - ac->begin(); - ac->setMessageType(argoIrMessageType_t::AC_CONTROL); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } - ac->setiFeel(iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setFlap(ac->convertSwingV(swingv)); - ac->setNight(night); - ac->setEco(econo); - ac->setMax(turbo); - ac->setFilter(filter); - ac->setLight(light); - // No Clean setting available. - // No Beep setting available - always beeps in this mode :) - ac->send(); -} - -/// Send an Argo A/C WREM-3 iFeel (room temp) silent (no beep) report. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] sensorTemp The room (iFeel) temperature setting -/// in degrees Celsius. -/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) -/// The overflow is *not* checked, though. -/// @note The value is rounded to nearest integer, rounding halfway cases -/// away from zero. E.g. 1.5 [C] becomes 2 [C]. -void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) { - ac->begin(); - ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT); - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - ac->send(); -} - -/// Send an Argo A/C WREM-3 Config command. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] param The parameter ID. -/// @param[in] value The parameter value. -/// @param[in] safe If true, will only allow setting the below parameters -/// in order to avoid accidentally setting a restricted -/// vendor-specific param and breaking the A/C device -/// @note Known parameters (P, where xx is the @c param) -/// P05 - Temperature Scale (0-Celsius, 1-Fahrenheit) -/// P06 - Transmission channel (0..3) -/// P12 - ECO mode power input limit (30..99, default: 75) -void IRac::argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, - const uint8_t value, bool safe /*= true*/) { - if (safe) { - switch (param) { - case 5: // temp. scale (note this is likely excess as not transmitted) - if (value > 1) { return; /* invalid */ } - break; - case 6: // channel (note this is likely excess as not transmitted) - if (value > 3) { return; /* invalid */ } - break; - case 12: // eco power limit - if (value < 30 || value > 99) { return; /* invalid */ } - break; - default: - return; /* invalid */ - } - } - ac->begin(); - ac->setMessageType(argoIrMessageType_t::CONFIG_PARAM_SET); - ac->setConfigEntry(param, value); - ac->send(); -} - -/// Send an Argo A/C WREM-3 Delay timer command. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] on Whether the unit is currently on. The timer, upon elapse -/// will toggle this state -/// @param[in] currentTime currentTime in minutes, starting from 00:00 -/// @note For timer mode, this value is not really used much so can be zero. -/// @param[in] delayMinutes Number of minutes after which the @c on state should -/// be toggled -/// @note Schedule timers are not exposed via this interface -void IRac::argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, - const uint16_t currentTime, const uint16_t delayMinutes) { - ac->begin(); - ac->setMessageType(argoIrMessageType_t::TIMER_COMMAND); - ac->setPower(on); - ac->setTimerType(argoTimerType_t::DELAY_TIMER); - ac->setCurrentTimeMinutes(currentTime); - // Note: Day of week is not set (no need) - ac->setDelayTimerMinutes(delayMinutes); - ac->send(); -} -#endif // SEND_ARGO - -#if SEND_BOSCH144 -/// Send a Bosch144 A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRBosch144AC 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. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @note -1 is Off, >= 0 is on. -void IRac::bosch144(IRBosch144AC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const bool quiet) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setMode(ac->convertMode(mode)); - ac->setQuiet(quiet); - ac->send(); // Send the state, which will also power on the unit. - // The following are all options/settings that create their own special - // messages. Often they only make sense to be sent after the unit is turned - // on. For instance, assuming a person wants to have the a/c on and in turbo - // mode. If we send the turbo message, it is ignored if the unit is off. - // Hence we send the special mode/setting messages after a normal message - // which will turn on the device. - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Sleep setting available. -} -#endif // SEND_BOSCH144 - -#if SEND_CARRIER_AC64 -/// Send a Carrier 64-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRCarrierAc64 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::carrier64(IRCarrierAc64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV((int8_t)swingv >= 0); - // 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. - ac->setSleep(sleep >= 0); // Convert to a boolean. - ac->send(); -} -#endif // SEND_CARRIER_AC64 - -#if SEND_COOLIX -/// Send a Coolix A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRCoolixAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::coolix(IRCoolixAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool light, - const bool clean, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Quiet setting available. - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } else { - ac->clearSensorTemp(); - } - ac->setZoneFollow(iFeel); - ac->send(); // Send the state, which will also power on the unit. - // The following are all options/settings that create their own special - // messages. Often they only make sense to be sent after the unit is turned - // on. For instance, assuming a person wants to have the a/c on and in turbo - // mode. If we send the turbo message, it is ignored if the unit is off. - // Hence we send the special mode/setting messages after a normal message - // which will turn on the device. - if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { - // Swing has a special command that needs to be sent independently. - ac->setSwing(); - ac->send(); - } - if (turbo) { - // Turbo has a special command that needs to be sent independently. - ac->setTurbo(); - ac->send(); - } - if (sleep >= 0) { - // Sleep has a special command that needs to be sent independently. - ac->setSleep(); - ac->send(); - } - if (light) { - // Light has a special command that needs to be sent independently. - ac->setLed(); - ac->send(); - } - if (clean) { - // Clean has a special command that needs to be sent independently. - ac->setClean(); - ac->send(); - } -} -#endif // SEND_COOLIX - -#if SEND_CORONA_AC -/// Send a Corona A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRCoronaAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] econo Run the device in economical mode. -void IRac::corona(IRCoronaAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool econo) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - // No Turbo setting available. - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - ac->send(); -} -#endif // SEND_CARRIER_AC64 - -#if SEND_DAIKIN -/// Send a Daikin A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikinESP 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::daikin(IRDaikinESP *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - // No Light setting available. - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setMold(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_DAIKIN - -#if SEND_DAIKIN128 -/// Send a Daikin 128-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin128 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::daikin128(IRDaikin128 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool light, - const bool econo, const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - // No Horizontal Swing setting avaliable. - ac->setQuiet(quiet); - ac->setLightToggle(light ? kDaikin128BitWall : 0); - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep > 0); - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_DAIKIN128 - -#if SEND_DAIKIN152 -/// Send a Daikin 152-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin152 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -void IRac::daikin152(IRDaikin152 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool econo) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV((int8_t)swingv >= 0); - // No Horizontal Swing setting avaliable. - ac->setQuiet(quiet); - // No Light setting available. - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_DAIKIN152 - -#if SEND_DAIKIN160 -/// Send a Daikin 160-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin160 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. -/// @param[in] swingv The vertical swing setting. -void IRac::daikin160(IRDaikin160 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->send(); -} -#endif // SEND_DAIKIN160 - -#if SEND_DAIKIN176 -/// Send a Daikin 176-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin176 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. -/// @param[in] swingh The horizontal swing setting. -void IRac::daikin176(IRDaikin176 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->send(); -} -#endif // SEND_DAIKIN176 - -#if SEND_DAIKIN2 -/// Send a Daikin2 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin2 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::daikin2(IRDaikin2 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter, const bool clean, - const bool beep, const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setLight(light ? 1 : 3); // On/High is 1, Off is 3. - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setPurify(filter); - ac->setMold(clean); - ac->setClean(true); // Hardwire auto clean to be on per request (@sheppy99) - ac->setBeep(beep ? 2 : 3); // On/Loud is 2, Off is 3. - if (sleep > 0) ac->enableSleepTimer(sleep); - if (clock >= 0) ac->setCurrentTime(clock); - ac->send(); -} -#endif // SEND_DAIKIN2 - -#if SEND_DAIKIN216 -/// Send a Daikin 216-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin216 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -void IRac::daikin216(IRDaikin216 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setPowerful(turbo); - ac->send(); -} -#endif // SEND_DAIKIN216 - -#if SEND_DAIKIN64 -/// Send a Daikin 64-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin64 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::daikin64(IRDaikin64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, - const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setTurbo(turbo); - ac->setQuiet(quiet); - ac->setSleep(sleep >= 0); - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_DAIKIN64 - -#if SEND_DELONGHI_AC -/// Send a Delonghi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDelonghiAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::delonghiac(IRDelonghiAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const bool turbo, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setBoost(turbo); - ac->setSleep(sleep >= 0); - ac->send(); -} -#endif // SEND_DELONGHI_AC - -#if SEND_ECOCLIM -/// Send an EcoClim A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IREcoclimAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::ecoclim(IREcoclimAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const int16_t sleep, - const int16_t clock) { - ac->begin(); - ac->setPower(on); - uint8_t new_mode; - if (sleep >= 0) // EcoClim has a descrete Sleep operation mode, not a setting - new_mode = kEcoclimSleep; // Override the requested operating mode. - else - new_mode = ac->convertMode(mode); // Not Sleep, so use the supplied mode. - ac->setMode(new_mode); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } else { - ac->setSensorTemp(degrees); //< Set to the desired temp - // until we can disable. - } - // No SwingV setting available - // No SwingH setting available - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Econo setting available. - // No Filter setting available. - // No Clean setting available - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_ECOCLIM - -#if SEND_ELECTRA_AC -/// Send an Electra A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRElectraAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] lighttoggle Should we toggle the LED/Display? -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::electra(IRElectraAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, const bool iFeel, - const bool turbo, const bool lighttoggle, const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setLightToggle(lighttoggle); - // No Econo setting available. - // No Filter setting available. - ac->setClean(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->setIFeel(iFeel); - ac->send(); -} -#endif // SEND_ELECTRA_AC - -#if SEND_FUJITSU_AC -/// Send a Fujitsu A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRFujitsuAC object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. -void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool filter, const bool clean, const int16_t sleep) { - ac->begin(); - ac->setModel(model); - if (on) { - // Do all special messages (except "Off") first, - // These need to be sent separately. - switch (ac->getModel()) { - // Some functions are only available on some models. - case fujitsu_ac_remote_model_t::ARREB1E: - if (turbo) { - ac->setCmd(kFujitsuAcCmdPowerful); - // Powerful is a separate command. - ac->send(); - } - if (econo) { - ac->setCmd(kFujitsuAcCmdEcono); - // Econo is a separate command. - ac->send(); - } - break; - default: - {}; - } - // Normal operation. - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFanSpeed(ac->convertFan(fan)); - uint8_t swing = kFujitsuAcSwingOff; - if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; - if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; - ac->setSwing(swing); - if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); - // No Light setting available. - ac->setFilter(filter); - ac->setClean(clean); - // No Beep setting available. - ac->setSleepTimer(sleep > 0 ? sleep : 0); - // No Sleep setting available. - // No Clock setting available. - ac->on(); // Ref: Issue #860 - } else { - // Off is special case/message. We don't need to send other messages. - ac->off(); - } - ac->send(); -} -#endif // SEND_FUJITSU_AC - -#if SEND_GOODWEATHER -/// Send a Goodweather A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGoodweatherAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::goodweather(IRGoodweatherAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff - : kGoodweatherSwingSlow); - ac->setTurbo(turbo); - ac->setLight(light); - // No Clean setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Horizontal Swing setting available. - // No Econo setting available. - // No Filter setting available. - // No Beep setting available. - // No Quiet setting available. - // No Clock setting available. - ac->setPower(on); - ac->send(); -} -#endif // SEND_GOODWEATHER - -#if SEND_GREE -/// Send a Gree A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGreeAC object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Toggle the device's economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool econo, - const bool light, const bool clean, const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. - ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setIFeel(iFeel); - ac->setLight(light); - ac->setTurbo(turbo); - ac->setEcono(econo); - ac->setXFan(clean); - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Econo setting available. - // No Filter setting available. - // No Beep setting available. - // No Quiet setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_GREE - -#if SEND_HAIER_AC -/// Send a Haier A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGreeAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::haier(IRHaierAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool filter, const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - if (clock >= 0) ac->setCurrTime(clock); - if (on) - ac->setCommand(kHaierAcCmdOn); - else - ac->setCommand(kHaierAcCmdOff); - ac->send(); -} -#endif // SEND_HAIER_AC - -#if SEND_HAIER_AC160 -/// Send a Haier 160 bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierAC160 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the clean mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] prevlight Previous LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haier160(IRHaierAC160 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool quiet, const bool filter, - const bool clean, const bool light, const bool prevlight, - const int16_t sleep) { - ac->begin(); - // No Model setting available. - ac->setMode(ac->convertMode(mode)); - ac->setUseFahrenheit(!celsius); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setHealth(filter); - ac->setClean(clean); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - // Light needs to be sent last as the "button" value seems to control it. - ac->setLightToggle(light ^ prevlight); - ac->send(); -} -#endif // SEND_HAIER_AC160 - -#if SEND_HAIER_AC176 -/// Send a Haier 176 bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haier176(IRHaierAC176 *ac, const haier_ac176_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool quiet, const bool filter, - const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setUseFahrenheit(!celsius); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - ac->send(); -} -#endif // SEND_HAIER_AC176 - -#if SEND_HAIER_AC_YRW02 -/// Send a Haier YRWO2 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haierYrwo2(IRHaierACYRW02 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool quiet, const bool filter, - const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setUseFahrenheit(!celsius); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - ac->send(); -} -#endif // SEND_HAIER_AC_YRW02 - -#if SEND_HITACHI_AC -/// Send a Hitachi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::hitachi(IRHitachiAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC - -#if SEND_HITACHI_AC1 -/// Send a Hitachi1 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc1 object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] power_toggle The power toggle 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] swing_toggle The swing_toggle setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @note The sleep mode used is the "Sleep 2" setting. -void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, - const bool on, const bool power_toggle, - const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool swing_toggle, const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setPowerToggle(power_toggle); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - ac->setSwingToggle(swing_toggle); - ac->setSleep((sleep >= 0) ? kHitachiAc1Sleep2 : kHitachiAc1SleepOff); - // No Sleep setting available. - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC1 - -#if SEND_HITACHI_AC264 -/// Send a Hitachi 264-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc264 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::hitachi264(IRHitachiAc264 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setPower(on); - // No Swing(V) setting available. - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC264 - -#if SEND_HITACHI_AC296 -/// Send a Hitachi 296-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc296 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::hitachi296(IRHitachiAc296 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setPower(on); - // No Swing(V) setting available. - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC296 - -#if SEND_HITACHI_AC344 -/// Send a Hitachi 344-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc344 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::hitachi344(IRHitachiAc344 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setPower(on); - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - - // SwingVToggle is special. Needs to be last method called. - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - ac->send(); -} -#endif // SEND_HITACHI_AC344 - -#if SEND_HITACHI_AC424 -/// Send a Hitachi 424-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc424 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. -/// @param[in] swingv The vertical swing setting. -void IRac::hitachi424(IRHitachiAc424 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setPower(on); - // SwingVToggle is special. Needs to be last method called. - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC424 - -#if SEND_KELON -/// Send a Kelon A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRKelonAc object to use. -/// @param[in] togglePower Whether to toggle the unit's power -/// @param[in] mode The operation mode setting. -/// @param[in] dryGrade The dehumidification intensity grade -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] toggleSwing Whether to toggle the swing setting -/// @param[in] superCool Run the device in Super cooling mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on -void IRac::kelon(IRKelonAc *ac, const bool togglePower, - const stdAc::opmode_t mode, const int8_t dryGrade, - const float degrees, const stdAc::fanspeed_t fan, - const bool toggleSwing, const bool superCool, - const int16_t sleep) { - ac->begin(); - ac->setMode(IRKelonAc::convertMode(mode)); - ac->setFan(IRKelonAc::convertFan(fan)); - ac->setTemp(static_cast(degrees)); - ac->setSleep(sleep >= 0); - ac->setSupercool(superCool); - ac->setDryGrade(dryGrade); - - ac->setTogglePower(togglePower); - ac->setToggleSwingVertical(toggleSwing); - - ac->send(); -} -#endif // SEND_KELON - -#if SEND_KELVINATOR -/// Send a Kelvinator A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRKelvinatorAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc -void IRac::kelvinator(IRKelvinatorAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool filter, const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan((uint8_t)fan); // No conversion needed. - ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. - ac->convertSwingV(swingv)); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setLight(light); - ac->setIonFilter(filter); - ac->setXFan(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_KELVINATOR - -#if SEND_LG -/// Send a LG A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRLgAc object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingv_prev The previous vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] light Turn on the LED/Display mode. -void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, - const stdAc::swingh_t swingh, const bool light) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv_prev)); - ac->updateSwingPrev(); - ac->setSwingV(ac->convertSwingV(swingv)); - const uint8_t pos = ac->convertVaneSwingV(swingv); - for (uint8_t vane = 0; vane < kLgAcSwingVMaxVanes; vane++) - ac->setVaneSwingV(vane, pos); - // Toggle the swingv for LG6711A20083V models if we need to. - // i.e. Off to Not-Off, send a toggle. Not-Off to Off, send a toggle. - if ((model == lg_ac_remote_model_t::LG6711A20083V) && - ((swingv == stdAc::swingv_t::kOff) != - (swingv_prev == stdAc::swingv_t::kOff))) - ac->setSwingV(kLgAcSwingVToggle); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_LG - -#if SEND_MIDEA -/// Send a Midea A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMideaAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] sensorTemp The room (iFeel) temperature sensor reading -/// in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] quiet_prev The device's previous quiet/silent mode. -/// @param[in] turbo Toggle the device's turbo/powerful mode. -/// @param[in] econo Toggle the device's economical mode. -/// @param[in] light Toggle the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @note On Danby A/C units, swingv controls the Ion Filter instead. -void IRac::midea(IRMideaAC *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool iFeel, const bool quiet, const bool quiet_prev, - const bool turbo, const bool econo, const bool light, - const bool clean, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setUseCelsius(celsius); - ac->setTemp(degrees, celsius); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(sensorTemp, celsius); - } - ac->setEnableSensorTemp(iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - ac->setQuiet(quiet, quiet_prev); - ac->setTurboToggle(turbo); - ac->setEconoToggle(econo); - ac->setLightToggle(light); - // No Filter setting available. - ac->setCleanToggle(clean); - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MIDEA - -#if SEND_MIRAGE -/// Send a Mirage 120-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. -/// @param[in] state The desired state to send. -void IRac::mirage(IRMirageAc *ac, const stdAc::state_t state) { - ac->begin(); - ac->fromCommon(state); - ac->send(); -} -#endif // SEND_MIRAGE - -#if SEND_MITSUBISHI_AC -/// Send a Mitsubishi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @note Clock can only be set in 10 minute increments. i.e. % 10. -void IRac::mitsubishi(IRMitsubishiAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const int16_t clock) { - ac->begin(); - // Uncomment next line if you *really* need the weekly timer enabled via IRac. - // ac->setWeeklyTimerEnabled(true); // Weekly Timer is disabled by default. - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setVane(ac->convertSwingV(swingv)); - ac->setVaneLeft(ac->convertSwingV(swingv)); - ac->setWideVane(ac->convertSwingH(swingh)); - if (quiet) ac->setFan(kMitsubishiAcFanSilent); - ac->setISave10C(false); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. - ac->send(); -} -#endif // SEND_MITSUBISHI_AC - -#if SEND_MITSUBISHI112 -/// Send a Mitsubishi 112-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishi112 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -void IRac::mitsubishi112(IRMitsubishi112 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - // FIXME - Econo - // ac->setEcono(econo); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHI112 - -#if SEND_MITSUBISHI136 -/// Send a Mitsubishi 136-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishi136 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -void IRac::mitsubishi136(IRMitsubishi136 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool quiet) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - ac->setQuiet(quiet); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHI136 - -#if SEND_MITSUBISHIHEAVY -/// Send a Mitsubishi Heavy 88-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy88Ac 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool econo, - const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - // No Filter setting available. - ac->setClean(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} - -/// Send a Mitsubishi Heavy 152-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy152Ac 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, - const bool econo, const bool filter, - const bool clean, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setSilent(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - ac->setClean(clean); - ac->setFilter(filter); - // No Beep setting available. - ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHIHEAVY - -#if SEND_NEOCLIMA -/// Send a Neoclima A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRNeoclimaAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::neoclima(IRNeoclimaAc *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const bool filter, const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setLight(light); - ac->setEcono(econo); - ac->setIon(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->setPower(on); - ac->send(); -} -#endif // SEND_NEOCLIMA - -#if SEND_PANASONIC_AC -/// Send a Panasonic A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRPanasonicAc object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool filter, - const int16_t clock) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setPowerful(turbo); - ac->setIon(filter); - // No Light setting available. - // No Econo setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_PANASONIC_AC - -#if SEND_PANASONIC_AC32 -/// Send a Panasonic A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRPanasonicAc32 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::panasonic32(IRPanasonicAc32 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - // No Filter setting available. - // No Light setting available. - // No Econo setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_PANASONIC_AC32 - -#if SEND_SAMSUNG_AC -/// Send a Samsung A/C message with the supplied settings. -/// @note Multiple IR messages may be generated & sent. -/// @param[in, out] ac A Ptr to an IRSamsungAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Toggle the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Toggle beep setting for receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. -/// @param[in] prevpower The power setting from the previous A/C state. -/// @param[in] prevsleep Nr. of minutes for sleep from the previous A/C state. -/// @param[in] forceextended Do we force sending the special extended message? -void IRac::samsung(IRSamsungAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, - const bool filter, const bool clean, - const bool beep, const int16_t sleep, - const bool prevpower, const int16_t prevsleep, - const bool forceextended) { - ac->begin(); - ac->stateReset(forceextended || (sleep != prevsleep), prevpower); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - ac->setQuiet(quiet); - ac->setPowerful(turbo); // FYI, `setEcono(true)` will override this. - ac->setDisplay(light); - ac->setEcono(econo); - ac->setIon(filter); - ac->setClean(clean); // Toggle - ac->setBeep(beep); // Toggle - ac->setSleepTimer((sleep <= 0) ? 0 : sleep); - // No Clock setting available. - // Do setMode() again as it can affect fan speed. - ac->setMode(ac->convertMode(mode)); - ac->send(); -} -#endif // SEND_SAMSUNG_AC - -#if SEND_SANYO_AC -/// Send a Sanyo A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRSanyoAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::sanyo(IRSanyoAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool iFeel, const bool beep, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } else { - ac->setSensorTemp(degrees); // Set the sensor temp to the desired - // (normal) temp. - } - ac->setSensor(!iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Econo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - ac->setBeep(beep); - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_SANYO_AC - -#if SEND_SANYO_AC88 -/// Send a Sanyo 88-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRSanyoAc88 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::sanyo88(IRSanyoAc88 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool filter, const int16_t sleep, - const int16_t clock) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Econo setting available. - // No Light setting available. - ac->setFilter(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_SANYO_AC88 - -#if SEND_SHARP_AC -/// Send a Sharp A/C message with the supplied settings. -/// @note Multiple IR messages may be generated & sent. -/// @param[in, out] ac A Ptr to an IRSharpAc object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] prev_power The power setting from the previous A/C state. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingv_prev The previous vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, - const bool on, const bool prev_power, - const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingv_t swingv_prev, const bool turbo, - const bool light, const bool filter, const bool clean) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan, model)); - if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv)); - // Econo deliberately not used as it cycles through 3 modes uncontrollably. - // ac->setEconoToggle(econo); - ac->setIon(filter); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setLightToggle(light); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - // Do setMode() again as it can affect fan speed and temp. - ac->setMode(ac->convertMode(mode)); - // Clean after mode, as it can affect the mode, temp & fan speed. - if (clean) { - // A/C needs to be off before we can enter clean mode. - ac->setPower(false, prev_power); - ac->send(); - } - ac->setClean(clean); - ac->setPower(on, prev_power); - if (turbo) { - ac->send(); // Send the current state. - // Set up turbo mode as it needs to be sent after everything else. - ac->setTurbo(true); - } - ac->send(); -} -#endif // SEND_SHARP_AC - -#if SEND_TCL112AC -/// Send a TCL 112-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -void IRac::tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setLight(light); - ac->setEcono(econo); - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TCL112AC - -#if SEND_TECHNIBEL_AC -/// Send a Technibel A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTechnibelAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::technibel(IRTechnibelAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TECHNIBEL_AC - -#if SEND_TECO -/// Send a Teco A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTecoAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::teco(IRTecoAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool light, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TECO - -#if SEND_TOSHIBA_AC -/// Send a Toshiba A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRToshibaAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (Pure/ion/pollen/etc) filter mode. -void IRac::toshiba(IRToshibaAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool econo, const bool filter) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // The API has no "step" option, so off is off, anything else is on. - ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff - : kToshibaAcSwingOn); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setEcono(econo); - // No Light setting available. - ac->setFilter(filter); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - // Do this last because Toshiba A/C has an odd quirk with how power off works. - ac->setPower(on); - ac->send(); -} -#endif // SEND_TOSHIBA_AC - -#if SEND_TROTEC -/// Send a Trotec A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrotecESP 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. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::trotec(IRTrotecESP *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setSpeed(ac->convertFan(fan)); - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TROTEC - -#if SEND_TROTEC_3550 -/// Send a Trotec 3550 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -void IRac::trotec3550(IRTrotec3550 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TROTEC_3550 - -#if SEND_TRUMA -/// Send a Truma A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrumaAc 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. -/// @param[in] quiet Run the device quietly if we can. -void IRac::truma(IRTrumaAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const bool quiet) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setQuiet(quiet); // Only available in Cool mode. - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TRUMA - -#if SEND_VESTEL_AC -/// Send a Vestel A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRVestelAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @param[in] sendNormal Do we send a Normal settings message at all? -/// i.e In addition to the clock/time/timer message -void IRac::vestel(IRVestelAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool filter, const int16_t sleep, - const int16_t clock, const bool sendNormal) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setIon(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (sendNormal) ac->send(); // Send the normal message. - if (clock >= 0) { - ac->setTime(clock); - ac->send(); // Setting the clock requires a different "timer" message. - } -} -#endif // SEND_VESTEL_AC - -#if SEND_VOLTAS -/// Send a Voltas A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRVoltas object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::voltas(IRVoltas *ac, - const voltas_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setEcono(econo); - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_VOLTAS - -#if SEND_WHIRLPOOL_AC -/// Send a Whirlpool A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRWhirlpoolAc object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setSuper(turbo); - ac->setLight(light); - // No Filter setting available - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (clock >= 0) ac->setClock(clock); - ac->setPowerToggle(on); - ac->send(); -} -#endif // SEND_WHIRLPOOL_AC - -#if SEND_TRANSCOLD -/// Send a Transcold A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRTranscoldAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @note -1 is Off, >= 0 is on. -void IRac::transcold(IRTranscoldAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Quiet setting available. - if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { - // Swing has a special command that needs to be sent independently. - ac->setSwing(); - ac->send(); - } - - ac->send(); -} -#endif // SEND_TRANSCOLD - -#if SEND_RHOSS -/// Send an Rhoss A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRRhossAc 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. -/// @param[in] swing The swing setting. -void IRac::rhoss(IRRhossAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swing) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setSwing(swing != stdAc::swingv_t::kOff); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // 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_RHOSS - -/// Create a new state base on the provided state that has been suitably fixed. -/// @note This is for use with Home Assistant, which requires mode to be off if -/// the power is off. -/// @param[in] state The state_t structure describing the desired a/c state. -/// @return A stdAc::state_t with the needed settings. -stdAc::state_t IRac::cleanState(const stdAc::state_t state) { - stdAc::state_t result = state; - // A hack for Home Assistant, it appears to need/want an Off opmode. - // So enforce the power is off if the mode is also off. - if (state.mode == stdAc::opmode_t::kOff) result.power = false; - return result; -} - -/// Create a new state base on desired & previous states but handle -/// any state changes for options that need to be toggled. -/// @param[in] desired The state_t structure describing the desired a/c state. -/// @param[in] prev A Ptr to the previous state_t structure. -/// @return A stdAc::state_t with the needed settings. -stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, - const stdAc::state_t *prev) { - stdAc::state_t result = desired; - // If we've been given a previous state AND the it's the same A/C basically. - if (prev != NULL && desired.protocol == prev->protocol && - desired.model == prev->model) { - // Check if we have to handle toggle settings for specific A/C protocols. - switch (desired.protocol) { - case decode_type_t::COOLIX: - case decode_type_t::TRANSCOLD: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - result.turbo = desired.turbo ^ prev->turbo; - result.light = desired.light ^ prev->light; - result.clean = desired.clean ^ prev->clean; - result.sleep = ((desired.sleep >= 0) ^ (prev->sleep >= 0)) ? 0 : -1; - break; - case decode_type_t::DAIKIN128: - result.power = desired.power ^ prev->power; - result.light = desired.light ^ prev->light; - break; - case decode_type_t::ELECTRA_AC: - result.light = desired.light ^ prev->light; - break; - case decode_type_t::FUJITSU_AC: - result.turbo = desired.turbo ^ prev->turbo; - result.econo = desired.econo ^ prev->econo; - break; - case decode_type_t::MIDEA: - result.turbo = desired.turbo ^ prev->turbo; - result.econo = desired.econo ^ prev->econo; - result.light = desired.light ^ prev->light; - result.clean = desired.clean ^ prev->clean; - // FALL THRU - case decode_type_t::CORONA_AC: - case decode_type_t::HITACHI_AC344: - case decode_type_t::HITACHI_AC424: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - break; - case decode_type_t::SHARP_AC: - result.light = desired.light ^ prev->light; - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - break; - case decode_type_t::KELON: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - // FALL-THRU - case decode_type_t::AIRWELL: - case decode_type_t::DAIKIN64: - case decode_type_t::PANASONIC_AC32: - case decode_type_t::WHIRLPOOL_AC: - result.power = desired.power ^ prev->power; - break; - case decode_type_t::MIRAGE: - if (desired.model == mirage_ac_remote_model_t::KKG29AC1) - result.light = desired.light ^ prev->light; - result.clean = desired.clean ^ prev->clean; - break; - case decode_type_t::PANASONIC_AC: - // CKP models use a power mode toggle. - if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) - result.power = desired.power ^ prev->power; - break; - case decode_type_t::SAMSUNG_AC: - result.beep = desired.beep ^ prev->beep; - result.clean = desired.clean ^ prev->clean; - break; - default: - {}; - } - } - return result; -} - -/// Send A/C message for a given device using common A/C settings. -/// @param[in] vendor The vendor/protocol type. -/// @param[in] model The A/C model if applicable. -/// @param[in] power The power setting. -/// @param[in] mode The operation mode setting. -/// @note Changing mode from "Off" to something else does NOT turn on a device. -/// You need to use `power` for that. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] fan The speed setting for the fan. -/// @note The following are all "if supported" by the underlying A/C classes. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. -/// Others it may be the time to enter/exit sleep mode. -/// i.e. Time in Nr. of mins since midnight. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @return True, if accepted/converted/attempted etc. False, if unsupported. -bool IRac::sendAc(const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, - const float degrees, const bool celsius, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep, const int16_t clock) { - stdAc::state_t to_send; - initState(&to_send, vendor, model, power, mode, degrees, celsius, fan, swingv, - swingh, quiet, turbo, econo, light, filter, clean, beep, sleep, - clock); - return this->sendAc(to_send, &to_send); -} - -/// Send A/C message for a given device using state_t structures. -/// @param[in] desired The state_t structure describing the desired new ac state -/// @param[in] prev A Ptr to the state_t structure containing the previous state -/// @note Changing mode from "Off" to something else does NOT turn on a device. -/// You need to use `power` for that. -/// @return True, if accepted/converted/attempted etc. False, if unsupported. -bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { - // Convert the temp from Fahrenheit to Celsius if we are not in Celsius mode. - float degC __attribute__((unused)) = - desired.celsius ? desired.degrees : fahrenheitToCelsius(desired.degrees); - // Convert the sensorTemp from Fahrenheit to Celsius if we are not in Celsius - // mode. - float sensorTempC __attribute__((unused)) = - desired.sensorTemperature ? desired.sensorTemperature - : fahrenheitToCelsius(desired.sensorTemperature); - // special `state_t` that is required to be sent based on that. - stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev); - // Some protocols expect a previous state for power. - // Construct a pointer-safe previous power state incase prev is NULL/NULLPTR. -#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) - const bool prev_power = (prev != NULL) ? prev->power : !send.power; - const int16_t prev_sleep = (prev != NULL) ? prev->sleep : -1; -#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) -#if (SEND_LG || SEND_SHARP_AC) - const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv - : stdAc::swingv_t::kOff; -#endif // (SEND_LG || SEND_SHARP_AC) -#if (SEND_HAIER_AC160) - const bool prev_light = (prev != NULL) ? prev->light : !send.light; -#endif // (SEND_HAIER_AC160) -#if SEND_MIDEA - const bool prev_quiet = (prev != NULL) ? prev->quiet : !send.quiet; -#endif // SEND_MIDEA - // Per vendor settings & setup. - switch (send.protocol) { -#if SEND_AIRTON - case AIRTON: - { - IRAirtonAc ac(_pin, _inverted, _modulation); - airton(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.turbo, send.light, send.econo, send.filter, - send.sleep); - break; - } -#endif // SEND_AIRTON -#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: - { - IRAmcorAc ac(_pin, _inverted, _modulation); - amcor(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_AMCOR -#if SEND_ARGO - case ARGO: - { - if (send.model == argo_ac_remote_model_t::SAC_WREM3) { - IRArgoAC_WREM3 ac(_pin, _inverted, _modulation); - switch (send.command) { - case stdAc::ac_command_t::kSensorTempReport: - argoWrem3_iFeelReport(&ac, sensorTempC); - break; - case stdAc::ac_command_t::kConfigCommand: - /// @warning: this is ABUSING current **common** parameters: - /// @c clock and @c sleep as config key and value - /// Hence, value pre-validation is performed (safe-mode) - /// to avoid accidental device misconfiguration - argoWrem3_ConfigSet(&ac, send.clock, send.sleep, true); - break; - case stdAc::ac_command_t::kTimerCommand: - argoWrem3_SetTimer(&ac, send.power, send.clock, send.sleep); - break; - case stdAc::ac_command_t::kControlCommand: - default: - argoWrem3_ACCommand(&ac, send.power, send.mode, degC, sensorTempC, - send.fanspeed, send.swingv, send.iFeel, send.quiet, send.econo, - send.turbo, send.filter, send.light); - break; - } - OUTPUT_DECODE_RESULTS_FOR_UT(ac); - } else { - IRArgoAC ac(_pin, _inverted, _modulation); - argo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.iFeel, send.turbo, send.sleep); - OUTPUT_DECODE_RESULTS_FOR_UT(ac); - } - break; - } -#endif // SEND_ARGO -#if SEND_BOSCH144 - case BOSCH144: - { - IRBosch144AC ac(_pin, _inverted, _modulation); - bosch144(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); - break; - } -#endif // SEND_BOSCH144 -#if SEND_CARRIER_AC64 - case CARRIER_AC64: - { - IRCarrierAc64 ac(_pin, _inverted, _modulation); - carrier64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.sleep); - break; - } -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - case COOLIX: - { - IRCoolixAC ac(_pin, _inverted, _modulation); - coolix(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.swingh, send.iFeel, send.turbo, send.light, - send.clean, send.sleep); - break; - } -#endif // SEND_COOLIX -#if SEND_CORONA_AC - case CORONA_AC: - { - IRCoronaAc ac(_pin, _inverted, _modulation); - corona(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.econo); - break; - } -#endif // SEND_CORONA_AC -#if SEND_DAIKIN - case DAIKIN: - { - IRDaikinESP ac(_pin, _inverted, _modulation); - daikin(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.econo, send.clean); - break; - } -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - case DAIKIN128: - { - IRDaikin128 ac(_pin, _inverted, _modulation); - daikin128(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.light, send.econo, send.sleep, - send.clock); - break; - } -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN152 - case DAIKIN152: - { - IRDaikin152 ac(_pin, _inverted, _modulation); - daikin152(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.econo); - break; - } -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - case DAIKIN160: - { - IRDaikin160 ac(_pin, _inverted, _modulation); - daikin160(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - case DAIKIN176: - { - IRDaikin176 ac(_pin, _inverted, _modulation); - daikin176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingh); - break; - } -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - case DAIKIN2: - { - IRDaikin2 ac(_pin, _inverted, _modulation); - daikin2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.light, send.econo, - send.filter, send.clean, send.beep, send.sleep, send.clock); - break; - } -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN216 - case DAIKIN216: - { - IRDaikin216 ac(_pin, _inverted, _modulation); - daikin216(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo); - break; - } -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN64 - case DAIKIN64: - { - IRDaikin64 ac(_pin, _inverted, _modulation); - daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.sleep, send.clock); - break; - } -#endif // SEND_DAIKIN64 -#if SEND_DELONGHI_AC - case DELONGHI_AC: - { - IRDelonghiAc ac(_pin, _inverted, _modulation); - delonghiac(&ac, send.power, send.mode, send.celsius, degC, send.fanspeed, - send.turbo, send.sleep); - break; - } -#endif // SEND_DELONGHI_AC -#if SEND_ECOCLIM - case ECOCLIM: - { - IREcoclimAc ac(_pin, _inverted, _modulation); - ecoclim(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.iFeel, send.clock); - break; - } -#endif // SEND_ECOCLIM -#if SEND_ELECTRA_AC - case ELECTRA_AC: - { - IRElectraAc ac(_pin, _inverted, _modulation); - electra(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.swingh, send.iFeel, send.turbo, send.light, - send.clean); - break; - } -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - case FUJITSU_AC: - { - IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)send.model, _inverted, - _modulation); - fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, - send.swingv, send.swingh, send.quiet, - send.turbo, send.econo, send.filter, send.clean, send.sleep); - break; - } -#endif // SEND_FUJITSU_AC -#if SEND_GOODWEATHER - case GOODWEATHER: - { - IRGoodweatherAc ac(_pin, _inverted, _modulation); - goodweather(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.light, send.sleep); - break; - } -#endif // SEND_GOODWEATHER -#if SEND_GREE - case GREE: - { - IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, - _modulation); - gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, - send.iFeel, send.turbo, send.econo, send.light, send.clean, - send.sleep); - break; - } -#endif // SEND_GREE -#if SEND_HAIER_AC - case HAIER_AC: - { - IRHaierAC ac(_pin, _inverted, _modulation); - haier(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC160 - case HAIER_AC160: - { - IRHaierAC160 ac(_pin, _inverted, _modulation); - haier160(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.turbo, send.filter, send.clean, - send.light, prev_light, send.sleep); - break; - } -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - case HAIER_AC176: - { - IRHaierAC176 ac(_pin, _inverted, _modulation); - haier176(&ac, (haier_ac176_remote_model_t)send.model, send.power, - send.mode, send.celsius, send.degrees, send.fanspeed, - send.swingv, send.swingh, send.turbo, send.filter, send.sleep); - break; - } -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - { - IRHaierACYRW02 ac(_pin, _inverted, _modulation); - haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.swingh, send.turbo, - send.filter, send.sleep); - break; - } -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HITACHI_AC - case HITACHI_AC: - { - IRHitachiAc ac(_pin, _inverted, _modulation); - hitachi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh); - break; - } -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - { - IRHitachiAc1 ac(_pin, _inverted, _modulation); - bool power_toggle = false; - bool swing_toggle = false; - if (prev != NULL) { - power_toggle = (send.power != prev->power); - swing_toggle = (send.swingv != prev->swingv) || - (send.swingh != prev->swingh); - } - hitachi1(&ac, (hitachi_ac1_remote_model_t)send.model, send.power, - power_toggle, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, swing_toggle, send.sleep); - break; - } -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC264 - case HITACHI_AC264: - { - IRHitachiAc264 ac(_pin, _inverted, _modulation); - hitachi264(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - case HITACHI_AC296: - { - IRHitachiAc296 ac(_pin, _inverted, _modulation); - hitachi296(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - case HITACHI_AC344: - { - IRHitachiAc344 ac(_pin, _inverted, _modulation); - hitachi344(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh); - break; - } -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - case HITACHI_AC424: - { - IRHitachiAc424 ac(_pin, _inverted, _modulation); - hitachi424(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_HITACHI_AC424 -#if SEND_KELON - case KELON: { - IRKelonAc ac(_pin, _inverted, _modulation); - kelon(&ac, send.power, send.mode, 0, send.degrees, send.fanspeed, - send.swingv != stdAc::swingv_t::kOff, send.turbo, send.sleep); - break; - } -#endif -#if SEND_KELVINATOR - case KELVINATOR: - { - IRKelvinatorAC ac(_pin, _inverted, _modulation); - kelvinator(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.light, send.filter, - send.clean); - break; - } -#endif // SEND_KELVINATOR -#if SEND_LG - case LG: - case LG2: - { - IRLgAc ac(_pin, _inverted, _modulation); - lg(&ac, (lg_ac_remote_model_t)send.model, send.power, send.mode, - send.degrees, send.fanspeed, send.swingv, prev_swingv, send.swingh, - send.light); - break; - } -#endif // SEND_LG -#if SEND_MIDEA - case MIDEA: - { - IRMideaAC ac(_pin, _inverted, _modulation); - midea(&ac, send.power, send.mode, send.celsius, send.degrees, - send.sensorTemperature, send.fanspeed, send.swingv, send.iFeel, - send.quiet, prev_quiet, send.turbo, send.econo, send.light, - send.clean, send.sleep); - break; - } -#endif // SEND_MIDEA -#if SEND_MIRAGE - case MIRAGE: - { - IRMirageAc ac(_pin, _inverted, _modulation); - mirage(&ac, send); - break; - } -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - { - IRMitsubishiAC ac(_pin, _inverted, _modulation); - mitsubishi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.clock); - break; - } -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI112 - case MITSUBISHI112: - { - IRMitsubishi112 ac(_pin, _inverted, _modulation); - mitsubishi112(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.quiet); - break; - } -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHI136 - case MITSUBISHI136: - { - IRMitsubishi136 ac(_pin, _inverted, _modulation); - mitsubishi136(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.quiet); - break; - } -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: - { - IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); - mitsubishiHeavy88(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.turbo, send.econo, - send.clean); - break; - } - case MITSUBISHI_HEAVY_152: - { - IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); - mitsubishiHeavy152(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.quiet, send.turbo, - send.econo, send.filter, send.clean, send.sleep); - break; - } -#endif // SEND_MITSUBISHIHEAVY -#if SEND_NEOCLIMA - case NEOCLIMA: - { - IRNeoclimaAc ac(_pin, _inverted, _modulation); - neoclima(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.swingh, send.turbo, - send.econo, send.light, send.filter, send.sleep); - break; - } -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - case PANASONIC_AC: - { - IRPanasonicAc ac(_pin, _inverted, _modulation); - panasonic(&ac, (panasonic_ac_remote_model_t)send.model, send.power, - send.mode, degC, send.fanspeed, send.swingv, send.swingh, - send.quiet, send.turbo, send.clock); - break; - } -#endif // SEND_PANASONIC_AC -#if SEND_PANASONIC_AC32 - case PANASONIC_AC32: - { - IRPanasonicAc32 ac(_pin, _inverted, _modulation); - panasonic32(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh); - break; - } -#endif // SEND_PANASONIC_AC32 -#if SEND_RHOSS - case RHOSS: - { - IRRhossAc ac(_pin, _inverted, _modulation); - rhoss(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - { - IRSamsungAc ac(_pin, _inverted, _modulation); - samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.econo, send.light, - send.filter, send.clean, send.beep, send.sleep, - prev_power, prev_sleep); - break; - } -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - case SANYO_AC: - { - IRSanyoAc ac(_pin, _inverted, _modulation); - sanyo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.iFeel, send.beep, send.sleep); - break; - } -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - case SANYO_AC88: - { - IRSanyoAc88 ac(_pin, _inverted, _modulation); - sanyo88(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_SANYO_AC88 -#if SEND_SHARP_AC - case SHARP_AC: - { - IRSharpAc ac(_pin, _inverted, _modulation); - sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power, - send.mode, degC, send.fanspeed, send.swingv, prev_swingv, - send.turbo, send.light, send.filter, send.clean); - break; - } -#endif // SEND_SHARP_AC -#if (SEND_TCL112AC || SEND_TEKNOPOINT) - case TCL112AC: - case TEKNOPOINT: - { - IRTcl112Ac ac(_pin, _inverted, _modulation); - tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model; - if (send.protocol == decode_type_t::TEKNOPOINT) - model = tcl_ac_remote_model_t::GZ055BE1; - tcl112(&ac, model, send.power, send.mode, - degC, send.fanspeed, send.swingv, send.swingh, send.quiet, - send.turbo, send.light, send.econo, send.filter); - break; - } -#endif // (SEND_TCL112AC || SEND_TEKNOPOINT) -#if SEND_TECHNIBEL_AC - case TECHNIBEL_AC: - { - IRTechnibelAc ac(_pin, _inverted, _modulation); - technibel(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.sleep); - break; - } -#endif // SEND_TECHNIBEL_AC -#if SEND_TECO - case TECO: - { - IRTecoAc ac(_pin, _inverted, _modulation); - teco(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.light, send.sleep); - break; - } -#endif // SEND_TECO -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - { - IRToshibaAC ac(_pin, _inverted, _modulation); - toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.econo, send.filter); - break; - } -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - case TROTEC: - { - IRTrotecESP ac(_pin, _inverted, _modulation); - trotec(&ac, send.power, send.mode, degC, send.fanspeed, send.sleep); - break; - } -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - case TROTEC_3550: - { - IRTrotec3550 ac(_pin, _inverted, _modulation); - trotec3550(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv); - break; - } -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - case TRUMA: - { - IRTrumaAc ac(_pin, _inverted, _modulation); - truma(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); - break; - } -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case VESTEL_AC: - { - IRVestelAc ac(_pin, _inverted, _modulation); - vestel(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_VESTEL_AC -#if SEND_VOLTAS - case VOLTAS: - { - IRVoltas ac(_pin, _inverted, _modulation); - voltas(&ac, (voltas_ac_remote_model_t)send.model, send.power, send.mode, - degC, send.fanspeed, send.swingv, send.swingh, send.turbo, - send.econo, send.light, send.sleep); - break; - } -#endif // SEND_VOLTAS -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - { - IRWhirlpoolAc ac(_pin, _inverted, _modulation); - whirlpool(&ac, (whirlpool_ac_remote_model_t)send.model, send.power, - send.mode, degC, send.fanspeed, send.swingv, send.turbo, - send.light, send.sleep, send.clock); - break; - } -#endif // SEND_WHIRLPOOL_AC -#if SEND_TRANSCOLD - case TRANSCOLD: - { - IRTranscoldAc ac(_pin, _inverted, _modulation); - transcold(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh); - break; - } -#endif // SEND_TRANSCOLD_AC - default: - return false; // Fail, didn't match anything. - } - return true; // Success. -} // NOLINT(readability/fn_size) - -/// Update the previous state to the current one. -void IRac::markAsSent(void) { - _prev = next; -} - -/// Send an A/C message based soley on our internal state. -/// @return True, if accepted/converted/attempted. False, if unsupported. -bool IRac::sendAc(void) { - bool success = this->sendAc(next, &_prev); - if (success) this->markAsSent(); - return success; -} - -/// Compare two AirCon states. -/// @note The comparison excludes the clock. -/// @param a A state_t to be compared. -/// @param b A state_t to be compared. -/// @return True if they differ, False if they don't. -bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { - return a.protocol != b.protocol || a.model != b.model || a.power != b.power || - a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || - a.fanspeed != b.fanspeed || a.swingv != b.swingv || - a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || - a.econo != b.econo || a.light != b.light || a.filter != b.filter || - a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep || - a.command != b.command || a.sensorTemperature != b.sensorTemperature || - a.iFeel != b.iFeel; -} - -/// Check if the internal state has changed from what was previously sent. -/// @note The comparison excludes the clock. -/// @return True if it has changed, False if not. -bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); } - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::ac_command_t IRac::strToCommandType(const char *str, - const stdAc::ac_command_t def) { - if (!STRCASECMP(str, kControlCommandStr)) - return stdAc::ac_command_t::kControlCommand; - else if (!STRCASECMP(str, kIFeelReportStr) || - !STRCASECMP(str, kIFeelStr)) - return stdAc::ac_command_t::kSensorTempReport; - else if (!STRCASECMP(str, kSetTimerCommandStr) || - !STRCASECMP(str, kTimerStr)) - return stdAc::ac_command_t::kTimerCommand; - else if (!STRCASECMP(str, kConfigCommandStr)) - return stdAc::ac_command_t::kConfigCommand; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::opmode_t IRac::strToOpmode(const char *str, - const stdAc::opmode_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr)) - return stdAc::opmode_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::opmode_t::kOff; - else if (!STRCASECMP(str, kCoolStr) || - !STRCASECMP(str, kCoolingStr)) - return stdAc::opmode_t::kCool; - else if (!STRCASECMP(str, kHeatStr) || - !STRCASECMP(str, kHeatingStr)) - return stdAc::opmode_t::kHeat; - else if (!STRCASECMP(str, kDryStr) || - !STRCASECMP(str, kDryingStr) || - !STRCASECMP(str, kDehumidifyStr)) - return stdAc::opmode_t::kDry; - else if (!STRCASECMP(str, kFanStr) || - // The following Fans strings with "only" are required to help with - // HomeAssistant & Google Home Climate integration. - // For compatibility only. - // Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes - !STRCASECMP(str, kFanOnlyStr) || - !STRCASECMP(str, kFan_OnlyStr) || - !STRCASECMP(str, kFanOnlyWithSpaceStr) || - !STRCASECMP(str, kFanOnlyNoSpaceStr)) - return stdAc::opmode_t::kFan; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::fanspeed_t IRac::strToFanspeed(const char *str, - const stdAc::fanspeed_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr)) - return stdAc::fanspeed_t::kAuto; - else if (!STRCASECMP(str, kMinStr) || - !STRCASECMP(str, kMinimumStr) || - !STRCASECMP(str, kLowestStr)) - return stdAc::fanspeed_t::kMin; - else if (!STRCASECMP(str, kLowStr) || - !STRCASECMP(str, kLoStr)) - return stdAc::fanspeed_t::kLow; - else if (!STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kMidStr)) - return stdAc::fanspeed_t::kMedium; - else if (!STRCASECMP(str, kHighStr) || - !STRCASECMP(str, kHiStr)) - return stdAc::fanspeed_t::kHigh; - else if (!STRCASECMP(str, kMaxStr) || - !STRCASECMP(str, kMaximumStr) || - !STRCASECMP(str, kHighestStr)) - return stdAc::fanspeed_t::kMax; - else if (!STRCASECMP(str, kMedHighStr)) - return stdAc::fanspeed_t::kMediumHigh; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::swingv_t IRac::strToSwingV(const char *str, - const stdAc::swingv_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr) || - !STRCASECMP(str, kOnStr) || - !STRCASECMP(str, kSwingStr)) - return stdAc::swingv_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::swingv_t::kOff; - else if (!STRCASECMP(str, kMinStr) || - !STRCASECMP(str, kMinimumStr) || - !STRCASECMP(str, kLowestStr) || - !STRCASECMP(str, kBottomStr) || - !STRCASECMP(str, kDownStr)) - return stdAc::swingv_t::kLowest; - else if (!STRCASECMP(str, kLowStr)) - return stdAc::swingv_t::kLow; - else if (!STRCASECMP(str, kMidStr) || - !STRCASECMP(str, kMiddleStr) || - !STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kCentreStr)) - return stdAc::swingv_t::kMiddle; - else if (!STRCASECMP(str, kUpperMiddleStr)) - return stdAc::swingv_t::kUpperMiddle; - else if (!STRCASECMP(str, kHighStr) || - !STRCASECMP(str, kHiStr)) - return stdAc::swingv_t::kHigh; - else if (!STRCASECMP(str, kHighestStr) || - !STRCASECMP(str, kMaxStr) || - !STRCASECMP(str, kMaximumStr) || - !STRCASECMP(str, kTopStr) || - !STRCASECMP(str, kUpStr)) - return stdAc::swingv_t::kHighest; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::swingh_t IRac::strToSwingH(const char *str, - const stdAc::swingh_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr) || - !STRCASECMP(str, kOnStr) || !STRCASECMP(str, kSwingStr)) - return stdAc::swingh_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::swingh_t::kOff; - else if (!STRCASECMP(str, kLeftMaxNoSpaceStr) || // "LeftMax" - !STRCASECMP(str, kLeftMaxStr) || // "Left Max" - !STRCASECMP(str, kMaxLeftNoSpaceStr) || // "MaxLeft" - !STRCASECMP(str, kMaxLeftStr)) // "Max Left" - return stdAc::swingh_t::kLeftMax; - else if (!STRCASECMP(str, kLeftStr)) - return stdAc::swingh_t::kLeft; - else if (!STRCASECMP(str, kMidStr) || - !STRCASECMP(str, kMiddleStr) || - !STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kCentreStr)) - return stdAc::swingh_t::kMiddle; - else if (!STRCASECMP(str, kRightStr)) - return stdAc::swingh_t::kRight; - else if (!STRCASECMP(str, kRightMaxNoSpaceStr) || // "RightMax" - !STRCASECMP(str, kRightMaxStr) || // "Right Max" - !STRCASECMP(str, kMaxRightNoSpaceStr) || // "MaxRight" - !STRCASECMP(str, kMaxRightStr)) // "Max Right" - return stdAc::swingh_t::kRightMax; - else if (!STRCASECMP(str, kWideStr)) - return stdAc::swingh_t::kWide; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @note Assumes str is the model code or an integer >= 1. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -/// @note After adding a new model you should update modelToStr() too. -int16_t IRac::strToModel(const char *str, const int16_t def) { - // Gree - if (!STRCASECMP(str, kYaw1fStr)) { - return gree_ac_remote_model_t::YAW1F; - } else if (!STRCASECMP(str, kYbofbStr)) { - return gree_ac_remote_model_t::YBOFB; - } else if (!STRCASECMP(str, kYx1fsfStr)) { - return gree_ac_remote_model_t::YX1FSF; - // Haier models - } else if (!STRCASECMP(str, kV9014557AStr)) { - return haier_ac176_remote_model_t::V9014557_A; - } else if (!STRCASECMP(str, kV9014557BStr)) { - return haier_ac176_remote_model_t::V9014557_B; - // HitachiAc1 models - } else if (!STRCASECMP(str, kRlt0541htaaStr)) { - return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; - } else if (!STRCASECMP(str, kRlt0541htabStr)) { - return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; - // Fujitsu A/C models - } else if (!STRCASECMP(str, kArrah2eStr)) { - return fujitsu_ac_remote_model_t::ARRAH2E; - } else if (!STRCASECMP(str, kArdb1Str)) { - return fujitsu_ac_remote_model_t::ARDB1; - } else if (!STRCASECMP(str, kArreb1eStr)) { - return fujitsu_ac_remote_model_t::ARREB1E; - } else if (!STRCASECMP(str, kArjw2Str)) { - return fujitsu_ac_remote_model_t::ARJW2; - } else if (!STRCASECMP(str, kArry4Str)) { - return fujitsu_ac_remote_model_t::ARRY4; - } else if (!STRCASECMP(str, kArrew4eStr)) { - return fujitsu_ac_remote_model_t::ARREW4E; - // LG A/C models - } else if (!STRCASECMP(str, kGe6711ar2853mStr)) { - return lg_ac_remote_model_t::GE6711AR2853M; - } else if (!STRCASECMP(str, kAkb75215403Str)) { - return lg_ac_remote_model_t::AKB75215403; - } else if (!STRCASECMP(str, kAkb74955603Str)) { - return lg_ac_remote_model_t::AKB74955603; - } else if (!STRCASECMP(str, kAkb73757604Str)) { - return lg_ac_remote_model_t::AKB73757604; - } else if (!STRCASECMP(str, kLg6711a20083vStr)) { - return lg_ac_remote_model_t::LG6711A20083V; - // Panasonic A/C families - } else if (!STRCASECMP(str, kLkeStr) || - !STRCASECMP(str, kPanasonicLkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicLke; - } else if (!STRCASECMP(str, kNkeStr) || - !STRCASECMP(str, kPanasonicNkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicNke; - } else if (!STRCASECMP(str, kDkeStr) || - !STRCASECMP(str, kPanasonicDkeStr) || - !STRCASECMP(str, kPkrStr) || - !STRCASECMP(str, kPanasonicPkrStr)) { - return panasonic_ac_remote_model_t::kPanasonicDke; - } else if (!STRCASECMP(str, kJkeStr) || - !STRCASECMP(str, kPanasonicJkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicJke; - } else if (!STRCASECMP(str, kCkpStr) || - !STRCASECMP(str, kPanasonicCkpStr)) { - return panasonic_ac_remote_model_t::kPanasonicCkp; - } else if (!STRCASECMP(str, kRkrStr) || - !STRCASECMP(str, kPanasonicRkrStr)) { - return panasonic_ac_remote_model_t::kPanasonicRkr; - // Sharp A/C Models - } else if (!STRCASECMP(str, kA907Str)) { - return sharp_ac_remote_model_t::A907; - } else if (!STRCASECMP(str, kA705Str)) { - return sharp_ac_remote_model_t::A705; - } else if (!STRCASECMP(str, kA903Str)) { - return sharp_ac_remote_model_t::A903; - // TCL A/C Models - } else if (!STRCASECMP(str, kTac09chsdStr)) { - return tcl_ac_remote_model_t::TAC09CHSD; - } else if (!STRCASECMP(str, kGz055be1Str)) { - return tcl_ac_remote_model_t::GZ055BE1; - // Voltas A/C models - } else if (!STRCASECMP(str, k122lzfStr)) { - return voltas_ac_remote_model_t::kVoltas122LZF; - // Whirlpool A/C models - } else if (!STRCASECMP(str, kDg11j13aStr) || - !STRCASECMP(str, kDg11j104Str)) { - return whirlpool_ac_remote_model_t::DG11J13A; - } else if (!STRCASECMP(str, kDg11j191Str)) { - return whirlpool_ac_remote_model_t::DG11J191; - // Argo A/C models - } else if (!STRCASECMP(str, kArgoWrem2Str)) { - return argo_ac_remote_model_t::SAC_WREM2; - } else if (!STRCASECMP(str, kArgoWrem3Str)) { - return argo_ac_remote_model_t::SAC_WREM3; - } else { - int16_t number = atoi(str); - if (number > 0) - return number; - else - return def; - } -} - -/// Convert the supplied str into the appropriate boolean value. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The boolean value to return if no conversion was possible. -/// @return The equivalent boolean value. -bool IRac::strToBool(const char *str, const bool def) { - if (!STRCASECMP(str, kOnStr) || - !STRCASECMP(str, k1Str) || - !STRCASECMP(str, kYesStr) || - !STRCASECMP(str, kTrueStr)) - return true; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, k0Str) || - !STRCASECMP(str, kNoStr) || - !STRCASECMP(str, kFalseStr)) - return false; - else - return def; -} - -/// Convert the supplied boolean into the appropriate String. -/// @param[in] value The boolean value to be converted. -/// @return The equivalent String for the locale. -String IRac::boolToString(const bool value) { - return value ? kOnStr : kOffStr; -} - -/// Convert the supplied operation mode into the appropriate String. -/// @param[in] cmdType The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::commandTypeToString(const stdAc::ac_command_t cmdType) { - switch (cmdType) { - case stdAc::ac_command_t::kControlCommand: return kControlCommandStr; - case stdAc::ac_command_t::kSensorTempReport: return kIFeelReportStr; - case stdAc::ac_command_t::kTimerCommand: return kSetTimerCommandStr; - case stdAc::ac_command_t::kConfigCommand: return kConfigCommandStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied operation mode into the appropriate String. -/// @param[in] mode The enum to be converted. -/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output. -/// @return The equivalent String for the locale. -String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) { - switch (mode) { - case stdAc::opmode_t::kOff: return kOffStr; - case stdAc::opmode_t::kAuto: return kAutoStr; - case stdAc::opmode_t::kCool: return kCoolStr; - case stdAc::opmode_t::kHeat: return kHeatStr; - case stdAc::opmode_t::kDry: return kDryStr; - case stdAc::opmode_t::kFan: return ha ? kFan_OnlyStr : kFanStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied fan speed enum into the appropriate String. -/// @param[in] speed The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kAuto: return kAutoStr; - case stdAc::fanspeed_t::kMax: return kMaxStr; - case stdAc::fanspeed_t::kHigh: return kHighStr; - case stdAc::fanspeed_t::kMedium: return kMediumStr; - case stdAc::fanspeed_t::kMediumHigh: return kMedHighStr; - case stdAc::fanspeed_t::kLow: return kLowStr; - case stdAc::fanspeed_t::kMin: return kMinStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied enum into the appropriate String. -/// @param[in] swingv The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::swingvToString(const stdAc::swingv_t swingv) { - switch (swingv) { - case stdAc::swingv_t::kOff: return kOffStr; - case stdAc::swingv_t::kAuto: return kAutoStr; - case stdAc::swingv_t::kHighest: return kHighestStr; - case stdAc::swingv_t::kHigh: return kHighStr; - case stdAc::swingv_t::kMiddle: return kMiddleStr; - case stdAc::swingv_t::kUpperMiddle: return kUpperMiddleStr; - case stdAc::swingv_t::kLow: return kLowStr; - case stdAc::swingv_t::kLowest: return kLowestStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied enum into the appropriate String. -/// @param[in] swingh The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::swinghToString(const stdAc::swingh_t swingh) { - switch (swingh) { - case stdAc::swingh_t::kOff: return kOffStr; - case stdAc::swingh_t::kAuto: return kAutoStr; - case stdAc::swingh_t::kLeftMax: return kLeftMaxStr; - case stdAc::swingh_t::kLeft: return kLeftStr; - case stdAc::swingh_t::kMiddle: return kMiddleStr; - case stdAc::swingh_t::kRight: return kRightStr; - case stdAc::swingh_t::kRightMax: return kRightMaxStr; - case stdAc::swingh_t::kWide: return kWideStr; - default: return kUnknownStr; - } -} - -namespace IRAcUtils { - /// Display the human readable state of an A/C message if we can. - /// @param[in] result A Ptr to the captured `decode_results` that contains an - /// A/C mesg. - /// @return A string with the human description of the A/C message. - /// An empty string if we can't. - String resultAcToString(const decode_results * const result) { - switch (result->decode_type) { -#if DECODE_AIRTON - case decode_type_t::AIRTON: { - IRAirtonAc ac(kGpioUnused); - ac.setRaw(result->value); // AIRTON uses value instead of state. - return ac.toString(); - } -#endif // DECODE_AIRTON -#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(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_AMCOR -#if DECODE_ARGO - case decode_type_t::ARGO: { - if (IRArgoAC_WREM3::isValidWrem3Message(result->state, result->bits, - true)) { - IRArgoAC_WREM3 ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } - IRArgoAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_ARGO -#if DECODE_BOSCH144 - case decode_type_t::BOSCH144: { - IRBosch144AC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_BOSCH144 -#if DECODE_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: { - IRCarrierAc64 ac(kGpioUnused); - ac.setRaw(result->value); // CARRIER_AC64 uses value instead of state. - return ac.toString(); - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_COOLIX - case decode_type_t::COOLIX: { - IRCoolixAC ac(kGpioUnused); - ac.on(); - ac.setRaw(result->value); // Coolix uses value instead of state. - return ac.toString(); - } -#endif // DECODE_COOLIX -#if DECODE_CORONA_AC - case decode_type_t::CORONA_AC: { - IRCoronaAc ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_CORONA_AC -#if DECODE_DAIKIN - case decode_type_t::DAIKIN: { - IRDaikinESP ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN128 - case decode_type_t::DAIKIN128: { - IRDaikin128 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - case decode_type_t::DAIKIN152: { - IRDaikin152 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - case decode_type_t::DAIKIN160: { - IRDaikin160 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - case decode_type_t::DAIKIN176: { - IRDaikin176 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN2 - case decode_type_t::DAIKIN2: { - IRDaikin2 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN2 -#if DECODE_DAIKIN216 - case decode_type_t::DAIKIN216: { - IRDaikin216 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN64 - case decode_type_t::DAIKIN64: { - IRDaikin64 ac(kGpioUnused); - ac.setRaw(result->value); // Daikin64 uses value instead of state. - return ac.toString(); - } -#endif // DECODE_DAIKIN64 -#if DECODE_DELONGHI_AC - case decode_type_t::DELONGHI_AC: { - IRDelonghiAc ac(kGpioUnused); - ac.setRaw(result->value); // DelonghiAc uses value instead of state. - return ac.toString(); - } -#endif // DECODE_DELONGHI_AC -#if DECODE_ECOCLIM - case decode_type_t::ECOCLIM: { - if (result->bits == kEcoclimBits) { - IREcoclimAc ac(kGpioUnused); - ac.setRaw(result->value); // EcoClim uses value instead of state. - return ac.toString(); - } - return ""; - } -#endif // DECODE_ECOCLIM -#if DECODE_ELECTRA_AC - case decode_type_t::ELECTRA_AC: { - IRElectraAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_ELECTRA_AC -#if DECODE_FUJITSU_AC - case decode_type_t::FUJITSU_AC: { - IRFujitsuAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_FUJITSU_AC -#if DECODE_GOODWEATHER - case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(kGpioUnused); - ac.setRaw(result->value); // Goodweather uses value instead of state. - return ac.toString(); - } -#endif // DECODE_GOODWEATHER -#if DECODE_GREE - case decode_type_t::GREE: { - IRGreeAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_GREE -#if DECODE_HAIER_AC - case decode_type_t::HAIER_AC: { - IRHaierAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC160 - case decode_type_t::HAIER_AC160: { - IRHaierAC160 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC160 -#if DECODE_HAIER_AC176 - case decode_type_t::HAIER_AC176: { - IRHaierAC176 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC176 -#if DECODE_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC_YRW02 -#if DECODE_HITACHI_AC - case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC -#if DECODE_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: { - IRHitachiAc1 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC1 -#if DECODE_HITACHI_AC264 - case decode_type_t::HITACHI_AC264: { - IRHitachiAc264 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC264 -#if DECODE_HITACHI_AC296 - case decode_type_t::HITACHI_AC296: { - IRHitachiAc296 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: { - IRHitachiAc344 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC424 -#if DECODE_KELON - case decode_type_t::KELON: { - IRKelonAc ac(kGpioUnused); - ac.setRaw(result->value); - return ac.toString(); - } -#endif // DECODE_KELON -#if DECODE_KELVINATOR - case decode_type_t::KELVINATOR: { - IRKelvinatorAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_KELVINATOR -#if DECODE_LG - case decode_type_t::LG: - case decode_type_t::LG2: { - IRLgAc ac(kGpioUnused); - ac.setRaw(result->value, result->decode_type); // Use value, not state. - return ac.isValidLgAc() ? ac.toString() : ""; - } -#endif // DECODE_LG -#if DECODE_MIDEA - case decode_type_t::MIDEA: { - IRMideaAC ac(kGpioUnused); - ac.setRaw(result->value); // Midea uses value instead of state. - return ac.toString(); - } -#endif // DECODE_MIDEA -#if DECODE_MIRAGE - case decode_type_t::MIRAGE: { - IRMirageAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MIRAGE -#if DECODE_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: { - IRMitsubishiAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI_AC -#if DECODE_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: { - IRMitsubishi112 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI112 -#if DECODE_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: { - IRMitsubishi136 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI136 -#if DECODE_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } - case decode_type_t::MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHIHEAVY -#if DECODE_NEOCLIMA - case decode_type_t::NEOCLIMA: { - IRNeoclimaAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_NEOCLIMA -#if DECODE_PANASONIC_AC - case decode_type_t::PANASONIC_AC: { - if (result->bits > kPanasonicAcShortBits) { - IRPanasonicAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: { - if (result->bits >= kPanasonicAc32Bits) { - IRPanasonicAc32 ac(kGpioUnused); - ac.setRaw(result->value); // Uses value instead of state. - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_RHOSS - case decode_type_t::RHOSS: { - IRRhossAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_RHOSS -#if DECODE_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: { - IRSamsungAc ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_SANYO_AC - case decode_type_t::SANYO_AC: { - IRSanyoAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - case decode_type_t::SANYO_AC88: { - IRSanyoAc88 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SANYO_AC88 -#if DECODE_SHARP_AC - case decode_type_t::SHARP_AC: { - IRSharpAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SHARP_AC -#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) - case decode_type_t::TCL112AC: - case decode_type_t::TEKNOPOINT: { - IRTcl112Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) -#if DECODE_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: { - IRTechnibelAc ac(kGpioUnused); - ac.setRaw(result->value); // TechnibelAc uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TECHNIBEL_AC -#if DECODE_TECO - case decode_type_t::TECO: { - IRTecoAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_TECO -#if DECODE_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_TRANSCOLD - case decode_type_t::TRANSCOLD: { - IRTranscoldAc ac(kGpioUnused); - ac.on(); - ac.setRaw(result->value); // TRANSCOLD uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TRANSCOLD -#if DECODE_TROTEC - case decode_type_t::TROTEC: { - IRTrotecESP ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - case decode_type_t::TROTEC_3550: { - IRTrotec3550 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_TROTEC_3550 -#if DECODE_TRUMA - case decode_type_t::TRUMA: { - IRTrumaAc ac(kGpioUnused); - ac.setRaw(result->value); // Truma uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TRUMA -#if DECODE_VESTEL_AC - case decode_type_t::VESTEL_AC: { - IRVestelAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_VESTEL_AC -#if DECODE_VOLTAS - case decode_type_t::VOLTAS: { - IRVoltas ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_VOLTAS -#if DECODE_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_YORK - case decode_type_t::YORK: { - IRYorkAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_YORK - default: - return ""; - } - } - - /// Convert a valid IR A/C remote message that we understand enough into a - /// Common A/C state. - /// @param[in] decode A PTR to a successful raw IR decode object. - /// @param[in] result A PTR to a state structure to store the result in. - /// @param[in] prev A PTR to a state structure which has the prev. state. - /// @return A boolean indicating success or failure. - bool decodeToState(const decode_results *decode, stdAc::state_t *result, - const stdAc::state_t *prev -/// @cond IGNORE -// *prev flagged as "unused" due to potential compiler warning when some -// protocols that use it are disabled. It really is used. - __attribute__((unused)) -/// @endcond - ) { - if (decode == NULL || result == NULL) return false; // Safety check. - switch (decode->decode_type) { -#if DECODE_AIRTON - case decode_type_t::AIRTON: { - IRAirtonAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_AIRTON -#if DECODE_AIRWELL - case decode_type_t::AIRWELL: { - IRAirwellAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_AIRWELL -#if DECODE_AMCOR - case decode_type_t::AMCOR: { - IRAmcorAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_AMCOR -#if DECODE_ARGO - case decode_type_t::ARGO: { - const uint16_t length = decode->bits / 8; - if (IRArgoAC_WREM3::isValidWrem3Message(decode->state, - decode->bits, true)) { - IRArgoAC_WREM3 ac(kGpioUnused); - ac.setRaw(decode->state, length); - *result = ac.toCommon(); - } else { - IRArgoAC ac(kGpioUnused); - switch (length) { - case kArgoStateLength: - case kArgoShortStateLength: - ac.setRaw(decode->state, length); - *result = ac.toCommon(); - break; - default: - return false; - } - } - break; - } -#endif // DECODE_ARGO -#if DECODE_BOSCH144 - case decode_type_t::BOSCH144: { - IRBosch144AC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_BOSCH144 -#if DECODE_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: { - IRCarrierAc64 ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_COOLIX - case decode_type_t::COOLIX: { - IRCoolixAC ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_COOLIX -#if DECODE_CORONA_AC - case decode_type_t::CORONA_AC: { - IRCoronaAc ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); - break; - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_DAIKIN - case decode_type_t::DAIKIN: { - IRDaikinESP ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN128 - case decode_type_t::DAIKIN128: { - IRDaikin128 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - case decode_type_t::DAIKIN152: { - IRDaikin152 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - case decode_type_t::DAIKIN160: { - IRDaikin160 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - case decode_type_t::DAIKIN176: { - IRDaikin176 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN2 - case decode_type_t::DAIKIN2: { - IRDaikin2 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN2 -#if DECODE_DAIKIN216 - case decode_type_t::DAIKIN216: { - IRDaikin216 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN64 - case decode_type_t::DAIKIN64: { - IRDaikin64 ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_DAIKIN64 -#if DECODE_DELONGHI_AC - case decode_type_t::DELONGHI_AC: { - IRDelonghiAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_DELONGHI_AC -#if DECODE_ECOCLIM - case decode_type_t::ECOCLIM: { - if (decode->bits == kEcoclimBits) { - IREcoclimAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - } else { - return false; - } - break; - } -#endif // DECODE_ECOCLIM -#if DECODE_ELECTRA_AC - case decode_type_t::ELECTRA_AC: { - IRElectraAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_ELECTRA_AC -#if DECODE_FUJITSU_AC - case decode_type_t::FUJITSU_AC: { - IRFujitsuAC ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_FUJITSU_AC -#if DECODE_GOODWEATHER - case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_GOODWEATHER -#if DECODE_GREE - case decode_type_t::GREE: { - IRGreeAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_GREE -#if DECODE_HAIER_AC - case decode_type_t::HAIER_AC: { - IRHaierAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC160 - case decode_type_t::HAIER_AC160: { - IRHaierAC160 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_HAIER_AC160 -#if DECODE_HAIER_AC176 - case decode_type_t::HAIER_AC176: { - IRHaierAC176 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC176 -#if DECODE_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC_YRW02 -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) - case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) -#if DECODE_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: { - IRHitachiAc1 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC1 -#if DECODE_HITACHI_AC264 - case decode_type_t::HITACHI_AC264: { - IRHitachiAc264 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC264 -#if DECODE_HITACHI_AC296 - case decode_type_t::HITACHI_AC296: { - IRHitachiAc296 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: { - IRHitachiAc344 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC424 -#if DECODE_KELON - case decode_type_t::KELON: { - IRKelonAc ac(kGpioUnused); - ac.setRaw(decode->value); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_KELON -#if DECODE_KELVINATOR - case decode_type_t::KELVINATOR: { - IRKelvinatorAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_KELVINATOR -#if DECODE_LG - case decode_type_t::LG: - case decode_type_t::LG2: { - IRLgAc ac(kGpioUnused); - ac.setRaw(decode->value, decode->decode_type); // Use value, not state. - if (!ac.isValidLgAc()) return false; - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_LG -#if DECODE_MIDEA - case decode_type_t::MIDEA: { - IRMideaAC ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_MIDEA -#if DECODE_MIRAGE - case decode_type_t::MIRAGE: { - IRMirageAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MIRAGE -#if DECODE_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: { - IRMitsubishiAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI_AC -#if DECODE_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: { - IRMitsubishi112 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI112 -#if DECODE_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: { - IRMitsubishi136 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI136 -#if DECODE_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } - case decode_type_t::MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHIHEAVY -#if DECODE_NEOCLIMA - case decode_type_t::NEOCLIMA: { - IRNeoclimaAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_NEOCLIMA -#if DECODE_PANASONIC_AC - case decode_type_t::PANASONIC_AC: { - IRPanasonicAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: { - IRPanasonicAc32 ac(kGpioUnused); - if (decode->bits >= kPanasonicAc32Bits) { - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - } else { - return false; - } - break; - } -#endif // DECODE_PANASONIC_AC32 -#if DECODE_RHOSS - case decode_type_t::RHOSS: { - IRRhossAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_RHOSS -#if DECODE_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: { - IRSamsungAc ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_SANYO_AC - case decode_type_t::SANYO_AC: { - IRSanyoAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - case decode_type_t::SANYO_AC88: { - IRSanyoAc88 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SANYO_AC88 -#if DECODE_SHARP_AC - case decode_type_t::SHARP_AC: { - IRSharpAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_SHARP_AC -#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) - case decode_type_t::TCL112AC: - case decode_type_t::TEKNOPOINT: { - IRTcl112Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - // Teknopoint uses the TCL protocol, but with a different model number. - // Just keep the original protocol type ... for now. - result->protocol = decode->decode_type; - break; - } -#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) -#if DECODE_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: { - IRTechnibelAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TECHNIBEL_AC -#if DECODE_TECO - case decode_type_t::TECO: { - IRTecoAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TECO -#if DECODE_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_TRANSCOLD - case decode_type_t::TRANSCOLD: { - IRTranscoldAc ac(kGpioUnused); - ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_TRANSCOLD -#if DECODE_TROTEC - case decode_type_t::TROTEC: { - IRTrotecESP ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - case decode_type_t::TROTEC_3550: { - IRTrotec3550 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_TROTEC_3550 -#if DECODE_TRUMA - case decode_type_t::TRUMA: { - IRTrumaAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TRUMA -#if DECODE_VESTEL_AC - case decode_type_t::VESTEL_AC: { - IRVestelAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_VESTEL_AC -#if DECODE_VOLTAS - case decode_type_t::VOLTAS: { - IRVoltas ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_VOLTAS -#if DECODE_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_YORK - case decode_type_t::YORK: { - IRYorkAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_YORK - default: - return false; - } - return true; - } -} // namespace IRAcUtils +// Copyright 2019 David Conran + +// Provide a universal/standard interface for sending A/C nessages. +// It does not provide complete and maximum granular control but tries +// to offer most common functionality across all supported devices. + +#include "IRac.h" +#ifndef UNIT_TEST +#include +#endif +#include +#ifndef ARDUINO +#include +#endif +#include +#if __cplusplus >= 201103L && defined(_GLIBCXX_USE_C99_MATH_TR1) + using std::roundf; +#else + using ::roundf; +#endif +#include "IRsend.h" +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" +#include "ir_Airton.h" +#include "ir_Airwell.h" +#include "ir_Amcor.h" +#include "ir_Argo.h" +#include "ir_Bosch.h" +#include "ir_Carrier.h" +#include "ir_Coolix.h" +#include "ir_Corona.h" +#include "ir_Daikin.h" +#include "ir_Ecoclim.h" +#include "ir_Electra.h" +#include "ir_Fujitsu.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelon.h" +#include "ir_Kelvinator.h" +#include "ir_LG.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" +#include "ir_Panasonic.h" +#include "ir_Rhoss.h" +#include "ir_Samsung.h" +#include "ir_Sanyo.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Technibel.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Transcold.h" +#include "ir_Trotec.h" +#include "ir_Truma.h" +#include "ir_Vestel.h" +#include "ir_Voltas.h" +#include "ir_Whirlpool.h" + +// On the ESP8266 platform we need to use a special version of string handling +// functions to handle the strings stored in the flash address space. +#ifndef STRCASECMP +#if defined(ESP8266) +#define STRCASECMP(LHS, RHS) \ + strcasecmp_P(LHS, reinterpret_cast(RHS)) +#else // ESP8266 +#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS) +#endif // ESP8266 +#endif // STRCASECMP + +#ifndef UNIT_TEST +#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) +#else +/* NOTE: THIS IS NOT A DOXYGEN COMMENT (would require ENABLE_PREPROCESSING-YES) +/// If compiling for UT *and* a test receiver @c IRrecv is provided via the +/// @c _utReceived param, this injects an "output" gadget @c _lastDecodeResults +/// into the @c IRAc::sendAc method, so that the UT code may parse the "sent" +/// value and drive further assertions +/// +/// @note The @c decode_results "returned" is a shallow copy (empty rawbuf), +/// mostly b/c the class does not have a custom/deep copy c-tor +/// and defining it would be an overkill for this purpose +/// @note For future maintainers: If @c IRAc class is ever refactored to use +/// polymorphism (static or dynamic)... this macro should be removed +/// and replaced with proper GMock injection. +*/ +#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) \ + { \ + if (_utReceiver) { \ + _lastDecodeResults = nullptr; \ + (ac)._irsend.makeDecodeResult(); \ + if (_utReceiver->decode(&(ac)._irsend.capture)) { \ + _lastDecodeResults = std::unique_ptr( \ + new decode_results((ac)._irsend.capture)); \ + _lastDecodeResults->rawbuf = nullptr; \ + } \ + } \ + } +#endif // UNIT_TEST + +/// Class constructor +/// @param[in] pin Gpio pin to use when transmitting IR messages. +/// @param[in] inverted true, gpio output defaults to high. false, to low. +/// @param[in] use_modulation true means use frequency modulation. false, don't. +IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { + _pin = pin; + _inverted = inverted; + _modulation = use_modulation; + this->markAsSent(); +} + +/// Initialise the given state with the supplied settings. +/// @param[out] state A Ptr to where the settings will be stored. +/// @param[in] vendor The vendor/protocol type. +/// @param[in] model The A/C model if applicable. +/// @param[in] power The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. +/// Others it may be the time to enter/exit sleep mode. +/// i.e. Time in Nr. of mins since midnight. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::initState(stdAc::state_t *state, + const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, + const int16_t clock) { + state->protocol = vendor; + state->model = model; + state->power = power; + state->mode = mode; + state->degrees = degrees; + state->celsius = celsius; + state->fanspeed = fan; + state->swingv = swingv; + state->swingh = swingh; + state->quiet = quiet; + state->turbo = turbo; + state->econo = econo; + state->light = light; + state->filter = filter; + state->clean = clean; + state->beep = beep; + state->sleep = sleep; + state->clock = clock; +} + +/// Initialise the given state with the supplied settings. +/// @param[out] state A Ptr to where the settings will be stored. +/// @note Sets all the parameters to reasonable base/automatic defaults. +void IRac::initState(stdAc::state_t *state) { + stdAc::state_t def; + *state = def; +} + +/// Get the current internal A/C climate state. +/// @return A Ptr to a state containing the current (to be sent) settings. +stdAc::state_t IRac::getState(void) { return next; } + +/// Get the previous internal A/C climate state that should have already been +/// sent to the device. i.e. What the A/C unit should already be set to. +/// @return A Ptr to a state containing the previously sent settings. +stdAc::state_t IRac::getStatePrev(void) { return _prev; } + +/// Is the given protocol supported by the IRac class? +/// @param[in] protocol The vendor/protocol type. +/// @return true if the protocol is supported by this class, otherwise false. +bool IRac::isProtocolSupported(const decode_type_t protocol) { + switch (protocol) { +#if SEND_AIRTON + case decode_type_t::AIRTON: +#endif // SEND_AIRTON +#if SEND_AIRWELL + case decode_type_t::AIRWELL: +#endif // SEND_AIRWELL +#if SEND_AMCOR + case decode_type_t::AMCOR: +#endif +#if SEND_ARGO + case decode_type_t::ARGO: +#endif +#if SEND_BOSCH144 + case decode_type_t::BOSCH144: +#endif +#if SEND_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + case decode_type_t::COOLIX: +#endif +#if SEND_CORONA_AC + case decode_type_t::CORONA_AC: +#endif +#if SEND_DAIKIN + case decode_type_t::DAIKIN: +#endif +#if SEND_DAIKIN128 + case decode_type_t::DAIKIN128: +#endif +#if SEND_DAIKIN152 + case decode_type_t::DAIKIN152: +#endif +#if SEND_DAIKIN160 + case decode_type_t::DAIKIN160: +#endif +#if SEND_DAIKIN176 + case decode_type_t::DAIKIN176: +#endif +#if SEND_DAIKIN2 + case decode_type_t::DAIKIN2: +#endif +#if SEND_DAIKIN216 + case decode_type_t::DAIKIN216: +#endif +#if SEND_DAIKIN64 + case decode_type_t::DAIKIN64: +#endif +#if SEND_DELONGHI_AC + case decode_type_t::DELONGHI_AC: +#endif +#if SEND_ECOCLIM + case decode_type_t::ECOCLIM: +#endif +#if SEND_ELECTRA_AC + case decode_type_t::ELECTRA_AC: +#endif +#if SEND_FUJITSU_AC + case decode_type_t::FUJITSU_AC: +#endif +#if SEND_FUJITSU_AC264 + case decode_type_t::FUJITSU_AC264: +#endif +#if SEND_GOODWEATHER + case decode_type_t::GOODWEATHER: +#endif +#if SEND_GREE + case decode_type_t::GREE: +#endif +#if SEND_HAIER_AC + case decode_type_t::HAIER_AC: +#endif +#if SEND_HAIER_AC160 + case decode_type_t::HAIER_AC160: +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + case decode_type_t::HAIER_AC176: +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: +#endif +#if SEND_HITACHI_AC + case decode_type_t::HITACHI_AC: +#endif +#if SEND_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: +#endif +#if SEND_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: +#endif +#if SEND_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: +#endif +#if SEND_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: +#endif +#if SEND_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: +#endif +#if SEND_KELON + case decode_type_t::KELON: +#endif +#if SEND_KELVINATOR + case decode_type_t::KELVINATOR: +#endif +#if SEND_LG + case decode_type_t::LG: + case decode_type_t::LG2: +#endif +#if SEND_MIDEA + case decode_type_t::MIDEA: +#endif // SEND_MIDEA +#if SEND_MIRAGE + case decode_type_t::MIRAGE: +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: +#endif +#if SEND_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: +#endif +#if SEND_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: +#endif +#if SEND_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: + case decode_type_t::MITSUBISHI_HEAVY_152: +#endif +#if SEND_NEOCLIMA + case decode_type_t::NEOCLIMA: +#endif +#if SEND_PANASONIC_AC + case decode_type_t::PANASONIC_AC: +#endif +#if SEND_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: +#endif +#if SEND_RHOSS + case decode_type_t::RHOSS: +#endif +#if SEND_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: +#endif +#if SEND_SANYO_AC + case decode_type_t::SANYO_AC: +#endif +#if SEND_SANYO_AC88 + case decode_type_t::SANYO_AC88: +#endif +#if SEND_SHARP_AC + case decode_type_t::SHARP_AC: +#endif +#if SEND_TCL112AC + case decode_type_t::TCL112AC: +#endif +#if SEND_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: +#endif +#if SEND_TECO + case decode_type_t::TECO: +#endif +#if SEND_TEKNOPOINT + case decode_type_t::TEKNOPOINT: +#endif // SEND_TEKNOPOINT +#if SEND_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: +#endif +#if SEND_TRANSCOLD + case decode_type_t::TRANSCOLD: +#endif +#if SEND_TROTEC + case decode_type_t::TROTEC: +#endif +#if SEND_TROTEC_3550 + case decode_type_t::TROTEC_3550: +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + case decode_type_t::TRUMA: +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case decode_type_t::VESTEL_AC: +#endif +#if SEND_VOLTAS + case decode_type_t::VOLTAS: +#endif +#if SEND_YORK + case decode_type_t::YORK: +#endif +#if SEND_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: +#endif + return true; + default: + return false; + } +} + +#if SEND_AIRTON +/// Send an Airton 56-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRAirtonAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/health/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::airton(IRAirtonAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool light, const bool econo, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Quiet setting available. + ac->setLight(light); + ac->setHealth(filter); + ac->setTurbo(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_AIRTON + +#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. +/// @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::amcor(IRAmcorAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setPower(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_AMCOR + +#if SEND_ARGO +/// Send an Argo A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRArgoAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::argo(IRArgoAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, + const bool turbo, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(static_cast(roundf(degrees))); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } + ac->setiFeel(iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setFlap(ac->convertSwingV(swingv)); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + ac->setMax(turbo); + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + ac->setNight(sleep >= 0); // Convert to a boolean. + ac->send(); +} + +/// Send an Argo A/C WREM-3 AC **control** message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The set temperature setting in degrees Celsius. +/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) +/// The overflow is *not* checked, though. +/// @note The value is rounded to nearest integer, rounding halfway cases +/// away from zero. E.g. 1.5 [C] becomes 2 [C]. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] night Enable night mode (raises temp by +1*C after 1h). +/// @param[in] econo Enable eco mode (limits power consumed). +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Enable filter mode +/// @param[in] light Enable device display/LEDs +void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on, + const stdAc::opmode_t mode, const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool iFeel, + const bool night, const bool econo, const bool turbo, const bool filter, + const bool light) { + ac->begin(); + ac->setMessageType(argoIrMessageType_t::AC_CONTROL); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } + ac->setiFeel(iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setFlap(ac->convertSwingV(swingv)); + ac->setNight(night); + ac->setEco(econo); + ac->setMax(turbo); + ac->setFilter(filter); + ac->setLight(light); + // No Clean setting available. + // No Beep setting available - always beeps in this mode :) + ac->send(); +} + +/// Send an Argo A/C WREM-3 iFeel (room temp) silent (no beep) report. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] sensorTemp The room (iFeel) temperature setting +/// in degrees Celsius. +/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) +/// The overflow is *not* checked, though. +/// @note The value is rounded to nearest integer, rounding halfway cases +/// away from zero. E.g. 1.5 [C] becomes 2 [C]. +void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) { + ac->begin(); + ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT); + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + ac->send(); +} + +/// Send an Argo A/C WREM-3 Config command. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] param The parameter ID. +/// @param[in] value The parameter value. +/// @param[in] safe If true, will only allow setting the below parameters +/// in order to avoid accidentally setting a restricted +/// vendor-specific param and breaking the A/C device +/// @note Known parameters (P, where xx is the @c param) +/// P05 - Temperature Scale (0-Celsius, 1-Fahrenheit) +/// P06 - Transmission channel (0..3) +/// P12 - ECO mode power input limit (30..99, default: 75) +void IRac::argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, + const uint8_t value, bool safe /*= true*/) { + if (safe) { + switch (param) { + case 5: // temp. scale (note this is likely excess as not transmitted) + if (value > 1) { return; /* invalid */ } + break; + case 6: // channel (note this is likely excess as not transmitted) + if (value > 3) { return; /* invalid */ } + break; + case 12: // eco power limit + if (value < 30 || value > 99) { return; /* invalid */ } + break; + default: + return; /* invalid */ + } + } + ac->begin(); + ac->setMessageType(argoIrMessageType_t::CONFIG_PARAM_SET); + ac->setConfigEntry(param, value); + ac->send(); +} + +/// Send an Argo A/C WREM-3 Delay timer command. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] on Whether the unit is currently on. The timer, upon elapse +/// will toggle this state +/// @param[in] currentTime currentTime in minutes, starting from 00:00 +/// @note For timer mode, this value is not really used much so can be zero. +/// @param[in] delayMinutes Number of minutes after which the @c on state should +/// be toggled +/// @note Schedule timers are not exposed via this interface +void IRac::argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, + const uint16_t currentTime, const uint16_t delayMinutes) { + ac->begin(); + ac->setMessageType(argoIrMessageType_t::TIMER_COMMAND); + ac->setPower(on); + ac->setTimerType(argoTimerType_t::DELAY_TIMER); + ac->setCurrentTimeMinutes(currentTime); + // Note: Day of week is not set (no need) + ac->setDelayTimerMinutes(delayMinutes); + ac->send(); +} +#endif // SEND_ARGO + +#if SEND_BOSCH144 +/// Send a Bosch144 A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRBosch144AC 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. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @note -1 is Off, >= 0 is on. +void IRac::bosch144(IRBosch144AC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const bool quiet) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setMode(ac->convertMode(mode)); + ac->setQuiet(quiet); + ac->send(); // Send the state, which will also power on the unit. + // The following are all options/settings that create their own special + // messages. Often they only make sense to be sent after the unit is turned + // on. For instance, assuming a person wants to have the a/c on and in turbo + // mode. If we send the turbo message, it is ignored if the unit is off. + // Hence we send the special mode/setting messages after a normal message + // which will turn on the device. + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Sleep setting available. +} +#endif // SEND_BOSCH144 + +#if SEND_CARRIER_AC64 +/// Send a Carrier 64-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRCarrierAc64 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::carrier64(IRCarrierAc64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV((int8_t)swingv >= 0); + // 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. + ac->setSleep(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_CARRIER_AC64 + +#if SEND_COOLIX +/// Send a Coolix A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRCoolixAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::coolix(IRCoolixAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool light, + const bool clean, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } else { + ac->clearSensorTemp(); + } + ac->setZoneFollow(iFeel); + ac->send(); // Send the state, which will also power on the unit. + // The following are all options/settings that create their own special + // messages. Often they only make sense to be sent after the unit is turned + // on. For instance, assuming a person wants to have the a/c on and in turbo + // mode. If we send the turbo message, it is ignored if the unit is off. + // Hence we send the special mode/setting messages after a normal message + // which will turn on the device. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + if (turbo) { + // Turbo has a special command that needs to be sent independently. + ac->setTurbo(); + ac->send(); + } + if (sleep >= 0) { + // Sleep has a special command that needs to be sent independently. + ac->setSleep(); + ac->send(); + } + if (light) { + // Light has a special command that needs to be sent independently. + ac->setLed(); + ac->send(); + } + if (clean) { + // Clean has a special command that needs to be sent independently. + ac->setClean(); + ac->send(); + } +} +#endif // SEND_COOLIX + +#if SEND_CORONA_AC +/// Send a Corona A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRCoronaAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] econo Run the device in economical mode. +void IRac::corona(IRCoronaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool econo) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_CARRIER_AC64 + +#if SEND_DAIKIN +/// Send a Daikin A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikinESP 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setMold(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN + +#if SEND_DAIKIN128 +/// Send a Daikin 128-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin128 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::daikin128(IRDaikin128 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool light, + const bool econo, const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + // No Horizontal Swing setting avaliable. + ac->setQuiet(quiet); + ac->setLightToggle(light ? kDaikin128BitWall : 0); + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep > 0); + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN128 + +#if SEND_DAIKIN152 +/// Send a Daikin 152-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin152 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +void IRac::daikin152(IRDaikin152 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV((int8_t)swingv >= 0); + // No Horizontal Swing setting avaliable. + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN152 + +#if SEND_DAIKIN160 +/// Send a Daikin 160-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin160 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. +/// @param[in] swingv The vertical swing setting. +void IRac::daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->send(); +} +#endif // SEND_DAIKIN160 + +#if SEND_DAIKIN176 +/// Send a Daikin 176-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin176 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. +/// @param[in] swingh The horizontal swing setting. +void IRac::daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->send(); +} +#endif // SEND_DAIKIN176 + +#if SEND_DAIKIN2 +/// Send a Daikin2 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin2 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setLight(light ? 1 : 3); // On/High is 1, Off is 3. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setPurify(filter); + ac->setMold(clean); + ac->setClean(true); // Hardwire auto clean to be on per request (@sheppy99) + ac->setBeep(beep ? 2 : 3); // On/Loud is 2, Off is 3. + if (sleep > 0) ac->enableSleepTimer(sleep); + if (clock >= 0) ac->setCurrentTime(clock); + ac->send(); +} +#endif // SEND_DAIKIN2 + +#if SEND_DAIKIN216 +/// Send a Daikin 216-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin216 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +void IRac::daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->send(); +} +#endif // SEND_DAIKIN216 + +#if SEND_DAIKIN64 +/// Send a Daikin 64-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin64 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::daikin64(IRDaikin64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, + const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setTurbo(turbo); + ac->setQuiet(quiet); + ac->setSleep(sleep >= 0); + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN64 + +#if SEND_DELONGHI_AC +/// Send a Delonghi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDelonghiAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::delonghiac(IRDelonghiAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const bool turbo, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setBoost(turbo); + ac->setSleep(sleep >= 0); + ac->send(); +} +#endif // SEND_DELONGHI_AC + +#if SEND_ECOCLIM +/// Send an EcoClim A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IREcoclimAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::ecoclim(IREcoclimAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const int16_t sleep, + const int16_t clock) { + ac->begin(); + ac->setPower(on); + uint8_t new_mode; + if (sleep >= 0) // EcoClim has a descrete Sleep operation mode, not a setting + new_mode = kEcoclimSleep; // Override the requested operating mode. + else + new_mode = ac->convertMode(mode); // Not Sleep, so use the supplied mode. + ac->setMode(new_mode); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } else { + ac->setSensorTemp(degrees); //< Set to the desired temp + // until we can disable. + } + // No SwingV setting available + // No SwingH setting available + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_ECOCLIM + +#if SEND_ELECTRA_AC +/// Send an Electra A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRElectraAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] lighttoggle Should we toggle the LED/Display? +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::electra(IRElectraAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool iFeel, + const bool turbo, const bool lighttoggle, const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLightToggle(lighttoggle); + // No Econo setting available. + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->setIFeel(iFeel); + ac->send(); +} +#endif // SEND_ELECTRA_AC + +#if SEND_FUJITSU_AC +/// Send a Fujitsu A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRFujitsuAC object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. +void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + if (on) { + // Do all special messages (except "Off") first, + // These need to be sent separately. + switch (ac->getModel()) { + // Some functions are only available on some models. + case fujitsu_ac_remote_model_t::ARREB1E: + if (turbo) { + ac->setCmd(kFujitsuAcCmdPowerful); + // Powerful is a separate command. + ac->send(); + } + if (econo) { + ac->setCmd(kFujitsuAcCmdEcono); + // Econo is a separate command. + ac->send(); + } + break; + default: + {}; + } + // Normal operation. + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFanSpeed(ac->convertFan(fan)); + uint8_t swing = kFujitsuAcSwingOff; + if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; + if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; + ac->setSwing(swing); + if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); + // No Light setting available. + ac->setFilter(filter); + ac->setClean(clean); + // No Beep setting available. + ac->setSleepTimer(sleep > 0 ? sleep : 0); + // No Sleep setting available. + // No Clock setting available. + ac->on(); // Ref: Issue #860 + } else { + // Off is special case/message. We don't need to send other messages. + ac->off(); + } + ac->send(); +} +#endif // SEND_FUJITSU_AC + +#if SEND_FUJITSU_AC264 +/// Send a Fujitsu A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRFujitsuAC264 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Toggle the device's turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignored. +void IRac::fujitsu264(IRFujitsuAC264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo, + const bool clean, const int16_t sleep, + const int16_t clock) { + ac->begin(); + if (on) { + // Do all special messages (except "Off") first, + // These need to be sent separately. + // Some functions are only available on some models. + if (turbo) { + ac->togglePowerful(); + // Powerful is a separate command. + ac->send(); + } + // Normal operation. + ac->setMode(ac->convertMode(mode)); + if (mode == stdAc::opmode_t::kAuto) + ac->setTempAuto(degrees); + else + ac->setTemp(degrees); + ac->setFanSpeed(ac->convertFanSpeed(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + if (quiet) ac->setFanSpeed(kFujitsuAc264FanSpeedQuiet); + ac->setEconomy(econo); + ac->setClean(clean); + ac->setSleepTimer(sleep > 0 ? sleep : 0); + if (clock >= 0) ac->setClock(clock); + ac->on(); + } else { + // Off is special case/message. We don't need to send other messages. + ac->off(); + } + ac->send(); +} +#endif // SEND_FUJITSU_AC264 + +#if SEND_GOODWEATHER +/// Send a Goodweather A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGoodweatherAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::goodweather(IRGoodweatherAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff + : kGoodweatherSwingSlow); + ac->setTurbo(turbo); + ac->setLight(light); + // No Clean setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_GOODWEATHER + +#if SEND_GREE +/// Send a Gree A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGreeAC object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Toggle the device's economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool econo, + const bool light, const bool clean, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setIFeel(iFeel); + ac->setLight(light); + ac->setTurbo(turbo); + ac->setEcono(econo); + ac->setXFan(clean); + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_GREE + +#if SEND_HAIER_AC +/// Send a Haier A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGreeAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + if (clock >= 0) ac->setCurrTime(clock); + if (on) + ac->setCommand(kHaierAcCmdOn); + else + ac->setCommand(kHaierAcCmdOff); + ac->send(); +} +#endif // SEND_HAIER_AC + +#if SEND_HAIER_AC160 +/// Send a Haier 160 bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierAC160 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the clean mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] prevlight Previous LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haier160(IRHaierAC160 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool quiet, const bool filter, + const bool clean, const bool light, const bool prevlight, + const int16_t sleep) { + ac->begin(); + // No Model setting available. + ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setHealth(filter); + ac->setClean(clean); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + // Light needs to be sent last as the "button" value seems to control it. + ac->setLightToggle(light ^ prevlight); + ac->send(); +} +#endif // SEND_HAIER_AC160 + +#if SEND_HAIER_AC176 +/// Send a Haier 176 bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haier176(IRHaierAC176 *ac, const haier_ac176_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC176 + +#if SEND_HAIER_AC_YRW02 +/// Send a Haier YRWO2 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haierYrwo2(IRHaierACYRW02 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC_YRW02 + +#if SEND_HITACHI_AC +/// Send a Hitachi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC + +#if SEND_HITACHI_AC1 +/// Send a Hitachi1 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc1 object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] power_toggle The power toggle 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] swing_toggle The swing_toggle setting. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @note The sleep mode used is the "Sleep 2" setting. +void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, + const bool on, const bool power_toggle, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool swing_toggle, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setPowerToggle(power_toggle); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + ac->setSwingToggle(swing_toggle); + ac->setSleep((sleep >= 0) ? kHitachiAc1Sleep2 : kHitachiAc1SleepOff); + // No Sleep setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC1 + +#if SEND_HITACHI_AC264 +/// Send a Hitachi 264-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc264 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::hitachi264(IRHitachiAc264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // No Swing(V) setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC264 + +#if SEND_HITACHI_AC296 +/// Send a Hitachi 296-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc296 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::hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // No Swing(V) setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC296 + +#if SEND_HITACHI_AC344 +/// Send a Hitachi 344-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc344 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::hitachi344(IRHitachiAc344 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setPower(on); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + + // SwingVToggle is special. Needs to be last method called. + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + ac->send(); +} +#endif // SEND_HITACHI_AC344 + +#if SEND_HITACHI_AC424 +/// Send a Hitachi 424-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc424 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. +/// @param[in] swingv The vertical swing setting. +void IRac::hitachi424(IRHitachiAc424 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // SwingVToggle is special. Needs to be last method called. + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC424 + +#if SEND_KELON +/// Send a Kelon A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRKelonAc object to use. +/// @param[in] togglePower Whether to toggle the unit's power +/// @param[in] mode The operation mode setting. +/// @param[in] dryGrade The dehumidification intensity grade +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] toggleSwing Whether to toggle the swing setting +/// @param[in] superCool Run the device in Super cooling mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on +void IRac::kelon(IRKelonAc *ac, const bool togglePower, + const stdAc::opmode_t mode, const int8_t dryGrade, + const float degrees, const stdAc::fanspeed_t fan, + const bool toggleSwing, const bool superCool, + const int16_t sleep) { + ac->begin(); + ac->setMode(IRKelonAc::convertMode(mode)); + ac->setFan(IRKelonAc::convertFan(fan)); + ac->setTemp(static_cast(degrees)); + ac->setSleep(sleep >= 0); + ac->setSupercool(superCool); + ac->setDryGrade(dryGrade); + + ac->setTogglePower(togglePower); + ac->setToggleSwingVertical(toggleSwing); + + ac->send(); +} +#endif // SEND_KELON + +#if SEND_KELVINATOR +/// Send a Kelvinator A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRKelvinatorAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc +void IRac::kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan((uint8_t)fan); // No conversion needed. + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setIonFilter(filter); + ac->setXFan(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_KELVINATOR + +#if SEND_LG +/// Send a LG A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRLgAc object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingv_prev The previous vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] light Turn on the LED/Display mode. +void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, + const stdAc::swingh_t swingh, const bool light) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv_prev)); + ac->updateSwingPrev(); + ac->setSwingV(ac->convertSwingV(swingv)); + const uint8_t pos = ac->convertVaneSwingV(swingv); + for (uint8_t vane = 0; vane < kLgAcSwingVMaxVanes; vane++) + ac->setVaneSwingV(vane, pos); + // Toggle the swingv for LG6711A20083V models if we need to. + // i.e. Off to Not-Off, send a toggle. Not-Off to Off, send a toggle. + if ((model == lg_ac_remote_model_t::LG6711A20083V) && + ((swingv == stdAc::swingv_t::kOff) != + (swingv_prev == stdAc::swingv_t::kOff))) + ac->setSwingV(kLgAcSwingVToggle); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_LG + +#if SEND_MIDEA +/// Send a Midea A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMideaAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] sensorTemp The room (iFeel) temperature sensor reading +/// in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] quiet_prev The device's previous quiet/silent mode. +/// @param[in] turbo Toggle the device's turbo/powerful mode. +/// @param[in] econo Toggle the device's economical mode. +/// @param[in] light Toggle the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @note On Danby A/C units, swingv controls the Ion Filter instead. +void IRac::midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool iFeel, const bool quiet, const bool quiet_prev, + const bool turbo, const bool econo, const bool light, + const bool clean, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setUseCelsius(celsius); + ac->setTemp(degrees, celsius); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(sensorTemp, celsius); + } + ac->setEnableSensorTemp(iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + ac->setQuiet(quiet, quiet_prev); + ac->setTurboToggle(turbo); + ac->setEconoToggle(econo); + ac->setLightToggle(light); + // No Filter setting available. + ac->setCleanToggle(clean); + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MIDEA + +#if SEND_MIRAGE +/// Send a Mirage 120-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. +/// @param[in] state The desired state to send. +void IRac::mirage(IRMirageAc *ac, const stdAc::state_t state) { + ac->begin(); + ac->fromCommon(state); + ac->send(); +} +#endif // SEND_MIRAGE + +#if SEND_MITSUBISHI_AC +/// Send a Mitsubishi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @note Clock can only be set in 10 minute increments. i.e. % 10. +void IRac::mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const int16_t clock) { + ac->begin(); + // Uncomment next line if you *really* need the weekly timer enabled via IRac. + // ac->setWeeklyTimerEnabled(true); // Weekly Timer is disabled by default. + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setVane(ac->convertSwingV(swingv)); + ac->setVaneLeft(ac->convertSwingV(swingv)); + ac->setWideVane(ac->convertSwingH(swingh)); + if (quiet) ac->setFan(kMitsubishiAcFanSilent); + ac->setISave10C(false); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. + ac->send(); +} +#endif // SEND_MITSUBISHI_AC + +#if SEND_MITSUBISHI112 +/// Send a Mitsubishi 112-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishi112 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +void IRac::mitsubishi112(IRMitsubishi112 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + // FIXME - Econo + // ac->setEcono(econo); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHI112 + +#if SEND_MITSUBISHI136 +/// Send a Mitsubishi 136-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishi136 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +void IRac::mitsubishi136(IRMitsubishi136 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + ac->setQuiet(quiet); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHI136 + +#if SEND_MITSUBISHIHEAVY +/// Send a Mitsubishi Heavy 88-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy88Ac 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, + const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} + +/// Send a Mitsubishi Heavy 152-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy152Ac 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, + const bool econo, const bool filter, + const bool clean, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setSilent(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + ac->setClean(clean); + ac->setFilter(filter); + // No Beep setting available. + ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHIHEAVY + +#if SEND_NEOCLIMA +/// Send a Neoclima A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRNeoclimaAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::neoclima(IRNeoclimaAc *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const bool filter, const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_NEOCLIMA + +#if SEND_PANASONIC_AC +/// Send a Panasonic A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRPanasonicAc object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool filter, + const int16_t clock) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->setIon(filter); + // No Light setting available. + // No Econo setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_PANASONIC_AC + +#if SEND_PANASONIC_AC32 +/// Send a Panasonic A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRPanasonicAc32 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::panasonic32(IRPanasonicAc32 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Filter setting available. + // No Light setting available. + // No Econo setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_PANASONIC_AC32 + +#if SEND_SAMSUNG_AC +/// Send a Samsung A/C message with the supplied settings. +/// @note Multiple IR messages may be generated & sent. +/// @param[in, out] ac A Ptr to an IRSamsungAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Toggle the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Toggle beep setting for receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. +/// @param[in] prevpower The power setting from the previous A/C state. +/// @param[in] prevsleep Nr. of minutes for sleep from the previous A/C state. +/// @param[in] forceextended Do we force sending the special extended message? +void IRac::samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, + const bool filter, const bool clean, + const bool beep, const int16_t sleep, + const bool prevpower, const int16_t prevsleep, + const bool forceextended) { + ac->begin(); + ac->stateReset(forceextended || (sleep != prevsleep), prevpower); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + ac->setQuiet(quiet); + ac->setPowerful(turbo); // FYI, `setEcono(true)` will override this. + ac->setDisplay(light); + ac->setEcono(econo); + ac->setIon(filter); + ac->setClean(clean); // Toggle + ac->setBeep(beep); // Toggle + ac->setSleepTimer((sleep <= 0) ? 0 : sleep); + // No Clock setting available. + // Do setMode() again as it can affect fan speed. + ac->setMode(ac->convertMode(mode)); + ac->send(); +} +#endif // SEND_SAMSUNG_AC + +#if SEND_SANYO_AC +/// Send a Sanyo A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRSanyoAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::sanyo(IRSanyoAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool iFeel, const bool beep, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } else { + ac->setSensorTemp(degrees); // Set the sensor temp to the desired + // (normal) temp. + } + ac->setSensor(!iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Econo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + ac->setBeep(beep); + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_SANYO_AC + +#if SEND_SANYO_AC88 +/// Send a Sanyo 88-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRSanyoAc88 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::sanyo88(IRSanyoAc88 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool filter, const int16_t sleep, + const int16_t clock) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Econo setting available. + // No Light setting available. + ac->setFilter(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_SANYO_AC88 + +#if SEND_SHARP_AC +/// Send a Sharp A/C message with the supplied settings. +/// @note Multiple IR messages may be generated & sent. +/// @param[in, out] ac A Ptr to an IRSharpAc object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] prev_power The power setting from the previous A/C state. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingv_prev The previous vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, + const bool on, const bool prev_power, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingv_t swingv_prev, const bool turbo, + const bool light, const bool filter, const bool clean) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan, model)); + if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv)); + // Econo deliberately not used as it cycles through 3 modes uncontrollably. + // ac->setEconoToggle(econo); + ac->setIon(filter); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setLightToggle(light); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + // Do setMode() again as it can affect fan speed and temp. + ac->setMode(ac->convertMode(mode)); + // Clean after mode, as it can affect the mode, temp & fan speed. + if (clean) { + // A/C needs to be off before we can enter clean mode. + ac->setPower(false, prev_power); + ac->send(); + } + ac->setClean(clean); + ac->setPower(on, prev_power); + if (turbo) { + ac->send(); // Send the current state. + // Set up turbo mode as it needs to be sent after everything else. + ac->setTurbo(true); + } + ac->send(); +} +#endif // SEND_SHARP_AC + +#if SEND_TCL112AC +/// Send a TCL 112-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +void IRac::tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TCL112AC + +#if SEND_TECHNIBEL_AC +/// Send a Technibel A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTechnibelAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::technibel(IRTechnibelAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECHNIBEL_AC + +#if SEND_TECO +/// Send a Teco A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTecoAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool light, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECO + +#if SEND_TOSHIBA_AC +/// Send a Toshiba A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRToshibaAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (Pure/ion/pollen/etc) filter mode. +void IRac::toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool econo, const bool filter) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // The API has no "step" option, so off is off, anything else is on. + ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff + : kToshibaAcSwingOn); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setEcono(econo); + // No Light setting available. + ac->setFilter(filter); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + // Do this last because Toshiba A/C has an odd quirk with how power off works. + ac->setPower(on); + ac->send(); +} +#endif // SEND_TOSHIBA_AC + +#if SEND_TROTEC +/// Send a Trotec A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrotecESP 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. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setSpeed(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC + +#if SEND_TROTEC_3550 +/// Send a Trotec 3550 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +void IRac::trotec3550(IRTrotec3550 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC_3550 + +#if SEND_TRUMA +/// Send a Truma A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrumaAc 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. +/// @param[in] quiet Run the device quietly if we can. +void IRac::truma(IRTrumaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setQuiet(quiet); // Only available in Cool mode. + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TRUMA + +#if SEND_VESTEL_AC +/// Send a Vestel A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRVestelAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @param[in] sendNormal Do we send a Normal settings message at all? +/// i.e In addition to the clock/time/timer message +void IRac::vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, const int16_t sleep, + const int16_t clock, const bool sendNormal) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (sendNormal) ac->send(); // Send the normal message. + if (clock >= 0) { + ac->setTime(clock); + ac->send(); // Setting the clock requires a different "timer" message. + } +} +#endif // SEND_VESTEL_AC + +#if SEND_VOLTAS +/// Send a Voltas A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRVoltas object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::voltas(IRVoltas *ac, + const voltas_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setEcono(econo); + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_VOLTAS + +#if SEND_WHIRLPOOL_AC +/// Send a Whirlpool A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRWhirlpoolAc object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setSuper(turbo); + ac->setLight(light); + // No Filter setting available + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->setPowerToggle(on); + ac->send(); +} +#endif // SEND_WHIRLPOOL_AC + +#if SEND_TRANSCOLD +/// Send a Transcold A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRTranscoldAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @note -1 is Off, >= 0 is on. +void IRac::transcold(IRTranscoldAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + + ac->send(); +} +#endif // SEND_TRANSCOLD + +#if SEND_RHOSS +/// Send an Rhoss A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRRhossAc 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. +/// @param[in] swing The swing setting. +void IRac::rhoss(IRRhossAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swing) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setSwing(swing != stdAc::swingv_t::kOff); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // 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_RHOSS + +/// Create a new state base on the provided state that has been suitably fixed. +/// @note This is for use with Home Assistant, which requires mode to be off if +/// the power is off. +/// @param[in] state The state_t structure describing the desired a/c state. +/// @return A stdAc::state_t with the needed settings. +stdAc::state_t IRac::cleanState(const stdAc::state_t state) { + stdAc::state_t result = state; + // A hack for Home Assistant, it appears to need/want an Off opmode. + // So enforce the power is off if the mode is also off. + if (state.mode == stdAc::opmode_t::kOff) result.power = false; + return result; +} + +/// Create a new state base on desired & previous states but handle +/// any state changes for options that need to be toggled. +/// @param[in] desired The state_t structure describing the desired a/c state. +/// @param[in] prev A Ptr to the previous state_t structure. +/// @return A stdAc::state_t with the needed settings. +stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev) { + stdAc::state_t result = desired; + // If we've been given a previous state AND the it's the same A/C basically. + if (prev != NULL && desired.protocol == prev->protocol && + desired.model == prev->model) { + // Check if we have to handle toggle settings for specific A/C protocols. + switch (desired.protocol) { + case decode_type_t::COOLIX: + case decode_type_t::TRANSCOLD: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + result.turbo = desired.turbo ^ prev->turbo; + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + result.sleep = ((desired.sleep >= 0) ^ (prev->sleep >= 0)) ? 0 : -1; + break; + case decode_type_t::DAIKIN128: + result.power = desired.power ^ prev->power; + result.light = desired.light ^ prev->light; + break; + case decode_type_t::ELECTRA_AC: + result.light = desired.light ^ prev->light; + break; + case decode_type_t::FUJITSU_AC: + result.turbo = desired.turbo ^ prev->turbo; + result.econo = desired.econo ^ prev->econo; + break; + case decode_type_t::MIDEA: + result.turbo = desired.turbo ^ prev->turbo; + result.econo = desired.econo ^ prev->econo; + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + // FALL THRU + case decode_type_t::CORONA_AC: + case decode_type_t::HITACHI_AC344: + case decode_type_t::HITACHI_AC424: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + break; + case decode_type_t::SHARP_AC: + result.light = desired.light ^ prev->light; + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + break; + case decode_type_t::KELON: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + // FALL-THRU + case decode_type_t::AIRWELL: + case decode_type_t::DAIKIN64: + case decode_type_t::PANASONIC_AC32: + case decode_type_t::WHIRLPOOL_AC: + result.power = desired.power ^ prev->power; + break; + case decode_type_t::MIRAGE: + if (desired.model == mirage_ac_remote_model_t::KKG29AC1) + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + break; + case decode_type_t::PANASONIC_AC: + // CKP models use a power mode toggle. + if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) + result.power = desired.power ^ prev->power; + break; + case decode_type_t::SAMSUNG_AC: + result.beep = desired.beep ^ prev->beep; + result.clean = desired.clean ^ prev->clean; + break; + default: + {}; + } + } + return result; +} + +/// Send A/C message for a given device using common A/C settings. +/// @param[in] vendor The vendor/protocol type. +/// @param[in] model The A/C model if applicable. +/// @param[in] power The power setting. +/// @param[in] mode The operation mode setting. +/// @note Changing mode from "Off" to something else does NOT turn on a device. +/// You need to use `power` for that. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] fan The speed setting for the fan. +/// @note The following are all "if supported" by the underlying A/C classes. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. +/// Others it may be the time to enter/exit sleep mode. +/// i.e. Time in Nr. of mins since midnight. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @return True, if accepted/converted/attempted etc. False, if unsupported. +bool IRac::sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + stdAc::state_t to_send; + initState(&to_send, vendor, model, power, mode, degrees, celsius, fan, swingv, + swingh, quiet, turbo, econo, light, filter, clean, beep, sleep, + clock); + return this->sendAc(to_send, &to_send); +} + +/// Send A/C message for a given device using state_t structures. +/// @param[in] desired The state_t structure describing the desired new ac state +/// @param[in] prev A Ptr to the state_t structure containing the previous state +/// @note Changing mode from "Off" to something else does NOT turn on a device. +/// You need to use `power` for that. +/// @return True, if accepted/converted/attempted etc. False, if unsupported. +bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { + // Convert the temp from Fahrenheit to Celsius if we are not in Celsius mode. + float degC __attribute__((unused)) = + desired.celsius ? desired.degrees : fahrenheitToCelsius(desired.degrees); + // Convert the sensorTemp from Fahrenheit to Celsius if we are not in Celsius + // mode. + float sensorTempC __attribute__((unused)) = + desired.sensorTemperature ? desired.sensorTemperature + : fahrenheitToCelsius(desired.sensorTemperature); + // special `state_t` that is required to be sent based on that. + stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev); + // Some protocols expect a previous state for power. + // Construct a pointer-safe previous power state incase prev is NULL/NULLPTR. +#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) + const bool prev_power = (prev != NULL) ? prev->power : !send.power; + const int16_t prev_sleep = (prev != NULL) ? prev->sleep : -1; +#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) +#if (SEND_LG || SEND_SHARP_AC) + const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv + : stdAc::swingv_t::kOff; +#endif // (SEND_LG || SEND_SHARP_AC) +#if (SEND_HAIER_AC160) + const bool prev_light = (prev != NULL) ? prev->light : !send.light; +#endif // (SEND_HAIER_AC160) +#if SEND_MIDEA + const bool prev_quiet = (prev != NULL) ? prev->quiet : !send.quiet; +#endif // SEND_MIDEA + // Per vendor settings & setup. + switch (send.protocol) { +#if SEND_AIRTON + case AIRTON: + { + IRAirtonAc ac(_pin, _inverted, _modulation); + airton(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.turbo, send.light, send.econo, send.filter, + send.sleep); + break; + } +#endif // SEND_AIRTON +#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: + { + IRAmcorAc ac(_pin, _inverted, _modulation); + amcor(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_AMCOR +#if SEND_ARGO + case ARGO: + { + if (send.model == argo_ac_remote_model_t::SAC_WREM3) { + IRArgoAC_WREM3 ac(_pin, _inverted, _modulation); + switch (send.command) { + case stdAc::ac_command_t::kSensorTempReport: + argoWrem3_iFeelReport(&ac, sensorTempC); + break; + case stdAc::ac_command_t::kConfigCommand: + /// @warning: this is ABUSING current **common** parameters: + /// @c clock and @c sleep as config key and value + /// Hence, value pre-validation is performed (safe-mode) + /// to avoid accidental device misconfiguration + argoWrem3_ConfigSet(&ac, send.clock, send.sleep, true); + break; + case stdAc::ac_command_t::kTimerCommand: + argoWrem3_SetTimer(&ac, send.power, send.clock, send.sleep); + break; + case stdAc::ac_command_t::kControlCommand: + default: + argoWrem3_ACCommand(&ac, send.power, send.mode, degC, sensorTempC, + send.fanspeed, send.swingv, send.iFeel, send.quiet, send.econo, + send.turbo, send.filter, send.light); + break; + } + OUTPUT_DECODE_RESULTS_FOR_UT(ac); + } else { + IRArgoAC ac(_pin, _inverted, _modulation); + argo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.iFeel, send.turbo, send.sleep); + OUTPUT_DECODE_RESULTS_FOR_UT(ac); + } + break; + } +#endif // SEND_ARGO +#if SEND_BOSCH144 + case BOSCH144: + { + IRBosch144AC ac(_pin, _inverted, _modulation); + bosch144(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); + break; + } +#endif // SEND_BOSCH144 +#if SEND_CARRIER_AC64 + case CARRIER_AC64: + { + IRCarrierAc64 ac(_pin, _inverted, _modulation); + carrier64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.sleep); + break; + } +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + case COOLIX: + { + IRCoolixAC ac(_pin, _inverted, _modulation); + coolix(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.swingh, send.iFeel, send.turbo, send.light, + send.clean, send.sleep); + break; + } +#endif // SEND_COOLIX +#if SEND_CORONA_AC + case CORONA_AC: + { + IRCoronaAc ac(_pin, _inverted, _modulation); + corona(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.econo); + break; + } +#endif // SEND_CORONA_AC +#if SEND_DAIKIN + case DAIKIN: + { + IRDaikinESP ac(_pin, _inverted, _modulation); + daikin(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.econo, send.clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + { + IRDaikin128 ac(_pin, _inverted, _modulation); + daikin128(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.light, send.econo, send.sleep, + send.clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN152 + case DAIKIN152: + { + IRDaikin152 ac(_pin, _inverted, _modulation); + daikin152(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.econo); + break; + } +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + case DAIKIN160: + { + IRDaikin160 ac(_pin, _inverted, _modulation); + daikin160(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + { + IRDaikin176 ac(_pin, _inverted, _modulation); + daikin176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingh); + break; + } +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + { + IRDaikin2 ac(_pin, _inverted, _modulation); + daikin2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.light, send.econo, + send.filter, send.clean, send.beep, send.sleep, send.clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + { + IRDaikin216 ac(_pin, _inverted, _modulation); + daikin216(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 + case DAIKIN64: + { + IRDaikin64 ac(_pin, _inverted, _modulation); + daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.sleep, send.clock); + break; + } +#endif // SEND_DAIKIN64 +#if SEND_DELONGHI_AC + case DELONGHI_AC: + { + IRDelonghiAc ac(_pin, _inverted, _modulation); + delonghiac(&ac, send.power, send.mode, send.celsius, degC, send.fanspeed, + send.turbo, send.sleep); + break; + } +#endif // SEND_DELONGHI_AC +#if SEND_ECOCLIM + case ECOCLIM: + { + IREcoclimAc ac(_pin, _inverted, _modulation); + ecoclim(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.iFeel, send.clock); + break; + } +#endif // SEND_ECOCLIM +#if SEND_ELECTRA_AC + case ELECTRA_AC: + { + IRElectraAc ac(_pin, _inverted, _modulation); + electra(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.swingh, send.iFeel, send.turbo, send.light, + send.clean); + break; + } +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + { + IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)send.model, _inverted, + _modulation); + fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode, + send.celsius, send.degrees, send.fanspeed, + send.swingv, send.swingh, send.quiet, + send.turbo, send.econo, send.filter, send.clean, send.sleep); + break; + } +#endif // SEND_FUJITSU_AC +#if SEND_FUJITSU_AC264 + case FUJITSU_AC264: + { + IRFujitsuAC264 ac(_pin, _inverted, _modulation); + fujitsu264(&ac, send.power, send.mode, send.degrees, send.fanspeed, + send.swingv, send.quiet, send.turbo, send.econo, + send.clean, send.sleep); + break; + } +#endif // SEND_FUJITSU_AC264 +#if SEND_GOODWEATHER + case GOODWEATHER: + { + IRGoodweatherAc ac(_pin, _inverted, _modulation); + goodweather(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.light, send.sleep); + break; + } +#endif // SEND_GOODWEATHER +#if SEND_GREE + case GREE: + { + IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, + _modulation); + gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, + send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, + send.iFeel, send.turbo, send.econo, send.light, send.clean, + send.sleep); + break; + } +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + { + IRHaierAC ac(_pin, _inverted, _modulation); + haier(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC160 + case HAIER_AC160: + { + IRHaierAC160 ac(_pin, _inverted, _modulation); + haier160(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.turbo, send.filter, send.clean, + send.light, prev_light, send.sleep); + break; + } +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + case HAIER_AC176: + { + IRHaierAC176 ac(_pin, _inverted, _modulation); + haier176(&ac, (haier_ac176_remote_model_t)send.model, send.power, + send.mode, send.celsius, send.degrees, send.fanspeed, + send.swingv, send.swingh, send.turbo, send.filter, send.sleep); + break; + } +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + { + IRHaierACYRW02 ac(_pin, _inverted, _modulation); + haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.swingh, send.turbo, + send.filter, send.sleep); + break; + } +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + { + IRHitachiAc ac(_pin, _inverted, _modulation); + hitachi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh); + break; + } +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + { + IRHitachiAc1 ac(_pin, _inverted, _modulation); + bool power_toggle = false; + bool swing_toggle = false; + if (prev != NULL) { + power_toggle = (send.power != prev->power); + swing_toggle = (send.swingv != prev->swingv) || + (send.swingh != prev->swingh); + } + hitachi1(&ac, (hitachi_ac1_remote_model_t)send.model, send.power, + power_toggle, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, swing_toggle, send.sleep); + break; + } +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + case HITACHI_AC264: + { + IRHitachiAc264 ac(_pin, _inverted, _modulation); + hitachi264(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + { + IRHitachiAc296 ac(_pin, _inverted, _modulation); + hitachi296(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + case HITACHI_AC344: + { + IRHitachiAc344 ac(_pin, _inverted, _modulation); + hitachi344(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh); + break; + } +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + case HITACHI_AC424: + { + IRHitachiAc424 ac(_pin, _inverted, _modulation); + hitachi424(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_HITACHI_AC424 +#if SEND_KELON + case KELON: { + IRKelonAc ac(_pin, _inverted, _modulation); + kelon(&ac, send.power, send.mode, 0, send.degrees, send.fanspeed, + send.swingv != stdAc::swingv_t::kOff, send.turbo, send.sleep); + break; + } +#endif +#if SEND_KELVINATOR + case KELVINATOR: + { + IRKelvinatorAC ac(_pin, _inverted, _modulation); + kelvinator(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.light, send.filter, + send.clean); + break; + } +#endif // SEND_KELVINATOR +#if SEND_LG + case LG: + case LG2: + { + IRLgAc ac(_pin, _inverted, _modulation); + lg(&ac, (lg_ac_remote_model_t)send.model, send.power, send.mode, + send.degrees, send.fanspeed, send.swingv, prev_swingv, send.swingh, + send.light); + break; + } +#endif // SEND_LG +#if SEND_MIDEA + case MIDEA: + { + IRMideaAC ac(_pin, _inverted, _modulation); + midea(&ac, send.power, send.mode, send.celsius, send.degrees, + send.sensorTemperature, send.fanspeed, send.swingv, send.iFeel, + send.quiet, prev_quiet, send.turbo, send.econo, send.light, + send.clean, send.sleep); + break; + } +#endif // SEND_MIDEA +#if SEND_MIRAGE + case MIRAGE: + { + IRMirageAc ac(_pin, _inverted, _modulation); + mirage(&ac, send); + break; + } +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + { + IRMitsubishiAC ac(_pin, _inverted, _modulation); + mitsubishi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.clock); + break; + } +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + { + IRMitsubishi112 ac(_pin, _inverted, _modulation); + mitsubishi112(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.quiet); + break; + } +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHI136 + case MITSUBISHI136: + { + IRMitsubishi136 ac(_pin, _inverted, _modulation); + mitsubishi136(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.quiet); + break; + } +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + { + IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); + mitsubishiHeavy88(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.turbo, send.econo, + send.clean); + break; + } + case MITSUBISHI_HEAVY_152: + { + IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); + mitsubishiHeavy152(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.quiet, send.turbo, + send.econo, send.filter, send.clean, send.sleep); + break; + } +#endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + case NEOCLIMA: + { + IRNeoclimaAc ac(_pin, _inverted, _modulation); + neoclima(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.swingh, send.turbo, + send.econo, send.light, send.filter, send.sleep); + break; + } +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + { + IRPanasonicAc ac(_pin, _inverted, _modulation); + panasonic(&ac, (panasonic_ac_remote_model_t)send.model, send.power, + send.mode, degC, send.fanspeed, send.swingv, send.swingh, + send.quiet, send.turbo, send.clock); + break; + } +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC32 + case PANASONIC_AC32: + { + IRPanasonicAc32 ac(_pin, _inverted, _modulation); + panasonic32(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh); + break; + } +#endif // SEND_PANASONIC_AC32 +#if SEND_RHOSS + case RHOSS: + { + IRRhossAc ac(_pin, _inverted, _modulation); + rhoss(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + { + IRSamsungAc ac(_pin, _inverted, _modulation); + samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.econo, send.light, + send.filter, send.clean, send.beep, send.sleep, + prev_power, prev_sleep); + break; + } +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + { + IRSanyoAc ac(_pin, _inverted, _modulation); + sanyo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.iFeel, send.beep, send.sleep); + break; + } +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + case SANYO_AC88: + { + IRSanyoAc88 ac(_pin, _inverted, _modulation); + sanyo88(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_SANYO_AC88 +#if SEND_SHARP_AC + case SHARP_AC: + { + IRSharpAc ac(_pin, _inverted, _modulation); + sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power, + send.mode, degC, send.fanspeed, send.swingv, prev_swingv, + send.turbo, send.light, send.filter, send.clean); + break; + } +#endif // SEND_SHARP_AC +#if (SEND_TCL112AC || SEND_TEKNOPOINT) + case TCL112AC: + case TEKNOPOINT: + { + IRTcl112Ac ac(_pin, _inverted, _modulation); + tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model; + if (send.protocol == decode_type_t::TEKNOPOINT) + model = tcl_ac_remote_model_t::GZ055BE1; + tcl112(&ac, model, send.power, send.mode, + degC, send.fanspeed, send.swingv, send.swingh, send.quiet, + send.turbo, send.light, send.econo, send.filter); + break; + } +#endif // (SEND_TCL112AC || SEND_TEKNOPOINT) +#if SEND_TECHNIBEL_AC + case TECHNIBEL_AC: + { + IRTechnibelAc ac(_pin, _inverted, _modulation); + technibel(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.sleep); + break; + } +#endif // SEND_TECHNIBEL_AC +#if SEND_TECO + case TECO: + { + IRTecoAc ac(_pin, _inverted, _modulation); + teco(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.light, send.sleep); + break; + } +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + { + IRToshibaAC ac(_pin, _inverted, _modulation); + toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.econo, send.filter); + break; + } +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + { + IRTrotecESP ac(_pin, _inverted, _modulation); + trotec(&ac, send.power, send.mode, degC, send.fanspeed, send.sleep); + break; + } +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + case TROTEC_3550: + { + IRTrotec3550 ac(_pin, _inverted, _modulation); + trotec3550(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv); + break; + } +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + case TRUMA: + { + IRTrumaAc ac(_pin, _inverted, _modulation); + truma(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); + break; + } +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case VESTEL_AC: + { + IRVestelAc ac(_pin, _inverted, _modulation); + vestel(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_VESTEL_AC +#if SEND_VOLTAS + case VOLTAS: + { + IRVoltas ac(_pin, _inverted, _modulation); + voltas(&ac, (voltas_ac_remote_model_t)send.model, send.power, send.mode, + degC, send.fanspeed, send.swingv, send.swingh, send.turbo, + send.econo, send.light, send.sleep); + break; + } +#endif // SEND_VOLTAS +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + { + IRWhirlpoolAc ac(_pin, _inverted, _modulation); + whirlpool(&ac, (whirlpool_ac_remote_model_t)send.model, send.power, + send.mode, degC, send.fanspeed, send.swingv, send.turbo, + send.light, send.sleep, send.clock); + break; + } +#endif // SEND_WHIRLPOOL_AC +#if SEND_TRANSCOLD + case TRANSCOLD: + { + IRTranscoldAc ac(_pin, _inverted, _modulation); + transcold(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh); + break; + } +#endif // SEND_TRANSCOLD_AC + default: + return false; // Fail, didn't match anything. + } + return true; // Success. +} // NOLINT(readability/fn_size) + +/// Update the previous state to the current one. +void IRac::markAsSent(void) { + _prev = next; +} + +/// Send an A/C message based soley on our internal state. +/// @return True, if accepted/converted/attempted. False, if unsupported. +bool IRac::sendAc(void) { + bool success = this->sendAc(next, &_prev); + if (success) this->markAsSent(); + return success; +} + +/// Compare two AirCon states. +/// @note The comparison excludes the clock. +/// @param a A state_t to be compared. +/// @param b A state_t to be compared. +/// @return True if they differ, False if they don't. +bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { + return a.protocol != b.protocol || a.model != b.model || a.power != b.power || + a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || + a.fanspeed != b.fanspeed || a.swingv != b.swingv || + a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || + a.econo != b.econo || a.light != b.light || a.filter != b.filter || + a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep || + a.command != b.command || a.sensorTemperature != b.sensorTemperature || + a.iFeel != b.iFeel; +} + +/// Check if the internal state has changed from what was previously sent. +/// @note The comparison excludes the clock. +/// @return True if it has changed, False if not. +bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); } + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::ac_command_t IRac::strToCommandType(const char *str, + const stdAc::ac_command_t def) { + if (!STRCASECMP(str, kControlCommandStr)) + return stdAc::ac_command_t::kControlCommand; + else if (!STRCASECMP(str, kIFeelReportStr) || + !STRCASECMP(str, kIFeelStr)) + return stdAc::ac_command_t::kSensorTempReport; + else if (!STRCASECMP(str, kSetTimerCommandStr) || + !STRCASECMP(str, kTimerStr)) + return stdAc::ac_command_t::kTimerCommand; + else if (!STRCASECMP(str, kConfigCommandStr)) + return stdAc::ac_command_t::kConfigCommand; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::opmode_t IRac::strToOpmode(const char *str, + const stdAc::opmode_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr)) + return stdAc::opmode_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::opmode_t::kOff; + else if (!STRCASECMP(str, kCoolStr) || + !STRCASECMP(str, kCoolingStr)) + return stdAc::opmode_t::kCool; + else if (!STRCASECMP(str, kHeatStr) || + !STRCASECMP(str, kHeatingStr)) + return stdAc::opmode_t::kHeat; + else if (!STRCASECMP(str, kDryStr) || + !STRCASECMP(str, kDryingStr) || + !STRCASECMP(str, kDehumidifyStr)) + return stdAc::opmode_t::kDry; + else if (!STRCASECMP(str, kFanStr) || + // The following Fans strings with "only" are required to help with + // HomeAssistant & Google Home Climate integration. + // For compatibility only. + // Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes + !STRCASECMP(str, kFanOnlyStr) || + !STRCASECMP(str, kFan_OnlyStr) || + !STRCASECMP(str, kFanOnlyWithSpaceStr) || + !STRCASECMP(str, kFanOnlyNoSpaceStr)) + return stdAc::opmode_t::kFan; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::fanspeed_t IRac::strToFanspeed(const char *str, + const stdAc::fanspeed_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr)) + return stdAc::fanspeed_t::kAuto; + else if (!STRCASECMP(str, kMinStr) || + !STRCASECMP(str, kMinimumStr) || + !STRCASECMP(str, kLowestStr)) + return stdAc::fanspeed_t::kMin; + else if (!STRCASECMP(str, kLowStr) || + !STRCASECMP(str, kLoStr)) + return stdAc::fanspeed_t::kLow; + else if (!STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kMidStr)) + return stdAc::fanspeed_t::kMedium; + else if (!STRCASECMP(str, kHighStr) || + !STRCASECMP(str, kHiStr)) + return stdAc::fanspeed_t::kHigh; + else if (!STRCASECMP(str, kMaxStr) || + !STRCASECMP(str, kMaximumStr) || + !STRCASECMP(str, kHighestStr)) + return stdAc::fanspeed_t::kMax; + else if (!STRCASECMP(str, kMedHighStr)) + return stdAc::fanspeed_t::kMediumHigh; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::swingv_t IRac::strToSwingV(const char *str, + const stdAc::swingv_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr) || + !STRCASECMP(str, kOnStr) || + !STRCASECMP(str, kSwingStr)) + return stdAc::swingv_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::swingv_t::kOff; + else if (!STRCASECMP(str, kMinStr) || + !STRCASECMP(str, kMinimumStr) || + !STRCASECMP(str, kLowestStr) || + !STRCASECMP(str, kBottomStr) || + !STRCASECMP(str, kDownStr)) + return stdAc::swingv_t::kLowest; + else if (!STRCASECMP(str, kLowStr)) + return stdAc::swingv_t::kLow; + else if (!STRCASECMP(str, kMidStr) || + !STRCASECMP(str, kMiddleStr) || + !STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kCentreStr)) + return stdAc::swingv_t::kMiddle; + else if (!STRCASECMP(str, kUpperMiddleStr)) + return stdAc::swingv_t::kUpperMiddle; + else if (!STRCASECMP(str, kHighStr) || + !STRCASECMP(str, kHiStr)) + return stdAc::swingv_t::kHigh; + else if (!STRCASECMP(str, kHighestStr) || + !STRCASECMP(str, kMaxStr) || + !STRCASECMP(str, kMaximumStr) || + !STRCASECMP(str, kTopStr) || + !STRCASECMP(str, kUpStr)) + return stdAc::swingv_t::kHighest; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::swingh_t IRac::strToSwingH(const char *str, + const stdAc::swingh_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr) || + !STRCASECMP(str, kOnStr) || !STRCASECMP(str, kSwingStr)) + return stdAc::swingh_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::swingh_t::kOff; + else if (!STRCASECMP(str, kLeftMaxNoSpaceStr) || // "LeftMax" + !STRCASECMP(str, kLeftMaxStr) || // "Left Max" + !STRCASECMP(str, kMaxLeftNoSpaceStr) || // "MaxLeft" + !STRCASECMP(str, kMaxLeftStr)) // "Max Left" + return stdAc::swingh_t::kLeftMax; + else if (!STRCASECMP(str, kLeftStr)) + return stdAc::swingh_t::kLeft; + else if (!STRCASECMP(str, kMidStr) || + !STRCASECMP(str, kMiddleStr) || + !STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kCentreStr)) + return stdAc::swingh_t::kMiddle; + else if (!STRCASECMP(str, kRightStr)) + return stdAc::swingh_t::kRight; + else if (!STRCASECMP(str, kRightMaxNoSpaceStr) || // "RightMax" + !STRCASECMP(str, kRightMaxStr) || // "Right Max" + !STRCASECMP(str, kMaxRightNoSpaceStr) || // "MaxRight" + !STRCASECMP(str, kMaxRightStr)) // "Max Right" + return stdAc::swingh_t::kRightMax; + else if (!STRCASECMP(str, kWideStr)) + return stdAc::swingh_t::kWide; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @note Assumes str is the model code or an integer >= 1. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +/// @note After adding a new model you should update modelToStr() too. +int16_t IRac::strToModel(const char *str, const int16_t def) { + // Gree + if (!STRCASECMP(str, kYaw1fStr)) { + return gree_ac_remote_model_t::YAW1F; + } else if (!STRCASECMP(str, kYbofbStr)) { + return gree_ac_remote_model_t::YBOFB; + } else if (!STRCASECMP(str, kYx1fsfStr)) { + return gree_ac_remote_model_t::YX1FSF; + // Haier models + } else if (!STRCASECMP(str, kV9014557AStr)) { + return haier_ac176_remote_model_t::V9014557_A; + } else if (!STRCASECMP(str, kV9014557BStr)) { + return haier_ac176_remote_model_t::V9014557_B; + // HitachiAc1 models + } else if (!STRCASECMP(str, kRlt0541htaaStr)) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; + } else if (!STRCASECMP(str, kRlt0541htabStr)) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; + // Fujitsu A/C models + } else if (!STRCASECMP(str, kArrah2eStr)) { + return fujitsu_ac_remote_model_t::ARRAH2E; + } else if (!STRCASECMP(str, kArdb1Str)) { + return fujitsu_ac_remote_model_t::ARDB1; + } else if (!STRCASECMP(str, kArreb1eStr)) { + return fujitsu_ac_remote_model_t::ARREB1E; + } else if (!STRCASECMP(str, kArjw2Str)) { + return fujitsu_ac_remote_model_t::ARJW2; + } else if (!STRCASECMP(str, kArry4Str)) { + return fujitsu_ac_remote_model_t::ARRY4; + } else if (!STRCASECMP(str, kArrew4eStr)) { + return fujitsu_ac_remote_model_t::ARREW4E; + // LG A/C models + } else if (!STRCASECMP(str, kGe6711ar2853mStr)) { + return lg_ac_remote_model_t::GE6711AR2853M; + } else if (!STRCASECMP(str, kAkb75215403Str)) { + return lg_ac_remote_model_t::AKB75215403; + } else if (!STRCASECMP(str, kAkb74955603Str)) { + return lg_ac_remote_model_t::AKB74955603; + } else if (!STRCASECMP(str, kAkb73757604Str)) { + return lg_ac_remote_model_t::AKB73757604; + } else if (!STRCASECMP(str, kLg6711a20083vStr)) { + return lg_ac_remote_model_t::LG6711A20083V; + // Panasonic A/C families + } else if (!STRCASECMP(str, kLkeStr) || + !STRCASECMP(str, kPanasonicLkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicLke; + } else if (!STRCASECMP(str, kNkeStr) || + !STRCASECMP(str, kPanasonicNkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicNke; + } else if (!STRCASECMP(str, kDkeStr) || + !STRCASECMP(str, kPanasonicDkeStr) || + !STRCASECMP(str, kPkrStr) || + !STRCASECMP(str, kPanasonicPkrStr)) { + return panasonic_ac_remote_model_t::kPanasonicDke; + } else if (!STRCASECMP(str, kJkeStr) || + !STRCASECMP(str, kPanasonicJkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicJke; + } else if (!STRCASECMP(str, kCkpStr) || + !STRCASECMP(str, kPanasonicCkpStr)) { + return panasonic_ac_remote_model_t::kPanasonicCkp; + } else if (!STRCASECMP(str, kRkrStr) || + !STRCASECMP(str, kPanasonicRkrStr)) { + return panasonic_ac_remote_model_t::kPanasonicRkr; + // Sharp A/C Models + } else if (!STRCASECMP(str, kA907Str)) { + return sharp_ac_remote_model_t::A907; + } else if (!STRCASECMP(str, kA705Str)) { + return sharp_ac_remote_model_t::A705; + } else if (!STRCASECMP(str, kA903Str)) { + return sharp_ac_remote_model_t::A903; + // TCL A/C Models + } else if (!STRCASECMP(str, kTac09chsdStr)) { + return tcl_ac_remote_model_t::TAC09CHSD; + } else if (!STRCASECMP(str, kGz055be1Str)) { + return tcl_ac_remote_model_t::GZ055BE1; + // Voltas A/C models + } else if (!STRCASECMP(str, k122lzfStr)) { + return voltas_ac_remote_model_t::kVoltas122LZF; + // Whirlpool A/C models + } else if (!STRCASECMP(str, kDg11j13aStr) || + !STRCASECMP(str, kDg11j104Str)) { + return whirlpool_ac_remote_model_t::DG11J13A; + } else if (!STRCASECMP(str, kDg11j191Str)) { + return whirlpool_ac_remote_model_t::DG11J191; + // Argo A/C models + } else if (!STRCASECMP(str, kArgoWrem2Str)) { + return argo_ac_remote_model_t::SAC_WREM2; + } else if (!STRCASECMP(str, kArgoWrem3Str)) { + return argo_ac_remote_model_t::SAC_WREM3; + } else { + int16_t number = atoi(str); + if (number > 0) + return number; + else + return def; + } +} + +/// Convert the supplied str into the appropriate boolean value. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The boolean value to return if no conversion was possible. +/// @return The equivalent boolean value. +bool IRac::strToBool(const char *str, const bool def) { + if (!STRCASECMP(str, kOnStr) || + !STRCASECMP(str, k1Str) || + !STRCASECMP(str, kYesStr) || + !STRCASECMP(str, kTrueStr)) + return true; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, k0Str) || + !STRCASECMP(str, kNoStr) || + !STRCASECMP(str, kFalseStr)) + return false; + else + return def; +} + +/// Convert the supplied boolean into the appropriate String. +/// @param[in] value The boolean value to be converted. +/// @return The equivalent String for the locale. +String IRac::boolToString(const bool value) { + return value ? kOnStr : kOffStr; +} + +/// Convert the supplied operation mode into the appropriate String. +/// @param[in] cmdType The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::commandTypeToString(const stdAc::ac_command_t cmdType) { + switch (cmdType) { + case stdAc::ac_command_t::kControlCommand: return kControlCommandStr; + case stdAc::ac_command_t::kSensorTempReport: return kIFeelReportStr; + case stdAc::ac_command_t::kTimerCommand: return kSetTimerCommandStr; + case stdAc::ac_command_t::kConfigCommand: return kConfigCommandStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied operation mode into the appropriate String. +/// @param[in] mode The enum to be converted. +/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output. +/// @return The equivalent String for the locale. +String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) { + switch (mode) { + case stdAc::opmode_t::kOff: return kOffStr; + case stdAc::opmode_t::kAuto: return kAutoStr; + case stdAc::opmode_t::kCool: return kCoolStr; + case stdAc::opmode_t::kHeat: return kHeatStr; + case stdAc::opmode_t::kDry: return kDryStr; + case stdAc::opmode_t::kFan: return ha ? kFan_OnlyStr : kFanStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied fan speed enum into the appropriate String. +/// @param[in] speed The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: return kAutoStr; + case stdAc::fanspeed_t::kMax: return kMaxStr; + case stdAc::fanspeed_t::kHigh: return kHighStr; + case stdAc::fanspeed_t::kMedium: return kMediumStr; + case stdAc::fanspeed_t::kMediumHigh: return kMedHighStr; + case stdAc::fanspeed_t::kLow: return kLowStr; + case stdAc::fanspeed_t::kMin: return kMinStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied enum into the appropriate String. +/// @param[in] swingv The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: return kOffStr; + case stdAc::swingv_t::kAuto: return kAutoStr; + case stdAc::swingv_t::kHighest: return kHighestStr; + case stdAc::swingv_t::kHigh: return kHighStr; + case stdAc::swingv_t::kMiddle: return kMiddleStr; + case stdAc::swingv_t::kUpperMiddle: return kUpperMiddleStr; + case stdAc::swingv_t::kLow: return kLowStr; + case stdAc::swingv_t::kLowest: return kLowestStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied enum into the appropriate String. +/// @param[in] swingh The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: return kOffStr; + case stdAc::swingh_t::kAuto: return kAutoStr; + case stdAc::swingh_t::kLeftMax: return kLeftMaxStr; + case stdAc::swingh_t::kLeft: return kLeftStr; + case stdAc::swingh_t::kMiddle: return kMiddleStr; + case stdAc::swingh_t::kRight: return kRightStr; + case stdAc::swingh_t::kRightMax: return kRightMaxStr; + case stdAc::swingh_t::kWide: return kWideStr; + default: return kUnknownStr; + } +} + +namespace IRAcUtils { + /// Display the human readable state of an A/C message if we can. + /// @param[in] result A Ptr to the captured `decode_results` that contains an + /// A/C mesg. + /// @return A string with the human description of the A/C message. + /// An empty string if we can't. + String resultAcToString(const decode_results * const result) { + switch (result->decode_type) { +#if DECODE_AIRTON + case decode_type_t::AIRTON: { + IRAirtonAc ac(kGpioUnused); + ac.setRaw(result->value); // AIRTON uses value instead of state. + return ac.toString(); + } +#endif // DECODE_AIRTON +#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(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + if (IRArgoAC_WREM3::isValidWrem3Message(result->state, result->bits, + true)) { + IRArgoAC_WREM3 ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } + IRArgoAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_ARGO +#if DECODE_BOSCH144 + case decode_type_t::BOSCH144: { + IRBosch144AC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_BOSCH144 +#if DECODE_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: { + IRCarrierAc64 ac(kGpioUnused); + ac.setRaw(result->value); // CARRIER_AC64 uses value instead of state. + return ac.toString(); + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.on(); + ac.setRaw(result->value); // Coolix uses value instead of state. + return ac.toString(); + } +#endif // DECODE_COOLIX +#if DECODE_CORONA_AC + case decode_type_t::CORONA_AC: { + IRCoronaAc ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_CORONA_AC +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN128 + case decode_type_t::DAIKIN128: { + IRDaikin128 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + case decode_type_t::DAIKIN152: { + IRDaikin152 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(result->value); // Daikin64 uses value instead of state. + return ac.toString(); + } +#endif // DECODE_DAIKIN64 +#if DECODE_DELONGHI_AC + case decode_type_t::DELONGHI_AC: { + IRDelonghiAc ac(kGpioUnused); + ac.setRaw(result->value); // DelonghiAc uses value instead of state. + return ac.toString(); + } +#endif // DECODE_DELONGHI_AC +#if DECODE_ECOCLIM + case decode_type_t::ECOCLIM: { + if (result->bits == kEcoclimBits) { + IREcoclimAc ac(kGpioUnused); + ac.setRaw(result->value); // EcoClim uses value instead of state. + return ac.toString(); + } + return ""; + } +#endif // DECODE_ECOCLIM +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_FUJITSU_AC +#if DECODE_FUJITSU_AC264 + case decode_type_t::FUJITSU_AC264: { + IRFujitsuAC264 ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_FUJITSU_AC264 +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(result->value); // Goodweather uses value instead of state. + return ac.toString(); + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC160 + case decode_type_t::HAIER_AC160: { + IRHaierAC160 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC160 +#if DECODE_HAIER_AC176 + case decode_type_t::HAIER_AC176: { + IRHaierAC176 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC176 +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC_YRW02 +#if DECODE_HITACHI_AC + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: { + IRHitachiAc344 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: { + IRHitachiAc424 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC424 +#if DECODE_KELON + case decode_type_t::KELON: { + IRKelonAc ac(kGpioUnused); + ac.setRaw(result->value); + return ac.toString(); + } +#endif // DECODE_KELON +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_KELVINATOR +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(result->value, result->decode_type); // Use value, not state. + return ac.isValidLgAc() ? ac.toString() : ""; + } +#endif // DECODE_LG +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(result->value); // Midea uses value instead of state. + return ac.toString(); + } +#endif // DECODE_MIDEA +#if DECODE_MIRAGE + case decode_type_t::MIRAGE: { + IRMirageAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MIRAGE +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI112 +#if DECODE_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: { + IRMitsubishi136 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI136 +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_NEOCLIMA +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + if (result->bits > kPanasonicAcShortBits) { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: { + if (result->bits >= kPanasonicAc32Bits) { + IRPanasonicAc32 ac(kGpioUnused); + ac.setRaw(result->value); // Uses value instead of state. + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_RHOSS + case decode_type_t::RHOSS: { + IRRhossAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_RHOSS +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + case decode_type_t::SANYO_AC88: { + IRSanyoAc88 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SANYO_AC88 +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SHARP_AC +#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) + case decode_type_t::TCL112AC: + case decode_type_t::TEKNOPOINT: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) +#if DECODE_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: { + IRTechnibelAc ac(kGpioUnused); + ac.setRaw(result->value); // TechnibelAc uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TECHNIBEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TRANSCOLD + case decode_type_t::TRANSCOLD: { + IRTranscoldAc ac(kGpioUnused); + ac.on(); + ac.setRaw(result->value); // TRANSCOLD uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TRANSCOLD +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + case decode_type_t::TROTEC_3550: { + IRTrotec3550 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC_3550 +#if DECODE_TRUMA + case decode_type_t::TRUMA: { + IRTrumaAc ac(kGpioUnused); + ac.setRaw(result->value); // Truma uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TRUMA +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_VOLTAS + case decode_type_t::VOLTAS: { + IRVoltas ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_VOLTAS +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_YORK + case decode_type_t::YORK: { + IRYorkAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_YORK + default: + return ""; + } + } + + /// Convert a valid IR A/C remote message that we understand enough into a + /// Common A/C state. + /// @param[in] decode A PTR to a successful raw IR decode object. + /// @param[in] result A PTR to a state structure to store the result in. + /// @param[in] prev A PTR to a state structure which has the prev. state. + /// @return A boolean indicating success or failure. + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev +/// @cond IGNORE +// *prev flagged as "unused" due to potential compiler warning when some +// protocols that use it are disabled. It really is used. + __attribute__((unused)) +/// @endcond + ) { + if (decode == NULL || result == NULL) return false; // Safety check. + switch (decode->decode_type) { +#if DECODE_AIRTON + case decode_type_t::AIRTON: { + IRAirtonAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_AIRTON +#if DECODE_AIRWELL + case decode_type_t::AIRWELL: { + IRAirwellAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_AIRWELL +#if DECODE_AMCOR + case decode_type_t::AMCOR: { + IRAmcorAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + const uint16_t length = decode->bits / 8; + if (IRArgoAC_WREM3::isValidWrem3Message(decode->state, + decode->bits, true)) { + IRArgoAC_WREM3 ac(kGpioUnused); + ac.setRaw(decode->state, length); + *result = ac.toCommon(); + } else { + IRArgoAC ac(kGpioUnused); + switch (length) { + case kArgoStateLength: + case kArgoShortStateLength: + ac.setRaw(decode->state, length); + *result = ac.toCommon(); + break; + default: + return false; + } + } + break; + } +#endif // DECODE_ARGO +#if DECODE_BOSCH144 + case decode_type_t::BOSCH144: { + IRBosch144AC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_BOSCH144 +#if DECODE_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: { + IRCarrierAc64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_COOLIX +#if DECODE_CORONA_AC + case decode_type_t::CORONA_AC: { + IRCoronaAc ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN128 + case decode_type_t::DAIKIN128: { + IRDaikin128 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + case decode_type_t::DAIKIN152: { + IRDaikin152 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_DAIKIN64 +#if DECODE_DELONGHI_AC + case decode_type_t::DELONGHI_AC: { + IRDelonghiAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_DELONGHI_AC +#if DECODE_ECOCLIM + case decode_type_t::ECOCLIM: { + if (decode->bits == kEcoclimBits) { + IREcoclimAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + } else { + return false; + } + break; + } +#endif // DECODE_ECOCLIM +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_FUJITSU_AC +#if DECODE_FUJITSU_AC264 + case decode_type_t::FUJITSU_AC264: { + IRFujitsuAC264 ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_FUJITSU_AC264 +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC160 + case decode_type_t::HAIER_AC160: { + IRHaierAC160 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_HAIER_AC160 +#if DECODE_HAIER_AC176 + case decode_type_t::HAIER_AC176: { + IRHaierAC176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC176 +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC_YRW02 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: { + IRHitachiAc344 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: { + IRHitachiAc424 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC424 +#if DECODE_KELON + case decode_type_t::KELON: { + IRKelonAc ac(kGpioUnused); + ac.setRaw(decode->value); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_KELON +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_KELVINATOR +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(decode->value, decode->decode_type); // Use value, not state. + if (!ac.isValidLgAc()) return false; + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_LG +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_MIDEA +#if DECODE_MIRAGE + case decode_type_t::MIRAGE: { + IRMirageAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MIRAGE +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI112 +#if DECODE_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: { + IRMitsubishi136 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI136 +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_NEOCLIMA +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: { + IRPanasonicAc32 ac(kGpioUnused); + if (decode->bits >= kPanasonicAc32Bits) { + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + } else { + return false; + } + break; + } +#endif // DECODE_PANASONIC_AC32 +#if DECODE_RHOSS + case decode_type_t::RHOSS: { + IRRhossAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_RHOSS +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + case decode_type_t::SANYO_AC88: { + IRSanyoAc88 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SANYO_AC88 +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_SHARP_AC +#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) + case decode_type_t::TCL112AC: + case decode_type_t::TEKNOPOINT: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + // Teknopoint uses the TCL protocol, but with a different model number. + // Just keep the original protocol type ... for now. + result->protocol = decode->decode_type; + break; + } +#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) +#if DECODE_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: { + IRTechnibelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECHNIBEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TRANSCOLD + case decode_type_t::TRANSCOLD: { + IRTranscoldAc ac(kGpioUnused); + ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_TRANSCOLD +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + case decode_type_t::TROTEC_3550: { + IRTrotec3550 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC_3550 +#if DECODE_TRUMA + case decode_type_t::TRUMA: { + IRTrumaAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TRUMA +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_VESTEL_AC +#if DECODE_VOLTAS + case decode_type_t::VOLTAS: { + IRVoltas ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_VOLTAS +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_YORK + case decode_type_t::YORK: { + IRYorkAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_YORK + default: + return false; + } + return true; + } +} // namespace IRAcUtils diff --git a/src/IRac.h b/src/IRac.h index 7f4a3cf01..1a6ab7f32 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -1,581 +1,591 @@ -#ifndef IRAC_H_ -#define IRAC_H_ - -// Copyright 2019 David Conran - -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "ir_Airton.h" -#include "ir_Airwell.h" -#include "ir_Amcor.h" -#include "ir_Argo.h" -#include "ir_Bosch.h" -#include "ir_Carrier.h" -#include "ir_Coolix.h" -#include "ir_Corona.h" -#include "ir_Daikin.h" -#include "ir_Delonghi.h" -#include "ir_Fujitsu.h" -#include "ir_Ecoclim.h" -#include "ir_Electra.h" -#include "ir_Goodweather.h" -#include "ir_Gree.h" -#include "ir_Haier.h" -#include "ir_Hitachi.h" -#include "ir_Kelon.h" -#include "ir_Kelvinator.h" -#include "ir_LG.h" -#include "ir_Midea.h" -#include "ir_Mirage.h" -#include "ir_Mitsubishi.h" -#include "ir_MitsubishiHeavy.h" -#include "ir_Neoclima.h" -#include "ir_Panasonic.h" -#include "ir_Rhoss.h" -#include "ir_Samsung.h" -#include "ir_Sanyo.h" -#include "ir_Sharp.h" -#include "ir_Tcl.h" -#include "ir_Technibel.h" -#include "ir_Teco.h" -#include "ir_Toshiba.h" -#include "ir_Transcold.h" -#include "ir_Trotec.h" -#include "ir_Truma.h" -#include "ir_Vestel.h" -#include "ir_Voltas.h" -#include "ir_Whirlpool.h" -#include "ir_York.h" - -// Constants -const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO. - -// Class -/// A universal/common/generic interface for controling supported A/Cs. -class IRac { - public: - explicit IRac(const uint16_t pin, const bool inverted = false, - const bool use_modulation = true); - static bool isProtocolSupported(const decode_type_t protocol); - static void initState(stdAc::state_t *state, - const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, - const float degrees, const bool celsius, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep, - const int16_t clock); - static void initState(stdAc::state_t *state); - void markAsSent(void); - bool sendAc(void); - bool sendAc(const stdAc::state_t desired, const stdAc::state_t *prev = NULL); - bool sendAc(const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, const float degrees, - const bool celsius, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep = -1, - const int16_t clock = -1); - static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b); - static bool strToBool(const char *str, const bool def = false); - static int16_t strToModel(const char *str, const int16_t def = -1); - static stdAc::ac_command_t strToCommandType(const char *str, - const stdAc::ac_command_t def = stdAc::ac_command_t::kControlCommand); - static stdAc::opmode_t strToOpmode( - const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); - static stdAc::fanspeed_t strToFanspeed( - const char *str, - const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); - static stdAc::swingv_t strToSwingV( - const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); - static stdAc::swingh_t strToSwingH( - const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); - static String boolToString(const bool value); - static String commandTypeToString(const stdAc::ac_command_t cmdType); - static String opmodeToString(const stdAc::opmode_t mode, - const bool ha = false); - static String fanspeedToString(const stdAc::fanspeed_t speed); - static String swingvToString(const stdAc::swingv_t swingv); - static String swinghToString(const stdAc::swingh_t swingh); - stdAc::state_t getState(void); - stdAc::state_t getStatePrev(void); - bool hasStateChanged(void); - stdAc::state_t next; ///< The state we want the device to be in after we send -#ifdef UNIT_TEST - /// @cond IGNORE - /// UT-specific - /// See @c OUTPUT_DECODE_RESULTS_FOR_UT macro description in IRac.cpp - std::shared_ptr _utReceiver = nullptr; - std::unique_ptr _lastDecodeResults = nullptr; - /// @endcond -#else - - private: -#endif // UNIT_TEST - uint16_t _pin; ///< The GPIO to use to transmit messages from. - 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_AIRTON - void airton(IRAirtonAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool light, const bool econo, const bool filter, - const int16_t sleep = -1); -#endif // SEND_AIRTON -#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, - const stdAc::fanspeed_t fan); -#endif // SEND_AMCOR -#if SEND_ARGO - void argo(IRArgoAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, const bool turbo, - const int16_t sleep = -1); - void argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, const bool night, - const bool econo, const bool turbo, const bool filter, const bool light); - void argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp); - void argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, - const uint8_t value, bool safe = true); - void argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, - const uint16_t currentTime, const uint16_t delayMinutes); -#endif // SEND_ARGO -#if SEND_BOSCH144 - void bosch144(IRBosch144AC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const bool quiet); -#endif // SEND_BOSCH144 -#if SEND_CARRIER_AC64 -void carrier64(IRCarrierAc64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep = -1); -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - void coolix(IRCoolixAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool light, - const bool clean, const int16_t sleep = -1); -#endif // SEND_COOLIX -#if SEND_CORONA_AC - void corona(IRCoronaAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool econo); -#endif // SEND_CORONA_AC -#if SEND_DAIKIN - void daikin(IRDaikinESP *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool clean); -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - void daikin128(IRDaikin128 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool light, - const bool econo, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_DAIKIN128 -#if SEND_DAIKIN152 - void daikin152(IRDaikin152 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool econo); -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - void daikin160(IRDaikin160 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv); -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - void daikin176(IRDaikin176 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingh_t swingh); -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - void daikin2(IRDaikin2 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter, const bool clean, - const bool beep, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN216 -void daikin216(IRDaikin216 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo); -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN64 - void daikin64(IRDaikin64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, - const int16_t sleep = -1, const int16_t clock = -1); -#endif // SEND_DAIKIN64 -#if SEND_DELONGHI_AC - void delonghiac(IRDelonghiAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const bool turbo, const int16_t sleep = -1); -#endif // SEND_DELONGHI_AC -#if SEND_ECOCLIM -void ecoclim(IREcoclimAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_ECOCLIM -#if SEND_ELECTRA_AC -void electra(IRElectraAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, const bool iFeel, const bool turbo, - const bool lighttoggle, const bool clean); -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool filter, const bool clean, const int16_t sleep = -1); -#endif // SEND_FUJITSU_AC -#if SEND_GOODWEATHER - void goodweather(IRGoodweatherAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep = -1); -#endif // SEND_GOODWEATHER -#if SEND_GREE - void gree(IRGreeAC *ac, const gree_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool econo, - const bool light, const bool clean, const int16_t sleep = -1); -#endif // SEND_GREE -#if SEND_HAIER_AC - void haier(IRHaierAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool filter, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC160 - void haier160(IRHaierAC160 *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool quiet, const bool filter, - const bool clean, const bool light, const bool prevlight, - const int16_t sleep = -1); -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - void haier176(IRHaierAC176 *ac, - const haier_ac176_remote_model_t model, const bool on, - const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool quiet, const bool filter, - const int16_t sleep = -1); -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - void haierYrwo2(IRHaierACYRW02 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, const bool turbo, - const bool quiet, const bool filter, - const int16_t sleep = -1); -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HITACHI_AC - void hitachi(IRHitachiAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - void hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, - const bool on, const bool power_toggle, - const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool swing_toggle, const int16_t sleep = -1); -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC264 - void hitachi264(IRHitachiAc264 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan); -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - void hitachi296(IRHitachiAc296 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan); -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - void hitachi344(IRHitachiAc344 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh); -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - void hitachi424(IRHitachiAc424 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv); -#endif // SEND_HITACHI_AC424 -#if SEND_KELON - void kelon(IRKelonAc *ac, const bool togglePower, const stdAc::opmode_t mode, - const int8_t dryGrade, const float degrees, - const stdAc::fanspeed_t fan, const bool toggleSwing, - const bool superCool, const int16_t sleep); -#endif // SEND_KELON -#if SEND_KELVINATOR - void kelvinator(IRKelvinatorAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool filter, const bool clean); -#endif // SEND_KELVINATOR -#if SEND_LG - void lg(IRLgAc *ac, const lg_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, - const stdAc::swingh_t swingh, const bool light); -#endif // SEND_LG -#if SEND_MIDEA - void midea(IRMideaAC *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool iFeel, const bool quiet, const bool quiet_prev, - const bool turbo, const bool econo, const bool light, - const bool clean, const int16_t sleep = -1); -#endif // SEND_MIDEA -#if SEND_MIRAGE - void mirage(IRMirageAc *ac, const stdAc::state_t state); -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - void mitsubishi(IRMitsubishiAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const int16_t clock = -1); -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI112 - void mitsubishi112(IRMitsubishi112 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet); -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHI136 - void mitsubishi136(IRMitsubishi136 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool quiet); -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHIHEAVY - void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool clean); - void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool filter, const bool clean, - const int16_t sleep = -1); -#endif // SEND_MITSUBISHIHEAVY -#if SEND_NEOCLIMA - void neoclima(IRNeoclimaAc *ac, const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const bool filter, const int16_t sleep = -1); -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool filter, - const int16_t clock = -1); -#endif // SEND_PANASONIC_AC -#if SEND_PANASONIC_AC32 - void panasonic32(IRPanasonicAc32 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); -#endif // SEND_PANASONIC_AC32 -#if SEND_RHOSS - void rhoss(IRRhossAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swing); -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - void samsung(IRSamsungAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep = -1, - const bool prevpower = true, const int16_t prevsleep = -1, - const bool forceextended = true); -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - void sanyo(IRSanyoAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, const bool beep, - const int16_t sleep = -1); -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - void sanyo88(IRSanyoAc88 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool filter, - const int16_t sleep = -1, const int16_t clock = -1); -#endif // SEND_SANYO_AC88 -#if SEND_SHARP_AC - void sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, - const bool on, const bool prev_power, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, - const bool turbo, const bool light, - const bool filter, const bool clean); -#endif // SEND_SHARP_AC -#if SEND_TCL112AC - void tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter); -#endif // SEND_TCL112AC -#if SEND_TECHNIBEL_AC - void technibel(IRTechnibelAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep = -1); -#endif // SEND_TECHNIBEL_AC -#if SEND_TECO - void teco(IRTecoAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool light, const int16_t sleep = -1); -#endif // SEND_TECO -#if SEND_TOSHIBA_AC - void toshiba(IRToshibaAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool econo, const bool filter); -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - void trotec(IRTrotecESP *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const int16_t sleep = -1); -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - void trotec3550(IRTrotec3550 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv); -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - void truma(IRTrumaAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const bool quiet); -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - void vestel(IRVestelAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool filter, - const int16_t sleep = -1, const int16_t clock = -1, - const bool sendNormal = true); -#endif // SEND_VESTEL_AC -#if SEND_VOLTAS - void voltas(IRVoltas *ac, const voltas_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const int16_t sleep = -1); -#endif // SEND_VOLTAS -#if SEND_WHIRLPOOL_AC - void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep = -1, const int16_t clock = -1); -#endif // SEND_WHIRLPOOL_AC -#if SEND_TRANSCOLD - void transcold(IRTranscoldAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); -#endif // SEND_TRANSCOLD -static stdAc::state_t cleanState(const stdAc::state_t state); -static stdAc::state_t handleToggles(const stdAc::state_t desired, - const stdAc::state_t *prev = NULL); -}; // IRac class - -/// Common functions for use with all A/Cs supported by the IRac class. -namespace IRAcUtils { - String resultAcToString(const decode_results * const results); - bool decodeToState(const decode_results *decode, stdAc::state_t *result, - const stdAc::state_t *prev = NULL); -} // namespace IRAcUtils -#endif // IRAC_H_ +#ifndef IRAC_H_ +#define IRAC_H_ + +// Copyright 2019 David Conran + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "ir_Airton.h" +#include "ir_Airwell.h" +#include "ir_Amcor.h" +#include "ir_Argo.h" +#include "ir_Bosch.h" +#include "ir_Carrier.h" +#include "ir_Coolix.h" +#include "ir_Corona.h" +#include "ir_Daikin.h" +#include "ir_Delonghi.h" +#include "ir_Fujitsu.h" +#include "ir_Ecoclim.h" +#include "ir_Electra.h" +#include "ir_Goodweather.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelon.h" +#include "ir_Kelvinator.h" +#include "ir_LG.h" +#include "ir_Midea.h" +#include "ir_Mirage.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" +#include "ir_Panasonic.h" +#include "ir_Rhoss.h" +#include "ir_Samsung.h" +#include "ir_Sanyo.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Technibel.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Transcold.h" +#include "ir_Trotec.h" +#include "ir_Truma.h" +#include "ir_Vestel.h" +#include "ir_Voltas.h" +#include "ir_Whirlpool.h" +#include "ir_York.h" + +// Constants +const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO. + +// Class +/// A universal/common/generic interface for controling supported A/Cs. +class IRac { + public: + explicit IRac(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + static bool isProtocolSupported(const decode_type_t protocol); + static void initState(stdAc::state_t *state, + const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, + const int16_t clock); + static void initState(stdAc::state_t *state); + void markAsSent(void); + bool sendAc(void); + bool sendAc(const stdAc::state_t desired, const stdAc::state_t *prev = NULL); + bool sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, const float degrees, + const bool celsius, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); + static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b); + static bool strToBool(const char *str, const bool def = false); + static int16_t strToModel(const char *str, const int16_t def = -1); + static stdAc::ac_command_t strToCommandType(const char *str, + const stdAc::ac_command_t def = stdAc::ac_command_t::kControlCommand); + static stdAc::opmode_t strToOpmode( + const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); + static stdAc::fanspeed_t strToFanspeed( + const char *str, + const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); + static stdAc::swingv_t strToSwingV( + const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); + static stdAc::swingh_t strToSwingH( + const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); + static String boolToString(const bool value); + static String commandTypeToString(const stdAc::ac_command_t cmdType); + static String opmodeToString(const stdAc::opmode_t mode, + const bool ha = false); + static String fanspeedToString(const stdAc::fanspeed_t speed); + static String swingvToString(const stdAc::swingv_t swingv); + static String swinghToString(const stdAc::swingh_t swingh); + stdAc::state_t getState(void); + stdAc::state_t getStatePrev(void); + bool hasStateChanged(void); + stdAc::state_t next; ///< The state we want the device to be in after we send +#ifdef UNIT_TEST + /// @cond IGNORE + /// UT-specific + /// See @c OUTPUT_DECODE_RESULTS_FOR_UT macro description in IRac.cpp + std::shared_ptr _utReceiver = nullptr; + std::unique_ptr _lastDecodeResults = nullptr; + /// @endcond +#else + + private: +#endif // UNIT_TEST + uint16_t _pin; ///< The GPIO to use to transmit messages from. + 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_AIRTON + void airton(IRAirtonAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool light, const bool econo, const bool filter, + const int16_t sleep = -1); +#endif // SEND_AIRTON +#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, + const stdAc::fanspeed_t fan); +#endif // SEND_AMCOR +#if SEND_ARGO + void argo(IRArgoAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, const bool turbo, + const int16_t sleep = -1); + void argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, const bool night, + const bool econo, const bool turbo, const bool filter, const bool light); + void argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp); + void argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, + const uint8_t value, bool safe = true); + void argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, + const uint16_t currentTime, const uint16_t delayMinutes); +#endif // SEND_ARGO +#if SEND_BOSCH144 + void bosch144(IRBosch144AC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const bool quiet); +#endif // SEND_BOSCH144 +#if SEND_CARRIER_AC64 +void carrier64(IRCarrierAc64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep = -1); +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + void coolix(IRCoolixAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool light, + const bool clean, const int16_t sleep = -1); +#endif // SEND_COOLIX +#if SEND_CORONA_AC + void corona(IRCoronaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool econo); +#endif // SEND_CORONA_AC +#if SEND_DAIKIN + void daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean); +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + void daikin128(IRDaikin128 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool light, + const bool econo, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + void daikin152(IRDaikin152 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo); +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + void daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + void daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh); +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + void daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 +void daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo); +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 + void daikin64(IRDaikin64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_DAIKIN64 +#if SEND_DELONGHI_AC + void delonghiac(IRDelonghiAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const bool turbo, const int16_t sleep = -1); +#endif // SEND_DELONGHI_AC +#if SEND_ECOCLIM +void ecoclim(IREcoclimAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_ECOCLIM +#if SEND_ELECTRA_AC +void electra(IRElectraAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool iFeel, const bool turbo, + const bool lighttoggle, const bool clean); +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, const int16_t sleep = -1); +#endif // SEND_FUJITSU_AC +#if SEND_FUJITSU_AC264 + void fujitsu264(IRFujitsuAC264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo, + const bool clean, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_FUJITSU_AC264 +#if SEND_GOODWEATHER + void goodweather(IRGoodweatherAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep = -1); +#endif // SEND_GOODWEATHER +#if SEND_GREE + void gree(IRGreeAC *ac, const gree_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool econo, + const bool light, const bool clean, const int16_t sleep = -1); +#endif // SEND_GREE +#if SEND_HAIER_AC + void haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC160 + void haier160(IRHaierAC160 *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool quiet, const bool filter, + const bool clean, const bool light, const bool prevlight, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + void haier176(IRHaierAC176 *ac, + const haier_ac176_remote_model_t model, const bool on, + const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + void haierYrwo2(IRHaierACYRW02 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool turbo, + const bool quiet, const bool filter, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + void hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + void hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, + const bool on, const bool power_toggle, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool swing_toggle, const int16_t sleep = -1); +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + void hitachi264(IRHitachiAc264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + void hitachi344(IRHitachiAc344 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh); +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + void hitachi424(IRHitachiAc424 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv); +#endif // SEND_HITACHI_AC424 +#if SEND_KELON + void kelon(IRKelonAc *ac, const bool togglePower, const stdAc::opmode_t mode, + const int8_t dryGrade, const float degrees, + const stdAc::fanspeed_t fan, const bool toggleSwing, + const bool superCool, const int16_t sleep); +#endif // SEND_KELON +#if SEND_KELVINATOR + void kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean); +#endif // SEND_KELVINATOR +#if SEND_LG + void lg(IRLgAc *ac, const lg_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, + const stdAc::swingh_t swingh, const bool light); +#endif // SEND_LG +#if SEND_MIDEA + void midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool iFeel, const bool quiet, const bool quiet_prev, + const bool turbo, const bool econo, const bool light, + const bool clean, const int16_t sleep = -1); +#endif // SEND_MIDEA +#if SEND_MIRAGE + void mirage(IRMirageAc *ac, const stdAc::state_t state); +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + void mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const int16_t clock = -1); +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 + void mitsubishi112(IRMitsubishi112 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet); +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHI136 + void mitsubishi136(IRMitsubishi136 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool quiet); +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHIHEAVY + void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool clean); + void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, + const int16_t sleep = -1); +#endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + void neoclima(IRNeoclimaAc *ac, const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const bool filter, const int16_t sleep = -1); +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool filter, + const int16_t clock = -1); +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC32 + void panasonic32(IRPanasonicAc32 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_PANASONIC_AC32 +#if SEND_RHOSS + void rhoss(IRRhossAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swing); +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + void samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const bool prevpower = true, const int16_t prevsleep = -1, + const bool forceextended = true); +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + void sanyo(IRSanyoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, const bool beep, + const int16_t sleep = -1); +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + void sanyo88(IRSanyoAc88 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool filter, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_SANYO_AC88 +#if SEND_SHARP_AC + void sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, + const bool on, const bool prev_power, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, + const bool turbo, const bool light, + const bool filter, const bool clean); +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + void tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter); +#endif // SEND_TCL112AC +#if SEND_TECHNIBEL_AC + void technibel(IRTechnibelAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep = -1); +#endif // SEND_TECHNIBEL_AC +#if SEND_TECO + void teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool light, const int16_t sleep = -1); +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + void toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool econo, const bool filter); +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + void trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep = -1); +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + void trotec3550(IRTrotec3550 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv); +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + void truma(IRTrumaAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const bool quiet); +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + void vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, + const int16_t sleep = -1, const int16_t clock = -1, + const bool sendNormal = true); +#endif // SEND_VESTEL_AC +#if SEND_VOLTAS + void voltas(IRVoltas *ac, const voltas_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const int16_t sleep = -1); +#endif // SEND_VOLTAS +#if SEND_WHIRLPOOL_AC + void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_WHIRLPOOL_AC +#if SEND_TRANSCOLD + void transcold(IRTranscoldAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_TRANSCOLD +static stdAc::state_t cleanState(const stdAc::state_t state); +static stdAc::state_t handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev = NULL); +}; // IRac class + +/// Common functions for use with all A/Cs supported by the IRac class. +namespace IRAcUtils { + String resultAcToString(const decode_results * const results); + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev = NULL); +} // namespace IRAcUtils +#endif // IRAC_H_ diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 173526104..80c240cbb 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1,2079 +1,2089 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2015 Sebastien Warin -// Copyright 2017, 2019 David Conran - -#include "IRrecv.h" -#include -#ifndef UNIT_TEST -#if defined(ESP8266) -extern "C" { -#include -#include -} -#endif // ESP8266 -#include -#endif // UNIT_TEST -#include -#ifdef UNIT_TEST -#include -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "IRutils.h" - -#ifdef UNIT_TEST -#undef ICACHE_RAM_ATTR -#define ICACHE_RAM_ATTR -#endif - -#ifndef USE_IRAM_ATTR -#if defined(ESP8266) -#if defined(IRAM_ATTR) -#define USE_IRAM_ATTR IRAM_ATTR -#else // IRAM_ATTR -#define USE_IRAM_ATTR ICACHE_RAM_ATTR -#endif // IRAM_ATTR -#endif // ESP8266 -#if defined(ESP32) -#define USE_IRAM_ATTR IRAM_ATTR -#endif // ESP32 -#endif // USE_IRAM_ATTR - -#define ONCE 0 - -// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR -// code on ESP32 -// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code -// on ESP8266 -// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for -// sending IR code on ESP8266 - -// Globals -#ifndef UNIT_TEST -#if defined(ESP8266) -namespace _IRrecv { -static ETSTimer timer; -} // namespace _IRrecv -#endif // ESP8266 -#if defined(ESP32) -// We need a horrible timer hack for ESP32 Arduino framework < v2.0.0 -#if !defined(_ESP32_IRRECV_TIMER_HACK) -// Version check -#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) -// No need for the hack if we are running version >= 2.0.0 -#define _ESP32_IRRECV_TIMER_HACK false -#else // Version check -// If no ESP_ARDUINO_VERSION_MAJOR is defined, or less than 2, then we are -// using an old ESP32 core, so we need the hack. -#define _ESP32_IRRECV_TIMER_HACK true -#endif // Version check -#endif // !defined(_ESP32_IRRECV_TIMER_HACK) - -#if _ESP32_IRRECV_TIMER_HACK -// Required structs/types from: -// https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58 -// These are needed to be able to directly manipulate the timer registers from -// inside an ISR. This is very very ugly. -// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 -// Note: This will need to be updated if it ever changes. -// -// Start of Horrible Hack! -typedef struct { - union { - struct { - uint32_t reserved0: 10; - uint32_t alarm_en: 1; - /*When set alarm is enabled*/ - uint32_t level_int_en: 1; - /*When set level type interrupt will be generated during alarm*/ - uint32_t edge_int_en: 1; - /*When set edge type interrupt will be generated during alarm*/ - uint32_t divider: 16; - /*Timer clock (T0/1_clk) pre-scale value.*/ - uint32_t autoreload: 1; - /*When set timer 0/1 auto-reload at alarming is enabled*/ - uint32_t increase: 1; - /*When set timer 0/1 time-base counter increment. - When cleared timer 0 time-base counter decrement.*/ - uint32_t enable: 1; - /*When set timer 0/1 time-base counter is enabled*/ - }; - uint32_t val; - } config; - uint32_t cnt_low; - /*Register to store timer 0/1 time-base counter current value lower 32 - bits.*/ - uint32_t cnt_high; - /*Register to store timer 0 time-base counter current value higher 32 - bits.*/ - uint32_t update; - /*Write any value will trigger a timer 0 time-base counter value update - (timer 0 current value will be stored in registers above)*/ - uint32_t alarm_low; - /*Timer 0 time-base counter value lower 32 bits that will trigger the - alarm*/ - uint32_t alarm_high; - /*Timer 0 time-base counter value higher 32 bits that will trigger the - alarm*/ - uint32_t load_low; - /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ - uint32_t load_high; - /*higher 32 bits of the value that will load into timer 0 time-base - counter*/ - uint32_t reload; - /*Write any value will trigger timer 0 time-base counter reload*/ -} hw_timer_reg_t; - -typedef struct hw_timer_s { - hw_timer_reg_t * dev; - uint8_t num; - uint8_t group; - uint8_t timer; - portMUX_TYPE lock; -} hw_timer_t; -#endif // _ESP32_IRRECV_TIMER_HACK / End of Horrible Hack. - -namespace _IRrecv { -static hw_timer_t * timer = NULL; -} // namespace _IRrecv -#endif // ESP32 -using _IRrecv::timer; -#endif // UNIT_TEST - -namespace _IRrecv { // Namespace extension -#if defined(ESP32) -portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; -#endif // ESP32 -volatile irparams_t params; -irparams_t *params_save; // A copy of the interrupt state while decoding. -} // namespace _IRrecv - -#if defined(ESP32) -using _IRrecv::mux; -#endif // ESP32 -using _IRrecv::params; -using _IRrecv::params_save; - -#ifndef UNIT_TEST -#if defined(ESP8266) -/// Interrupt handler for when the timer runs out. -/// It signals to the library that capturing of IR data has stopped. -/// @param[in] arg Unused. (ESP8266 Only) -static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { - os_intr_lock(); -#endif // ESP8266 -/// @cond IGNORE -#if defined(ESP32) -/// Interrupt handler for when the timer runs out. -/// It signals to the library that capturing of IR data has stopped. -/// @note ESP32 version -static void USE_IRAM_ATTR read_timeout(void) { -/// @endcond - portENTER_CRITICAL(&mux); -#endif // ESP32 - if (params.rawlen) params.rcvstate = kStopState; -#if defined(ESP8266) - os_intr_unlock(); -#endif // ESP8266 -#if defined(ESP32) - portEXIT_CRITICAL(&mux); -#endif // ESP32 -} - -/// Interrupt handler for changes on the GPIO pin handling incoming IR messages. -static void USE_IRAM_ATTR gpio_intr() { - uint32_t now = micros(); - static uint32_t start = 0; - -#if defined(ESP8266) - uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); - os_timer_disarm(&timer); - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); -#endif // ESP8266 - - // Grab a local copy of rawlen to reduce instructions used in IRAM. - // This is an ugly premature optimisation code-wise, but we do everything we - // can to save IRAM. - // It seems referencing the value via the structure uses more instructions. - // Less instructions means faster and less IRAM used. - // N.B. It saves about 13 bytes of IRAM. - uint16_t rawlen = params.rawlen; - - if (rawlen >= params.bufsize) { - params.overflow = true; - params.rcvstate = kStopState; - } - - if (params.rcvstate == kStopState) return; - - if (params.rcvstate == kIdleState) { - params.rcvstate = kMarkState; - params.rawbuf[rawlen] = 1; - } else { - if (now < start) - params.rawbuf[rawlen] = (UINT32_MAX - start + now) / kRawTick; - else - params.rawbuf[rawlen] = (now - start) / kRawTick; - } - params.rawlen++; - - start = now; - -#if defined(ESP8266) - os_timer_arm(&timer, params.timeout, ONCE); -#endif // ESP8266 -#if defined(ESP32) - // Reset the timeout. - // -#if _ESP32_IRRECV_TIMER_HACK - // The following three lines of code are the equiv of: - // `timerWrite(timer, 0);` - // We can't call that routine safely from inside an ISR as that procedure - // is not stored in IRAM. Hence, we do it manually so that it's covered by - // USE_IRAM_ATTR in this ISR. - // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 - // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L106-L110 - timer->dev->load_high = (uint32_t) 0; - timer->dev->load_low = (uint32_t) 0; - timer->dev->reload = 1; - // The next line is the same, but instead replaces: - // `timerAlarmEnable(timer);` - // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 - // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178 - timer->dev->config.alarm_en = 1; -#else // _ESP32_IRRECV_TIMER_HACK - timerWrite(timer, 0); - timerAlarmEnable(timer); -#endif // _ESP32_IRRECV_TIMER_HACK -#endif // ESP32 -} -#endif // UNIT_TEST - -// Start of IRrecv class ------------------- - -/// Class constructor -/// Args: -/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is -/// connected to. -/// @param[in] bufsize Nr. of entries to have in the capture buffer. -/// (Default: kRawBuf) -/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop -/// capturing data. (Default: kTimeoutMs) -/// @param[in] save_buffer Use a second (save) buffer to decode from. -/// (Default: false) -/// @param[in] timer_num Nr. of the ESP32 timer to use. (0 to 3) (ESP32 Only) -/// or (0 to 1) (ESP32-C3) -#if defined(ESP32) -IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, - const uint8_t timeout, const bool save_buffer, - const uint8_t timer_num) { - // Ensure we use a valid timer number. - _timer_num = std::min(timer_num, - (uint8_t)( -#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS - SOC_TIMER_GROUP_TOTAL_TIMERS - 1)); -#else // SOC_TIMER_GROUP_TOTAL_TIMERS - 3)); -#endif // SOC_TIMER_GROUP_TOTAL_TIMERS -#else // ESP32 -/// @cond IGNORE -/// Class constructor -/// Args: -/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is -/// connected to. -/// @param[in] bufsize Nr. of entries to have in the capture buffer. -/// (Default: kRawBuf) -/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop -/// capturing data. (Default: kTimeoutMs) -/// @param[in] save_buffer Use a second (save) buffer to decode from. -/// (Default: false) -IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, - const uint8_t timeout, const bool save_buffer) { -/// @endcond -#endif // ESP32 - params.recvpin = recvpin; - params.bufsize = bufsize; - // Ensure we are going to be able to store all possible values in the - // capture buffer. - params.timeout = std::min(timeout, (uint8_t)kMaxTimeoutMs); - params.rawbuf = new uint16_t[bufsize]; - if (params.rawbuf == NULL) { - DPRINTLN( - "Could not allocate memory for the primary IR buffer.\n" - "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); -#ifndef UNIT_TEST - ESP.restart(); // Mem alloc failure. Reboot. -#endif - } - // If we have been asked to use a save buffer (for decoding), then create one. - if (save_buffer) { - params_save = new irparams_t; - params_save->rawbuf = new uint16_t[bufsize]; - // Check we allocated the memory successfully. - if (params_save->rawbuf == NULL) { - DPRINTLN( - "Could not allocate memory for the second IR buffer.\n" - "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); -#ifndef UNIT_TEST - ESP.restart(); // Mem alloc failure. Reboot. -#endif - } - } else { - params_save = NULL; - } -#if DECODE_HASH - _unknown_threshold = kUnknownThreshold; -#endif // DECODE_HASH - _tolerance = kTolerance; -} - -/// Class destructor -/// Cleans up after the object is no longer needed. -/// e.g. Frees up all memory used by the various buffers, and disables any -/// timers or interrupts used. -IRrecv::~IRrecv(void) { - disableIRIn(); -#if defined(ESP32) - if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. -#endif // ESP32 - delete[] params.rawbuf; - if (params_save != NULL) { - delete[] params_save->rawbuf; - delete params_save; - } -} - -/// Set up and (re)start the IR capture mechanism. -/// @param[in] pullup A flag indicating should the GPIO use the internal pullup -/// resistor. (Default: `false`. i.e. No.) -void IRrecv::enableIRIn(const bool pullup) { - // ESP32's seem to require explicitly setting the GPIO to INPUT etc. - // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. - if (pullup) { -#ifndef UNIT_TEST - pinMode(params.recvpin, INPUT_PULLUP); - } else { - pinMode(params.recvpin, INPUT); -#endif // UNIT_TEST - } -#if defined(ESP32) - // Initialise the ESP32 timer. - // 80MHz / 80 = 1 uSec granularity. - timer = timerBegin(_timer_num, 80, true); -#ifdef DEBUG - if (timer == NULL) { - DPRINT("FATAL: Unable enable system timer: "); - DPRINTLN((uint16_t)_timer_num); - } -#endif // DEBUG - assert(timer != NULL); // Check we actually got the timer. - // Set the timer so it only fires once, and set it's trigger in uSeconds. - timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); - // Note: Interrupt needs to be attached before it can be enabled or disabled. - // Note: EDGE (true) is not supported, use LEVEL (false). Ref: #1713 - // See: https://github.com/espressif/arduino-esp32/blob/caef4006af491130136b219c1205bdcf8f08bf2b/cores/esp32/esp32-hal-timer.c#L224-L227 - timerAttachInterrupt(timer, &read_timeout, false); -#endif // ESP32 - - // Initialise state machine variables - resume(); - -#ifndef UNIT_TEST -#if defined(ESP8266) - // Initialise ESP8266 timer. - os_timer_disarm(&timer); - os_timer_setfn(&timer, reinterpret_cast(read_timeout), - NULL); -#endif // ESP8266 - // Attach Interrupt - attachInterrupt(params.recvpin, gpio_intr, CHANGE); -#endif // UNIT_TEST -} - -/// Stop collection of any received IR data. -/// Disable any timers and interrupts. -void IRrecv::disableIRIn(void) { -#ifndef UNIT_TEST -#if defined(ESP8266) - os_timer_disarm(&timer); -#endif // ESP8266 -#if defined(ESP32) - timerAlarmDisable(timer); - timerDetachInterrupt(timer); - timerEnd(timer); -#endif // ESP32 - detachInterrupt(params.recvpin); -#endif // UNIT_TEST -} - -/// Pause collection of received IR data. -/// @see IRrecv class constructor -void IRrecv::pause(void) { - params.rcvstate = kStopState; - params.rawlen = 0; - params.overflow = false; -#if defined(ESP32) - gpio_intr_disable((gpio_num_t)params.recvpin); -#endif // ESP32 -} - -/// Resume collection of received IR data. -/// @note This is required if `decode()` is successful and `save_buffer` was -/// not set when the class was instanciated. -/// @see IRrecv class constructor -void IRrecv::resume(void) { - params.rcvstate = kIdleState; - params.rawlen = 0; - params.overflow = false; -#if defined(ESP32) - timerAlarmDisable(timer); - gpio_intr_enable((gpio_num_t)params.recvpin); -#endif // ESP32 -} - -/// Make a copy of the interrupt state & buffer data. -/// Needed because irparams is marked as volatile, thus memcpy() isn't allowed. -/// Only call this when you know the interrupt handlers won't modify anything. -/// i.e. In kStopState. -/// @param[in] src Pointer to an irparams_t structure to copy from. -/// @param[out] dst Pointer to an irparams_t structure to copy to. -void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { - // Typecast src and dst addresses to (char *) - char *csrc = (char *)src; // NOLINT(readability/casting) - char *cdst = (char *)dst; // NOLINT(readability/casting) - - // Save the pointer to the destination's rawbuf so we don't lose it as - // the for-loop/copy after this will overwrite it with src's rawbuf pointer. - // This isn't immediately obvious due to typecasting/different variable names. - uint16_t *dst_rawbuf_ptr; - dst_rawbuf_ptr = dst->rawbuf; - - // Copy contents of src[] to dst[] - for (uint16_t i = 0; i < sizeof(irparams_t); i++) cdst[i] = csrc[i]; - - // Restore the buffer pointer - dst->rawbuf = dst_rawbuf_ptr; - - // Copy the rawbuf - for (uint16_t i = 0; i < dst->bufsize; i++) dst->rawbuf[i] = src->rawbuf[i]; -} - -/// Obtain the maximum number of entries possible in the capture buffer. -/// i.e. It's size. -/// @return The size of the buffer that is in use by the object. -uint16_t IRrecv::getBufSize(void) { return params.bufsize; } - -#if DECODE_HASH -/// Set the minimum length we will consider for reporting UNKNOWN message types. -/// @param[in] length Min nr. of mark/space pulses required to be considered. -void IRrecv::setUnknownThreshold(const uint16_t length) { - _unknown_threshold = length; -} -#endif // DECODE_HASH - - -/// Set the base tolerance percentage for matching incoming IR messages. -/// @param[in] percent An integer percentage. (0-100) -void IRrecv::setTolerance(const uint8_t percent) { - _tolerance = std::min(percent, (uint8_t)100); -} - -/// Get the base tolerance percentage for matching incoming IR messages. -/// @return A integer percentage. -uint8_t IRrecv::getTolerance(void) { return _tolerance; } - -#if ENABLE_NOISE_FILTER_OPTION -/// Remove or merge pulses in the capture buffer that are too short. -/// @param[in,out] results Ptr to the decode_results we are going to filter. -/// @param[in] floor Only allow values in the buffer large than this. -/// (in microSeconds) -void IRrecv::crudeNoiseFilter(decode_results *results, const uint16_t floor) { - if (floor == 0) return; // Nothing to do. - const uint16_t kTickFloor = floor / kRawTick; - const uint16_t kBufSize = getBufSize(); - uint16_t offset = kStartOffset; - while (offset < results->rawlen && offset + 2 < kBufSize) { - uint16_t curr = results->rawbuf[offset]; - uint16_t next = results->rawbuf[offset + 1]; - uint16_t addition = curr + next; - if (curr < kTickFloor) { // Is it too short? - // Shuffle the buffer down. i.e. Remove the mark & space pair. - // Note: `memcpy()` can't be used as rawbuf is `volatile`. - for (uint16_t i = offset + 2; i <= results->rawlen && i < kBufSize; i++) - results->rawbuf[i - 2] = results->rawbuf[i]; - if (offset > 1) { // There is a previous pair we can add to. - // Merge this pair into into the previous space. - results->rawbuf[offset - 1] += addition; - } - results->rawlen -= 2; // Adjust the length. - } else { - offset++; // Move along. - } - } -} -#endif // ENABLE_NOISE_FILTER_OPTION - -/// Decodes the received IR message. -/// If the interrupt state is saved, we will immediately resume waiting -/// for the next IR message to avoid missing messages. -/// @note There is a trade-off here. Saving the state means less time lost until -/// we can receiving the next message vs. using more RAM. Choose appropriately. -/// @param[out] results A PTR to where the decoded IR message will be stored. -/// @param[out] save A PTR to an irparams_t instance in which to save -/// the interrupt's memory/state. NULL means don't save it. -/// @param[in] max_skip Maximum Nr. of pulses at the begining of a capture we -/// can skip when attempting to find a protocol we can successfully decode. -/// This parameter can dramatically improve detection of protocols -/// when there is light IR interference just before an incoming IR -/// message, however, it comes at a steep performace price. -/// (Default is 0. No skipping.) -/// @warning Increasing the `max_skip` value will dramatically (linearly) -/// increase the cpu time & usage to decode protocols. -/// e.g. 0 -> 1 will be a 2x increase in cpu usage/time. -/// 0 -> 2 will be a 3x increase etc. -/// If you are going to do this, consider disabling protocol decoding for -/// protocols you are not expecting. -/// @param[in] noise_floor Pulses below this size (in usecs) will be removed or -/// merged prior to any decoding. This is to try to remove noise/poor -/// readings & slightly increase the chances of a successful decode but at the -/// cost of data fidelity & integrity. -/// (Defaults to 0 usecs. i.e. Don't filter; which is safe!) -/// @warning DANGER: **Here Be Dragons!** -/// If you set the `noise_floor` value too high, it **WILL** break decoding -/// of some protocols. You have been warned! -/// **Any** non-zero value has the potential to **cook** the captured raw data -/// i.e. The raw data is going to lie to you. -/// It may obscure hardware, circuit, & environment issues thus making it -/// impossible to support you accurately or confidently. -/// Values of <= 50 usecs will probably be safe. -/// 51 - 100 usecs **might** be okay. -/// 100 - 150 usecs is "Danger, Will Robinson!". -/// 150 - 200 usecs expect broken protocols. -/// At 200+ usecs, you **have** protocols you can't decode!! -/// @return A boolean indicating if an IR message is ready or not. -bool IRrecv::decode(decode_results *results, irparams_t *save, - uint8_t max_skip, uint16_t noise_floor) { - // Proceed only if an IR message been received. -#ifndef UNIT_TEST - if (params.rcvstate != kStopState) return false; -#endif - - // Clear the entry we are currently pointing to when we got the timeout. - // i.e. Stopped collecting IR data. - // It's junk as we never wrote an entry to it and can only confuse decoding. - // This is done here rather than logically the best place in read_timeout() - // as it saves a few bytes of ICACHE_RAM as that routine is bound to an - // interrupt. decode() is not stored in ICACHE_RAM. - // Another better option would be to zero the entire irparams.rawbuf[] on - // resume() but that is a much more expensive operation compare to this. - // However, don't do this if rawbuf is already full as we stomp over the heap. - // See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516 - if (!params.overflow) params.rawbuf[params.rawlen] = 0; - - bool resumed = false; // Flag indicating if we have resumed. - - // If we were requested to use a save buffer previously, do so. - if (save == NULL) save = params_save; - - if (save == NULL) { - // We haven't been asked to copy it so use the existing memory. -#ifndef UNIT_TEST - results->rawbuf = params.rawbuf; - results->rawlen = params.rawlen; - results->overflow = params.overflow; -#endif - } else { - copyIrParams(¶ms, save); // Duplicate the interrupt's memory. - resume(); // It's now safe to rearm. The IR message won't be overridden. - resumed = true; - // Point the results at the saved copy. - results->rawbuf = save->rawbuf; - results->rawlen = save->rawlen; - results->overflow = save->overflow; - } - - // Reset any previously partially processed results. - results->decode_type = UNKNOWN; - results->bits = 0; - results->value = 0; - results->address = 0; - results->command = 0; - results->repeat = false; - -#if ENABLE_NOISE_FILTER_OPTION - crudeNoiseFilter(results, noise_floor); -#endif // ENABLE_NOISE_FILTER_OPTION - // Keep looking for protocols until we've run out of entries to skip or we - // find a valid protocol message. - for (uint16_t offset = kStartOffset; - offset <= (max_skip * 2) + kStartOffset; - offset += 2) { -#if DECODE_AIWA_RC_T501 - DPRINTLN("Attempting Aiwa RC T501 decode"); - // Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC() - // because the protocols are similar. This protocol is more specific than - // those ones, so should go before them. - if (decodeAiwaRCT501(results, offset)) return true; -#endif -#if DECODE_SANYO - DPRINTLN("Attempting Sanyo LC7461 decode"); - // Try decodeSanyoLC7461() before decodeNEC() because the protocols are - // similar in timings & structure, but the Sanyo one is much longer than the - // NEC protocol (42 vs 32 bits) so this one should be tried first to try to - // reduce false detection as a NEC packet. - if (decodeSanyoLC7461(results, offset)) return true; -#endif -#if DECODE_CARRIER_AC - DPRINTLN("Attempting Carrier AC decode"); - // Try decodeCarrierAC() before decodeNEC() because the protocols are - // similar in timings & structure, but the Carrier one is much longer than - // the NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodeCarrierAC(results, offset)) return true; -#endif -#if DECODE_PIONEER - DPRINTLN("Attempting Pioneer decode"); - // Try decodePioneer() before decodeNEC() because the protocols are - // similar in timings & structure, but the Pioneer one is much longer than - // the NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodePioneer(results, offset)) return true; -#endif -#if DECODE_EPSON - DPRINTLN("Attempting Epson decode"); - // Try decodeEpson() before decodeNEC() because the protocols are - // similar in timings & structure, but the Epson one is much longer than the - // NEC protocol (3x32 identical bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodeEpson(results, offset)) return true; -#endif -#if DECODE_NEC - DPRINTLN("Attempting NEC decode"); - if (decodeNEC(results, offset)) return true; -#endif -#if DECODE_MILESTAG2 - DPRINTLN("Attempting MilesTag2 decode"); - // Try decodeMilestag2() before decodeSony() because the protocols are - // similar in timings & structure, but the Miles one differs in nbits - // so this one should be tried first to try to reduce false detection - if (decodeMilestag2(results, offset, kMilesTag2MsgBits) || - decodeMilestag2(results, offset, kMilesTag2ShotBits)) return true; -#endif -#if DECODE_SONY - DPRINTLN("Attempting Sony decode"); - if (decodeSony(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI - DPRINTLN("Attempting Mitsubishi decode"); - if (decodeMitsubishi(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI_AC - DPRINTLN("Attempting Mitsubishi AC decode"); - if (decodeMitsubishiAC(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI2 - DPRINTLN("Attempting Mitsubishi2 decode"); - if (decodeMitsubishi2(results, offset)) return true; -#endif -#if DECODE_RC5 - DPRINTLN("Attempting RC5 decode"); - if (decodeRC5(results, offset)) return true; -#endif -#if DECODE_RC6 - DPRINTLN("Attempting RC6 decode"); - if (decodeRC6(results, offset)) return true; -#endif -#if DECODE_RCMM - DPRINTLN("Attempting RC-MM decode"); - if (decodeRCMM(results, offset)) return true; -#endif -#if DECODE_FUJITSU_AC - // Fujitsu A/C needs to precede Panasonic and Denon as it has a short - // message which looks exactly the same as a Panasonic/Denon message. - DPRINTLN("Attempting Fujitsu A/C decode"); - if (decodeFujitsuAC(results, offset)) return true; -#endif -#if DECODE_DENON - // Denon needs to precede Panasonic as it is a special case of Panasonic. - DPRINTLN("Attempting Denon decode"); - if (decodeDenon(results, offset, kDenon48Bits) || - decodeDenon(results, offset, kDenonBits) || - decodeDenon(results, offset, kDenonLegacyBits)) - return true; -#endif -#if DECODE_PANASONIC - DPRINTLN("Attempting Panasonic (48-bit) decode"); - if (decodePanasonic(results, offset)) return true; - DPRINTLN("Attempting Panasonic (40-bit) decode"); - if (decodePanasonic(results, offset, kPanasonic40Bits, true, - kPanasonic40Manufacturer)) return true; -#endif // DECODE_PANASONIC -#if DECODE_LG - DPRINTLN("Attempting LG (28-bit) decode"); - if (decodeLG(results, offset, kLgBits, true)) return true; - DPRINTLN("Attempting LG (32-bit) decode"); - // LG32 should be tried before Samsung - if (decodeLG(results, offset, kLg32Bits, true)) return true; -#endif -#if DECODE_GICABLE - // Note: Needs to happen before JVC decode, because it looks similar except - // with a required NEC-like repeat code. - DPRINTLN("Attempting GICable decode"); - if (decodeGICable(results, offset)) return true; -#endif -#if DECODE_JVC - DPRINTLN("Attempting JVC decode"); - if (decodeJVC(results, offset)) return true; -#endif -#if DECODE_SAMSUNG - DPRINTLN("Attempting SAMSUNG decode"); - if (decodeSAMSUNG(results, offset)) return true; -#endif -#if DECODE_SAMSUNG36 - DPRINTLN("Attempting Samsung36 decode"); - if (decodeSamsung36(results, offset)) return true; -#endif -#if DECODE_WHYNTER - DPRINTLN("Attempting Whynter decode"); - if (decodeWhynter(results, offset)) return true; -#endif -#if DECODE_DISH - DPRINTLN("Attempting DISH decode"); - if (decodeDISH(results, offset)) return true; -#endif -#if DECODE_SHARP - DPRINTLN("Attempting Sharp decode"); - if (decodeSharp(results, offset)) return true; -#endif -#if DECODE_BOSCH144 - DPRINTLN("Attempting Bosch 144-bit decode"); - // Bosch is similar to Coolix, so it must be attempted before decodeCOOLIX. - if (decodeBosch144(results, offset)) return true; -#endif // DECODE_BOSCH144 -#if DECODE_COOLIX - DPRINTLN("Attempting Coolix 24-bit decode"); - if (decodeCOOLIX(results, offset)) return true; -#endif // DECODE_COOLIX -#if DECODE_NIKAI - DPRINTLN("Attempting Nikai decode"); - if (decodeNikai(results, offset)) return true; -#endif -#if DECODE_KELVINATOR - // Kelvinator based-devices use a similar code to Gree ones, to avoid false - // matches this needs to happen before decodeGree(). - DPRINTLN("Attempting Kelvinator decode"); - if (decodeKelvinator(results, offset)) return true; -#endif -#if DECODE_DAIKIN - DPRINTLN("Attempting Daikin decode"); - if (decodeDaikin(results, offset)) return true; -#endif -#if DECODE_DAIKIN2 - DPRINTLN("Attempting Daikin2 decode"); - if (decodeDaikin2(results, offset)) return true; -#endif -#if DECODE_DAIKIN216 - DPRINTLN("Attempting Daikin216 decode"); - if (decodeDaikin216(results, offset)) return true; -#endif -#if DECODE_TOSHIBA_AC - DPRINTLN("Attempting Toshiba AC 72bit decode"); - if (decodeToshibaAC(results, offset)) return true; - DPRINTLN("Attempting Toshiba AC 80bit decode"); - if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true; - DPRINTLN("Attempting Toshiba AC 56bit decode"); - if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true; -#endif -#if DECODE_MIDEA - DPRINTLN("Attempting Midea decode"); - if (decodeMidea(results, offset)) return true; -#endif -#if DECODE_MAGIQUEST - DPRINTLN("Attempting Magiquest decode"); - if (decodeMagiQuest(results, offset)) return true; -#endif - /* NOTE: Disabled due to poor quality. -#if DECODE_SANYO - // The Sanyo S866500B decoder is very poor quality & depricated. - // *IF* you are going to enable it, do it near last to avoid false positive - // matches. - DPRINTLN("Attempting Sanyo SA8650B decode"); - if (decodeSanyo(results, offset)) - return true; -#endif - */ -#if DECODE_NEC - // Some devices send NEC-like codes that don't follow the true NEC spec. - // This should detect those. e.g. Apple TV remote etc. - // This needs to be done after all other codes that use strict and some - // other protocols that are NEC-like as well, as turning off strict may - // cause this to match other valid protocols. - DPRINTLN("Attempting NEC (non-strict) decode"); - if (decodeNEC(results, offset, kNECBits, false)) { - results->decode_type = NEC_LIKE; - return true; - } -#endif -#if DECODE_LASERTAG - DPRINTLN("Attempting Lasertag decode"); - if (decodeLasertag(results, offset)) return true; -#endif -#if DECODE_GREE - // Gree based-devices use a similar code to Kelvinator ones, to avoid false - // matches this needs to happen after decodeKelvinator(). - DPRINTLN("Attempting Gree decode"); - if (decodeGree(results, offset)) return true; -#endif -#if DECODE_HAIER_AC - DPRINTLN("Attempting Haier AC decode"); - if (decodeHaierAC(results, offset)) return true; -#endif -#if DECODE_HAIER_AC_YRW02 - DPRINTLN("Attempting Haier AC YR-W02 decode"); - if (decodeHaierACYRW02(results, offset)) return true; -#endif -#if DECODE_HAIER_AC176 - DPRINTLN("Attempting Haier AC 176 bit decode"); - if (decodeHaierAC176(results, offset)) return true; -#endif // DECODE_HAIER_AC176 -#if DECODE_HITACHI_AC424 - // HitachiAc424 should be checked before HitachiAC, HitachiAC2, - // & HitachiAC184 - DPRINTLN("Attempting Hitachi AC 424 decode"); - if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true; -#endif // DECODE_HITACHI_AC424 -#if DECODE_MITSUBISHI136 - // Needs to happen before HitachiAc3 decode. - DPRINTLN("Attempting Mitsubishi136 decode"); - if (decodeMitsubishi136(results, offset)) return true; -#endif // DECODE_MITSUBISHI136 -#if DECODE_HITACHI_AC3 - // HitachiAc3 should be checked before HitachiAC & HitachiAC2 - // Attempt normal before the short version. - DPRINTLN("Attempting Hitachi AC3 decode"); - // Order these in decreasing bit size, as it is more optimal. - if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) || - decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3MinBits)) - return true; -#endif // DECODE_HITACHI_AC3 -#if DECODE_HITACHI_AC344 - // HitachiAC344 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC344 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false)) - return true; -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC264 - // HitachiAC264 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC264 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc264Bits, true, false)) - return true; -#endif // DECODE_HITACHI_AC264 -#if DECODE_HITACHI_AC296 - // HitachiAC296 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC296 decode"); - if (decodeHitachiAc296(results, offset, kHitachiAc296Bits, true)) - return true; -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC2 - // HitachiAC2 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC2 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc2Bits)) return true; -#endif // DECODE_HITACHI_AC2 -#if DECODE_HITACHI_AC - DPRINTLN("Attempting Hitachi AC decode"); - if (decodeHitachiAC(results, offset, kHitachiAcBits)) return true; -#endif -#if DECODE_HITACHI_AC1 - DPRINTLN("Attempting Hitachi AC1 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc1Bits)) return true; -#endif -#if DECODE_WHIRLPOOL_AC - DPRINTLN("Attempting Whirlpool AC decode"); - if (decodeWhirlpoolAC(results, offset)) return true; -#endif -#if DECODE_SAMSUNG_AC - DPRINTLN("Attempting Samsung AC (extended) decode"); - // Check the extended size first, as it should fail fast due to longer - // length. - if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits)) return true; - // Now check for the more common length. - DPRINTLN("Attempting Samsung AC decode"); - if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true; -#endif -#if DECODE_ELECTRA_AC - DPRINTLN("Attempting Electra AC decode"); - if (decodeElectraAC(results, offset)) return true; -#endif -#if DECODE_PANASONIC_AC - DPRINTLN("Attempting Panasonic AC decode"); - if (decodePanasonicAC(results, offset)) return true; - DPRINTLN("Attempting Panasonic AC short decode"); - if (decodePanasonicAC(results, offset, kPanasonicAcShortBits)) return true; -#endif -#if DECODE_LUTRON - DPRINTLN("Attempting Lutron decode"); - if (decodeLutron(results, offset)) return true; -#endif -#if DECODE_MWM - DPRINTLN("Attempting MWM decode"); - if (decodeMWM(results, offset)) return true; -#endif -#if DECODE_VESTEL_AC - DPRINTLN("Attempting Vestel AC decode"); - if (decodeVestelAc(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI112 || DECODE_TCL112AC - // Mitsubish112 and Tcl112 share the same decoder. - DPRINTLN("Attempting Mitsubishi112/TCL112AC decode"); - if (decodeMitsubishi112(results, offset)) return true; -#endif // DECODE_MITSUBISHI112 || DECODE_TCL112AC -#if DECODE_TECO - DPRINTLN("Attempting Teco decode"); - if (decodeTeco(results, offset)) return true; -#endif -#if DECODE_LEGOPF - DPRINTLN("Attempting LEGOPF decode"); - if (decodeLegoPf(results, offset)) return true; -#endif -#if DECODE_MITSUBISHIHEAVY - DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); - if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy152Bits)) - return true; - DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); - if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy88Bits)) - return true; -#endif -#if DECODE_ARGO - DPRINTLN("Attempting Argo WREM3 decode (AC Control)"); - if (decodeArgoWREM3(results, offset, kArgo3AcControlStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM3 decode (iFeel report)"); - if (decodeArgoWREM3(results, offset, kArgo3iFeelReportStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM3 decode (Config)"); - if (decodeArgoWREM3(results, offset, kArgo3ConfigStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM3 decode (Timer)"); - if (decodeArgoWREM3(results, offset, kArgo3TimerStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM2 decode"); - if (decodeArgo(results, offset, kArgoBits) || - decodeArgo(results, offset, kArgoShortBits, false)) return true; -#endif // DECODE_ARGO -#if DECODE_SHARP_AC - DPRINTLN("Attempting SHARP_AC decode"); - if (decodeSharpAc(results, offset)) return true; -#endif -#if DECODE_GOODWEATHER - DPRINTLN("Attempting GOODWEATHER decode"); - if (decodeGoodweather(results, offset)) return true; -#endif // DECODE_GOODWEATHER -#if DECODE_INAX - DPRINTLN("Attempting Inax decode"); - if (decodeInax(results, offset)) return true; -#endif // DECODE_INAX -#if DECODE_TROTEC - DPRINTLN("Attempting Trotec decode"); - if (decodeTrotec(results, offset)) return true; -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - DPRINTLN("Attempting Trotec 3550 decode"); - if (decodeTrotec3550(results, offset)) return true; -#endif // DECODE_TROTEC_3550 -#if DECODE_DAIKIN160 - DPRINTLN("Attempting Daikin160 decode"); - if (decodeDaikin160(results, offset)) return true; -#endif // DECODE_DAIKIN160 -#if DECODE_NEOCLIMA - DPRINTLN("Attempting Neoclima decode"); - if (decodeNeoclima(results, offset)) return true; -#endif // DECODE_NEOCLIMA -#if DECODE_DAIKIN176 - DPRINTLN("Attempting Daikin176 decode"); - if (decodeDaikin176(results, offset)) return true; -#endif // DECODE_DAIKIN176 -#if DECODE_DAIKIN128 - DPRINTLN("Attempting Daikin128 decode"); - if (decodeDaikin128(results, offset)) return true; -#endif // DECODE_DAIKIN128 -#if DECODE_AMCOR - DPRINTLN("Attempting Amcor decode"); - if (decodeAmcor(results, offset)) return true; -#endif // DECODE_AMCOR -#if DECODE_DAIKIN152 - DPRINTLN("Attempting Daikin152 decode"); - if (decodeDaikin152(results, offset)) return true; -#endif // DECODE_DAIKIN152 -#if DECODE_SYMPHONY - DPRINTLN("Attempting Symphony decode"); - if (decodeSymphony(results, offset)) return true; -#endif // DECODE_SYMPHONY -#if DECODE_DAIKIN64 - DPRINTLN("Attempting Daikin64 decode"); - if (decodeDaikin64(results, offset)) return true; -#endif // DECODE_DAIKIN64 -#if DECODE_AIRWELL - DPRINTLN("Attempting Airwell decode"); - if (decodeAirwell(results, offset)) return true; -#endif // DECODE_AIRWELL -#if DECODE_DELONGHI_AC - DPRINTLN("Attempting Delonghi AC decode"); - if (decodeDelonghiAc(results, offset)) return true; -#endif // DECODE_DELONGHI_AC -#if DECODE_DOSHISHA - DPRINTLN("Attempting Doshisha decode"); - if (decodeDoshisha(results, offset)) return true; -#endif // DECODE_DOSHISHA -#if DECODE_TRUMA - // Needs to happen before decodeMultibrackets() as they can appear similar. - DPRINTLN("Attempting Truma decode"); - if (decodeTruma(results, offset)) return true; -#endif // DECODE_TRUMA -#if DECODE_MULTIBRACKETS - DPRINTLN("Attempting Multibrackets decode"); - if (decodeMultibrackets(results, offset)) return true; -#endif // DECODE_MULTIBRACKETS -#if DECODE_CARRIER_AC40 - DPRINTLN("Attempting Carrier 40bit decode"); - if (decodeCarrierAC40(results, offset)) return true; -#endif // DECODE_CARRIER_AC40 -#if DECODE_CARRIER_AC64 - DPRINTLN("Attempting Carrier 64bit decode"); - if (decodeCarrierAC64(results, offset)) return true; -#endif // DECODE_CARRIER_AC64 -#if DECODE_TECHNIBEL_AC - DPRINTLN("Attempting Technibel AC decode"); - if (decodeTechnibelAc(results, offset)) return true; -#endif // DECODE_TECHNIBEL_AC -#if DECODE_CORONA_AC - DPRINTLN("Attempting CoronaAc decode"); - if (decodeCoronaAc(results, offset)) return true; -#endif // DECODE_CORONA_AC -#if DECODE_MIDEA24 - DPRINTLN("Attempting Midea-Nec decode"); - if (decodeMidea24(results, offset)) return true; -#endif // DECODE_MIDEA24 -#if DECODE_ZEPEAL - DPRINTLN("Attempting Zepeal decode"); - if (decodeZepeal(results, offset)) return true; -#endif // DECODE_ZEPEAL -#if DECODE_SANYO_AC - DPRINTLN("Attempting Sanyo AC decode"); - if (decodeSanyoAc(results, offset)) return true; -#endif // DECODE_SANYO_AC -#if DECODE_VOLTAS - DPRINTLN("Attempting Voltas decode"); - if (decodeVoltas(results)) return true; -#endif // DECODE_VOLTAS -#if DECODE_METZ - DPRINTLN("Attempting Metz decode"); - if (decodeMetz(results, offset)) return true; -#endif // DECODE_METZ -#if DECODE_TRANSCOLD - DPRINTLN("Attempting Transcold decode"); - if (decodeTranscold(results, offset)) return true; -#endif // DECODE_TRANSCOLD -#if DECODE_MIRAGE - DPRINTLN("Attempting Mirage decode"); - if (decodeMirage(results, offset)) return true; -#endif // DECODE_MIRAGE -#if DECODE_ELITESCREENS - DPRINTLN("Attempting EliteScreens decode"); - if (decodeElitescreens(results, offset)) return true; -#endif // DECODE_ELITESCREENS -#if DECODE_PANASONIC_AC32 - DPRINTLN("Attempting Panasonic AC (32bit) long decode"); - if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits)) return true; - DPRINTLN("Attempting Panasonic AC (32bit) short decode"); - if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits / 2)) - return true; -#endif // DECODE_PANASONIC_AC32 -#if DECODE_ECOCLIM - DPRINTLN("Attempting Ecoclim decode"); - if (decodeEcoclim(results, offset, kEcoclimBits) || - decodeEcoclim(results, offset, kEcoclimShortBits)) return true; -#endif // DECODE_ECOCLIM -#if DECODE_XMP - DPRINTLN("Attempting XMP decode"); - if (decodeXmp(results, offset, kXmpBits)) return true; -#endif // DECODE_XMP -#if DECODE_TEKNOPOINT - DPRINTLN("Attempting Teknopoint decode"); - if (decodeTeknopoint(results, offset)) return true; -#endif // DECODE_TEKNOPOINT -#if DECODE_KELON168 - DPRINTLN("Attempting Kelon 168-bit decode"); - if (decodeKelon168(results, offset)) return true; -#endif // DECODE_KELON168 -#if DECODE_KELON - DPRINTLN("Attempting Kelon 48-bit decode"); - if (decodeKelon(results, offset)) return true; -#endif // DECODE_KELON -#if DECODE_SANYO_AC88 - DPRINTLN("Attempting SanyoAc88 decode"); - if (decodeSanyoAc88(results, offset)) return true; -#endif // DECODE_SANYO_AC88 -#if DECODE_BOSE - DPRINTLN("Attempting Bose decode"); - if (decodeBose(results, offset)) return true; -#endif // DECODE_BOSE -#if DECODE_ARRIS - DPRINTLN("Attempting Arris decode"); - if (decodeArris(results, offset)) return true; -#endif // DECODE_ARRIS -#if DECODE_RHOSS - DPRINTLN("Attempting Rhoss decode"); - if (decodeRhoss(results, offset)) return true; -#endif // DECODE_RHOSS -#if DECODE_AIRTON - DPRINTLN("Attempting Airton decode"); - if (decodeAirton(results, offset)) return true; -#endif // DECODE_AIRTON -#if DECODE_COOLIX48 - DPRINTLN("Attempting Coolix 48-bit decode"); - if (decodeCoolix48(results, offset)) return true; -#endif // DECODE_COOLIX48 -#if DECODE_DAIKIN200 - DPRINTLN("Attempting Daikin 200-bit decode"); - if (decodeDaikin200(results, offset)) return true; -#endif // DECODE_DAIKIN200 -#if DECODE_HAIER_AC160 - DPRINTLN("Attempting Haier AC 160 bit decode"); - if (decodeHaierAC160(results, offset)) return true; -#endif // DECODE_HAIER_AC160 -#if DECODE_CARRIER_AC128 - DPRINTLN("Attempting Carrier AC 128-bit decode"); - if (decodeCarrierAC128(results, offset)) return true; -#endif // DECODE_CARRIER_AC128 -#if DECODE_TOTO - DPRINTLN("Attempting Toto 48/24-bit decode"); - if (decodeToto(results, offset, kTotoLongBits) || // Long needs to be first - decodeToto(results, offset, kTotoShortBits)) return true; -#endif // DECODE_TOTO -#if DECODE_CLIMABUTLER - DPRINTLN("Attempting ClimaButler decode"); - if (decodeClimaButler(results)) return true; -#endif // DECODE_CLIMABUTLER -#if DECODE_TCL96AC - DPRINTLN("Attempting TCL AC 96-bit decode"); - if (decodeTcl96Ac(results, offset)) return true; -#endif // DECODE_TCL96AC -#if DECODE_SANYO_AC152 - DPRINTLN("Attempting Sanyo AC 152-bit decode"); - if (decodeSanyoAc152(results, offset)) return true; -#endif // DECODE_SANYO_AC152 -#if DECODE_DAIKIN312 - DPRINTLN("Attempting Daikin 312-bit decode"); - if (decodeDaikin312(results, offset)) return true; -#endif // DECODE_DAIKIN312 -#if DECODE_GORENJE - DPRINTLN("Attempting GORENJE decode"); - if (decodeGorenje(results, offset)) return true; -#endif // DECODE_GORENJE -#if DECODE_WOWWEE - DPRINTLN("Attempting WOWWEE decode"); - if (decodeWowwee(results, offset)) return true; -#endif // DECODE_WOWWEE -#if DECODE_CARRIER_AC84 - DPRINTLN("Attempting Carrier A/C 84-bit decode"); - if (decodeCarrierAC84(results, offset)) return true; -#endif // DECODE_CARRIER_AC84 -#if DECODE_YORK - DPRINTLN("Attempting York decode"); - if (decodeYork(results, offset, kYorkBits)) return true; -#endif // DECODE_YORK - // Typically new protocols are added above this line. - } -#if DECODE_HASH - // decodeHash returns a hash on any input. - // Thus, it needs to be last in the list. - // If you add any decodes, add them before this. - if (decodeHash(results)) { - return true; - } -#endif // DECODE_HASH - // Throw away and start over - if (!resumed) // Check if we have already resumed. - resume(); - return false; -} // NOLINT(readability/fn_size) - -/// Convert the tolerance percentage into something valid. -/// @param[in] percentage An integer percentage. -uint8_t IRrecv::_validTolerance(const uint8_t percentage) { - return (percentage > 100) ? _tolerance : percentage; -} - -/// Calculate the lower bound of the nr. of ticks. -/// @param[in] usecs Nr. of uSeconds. -/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% -/// @param[in] delta A non-scaling amount to reduce usecs by. -/// @return Nr. of ticks. -uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, - const uint16_t delta) { - // max() used to ensure the result can't drop below 0 before the cast. - return ((uint32_t)std::max( - (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), - (int32_t)0)); -} - -/// Calculate the upper bound of the nr. of ticks. -/// @param[in] usecs Nr. of uSeconds. -/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% -/// @param[in] delta A non-scaling amount to increase usecs by. -/// @return Nr. of ticks. -uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance, - const uint16_t delta) { - return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 + - delta); -} - -/// Check if we match a pulse(measured) with the desired within -/// +/-tolerance percent and/or +/- a fixed delta range. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] delta A non-scaling (+/-) error margin (in useconds). -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, - uint16_t delta) { - measured *= kRawTick; // Convert to uSecs. - DPRINT("Matching: "); - DPRINT(ticksLow(desired, tolerance, delta)); - DPRINT(" <= "); - DPRINT(measured); - DPRINT(" <= "); - DPRINTLN(ticksHigh(desired, tolerance, delta)); -#ifdef UNIT_TEST - // Sanity checks that we don't have values that cause integer over/underflow. - // Only performed during testing so there is no performance hit in normal - // operation. - assert(ticksLow(desired, tolerance, delta) <= desired); - // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) - assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); - // Check if our high mark is below where we started. This could happen. - // If there is a legit case, then this should be removed. - assert(ticksHigh(desired, tolerance, delta) >= desired); -#endif // UNIT_TEST - return (measured >= ticksLow(desired, tolerance, delta) && - measured <= ticksHigh(desired, tolerance, delta)); -} - -/// Check if we match a pulse(measured) of at least desired within -/// tolerance percent and/or a fixed delta margin. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] delta A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, - uint8_t tolerance, uint16_t delta) { - measured *= kRawTick; // Convert to uSecs. - DPRINT("Matching ATLEAST "); - DPRINT(measured); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(". Matching: "); - DPRINT(measured); - DPRINT(" >= "); - DPRINT(ticksLow(std::min(desired, (uint32_t)MS_TO_USEC(params.timeout)), - tolerance, delta)); - DPRINT(" [min("); - DPRINT(ticksLow(desired, tolerance, delta)); - DPRINT(", "); - DPRINT(ticksLow(MS_TO_USEC(params.timeout), tolerance, delta)); - DPRINTLN(")]"); -#ifdef UNIT_TEST - // Sanity checks that we don't have values that cause integer over/underflow. - // Only performed during testing so there is no performance hit in normal - // operation. - assert(ticksLow(desired, tolerance, delta) <= desired); - // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) - assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); - // Check if our high mark is below where we started. This could happen. - // If there is a legit case, then this should be removed. - assert(ticksHigh(desired, tolerance, delta) >= desired); -#endif // UNIT_TEST - // We really should never get a value of 0, except as the last value - // in the buffer. If that is the case, then assume infinity and return true. - if (measured == 0) return true; - return measured >= ticksLow(std::min(desired, - (uint32_t)MS_TO_USEC(params.timeout)), - tolerance, delta); -} - -/// Check if we match a mark signal(measured) with the desired within -/// +/-tolerance percent, after an expected is excess is added. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchMark(uint32_t measured, uint32_t desired, uint8_t tolerance, - int16_t excess) { - DPRINT("Matching MARK "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" + "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired + excess, tolerance); -} - -/// Check if we match a mark signal(measured) with the desired within a -/// range (in uSeconds) either side of the desired, after an expected is excess -/// is added. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] range The range limit from desired to accept in uSeconds. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchMarkRange(const uint32_t measured, const uint32_t desired, - const uint16_t range, const int16_t excess) { - DPRINT("Matching MARK "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" + "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired + excess, 0, range); -} - -/// Check if we match a space signal(measured) with the desired within -/// +/-tolerance percent, after an expected is excess is removed. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance, - int16_t excess) { - DPRINT("Matching SPACE "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" - "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired - excess, tolerance); -} - -/// Check if we match a space signal(measured) with the desired within a -/// range (in uSeconds) either side of the desired, after an expected is excess -/// is removed. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] range The range limit from desired to accept in uSeconds. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchSpaceRange(const uint32_t measured, const uint32_t desired, - const uint16_t range, const int16_t excess) { - DPRINT("Matching SPACE "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" - "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired - excess, 0, range); -} - -#if DECODE_HASH -/// Compare two tick values. -/// @param[in] oldval Nr. of ticks. -/// @param[in] newval Nr. of ticks. -/// @return 0 if newval is shorter, 1 if it is equal, & 2 if it is longer. -/// @note Use a tolerance of 20% -uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) { - if (newval < oldval * 0.8) - return 0; - else if (oldval < newval * 0.8) - return 2; - else - return 1; -} - -/// Decode any arbitrary IR message into a 32-bit code value. -/// Instead of decoding using a standard encoding scheme -/// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. -/// -/// The algorithm: look at the sequence of MARK signals, and see if each one -/// is shorter (0), the same length (1), or longer (2) than the previous. -/// Do the same with the SPACE signals. Hash the resulting sequence of 0's, -/// 1's, and 2's to a 32-bit value. This will give a unique value for each -/// different code (probably), for most code systems. -/// @see http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html -/// @note This isn't a "real" decoding, just an arbitrary value. -/// Hopefully this code is unique for each button. -bool IRrecv::decodeHash(decode_results *results) { - // Require at least some samples to prevent triggering on noise - if (results->rawlen < _unknown_threshold) return false; - int32_t hash = kFnvBasis32; - // 'rawlen - 2' to avoid the look ahead from going out of bounds. - // Should probably be -3 to avoid comparing the trailing space entry, - // however it is left this way for compatibility with previously captured - // values. - for (uint16_t i = 1; i < results->rawlen - 2; i++) { - uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); - // Add value into the hash - hash = (hash * kFnvPrime32) ^ value; - } - results->value = hash & 0xFFFFFFFF; - results->bits = results->rawlen / 2; - results->address = 0; - results->command = 0; - results->decode_type = UNKNOWN; - return true; -} -#endif // DECODE_HASH - -/// Match & decode the typical data section of an IR message. -/// The data value is stored in the least significant bits reguardless of the -/// bit ordering requested. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] expectlastspace Do we expect a space at the end of the message? -/// @return A match_result_t structure containing the success (or not), the -/// data value, and how many buffer entries were used. -match_result_t IRrecv::matchData( - volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark, - const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance, const int16_t excess, const bool MSBfirst, - const bool expectlastspace) { - match_result_t result; - result.success = false; // Fail by default. - result.data = 0; - if (expectlastspace) { // We are expecting data with a final space. - for (result.used = 0; result.used < nbits * 2; - result.used += 2, data_ptr += 2) { - // Is the bit a '1'? - if (matchMark(*data_ptr, onemark, tolerance, excess) && - matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) { - result.data = (result.data << 1) | 1; - } else if (matchMark(*data_ptr, zeromark, tolerance, excess) && - matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) { - result.data <<= 1; // The bit is a '0'. - } else { - if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2); - return result; // It's neither, so fail. - } - } - result.success = true; - } else { // We are expecting data without a final space. - // Match all but the last bit, as it may not match easily. - result = matchData(data_ptr, nbits ? nbits - 1 : 0, onemark, onespace, - zeromark, zerospace, tolerance, excess, true, true); - if (result.success) { - // Is the bit a '1'? - if (matchMark(*(data_ptr + result.used), onemark, tolerance, excess)) - result.data = (result.data << 1) | 1; - else if (matchMark(*(data_ptr + result.used), zeromark, tolerance, - excess)) - result.data <<= 1; // The bit is a '0'. - else - result.success = false; - if (result.success) result.used++; - } - } - if (!MSBfirst) result.data = reverseBits(result.data, nbits); - return result; -} - -/// Match & decode the typical data section of an IR message. -/// The bytes are stored at result_ptr. The first byte in the result equates to -/// the first byte encountered, and so on. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbytes Nr. of data bytes we expect. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] expectlastspace Do we expect a space at the end of the message? -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, - const uint16_t remaining, const uint16_t nbytes, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance, const int16_t excess, - const bool MSBfirst, const bool expectlastspace) { - // Check if there is enough capture buffer to possibly have the desired bytes. - if (remaining + expectlastspace < (nbytes * 8 * 2) + 1) - return 0; // Nope, so abort. - uint16_t offset = 0; - for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { - bool lastspace = (byte_pos + 1 == nbytes) ? expectlastspace : true; - match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, - zeromark, zerospace, tolerance, excess, - MSBfirst, lastspace); - if (result.success == false) return 0; // Fail - result_ptr[byte_pos] = (uint8_t)result.data; - offset += result.used; - } - return offset; -} - -/// Match & decode a generic/typical IR message. -/// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag -/// `use_bits`. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[out] result_bits_ptr A pointer to where to start storing the bits we -/// decoded. -/// @param[out] result_bytes_ptr A pointer to where to start storing the bytes -/// we decoded. -/// @param[in] use_bits A flag indicating if we are to decode bits or bytes. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_bits_ptr, - uint8_t *result_bytes_ptr, - const bool use_bits, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - // If we are expecting byte sizes, check it's a factor of 8 or fail. - if (!use_bits && nbits % 8 != 0) return 0; - // Calculate if we expect a trailing space in the data section. - const bool kexpectspace = footermark || (onespace != zerospace); - // Calculate how much remaining buffer is required. - uint16_t min_remaining = nbits * 2 - (kexpectspace ? 0 : 1); - - if (hdrmark) min_remaining++; - if (hdrspace) min_remaining++; - if (footermark) min_remaining++; - // Don't need to extend for footerspace because it could be the end of message - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) return 0; // Nope, so abort. - uint16_t offset = 0; - - // Header - if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) - return 0; - if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, - excess)) - return 0; - - // Data - if (use_bits) { // Bits. - match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, - onemark, onespace, - zeromark, zerospace, tolerance, - excess, MSBfirst, kexpectspace); - if (!result.success) return 0; - *result_bits_ptr = result.data; - offset += result.used; - } else { // bytes - uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, - remaining - offset, nbits / 8, - onemark, onespace, - zeromark, zerospace, tolerance, - excess, MSBfirst, kexpectspace); - if (!data_used) return 0; - offset += data_used; - } - // Footer - if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, - excess)) - return 0; - // If we have something still to match & haven't reached the end of the buffer - if (footerspace && offset < remaining) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } else { - if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } - offset++; - } - return offset; -} - -/// Match & decode a generic/typical <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// -/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, - hdrmark, hdrspace, onemark, onespace, - zeromark, zerospace, footermark, footerspace, atleast, - tolerance, excess, MSBfirst); -} - -/// Match & decode a generic/typical > 64bit IR message. -/// The bytes are stored at result_ptr. The first byte in the result equates to -/// the first byte encountered, and so on. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, - uint8_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, - hdrmark, hdrspace, onemark, onespace, - zeromark, zerospace, footermark, footerspace, atleast, - tolerance, excess, MSBfirst); -} - -/// Match & decode a generic/typical constant bit time <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] one Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] zero Nr. of uSeconds in an expected mark signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @note Parameters one + zero add up to the total time for a bit. -/// e.g. mark(one) + space(zero) is a `1`, mark(zero) + space(one) is a `0`. -uint16_t IRrecv::matchGenericConstBitTime(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t one, - const uint32_t zero, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - uint16_t offset = 0; - uint64_t result = 0; - // If we expect a footermark, then this can be processed like normal. - if (footermark) - return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, - hdrmark, hdrspace, one, zero, zero, one, - footermark, footerspace, atleast, - tolerance, excess, MSBfirst); - // Overwise handle like normal, except for the last bit. and no footer. - uint16_t bits = (nbits > 0) ? nbits - 1 : 0; // Make sure we don't underflow. - offset = _matchGeneric(data_ptr, &result, NULL, true, remaining, bits, - hdrmark, hdrspace, one, zero, zero, one, 0, 0, false, - tolerance, excess, true); - if (!offset) return 0; // Didn't match. - // Now for the last bit. - if (remaining <= offset) return 0; // Not enough buffer. - result <<= 1; - bool last_bit = 0; - // Is the mark a '1' or a `0`? - if (matchMark(*(data_ptr + offset), one, tolerance, excess)) { // 1 - last_bit = 1; - result |= 1; - } else if (matchMark(*(data_ptr + offset), zero, tolerance, excess)) { // 0 - last_bit = 0; - } else { - return 0; // It's neither, so fail. - } - offset++; - uint32_t expected_space = (last_bit ? zero : one) + footerspace; - // If we are not at the end of the buffer, check for at least the expected - // space value. - if (remaining > offset) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), expected_space, tolerance, - excess)) - return false; - } else { - if (!matchSpace(*(data_ptr + offset), expected_space, tolerance)) - return false; - } - offset++; - } - if (!MSBfirst) result = reverseBits(result, nbits); - *result_ptr = result; - return offset; -} - -/// Match & decode a Manchester Code <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// i.e. 1/2 wavelength -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf -uint16_t IRrecv::matchManchester(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t half_period, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst, - const bool GEThomas) { - uint16_t offset = 0; - uint16_t bank = 0; - uint16_t entry = 0; - - // Calculate how much remaining buffer is required. - // Shortest case is nbits. Longest case is 2 * nbits. - uint16_t min_remaining = nbits; - - if (hdrmark) min_remaining++; - if (hdrspace) min_remaining++; - if (footermark) min_remaining++; - // Don't need to extend for footerspace because it could be the end of message - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) return 0; // Nope, so abort. - - // Header - if (hdrmark) { - entry = *(data_ptr + offset++); - if (!hdrspace) { // If we have no Header Space ... - // Do we have a data 'mark' half period merged with the header mark? - if (matchMark(entry, hdrmark + half_period, - tolerance, excess)) { - // Looks like we do. - bank = entry * kRawTick - hdrmark; - } else if (!matchMark(entry, hdrmark, tolerance, excess)) { - return 0; // It's not a normal header mark, so fail. - } - } else if (!matchMark(entry, hdrmark, tolerance, excess)) { - return 0; // It's not a normal header mark, so fail. - } - } - if (hdrspace) { - entry = *(data_ptr + offset++); - // Check to see if the header space has merged with a data space half period - if (matchSpace(entry, hdrspace + half_period, tolerance, excess)) { - // Looks like we do. - bank = entry * kRawTick - hdrspace; - } else if (!matchSpace(entry, hdrspace, tolerance, excess)) { - return 0; // It's not a normal header space, so fail. - } - } - - if (!match(bank / kRawTick, half_period, tolerance, excess)) bank = 0; - // Data - uint16_t used = matchManchesterData(data_ptr + offset, result_ptr, - remaining - offset, nbits, half_period, - bank, tolerance, excess, MSBfirst, - GEThomas); - if (!used) return 0; // Data did match. - offset += used; - // Footer - if (footermark && - !(matchMark(*(data_ptr + offset), footermark + half_period, - tolerance, excess) || - matchMark(*(data_ptr + offset), footermark, tolerance, excess))) - return 0; - offset++; - // If we have something still to match & haven't reached the end of the buffer - if (footerspace && offset < remaining) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } else { - if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess) && - !matchSpace(*(data_ptr + offset), footerspace + half_period, - tolerance, excess)) - return 0; - } - offset++; - } - return offset; -} - -/// Match & decode a Manchester Code data (<= 64bits. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// i.e. 1/2 wavelength -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] starting_balance Amount of uSeconds to assume exists prior to -/// the current value pointed too. -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf -/// @todo Clean up and optimise this. It is just "get it working code" atm. -uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t half_period, - const uint16_t starting_balance, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst, - const bool GEThomas) { - DPRINTLN("DEBUG: Entered matchManchesterData"); - uint16_t offset = 0; - uint64_t data = 0; - uint16_t nr_half_periods = 0; - const uint16_t expected_half_periods = nbits * 2; - // Flip the bit if we have a starting balance. ie. Carry over from the header. - bool currentBit = starting_balance ? !GEThomas : GEThomas; - const uint16_t raw_half_period = half_period / kRawTick; - - // Calculate how much remaining buffer is required. - // Shortest case is nbits. Longest case is 2 * nbits. - uint16_t min_remaining = nbits; - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) { - DPRINTLN("DEBUG: Ran out of capture buffer!"); - return 0; // Nope, so abort. - } - - // Convert to ticks. Optimisation: Saves on math/extra instructions later. - uint16_t bank = starting_balance / kRawTick; - - // Data - // Loop through the buffer till we run out of buffer, or nr of half periods. - // Possible patterns are: - // short + short = 1 bit (Add the value of the previous bit again) - // short + long + short = 2 bits (Add the previous bit again, then flip & add) - // short + long + long + short = 3 bits (add prev, flip & add, flip & add) - // We can't start with a long. - // - // The general approach is thus: - // Check we have a short interval, next or in the bank. - // If the next timing value is long, act according and reset the bank to - // a short balance. - // or - // If it is short, act accordingly and declare the bank empty. - // Repeat. - while ((offset < remaining || bank) && - nr_half_periods < expected_half_periods) { - // Get the next entry if we haven't anything existing to process. - DPRINT("DEBUG: Offset = "); - DPRINTLN(offset); - if (!bank) bank = *(data_ptr + offset++); - DPRINT("DEBUG: Bank = "); - DPRINTLN(bank * kRawTick); - // Check if we don't have a short interval. - DPRINTLN("DEBUG: Checking for short interval"); - if (!match(bank, half_period, tolerance, excess)) { - DPRINTLN("DEBUG: It is. Exiting"); - return 0; // Not valid. - } - // We've succeeded in matching half a period, so count it. - nr_half_periods++; - DPRINT("DEBUG: Half Periods = "); - DPRINTLN(nr_half_periods); - // We've now used up our bank, so refill it with the next item, unless we - // are at the end of the capture buffer. - // If we are assume a single half period of "space". - if (offset < remaining) { - DPRINT("DEBUG: Offset = "); - DPRINTLN(offset); - bank = *(data_ptr + offset++); - } else if (offset == remaining) { - bank = raw_half_period; - } else { - return 0; // We are out of buffer, so abort! - } - DPRINT("DEBUG: Bank = "); - DPRINTLN(bank * kRawTick); - - // Shift the data along and add our new bit. - DPRINT("DEBUG: Adding bit: "); - DPRINTLN((currentBit ? "1" : "0")); - data <<= 1; - data |= currentBit; - - // Check if we have a long interval. - if (match(bank, half_period * 2, tolerance, excess)) { - // It is, so flip the bit we need to append, and remove a half_period of - // time from the bank. - DPRINTLN("DEBUG: long interval detected"); - currentBit = !currentBit; - bank -= raw_half_period; - } else if (match(bank, half_period, tolerance, excess)) { - // It is a short interval, so eat up all the time and move on. - DPRINTLN("DEBUG: short interval detected"); - bank = 0; - } else if (nr_half_periods == expected_half_periods - 1 && - matchAtLeast(bank, half_period, tolerance, excess)) { - // We are at the end of the data & it is a short interval, so eat up all - // the time and move on. - bank = 0; - // Reduce the offset as we are at the end of the data doing a - // matchAtLeast() because we could be processing part of a footer. - offset--; - } else { - // The length isn't what we expected (neither long or short), so bail. - return 0; - } - nr_half_periods++; - } - - // Clean up and process the data. - if (!MSBfirst) data = reverseBits(data, nbits); - // Trim the data to size. - *result_ptr = GETBITS64(data, 0, nbits); - return offset; -} - -#if UNIT_TEST -/// Unit test helper to get access to the params structure. -volatile irparams_t *IRrecv::_getParamsPtr(void) { - return ¶ms; -} -#endif // UNIT_TEST -// End of IRrecv class ------------------- +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2015 Sebastien Warin +// Copyright 2017, 2019 David Conran + +#include "IRrecv.h" +#include +#ifndef UNIT_TEST +#if defined(ESP8266) +extern "C" { +#include +#include +} +#endif // ESP8266 +#include +#endif // UNIT_TEST +#include +#ifdef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "IRutils.h" + +#ifdef UNIT_TEST +#undef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR +#endif + +#ifndef USE_IRAM_ATTR +#if defined(ESP8266) +#if defined(IRAM_ATTR) +#define USE_IRAM_ATTR IRAM_ATTR +#else // IRAM_ATTR +#define USE_IRAM_ATTR ICACHE_RAM_ATTR +#endif // IRAM_ATTR +#endif // ESP8266 +#if defined(ESP32) +#define USE_IRAM_ATTR IRAM_ATTR +#endif // ESP32 +#endif // USE_IRAM_ATTR + +#define ONCE 0 + +// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR +// code on ESP32 +// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code +// on ESP8266 +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for +// sending IR code on ESP8266 + +// Globals +#ifndef UNIT_TEST +#if defined(ESP8266) +namespace _IRrecv { +static ETSTimer timer; +} // namespace _IRrecv +#endif // ESP8266 +#if defined(ESP32) +// We need a horrible timer hack for ESP32 Arduino framework < v2.0.0 +#if !defined(_ESP32_IRRECV_TIMER_HACK) +// Version check +#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) +// No need for the hack if we are running version >= 2.0.0 +#define _ESP32_IRRECV_TIMER_HACK false +#else // Version check +// If no ESP_ARDUINO_VERSION_MAJOR is defined, or less than 2, then we are +// using an old ESP32 core, so we need the hack. +#define _ESP32_IRRECV_TIMER_HACK true +#endif // Version check +#endif // !defined(_ESP32_IRRECV_TIMER_HACK) + +#if _ESP32_IRRECV_TIMER_HACK +// Required structs/types from: +// https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58 +// These are needed to be able to directly manipulate the timer registers from +// inside an ISR. This is very very ugly. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 +// Note: This will need to be updated if it ever changes. +// +// Start of Horrible Hack! +typedef struct { + union { + struct { + uint32_t reserved0: 10; + uint32_t alarm_en: 1; + /*When set alarm is enabled*/ + uint32_t level_int_en: 1; + /*When set level type interrupt will be generated during alarm*/ + uint32_t edge_int_en: 1; + /*When set edge type interrupt will be generated during alarm*/ + uint32_t divider: 16; + /*Timer clock (T0/1_clk) pre-scale value.*/ + uint32_t autoreload: 1; + /*When set timer 0/1 auto-reload at alarming is enabled*/ + uint32_t increase: 1; + /*When set timer 0/1 time-base counter increment. + When cleared timer 0 time-base counter decrement.*/ + uint32_t enable: 1; + /*When set timer 0/1 time-base counter is enabled*/ + }; + uint32_t val; + } config; + uint32_t cnt_low; + /*Register to store timer 0/1 time-base counter current value lower 32 + bits.*/ + uint32_t cnt_high; + /*Register to store timer 0 time-base counter current value higher 32 + bits.*/ + uint32_t update; + /*Write any value will trigger a timer 0 time-base counter value update + (timer 0 current value will be stored in registers above)*/ + uint32_t alarm_low; + /*Timer 0 time-base counter value lower 32 bits that will trigger the + alarm*/ + uint32_t alarm_high; + /*Timer 0 time-base counter value higher 32 bits that will trigger the + alarm*/ + uint32_t load_low; + /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ + uint32_t load_high; + /*higher 32 bits of the value that will load into timer 0 time-base + counter*/ + uint32_t reload; + /*Write any value will trigger timer 0 time-base counter reload*/ +} hw_timer_reg_t; + +typedef struct hw_timer_s { + hw_timer_reg_t * dev; + uint8_t num; + uint8_t group; + uint8_t timer; + portMUX_TYPE lock; +} hw_timer_t; +#endif // _ESP32_IRRECV_TIMER_HACK / End of Horrible Hack. + +namespace _IRrecv { +static hw_timer_t * timer = NULL; +} // namespace _IRrecv +#endif // ESP32 +using _IRrecv::timer; +#endif // UNIT_TEST + +namespace _IRrecv { // Namespace extension +#if defined(ESP32) +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; +#endif // ESP32 +volatile irparams_t params; +irparams_t *params_save; // A copy of the interrupt state while decoding. +} // namespace _IRrecv + +#if defined(ESP32) +using _IRrecv::mux; +#endif // ESP32 +using _IRrecv::params; +using _IRrecv::params_save; + +#ifndef UNIT_TEST +#if defined(ESP8266) +/// Interrupt handler for when the timer runs out. +/// It signals to the library that capturing of IR data has stopped. +/// @param[in] arg Unused. (ESP8266 Only) +static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { + os_intr_lock(); +#endif // ESP8266 +/// @cond IGNORE +#if defined(ESP32) +/// Interrupt handler for when the timer runs out. +/// It signals to the library that capturing of IR data has stopped. +/// @note ESP32 version +static void USE_IRAM_ATTR read_timeout(void) { +/// @endcond + portENTER_CRITICAL(&mux); +#endif // ESP32 + if (params.rawlen) params.rcvstate = kStopState; +#if defined(ESP8266) + os_intr_unlock(); +#endif // ESP8266 +#if defined(ESP32) + portEXIT_CRITICAL(&mux); +#endif // ESP32 +} + +/// Interrupt handler for changes on the GPIO pin handling incoming IR messages. +static void USE_IRAM_ATTR gpio_intr() { + uint32_t now = micros(); + static uint32_t start = 0; + +#if defined(ESP8266) + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + os_timer_disarm(&timer); + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); +#endif // ESP8266 + + // Grab a local copy of rawlen to reduce instructions used in IRAM. + // This is an ugly premature optimisation code-wise, but we do everything we + // can to save IRAM. + // It seems referencing the value via the structure uses more instructions. + // Less instructions means faster and less IRAM used. + // N.B. It saves about 13 bytes of IRAM. + uint16_t rawlen = params.rawlen; + + if (rawlen >= params.bufsize) { + params.overflow = true; + params.rcvstate = kStopState; + } + + if (params.rcvstate == kStopState) return; + + if (params.rcvstate == kIdleState) { + params.rcvstate = kMarkState; + params.rawbuf[rawlen] = 1; + } else { + if (now < start) + params.rawbuf[rawlen] = (UINT32_MAX - start + now) / kRawTick; + else + params.rawbuf[rawlen] = (now - start) / kRawTick; + } + params.rawlen++; + + start = now; + +#if defined(ESP8266) + os_timer_arm(&timer, params.timeout, ONCE); +#endif // ESP8266 +#if defined(ESP32) + // Reset the timeout. + // +#if _ESP32_IRRECV_TIMER_HACK + // The following three lines of code are the equiv of: + // `timerWrite(timer, 0);` + // We can't call that routine safely from inside an ISR as that procedure + // is not stored in IRAM. Hence, we do it manually so that it's covered by + // USE_IRAM_ATTR in this ISR. + // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 + // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L106-L110 + timer->dev->load_high = (uint32_t) 0; + timer->dev->load_low = (uint32_t) 0; + timer->dev->reload = 1; + // The next line is the same, but instead replaces: + // `timerAlarmEnable(timer);` + // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 + // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178 + timer->dev->config.alarm_en = 1; +#else // _ESP32_IRRECV_TIMER_HACK + timerWrite(timer, 0); + timerAlarmEnable(timer); +#endif // _ESP32_IRRECV_TIMER_HACK +#endif // ESP32 +} +#endif // UNIT_TEST + +// Start of IRrecv class ------------------- + +/// Class constructor +/// Args: +/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is +/// connected to. +/// @param[in] bufsize Nr. of entries to have in the capture buffer. +/// (Default: kRawBuf) +/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop +/// capturing data. (Default: kTimeoutMs) +/// @param[in] save_buffer Use a second (save) buffer to decode from. +/// (Default: false) +/// @param[in] timer_num Nr. of the ESP32 timer to use. (0 to 3) (ESP32 Only) +/// or (0 to 1) (ESP32-C3) +#if defined(ESP32) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer, + const uint8_t timer_num) { + // Ensure we use a valid timer number. + _timer_num = std::min(timer_num, + (uint8_t)( +#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS + SOC_TIMER_GROUP_TOTAL_TIMERS - 1)); +#else // SOC_TIMER_GROUP_TOTAL_TIMERS + 3)); +#endif // SOC_TIMER_GROUP_TOTAL_TIMERS +#else // ESP32 +/// @cond IGNORE +/// Class constructor +/// Args: +/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is +/// connected to. +/// @param[in] bufsize Nr. of entries to have in the capture buffer. +/// (Default: kRawBuf) +/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop +/// capturing data. (Default: kTimeoutMs) +/// @param[in] save_buffer Use a second (save) buffer to decode from. +/// (Default: false) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer) { +/// @endcond +#endif // ESP32 + params.recvpin = recvpin; + params.bufsize = bufsize; + // Ensure we are going to be able to store all possible values in the + // capture buffer. + params.timeout = std::min(timeout, (uint8_t)kMaxTimeoutMs); + params.rawbuf = new uint16_t[bufsize]; + if (params.rawbuf == NULL) { + DPRINTLN( + "Could not allocate memory for the primary IR buffer.\n" + "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); +#ifndef UNIT_TEST + ESP.restart(); // Mem alloc failure. Reboot. +#endif + } + // If we have been asked to use a save buffer (for decoding), then create one. + if (save_buffer) { + params_save = new irparams_t; + params_save->rawbuf = new uint16_t[bufsize]; + // Check we allocated the memory successfully. + if (params_save->rawbuf == NULL) { + DPRINTLN( + "Could not allocate memory for the second IR buffer.\n" + "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); +#ifndef UNIT_TEST + ESP.restart(); // Mem alloc failure. Reboot. +#endif + } + } else { + params_save = NULL; + } +#if DECODE_HASH + _unknown_threshold = kUnknownThreshold; +#endif // DECODE_HASH + _tolerance = kTolerance; +} + +/// Class destructor +/// Cleans up after the object is no longer needed. +/// e.g. Frees up all memory used by the various buffers, and disables any +/// timers or interrupts used. +IRrecv::~IRrecv(void) { + disableIRIn(); +#if defined(ESP32) + if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. +#endif // ESP32 + delete[] params.rawbuf; + if (params_save != NULL) { + delete[] params_save->rawbuf; + delete params_save; + } +} + +/// Set up and (re)start the IR capture mechanism. +/// @param[in] pullup A flag indicating should the GPIO use the internal pullup +/// resistor. (Default: `false`. i.e. No.) +void IRrecv::enableIRIn(const bool pullup) { + // ESP32's seem to require explicitly setting the GPIO to INPUT etc. + // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. + if (pullup) { +#ifndef UNIT_TEST + pinMode(params.recvpin, INPUT_PULLUP); + } else { + pinMode(params.recvpin, INPUT); +#endif // UNIT_TEST + } +#if defined(ESP32) + // Initialise the ESP32 timer. + // 80MHz / 80 = 1 uSec granularity. + timer = timerBegin(_timer_num, 80, true); +#ifdef DEBUG + if (timer == NULL) { + DPRINT("FATAL: Unable enable system timer: "); + DPRINTLN((uint16_t)_timer_num); + } +#endif // DEBUG + assert(timer != NULL); // Check we actually got the timer. + // Set the timer so it only fires once, and set it's trigger in uSeconds. + timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); + // Note: Interrupt needs to be attached before it can be enabled or disabled. + // Note: EDGE (true) is not supported, use LEVEL (false). Ref: #1713 + // See: https://github.com/espressif/arduino-esp32/blob/caef4006af491130136b219c1205bdcf8f08bf2b/cores/esp32/esp32-hal-timer.c#L224-L227 + timerAttachInterrupt(timer, &read_timeout, false); +#endif // ESP32 + + // Initialise state machine variables + resume(); + +#ifndef UNIT_TEST +#if defined(ESP8266) + // Initialise ESP8266 timer. + os_timer_disarm(&timer); + os_timer_setfn(&timer, reinterpret_cast(read_timeout), + NULL); +#endif // ESP8266 + // Attach Interrupt + attachInterrupt(params.recvpin, gpio_intr, CHANGE); +#endif // UNIT_TEST +} + +/// Stop collection of any received IR data. +/// Disable any timers and interrupts. +void IRrecv::disableIRIn(void) { +#ifndef UNIT_TEST +#if defined(ESP8266) + os_timer_disarm(&timer); +#endif // ESP8266 +#if defined(ESP32) + timerAlarmDisable(timer); + timerDetachInterrupt(timer); + timerEnd(timer); +#endif // ESP32 + detachInterrupt(params.recvpin); +#endif // UNIT_TEST +} + +/// Pause collection of received IR data. +/// @see IRrecv class constructor +void IRrecv::pause(void) { + params.rcvstate = kStopState; + params.rawlen = 0; + params.overflow = false; +#if defined(ESP32) + gpio_intr_disable((gpio_num_t)params.recvpin); +#endif // ESP32 +} + +/// Resume collection of received IR data. +/// @note This is required if `decode()` is successful and `save_buffer` was +/// not set when the class was instanciated. +/// @see IRrecv class constructor +void IRrecv::resume(void) { + params.rcvstate = kIdleState; + params.rawlen = 0; + params.overflow = false; +#if defined(ESP32) + timerAlarmDisable(timer); + gpio_intr_enable((gpio_num_t)params.recvpin); +#endif // ESP32 +} + +/// Make a copy of the interrupt state & buffer data. +/// Needed because irparams is marked as volatile, thus memcpy() isn't allowed. +/// Only call this when you know the interrupt handlers won't modify anything. +/// i.e. In kStopState. +/// @param[in] src Pointer to an irparams_t structure to copy from. +/// @param[out] dst Pointer to an irparams_t structure to copy to. +void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { + // Typecast src and dst addresses to (char *) + char *csrc = (char *)src; // NOLINT(readability/casting) + char *cdst = (char *)dst; // NOLINT(readability/casting) + + // Save the pointer to the destination's rawbuf so we don't lose it as + // the for-loop/copy after this will overwrite it with src's rawbuf pointer. + // This isn't immediately obvious due to typecasting/different variable names. + uint16_t *dst_rawbuf_ptr; + dst_rawbuf_ptr = dst->rawbuf; + + // Copy contents of src[] to dst[] + for (uint16_t i = 0; i < sizeof(irparams_t); i++) cdst[i] = csrc[i]; + + // Restore the buffer pointer + dst->rawbuf = dst_rawbuf_ptr; + + // Copy the rawbuf + for (uint16_t i = 0; i < dst->bufsize; i++) dst->rawbuf[i] = src->rawbuf[i]; +} + +/// Obtain the maximum number of entries possible in the capture buffer. +/// i.e. It's size. +/// @return The size of the buffer that is in use by the object. +uint16_t IRrecv::getBufSize(void) { return params.bufsize; } + +#if DECODE_HASH +/// Set the minimum length we will consider for reporting UNKNOWN message types. +/// @param[in] length Min nr. of mark/space pulses required to be considered. +void IRrecv::setUnknownThreshold(const uint16_t length) { + _unknown_threshold = length; +} +#endif // DECODE_HASH + + +/// Set the base tolerance percentage for matching incoming IR messages. +/// @param[in] percent An integer percentage. (0-100) +void IRrecv::setTolerance(const uint8_t percent) { + _tolerance = std::min(percent, (uint8_t)100); +} + +/// Get the base tolerance percentage for matching incoming IR messages. +/// @return A integer percentage. +uint8_t IRrecv::getTolerance(void) { return _tolerance; } + +#if ENABLE_NOISE_FILTER_OPTION +/// Remove or merge pulses in the capture buffer that are too short. +/// @param[in,out] results Ptr to the decode_results we are going to filter. +/// @param[in] floor Only allow values in the buffer large than this. +/// (in microSeconds) +void IRrecv::crudeNoiseFilter(decode_results *results, const uint16_t floor) { + if (floor == 0) return; // Nothing to do. + const uint16_t kTickFloor = floor / kRawTick; + const uint16_t kBufSize = getBufSize(); + uint16_t offset = kStartOffset; + while (offset < results->rawlen && offset + 2 < kBufSize) { + uint16_t curr = results->rawbuf[offset]; + uint16_t next = results->rawbuf[offset + 1]; + uint16_t addition = curr + next; + if (curr < kTickFloor) { // Is it too short? + // Shuffle the buffer down. i.e. Remove the mark & space pair. + // Note: `memcpy()` can't be used as rawbuf is `volatile`. + for (uint16_t i = offset + 2; i <= results->rawlen && i < kBufSize; i++) + results->rawbuf[i - 2] = results->rawbuf[i]; + if (offset > 1) { // There is a previous pair we can add to. + // Merge this pair into into the previous space. + results->rawbuf[offset - 1] += addition; + } + results->rawlen -= 2; // Adjust the length. + } else { + offset++; // Move along. + } + } +} +#endif // ENABLE_NOISE_FILTER_OPTION + +/// Decodes the received IR message. +/// If the interrupt state is saved, we will immediately resume waiting +/// for the next IR message to avoid missing messages. +/// @note There is a trade-off here. Saving the state means less time lost until +/// we can receiving the next message vs. using more RAM. Choose appropriately. +/// @param[out] results A PTR to where the decoded IR message will be stored. +/// @param[out] save A PTR to an irparams_t instance in which to save +/// the interrupt's memory/state. NULL means don't save it. +/// @param[in] max_skip Maximum Nr. of pulses at the begining of a capture we +/// can skip when attempting to find a protocol we can successfully decode. +/// This parameter can dramatically improve detection of protocols +/// when there is light IR interference just before an incoming IR +/// message, however, it comes at a steep performace price. +/// (Default is 0. No skipping.) +/// @warning Increasing the `max_skip` value will dramatically (linearly) +/// increase the cpu time & usage to decode protocols. +/// e.g. 0 -> 1 will be a 2x increase in cpu usage/time. +/// 0 -> 2 will be a 3x increase etc. +/// If you are going to do this, consider disabling protocol decoding for +/// protocols you are not expecting. +/// @param[in] noise_floor Pulses below this size (in usecs) will be removed or +/// merged prior to any decoding. This is to try to remove noise/poor +/// readings & slightly increase the chances of a successful decode but at the +/// cost of data fidelity & integrity. +/// (Defaults to 0 usecs. i.e. Don't filter; which is safe!) +/// @warning DANGER: **Here Be Dragons!** +/// If you set the `noise_floor` value too high, it **WILL** break decoding +/// of some protocols. You have been warned! +/// **Any** non-zero value has the potential to **cook** the captured raw data +/// i.e. The raw data is going to lie to you. +/// It may obscure hardware, circuit, & environment issues thus making it +/// impossible to support you accurately or confidently. +/// Values of <= 50 usecs will probably be safe. +/// 51 - 100 usecs **might** be okay. +/// 100 - 150 usecs is "Danger, Will Robinson!". +/// 150 - 200 usecs expect broken protocols. +/// At 200+ usecs, you **have** protocols you can't decode!! +/// @return A boolean indicating if an IR message is ready or not. +bool IRrecv::decode(decode_results *results, irparams_t *save, + uint8_t max_skip, uint16_t noise_floor) { + // Proceed only if an IR message been received. +#ifndef UNIT_TEST + if (params.rcvstate != kStopState) return false; +#endif + + // Clear the entry we are currently pointing to when we got the timeout. + // i.e. Stopped collecting IR data. + // It's junk as we never wrote an entry to it and can only confuse decoding. + // This is done here rather than logically the best place in read_timeout() + // as it saves a few bytes of ICACHE_RAM as that routine is bound to an + // interrupt. decode() is not stored in ICACHE_RAM. + // Another better option would be to zero the entire irparams.rawbuf[] on + // resume() but that is a much more expensive operation compare to this. + // However, don't do this if rawbuf is already full as we stomp over the heap. + // See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516 + if (!params.overflow) params.rawbuf[params.rawlen] = 0; + + bool resumed = false; // Flag indicating if we have resumed. + + // If we were requested to use a save buffer previously, do so. + if (save == NULL) save = params_save; + + if (save == NULL) { + // We haven't been asked to copy it so use the existing memory. +#ifndef UNIT_TEST + results->rawbuf = params.rawbuf; + results->rawlen = params.rawlen; + results->overflow = params.overflow; +#endif + } else { + copyIrParams(¶ms, save); // Duplicate the interrupt's memory. + resume(); // It's now safe to rearm. The IR message won't be overridden. + resumed = true; + // Point the results at the saved copy. + results->rawbuf = save->rawbuf; + results->rawlen = save->rawlen; + results->overflow = save->overflow; + } + + // Reset any previously partially processed results. + results->decode_type = UNKNOWN; + results->bits = 0; + results->value = 0; + results->address = 0; + results->command = 0; + results->repeat = false; + +#if ENABLE_NOISE_FILTER_OPTION + crudeNoiseFilter(results, noise_floor); +#endif // ENABLE_NOISE_FILTER_OPTION + // Keep looking for protocols until we've run out of entries to skip or we + // find a valid protocol message. + for (uint16_t offset = kStartOffset; + offset <= (max_skip * 2) + kStartOffset; + offset += 2) { +#if DECODE_AIWA_RC_T501 + DPRINTLN("Attempting Aiwa RC T501 decode"); + // Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC() + // because the protocols are similar. This protocol is more specific than + // those ones, so should go before them. + if (decodeAiwaRCT501(results, offset)) return true; +#endif +#if DECODE_SANYO + DPRINTLN("Attempting Sanyo LC7461 decode"); + // Try decodeSanyoLC7461() before decodeNEC() because the protocols are + // similar in timings & structure, but the Sanyo one is much longer than the + // NEC protocol (42 vs 32 bits) so this one should be tried first to try to + // reduce false detection as a NEC packet. + if (decodeSanyoLC7461(results, offset)) return true; +#endif +#if DECODE_CARRIER_AC + DPRINTLN("Attempting Carrier AC decode"); + // Try decodeCarrierAC() before decodeNEC() because the protocols are + // similar in timings & structure, but the Carrier one is much longer than + // the NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodeCarrierAC(results, offset)) return true; +#endif +#if DECODE_PIONEER + DPRINTLN("Attempting Pioneer decode"); + // Try decodePioneer() before decodeNEC() because the protocols are + // similar in timings & structure, but the Pioneer one is much longer than + // the NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodePioneer(results, offset)) return true; +#endif +#if DECODE_EPSON + DPRINTLN("Attempting Epson decode"); + // Try decodeEpson() before decodeNEC() because the protocols are + // similar in timings & structure, but the Epson one is much longer than the + // NEC protocol (3x32 identical bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodeEpson(results, offset)) return true; +#endif +#if DECODE_NEC + DPRINTLN("Attempting NEC decode"); + if (decodeNEC(results, offset)) return true; +#endif +#if DECODE_MILESTAG2 + DPRINTLN("Attempting MilesTag2 decode"); + // Try decodeMilestag2() before decodeSony() because the protocols are + // similar in timings & structure, but the Miles one differs in nbits + // so this one should be tried first to try to reduce false detection + if (decodeMilestag2(results, offset, kMilesTag2MsgBits) || + decodeMilestag2(results, offset, kMilesTag2ShotBits)) return true; +#endif +#if DECODE_SONY + DPRINTLN("Attempting Sony decode"); + if (decodeSony(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI + DPRINTLN("Attempting Mitsubishi decode"); + if (decodeMitsubishi(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI_AC + DPRINTLN("Attempting Mitsubishi AC decode"); + if (decodeMitsubishiAC(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI2 + DPRINTLN("Attempting Mitsubishi2 decode"); + if (decodeMitsubishi2(results, offset)) return true; +#endif +#if DECODE_RC5 + DPRINTLN("Attempting RC5 decode"); + if (decodeRC5(results, offset)) return true; +#endif +#if DECODE_RC6 + DPRINTLN("Attempting RC6 decode"); + if (decodeRC6(results, offset)) return true; +#endif +#if DECODE_RCMM + DPRINTLN("Attempting RC-MM decode"); + if (decodeRCMM(results, offset)) return true; +#endif +#if DECODE_FUJITSU_AC + // Fujitsu A/C needs to precede Panasonic and Denon as it has a short + // message which looks exactly the same as a Panasonic/Denon message. + DPRINTLN("Attempting Fujitsu A/C decode"); + if (decodeFujitsuAC(results, offset)) return true; +#endif +#if DECODE_FUJITSU_AC264 + // FujitsuAC264 should be checked before FujitsuAC + // Fujitsu A/C needs to precede Panasonic and Denon as it has a short + // message which looks exactly the same as a Panasonic/Denon message. + DPRINTLN("Attempting Fujitsu A/C264 decode"); + if (decodeFujitsuAC264(results, offset, kFujitsuAc264Bits) || + decodeFujitsuAC264(results, offset, kFujitsuAc264BitsMiddle) || + decodeFujitsuAC264(results, offset, kFujitsuAc264BitsShort)) + return true; +#endif +#if DECODE_DENON + // Denon needs to precede Panasonic as it is a special case of Panasonic. + DPRINTLN("Attempting Denon decode"); + if (decodeDenon(results, offset, kDenon48Bits) || + decodeDenon(results, offset, kDenonBits) || + decodeDenon(results, offset, kDenonLegacyBits)) + return true; +#endif +#if DECODE_PANASONIC + DPRINTLN("Attempting Panasonic (48-bit) decode"); + if (decodePanasonic(results, offset)) return true; + DPRINTLN("Attempting Panasonic (40-bit) decode"); + if (decodePanasonic(results, offset, kPanasonic40Bits, true, + kPanasonic40Manufacturer)) return true; +#endif // DECODE_PANASONIC +#if DECODE_LG + DPRINTLN("Attempting LG (28-bit) decode"); + if (decodeLG(results, offset, kLgBits, true)) return true; + DPRINTLN("Attempting LG (32-bit) decode"); + // LG32 should be tried before Samsung + if (decodeLG(results, offset, kLg32Bits, true)) return true; +#endif +#if DECODE_GICABLE + // Note: Needs to happen before JVC decode, because it looks similar except + // with a required NEC-like repeat code. + DPRINTLN("Attempting GICable decode"); + if (decodeGICable(results, offset)) return true; +#endif +#if DECODE_JVC + DPRINTLN("Attempting JVC decode"); + if (decodeJVC(results, offset)) return true; +#endif +#if DECODE_SAMSUNG + DPRINTLN("Attempting SAMSUNG decode"); + if (decodeSAMSUNG(results, offset)) return true; +#endif +#if DECODE_SAMSUNG36 + DPRINTLN("Attempting Samsung36 decode"); + if (decodeSamsung36(results, offset)) return true; +#endif +#if DECODE_WHYNTER + DPRINTLN("Attempting Whynter decode"); + if (decodeWhynter(results, offset)) return true; +#endif +#if DECODE_DISH + DPRINTLN("Attempting DISH decode"); + if (decodeDISH(results, offset)) return true; +#endif +#if DECODE_SHARP + DPRINTLN("Attempting Sharp decode"); + if (decodeSharp(results, offset)) return true; +#endif +#if DECODE_BOSCH144 + DPRINTLN("Attempting Bosch 144-bit decode"); + // Bosch is similar to Coolix, so it must be attempted before decodeCOOLIX. + if (decodeBosch144(results, offset)) return true; +#endif // DECODE_BOSCH144 +#if DECODE_COOLIX + DPRINTLN("Attempting Coolix 24-bit decode"); + if (decodeCOOLIX(results, offset)) return true; +#endif // DECODE_COOLIX +#if DECODE_NIKAI + DPRINTLN("Attempting Nikai decode"); + if (decodeNikai(results, offset)) return true; +#endif +#if DECODE_KELVINATOR + // Kelvinator based-devices use a similar code to Gree ones, to avoid false + // matches this needs to happen before decodeGree(). + DPRINTLN("Attempting Kelvinator decode"); + if (decodeKelvinator(results, offset)) return true; +#endif +#if DECODE_DAIKIN + DPRINTLN("Attempting Daikin decode"); + if (decodeDaikin(results, offset)) return true; +#endif +#if DECODE_DAIKIN2 + DPRINTLN("Attempting Daikin2 decode"); + if (decodeDaikin2(results, offset)) return true; +#endif +#if DECODE_DAIKIN216 + DPRINTLN("Attempting Daikin216 decode"); + if (decodeDaikin216(results, offset)) return true; +#endif +#if DECODE_TOSHIBA_AC + DPRINTLN("Attempting Toshiba AC 72bit decode"); + if (decodeToshibaAC(results, offset)) return true; + DPRINTLN("Attempting Toshiba AC 80bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true; + DPRINTLN("Attempting Toshiba AC 56bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true; +#endif +#if DECODE_MIDEA + DPRINTLN("Attempting Midea decode"); + if (decodeMidea(results, offset)) return true; +#endif +#if DECODE_MAGIQUEST + DPRINTLN("Attempting Magiquest decode"); + if (decodeMagiQuest(results, offset)) return true; +#endif + /* NOTE: Disabled due to poor quality. +#if DECODE_SANYO + // The Sanyo S866500B decoder is very poor quality & depricated. + // *IF* you are going to enable it, do it near last to avoid false positive + // matches. + DPRINTLN("Attempting Sanyo SA8650B decode"); + if (decodeSanyo(results, offset)) + return true; +#endif + */ +#if DECODE_NEC + // Some devices send NEC-like codes that don't follow the true NEC spec. + // This should detect those. e.g. Apple TV remote etc. + // This needs to be done after all other codes that use strict and some + // other protocols that are NEC-like as well, as turning off strict may + // cause this to match other valid protocols. + DPRINTLN("Attempting NEC (non-strict) decode"); + if (decodeNEC(results, offset, kNECBits, false)) { + results->decode_type = NEC_LIKE; + return true; + } +#endif +#if DECODE_LASERTAG + DPRINTLN("Attempting Lasertag decode"); + if (decodeLasertag(results, offset)) return true; +#endif +#if DECODE_GREE + // Gree based-devices use a similar code to Kelvinator ones, to avoid false + // matches this needs to happen after decodeKelvinator(). + DPRINTLN("Attempting Gree decode"); + if (decodeGree(results, offset)) return true; +#endif +#if DECODE_HAIER_AC + DPRINTLN("Attempting Haier AC decode"); + if (decodeHaierAC(results, offset)) return true; +#endif +#if DECODE_HAIER_AC_YRW02 + DPRINTLN("Attempting Haier AC YR-W02 decode"); + if (decodeHaierACYRW02(results, offset)) return true; +#endif +#if DECODE_HAIER_AC176 + DPRINTLN("Attempting Haier AC 176 bit decode"); + if (decodeHaierAC176(results, offset)) return true; +#endif // DECODE_HAIER_AC176 +#if DECODE_HITACHI_AC424 + // HitachiAc424 should be checked before HitachiAC, HitachiAC2, + // & HitachiAC184 + DPRINTLN("Attempting Hitachi AC 424 decode"); + if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true; +#endif // DECODE_HITACHI_AC424 +#if DECODE_MITSUBISHI136 + // Needs to happen before HitachiAc3 decode. + DPRINTLN("Attempting Mitsubishi136 decode"); + if (decodeMitsubishi136(results, offset)) return true; +#endif // DECODE_MITSUBISHI136 +#if DECODE_HITACHI_AC3 + // HitachiAc3 should be checked before HitachiAC & HitachiAC2 + // Attempt normal before the short version. + DPRINTLN("Attempting Hitachi AC3 decode"); + // Order these in decreasing bit size, as it is more optimal. + if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits)) + return true; +#endif // DECODE_HITACHI_AC3 +#if DECODE_HITACHI_AC344 + // HitachiAC344 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC344 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false)) + return true; +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC264 + // HitachiAC264 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC264 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc264Bits, true, false)) + return true; +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + // HitachiAC296 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC296 decode"); + if (decodeHitachiAc296(results, offset, kHitachiAc296Bits, true)) + return true; +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC2 + // HitachiAC2 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC2 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc2Bits)) return true; +#endif // DECODE_HITACHI_AC2 +#if DECODE_HITACHI_AC + DPRINTLN("Attempting Hitachi AC decode"); + if (decodeHitachiAC(results, offset, kHitachiAcBits)) return true; +#endif +#if DECODE_HITACHI_AC1 + DPRINTLN("Attempting Hitachi AC1 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc1Bits)) return true; +#endif +#if DECODE_WHIRLPOOL_AC + DPRINTLN("Attempting Whirlpool AC decode"); + if (decodeWhirlpoolAC(results, offset)) return true; +#endif +#if DECODE_SAMSUNG_AC + DPRINTLN("Attempting Samsung AC (extended) decode"); + // Check the extended size first, as it should fail fast due to longer + // length. + if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits)) return true; + // Now check for the more common length. + DPRINTLN("Attempting Samsung AC decode"); + if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true; +#endif +#if DECODE_ELECTRA_AC + DPRINTLN("Attempting Electra AC decode"); + if (decodeElectraAC(results, offset)) return true; +#endif +#if DECODE_PANASONIC_AC + DPRINTLN("Attempting Panasonic AC decode"); + if (decodePanasonicAC(results, offset)) return true; + DPRINTLN("Attempting Panasonic AC short decode"); + if (decodePanasonicAC(results, offset, kPanasonicAcShortBits)) return true; +#endif +#if DECODE_LUTRON + DPRINTLN("Attempting Lutron decode"); + if (decodeLutron(results, offset)) return true; +#endif +#if DECODE_MWM + DPRINTLN("Attempting MWM decode"); + if (decodeMWM(results, offset)) return true; +#endif +#if DECODE_VESTEL_AC + DPRINTLN("Attempting Vestel AC decode"); + if (decodeVestelAc(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI112 || DECODE_TCL112AC + // Mitsubish112 and Tcl112 share the same decoder. + DPRINTLN("Attempting Mitsubishi112/TCL112AC decode"); + if (decodeMitsubishi112(results, offset)) return true; +#endif // DECODE_MITSUBISHI112 || DECODE_TCL112AC +#if DECODE_TECO + DPRINTLN("Attempting Teco decode"); + if (decodeTeco(results, offset)) return true; +#endif +#if DECODE_LEGOPF + DPRINTLN("Attempting LEGOPF decode"); + if (decodeLegoPf(results, offset)) return true; +#endif +#if DECODE_MITSUBISHIHEAVY + DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); + if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy152Bits)) + return true; + DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); + if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy88Bits)) + return true; +#endif +#if DECODE_ARGO + DPRINTLN("Attempting Argo WREM3 decode (AC Control)"); + if (decodeArgoWREM3(results, offset, kArgo3AcControlStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM3 decode (iFeel report)"); + if (decodeArgoWREM3(results, offset, kArgo3iFeelReportStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM3 decode (Config)"); + if (decodeArgoWREM3(results, offset, kArgo3ConfigStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM3 decode (Timer)"); + if (decodeArgoWREM3(results, offset, kArgo3TimerStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM2 decode"); + if (decodeArgo(results, offset, kArgoBits) || + decodeArgo(results, offset, kArgoShortBits, false)) return true; +#endif // DECODE_ARGO +#if DECODE_SHARP_AC + DPRINTLN("Attempting SHARP_AC decode"); + if (decodeSharpAc(results, offset)) return true; +#endif +#if DECODE_GOODWEATHER + DPRINTLN("Attempting GOODWEATHER decode"); + if (decodeGoodweather(results, offset)) return true; +#endif // DECODE_GOODWEATHER +#if DECODE_INAX + DPRINTLN("Attempting Inax decode"); + if (decodeInax(results, offset)) return true; +#endif // DECODE_INAX +#if DECODE_TROTEC + DPRINTLN("Attempting Trotec decode"); + if (decodeTrotec(results, offset)) return true; +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + DPRINTLN("Attempting Trotec 3550 decode"); + if (decodeTrotec3550(results, offset)) return true; +#endif // DECODE_TROTEC_3550 +#if DECODE_DAIKIN160 + DPRINTLN("Attempting Daikin160 decode"); + if (decodeDaikin160(results, offset)) return true; +#endif // DECODE_DAIKIN160 +#if DECODE_NEOCLIMA + DPRINTLN("Attempting Neoclima decode"); + if (decodeNeoclima(results, offset)) return true; +#endif // DECODE_NEOCLIMA +#if DECODE_DAIKIN176 + DPRINTLN("Attempting Daikin176 decode"); + if (decodeDaikin176(results, offset)) return true; +#endif // DECODE_DAIKIN176 +#if DECODE_DAIKIN128 + DPRINTLN("Attempting Daikin128 decode"); + if (decodeDaikin128(results, offset)) return true; +#endif // DECODE_DAIKIN128 +#if DECODE_AMCOR + DPRINTLN("Attempting Amcor decode"); + if (decodeAmcor(results, offset)) return true; +#endif // DECODE_AMCOR +#if DECODE_DAIKIN152 + DPRINTLN("Attempting Daikin152 decode"); + if (decodeDaikin152(results, offset)) return true; +#endif // DECODE_DAIKIN152 +#if DECODE_SYMPHONY + DPRINTLN("Attempting Symphony decode"); + if (decodeSymphony(results, offset)) return true; +#endif // DECODE_SYMPHONY +#if DECODE_DAIKIN64 + DPRINTLN("Attempting Daikin64 decode"); + if (decodeDaikin64(results, offset)) return true; +#endif // DECODE_DAIKIN64 +#if DECODE_AIRWELL + DPRINTLN("Attempting Airwell decode"); + if (decodeAirwell(results, offset)) return true; +#endif // DECODE_AIRWELL +#if DECODE_DELONGHI_AC + DPRINTLN("Attempting Delonghi AC decode"); + if (decodeDelonghiAc(results, offset)) return true; +#endif // DECODE_DELONGHI_AC +#if DECODE_DOSHISHA + DPRINTLN("Attempting Doshisha decode"); + if (decodeDoshisha(results, offset)) return true; +#endif // DECODE_DOSHISHA +#if DECODE_TRUMA + // Needs to happen before decodeMultibrackets() as they can appear similar. + DPRINTLN("Attempting Truma decode"); + if (decodeTruma(results, offset)) return true; +#endif // DECODE_TRUMA +#if DECODE_MULTIBRACKETS + DPRINTLN("Attempting Multibrackets decode"); + if (decodeMultibrackets(results, offset)) return true; +#endif // DECODE_MULTIBRACKETS +#if DECODE_CARRIER_AC40 + DPRINTLN("Attempting Carrier 40bit decode"); + if (decodeCarrierAC40(results, offset)) return true; +#endif // DECODE_CARRIER_AC40 +#if DECODE_CARRIER_AC64 + DPRINTLN("Attempting Carrier 64bit decode"); + if (decodeCarrierAC64(results, offset)) return true; +#endif // DECODE_CARRIER_AC64 +#if DECODE_TECHNIBEL_AC + DPRINTLN("Attempting Technibel AC decode"); + if (decodeTechnibelAc(results, offset)) return true; +#endif // DECODE_TECHNIBEL_AC +#if DECODE_CORONA_AC + DPRINTLN("Attempting CoronaAc decode"); + if (decodeCoronaAc(results, offset)) return true; +#endif // DECODE_CORONA_AC +#if DECODE_MIDEA24 + DPRINTLN("Attempting Midea-Nec decode"); + if (decodeMidea24(results, offset)) return true; +#endif // DECODE_MIDEA24 +#if DECODE_ZEPEAL + DPRINTLN("Attempting Zepeal decode"); + if (decodeZepeal(results, offset)) return true; +#endif // DECODE_ZEPEAL +#if DECODE_SANYO_AC + DPRINTLN("Attempting Sanyo AC decode"); + if (decodeSanyoAc(results, offset)) return true; +#endif // DECODE_SANYO_AC +#if DECODE_VOLTAS + DPRINTLN("Attempting Voltas decode"); + if (decodeVoltas(results)) return true; +#endif // DECODE_VOLTAS +#if DECODE_METZ + DPRINTLN("Attempting Metz decode"); + if (decodeMetz(results, offset)) return true; +#endif // DECODE_METZ +#if DECODE_TRANSCOLD + DPRINTLN("Attempting Transcold decode"); + if (decodeTranscold(results, offset)) return true; +#endif // DECODE_TRANSCOLD +#if DECODE_MIRAGE + DPRINTLN("Attempting Mirage decode"); + if (decodeMirage(results, offset)) return true; +#endif // DECODE_MIRAGE +#if DECODE_ELITESCREENS + DPRINTLN("Attempting EliteScreens decode"); + if (decodeElitescreens(results, offset)) return true; +#endif // DECODE_ELITESCREENS +#if DECODE_PANASONIC_AC32 + DPRINTLN("Attempting Panasonic AC (32bit) long decode"); + if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits)) return true; + DPRINTLN("Attempting Panasonic AC (32bit) short decode"); + if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits / 2)) + return true; +#endif // DECODE_PANASONIC_AC32 +#if DECODE_ECOCLIM + DPRINTLN("Attempting Ecoclim decode"); + if (decodeEcoclim(results, offset, kEcoclimBits) || + decodeEcoclim(results, offset, kEcoclimShortBits)) return true; +#endif // DECODE_ECOCLIM +#if DECODE_XMP + DPRINTLN("Attempting XMP decode"); + if (decodeXmp(results, offset, kXmpBits)) return true; +#endif // DECODE_XMP +#if DECODE_TEKNOPOINT + DPRINTLN("Attempting Teknopoint decode"); + if (decodeTeknopoint(results, offset)) return true; +#endif // DECODE_TEKNOPOINT +#if DECODE_KELON168 + DPRINTLN("Attempting Kelon 168-bit decode"); + if (decodeKelon168(results, offset)) return true; +#endif // DECODE_KELON168 +#if DECODE_KELON + DPRINTLN("Attempting Kelon 48-bit decode"); + if (decodeKelon(results, offset)) return true; +#endif // DECODE_KELON +#if DECODE_SANYO_AC88 + DPRINTLN("Attempting SanyoAc88 decode"); + if (decodeSanyoAc88(results, offset)) return true; +#endif // DECODE_SANYO_AC88 +#if DECODE_BOSE + DPRINTLN("Attempting Bose decode"); + if (decodeBose(results, offset)) return true; +#endif // DECODE_BOSE +#if DECODE_ARRIS + DPRINTLN("Attempting Arris decode"); + if (decodeArris(results, offset)) return true; +#endif // DECODE_ARRIS +#if DECODE_RHOSS + DPRINTLN("Attempting Rhoss decode"); + if (decodeRhoss(results, offset)) return true; +#endif // DECODE_RHOSS +#if DECODE_AIRTON + DPRINTLN("Attempting Airton decode"); + if (decodeAirton(results, offset)) return true; +#endif // DECODE_AIRTON +#if DECODE_COOLIX48 + DPRINTLN("Attempting Coolix 48-bit decode"); + if (decodeCoolix48(results, offset)) return true; +#endif // DECODE_COOLIX48 +#if DECODE_DAIKIN200 + DPRINTLN("Attempting Daikin 200-bit decode"); + if (decodeDaikin200(results, offset)) return true; +#endif // DECODE_DAIKIN200 +#if DECODE_HAIER_AC160 + DPRINTLN("Attempting Haier AC 160 bit decode"); + if (decodeHaierAC160(results, offset)) return true; +#endif // DECODE_HAIER_AC160 +#if DECODE_CARRIER_AC128 + DPRINTLN("Attempting Carrier AC 128-bit decode"); + if (decodeCarrierAC128(results, offset)) return true; +#endif // DECODE_CARRIER_AC128 +#if DECODE_TOTO + DPRINTLN("Attempting Toto 48/24-bit decode"); + if (decodeToto(results, offset, kTotoLongBits) || // Long needs to be first + decodeToto(results, offset, kTotoShortBits)) return true; +#endif // DECODE_TOTO +#if DECODE_CLIMABUTLER + DPRINTLN("Attempting ClimaButler decode"); + if (decodeClimaButler(results)) return true; +#endif // DECODE_CLIMABUTLER +#if DECODE_TCL96AC + DPRINTLN("Attempting TCL AC 96-bit decode"); + if (decodeTcl96Ac(results, offset)) return true; +#endif // DECODE_TCL96AC +#if DECODE_SANYO_AC152 + DPRINTLN("Attempting Sanyo AC 152-bit decode"); + if (decodeSanyoAc152(results, offset)) return true; +#endif // DECODE_SANYO_AC152 +#if DECODE_DAIKIN312 + DPRINTLN("Attempting Daikin 312-bit decode"); + if (decodeDaikin312(results, offset)) return true; +#endif // DECODE_DAIKIN312 +#if DECODE_GORENJE + DPRINTLN("Attempting GORENJE decode"); + if (decodeGorenje(results, offset)) return true; +#endif // DECODE_GORENJE +#if DECODE_WOWWEE + DPRINTLN("Attempting WOWWEE decode"); + if (decodeWowwee(results, offset)) return true; +#endif // DECODE_WOWWEE +#if DECODE_CARRIER_AC84 + DPRINTLN("Attempting Carrier A/C 84-bit decode"); + if (decodeCarrierAC84(results, offset)) return true; +#endif // DECODE_CARRIER_AC84 +#if DECODE_YORK + DPRINTLN("Attempting York decode"); + if (decodeYork(results, offset, kYorkBits)) return true; +#endif // DECODE_YORK + // Typically new protocols are added above this line. + } +#if DECODE_HASH + // decodeHash returns a hash on any input. + // Thus, it needs to be last in the list. + // If you add any decodes, add them before this. + if (decodeHash(results)) { + return true; + } +#endif // DECODE_HASH + // Throw away and start over + if (!resumed) // Check if we have already resumed. + resume(); + return false; +} // NOLINT(readability/fn_size) + +/// Convert the tolerance percentage into something valid. +/// @param[in] percentage An integer percentage. +uint8_t IRrecv::_validTolerance(const uint8_t percentage) { + return (percentage > 100) ? _tolerance : percentage; +} + +/// Calculate the lower bound of the nr. of ticks. +/// @param[in] usecs Nr. of uSeconds. +/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% +/// @param[in] delta A non-scaling amount to reduce usecs by. +/// @return Nr. of ticks. +uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { + // max() used to ensure the result can't drop below 0 before the cast. + return ((uint32_t)std::max( + (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), + (int32_t)0)); +} + +/// Calculate the upper bound of the nr. of ticks. +/// @param[in] usecs Nr. of uSeconds. +/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% +/// @param[in] delta A non-scaling amount to increase usecs by. +/// @return Nr. of ticks. +uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { + return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 + + delta); +} + +/// Check if we match a pulse(measured) with the desired within +/// +/-tolerance percent and/or +/- a fixed delta range. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] delta A non-scaling (+/-) error margin (in useconds). +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, + uint16_t delta) { + measured *= kRawTick; // Convert to uSecs. + DPRINT("Matching: "); + DPRINT(ticksLow(desired, tolerance, delta)); + DPRINT(" <= "); + DPRINT(measured); + DPRINT(" <= "); + DPRINTLN(ticksHigh(desired, tolerance, delta)); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST + return (measured >= ticksLow(desired, tolerance, delta) && + measured <= ticksHigh(desired, tolerance, delta)); +} + +/// Check if we match a pulse(measured) of at least desired within +/// tolerance percent and/or a fixed delta margin. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] delta A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, + uint8_t tolerance, uint16_t delta) { + measured *= kRawTick; // Convert to uSecs. + DPRINT("Matching ATLEAST "); + DPRINT(measured); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(". Matching: "); + DPRINT(measured); + DPRINT(" >= "); + DPRINT(ticksLow(std::min(desired, (uint32_t)MS_TO_USEC(params.timeout)), + tolerance, delta)); + DPRINT(" [min("); + DPRINT(ticksLow(desired, tolerance, delta)); + DPRINT(", "); + DPRINT(ticksLow(MS_TO_USEC(params.timeout), tolerance, delta)); + DPRINTLN(")]"); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST + // We really should never get a value of 0, except as the last value + // in the buffer. If that is the case, then assume infinity and return true. + if (measured == 0) return true; + return measured >= ticksLow(std::min(desired, + (uint32_t)MS_TO_USEC(params.timeout)), + tolerance, delta); +} + +/// Check if we match a mark signal(measured) with the desired within +/// +/-tolerance percent, after an expected is excess is added. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchMark(uint32_t measured, uint32_t desired, uint8_t tolerance, + int16_t excess) { + DPRINT("Matching MARK "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" + "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired + excess, tolerance); +} + +/// Check if we match a mark signal(measured) with the desired within a +/// range (in uSeconds) either side of the desired, after an expected is excess +/// is added. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] range The range limit from desired to accept in uSeconds. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchMarkRange(const uint32_t measured, const uint32_t desired, + const uint16_t range, const int16_t excess) { + DPRINT("Matching MARK "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" + "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired + excess, 0, range); +} + +/// Check if we match a space signal(measured) with the desired within +/// +/-tolerance percent, after an expected is excess is removed. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance, + int16_t excess) { + DPRINT("Matching SPACE "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" - "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired - excess, tolerance); +} + +/// Check if we match a space signal(measured) with the desired within a +/// range (in uSeconds) either side of the desired, after an expected is excess +/// is removed. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] range The range limit from desired to accept in uSeconds. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchSpaceRange(const uint32_t measured, const uint32_t desired, + const uint16_t range, const int16_t excess) { + DPRINT("Matching SPACE "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" - "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired - excess, 0, range); +} + +#if DECODE_HASH +/// Compare two tick values. +/// @param[in] oldval Nr. of ticks. +/// @param[in] newval Nr. of ticks. +/// @return 0 if newval is shorter, 1 if it is equal, & 2 if it is longer. +/// @note Use a tolerance of 20% +uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) { + if (newval < oldval * 0.8) + return 0; + else if (oldval < newval * 0.8) + return 2; + else + return 1; +} + +/// Decode any arbitrary IR message into a 32-bit code value. +/// Instead of decoding using a standard encoding scheme +/// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. +/// +/// The algorithm: look at the sequence of MARK signals, and see if each one +/// is shorter (0), the same length (1), or longer (2) than the previous. +/// Do the same with the SPACE signals. Hash the resulting sequence of 0's, +/// 1's, and 2's to a 32-bit value. This will give a unique value for each +/// different code (probably), for most code systems. +/// @see http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html +/// @note This isn't a "real" decoding, just an arbitrary value. +/// Hopefully this code is unique for each button. +bool IRrecv::decodeHash(decode_results *results) { + // Require at least some samples to prevent triggering on noise + if (results->rawlen < _unknown_threshold) return false; + int32_t hash = kFnvBasis32; + // 'rawlen - 2' to avoid the look ahead from going out of bounds. + // Should probably be -3 to avoid comparing the trailing space entry, + // however it is left this way for compatibility with previously captured + // values. + for (uint16_t i = 1; i < results->rawlen - 2; i++) { + uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); + // Add value into the hash + hash = (hash * kFnvPrime32) ^ value; + } + results->value = hash & 0xFFFFFFFF; + results->bits = results->rawlen / 2; + results->address = 0; + results->command = 0; + results->decode_type = UNKNOWN; + return true; +} +#endif // DECODE_HASH + +/// Match & decode the typical data section of an IR message. +/// The data value is stored in the least significant bits reguardless of the +/// bit ordering requested. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] expectlastspace Do we expect a space at the end of the message? +/// @return A match_result_t structure containing the success (or not), the +/// data value, and how many buffer entries were used. +match_result_t IRrecv::matchData( + volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark, + const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance, const int16_t excess, const bool MSBfirst, + const bool expectlastspace) { + match_result_t result; + result.success = false; // Fail by default. + result.data = 0; + if (expectlastspace) { // We are expecting data with a final space. + for (result.used = 0; result.used < nbits * 2; + result.used += 2, data_ptr += 2) { + // Is the bit a '1'? + if (matchMark(*data_ptr, onemark, tolerance, excess) && + matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) { + result.data = (result.data << 1) | 1; + } else if (matchMark(*data_ptr, zeromark, tolerance, excess) && + matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) { + result.data <<= 1; // The bit is a '0'. + } else { + if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2); + return result; // It's neither, so fail. + } + } + result.success = true; + } else { // We are expecting data without a final space. + // Match all but the last bit, as it may not match easily. + result = matchData(data_ptr, nbits ? nbits - 1 : 0, onemark, onespace, + zeromark, zerospace, tolerance, excess, true, true); + if (result.success) { + // Is the bit a '1'? + if (matchMark(*(data_ptr + result.used), onemark, tolerance, excess)) + result.data = (result.data << 1) | 1; + else if (matchMark(*(data_ptr + result.used), zeromark, tolerance, + excess)) + result.data <<= 1; // The bit is a '0'. + else + result.success = false; + if (result.success) result.used++; + } + } + if (!MSBfirst) result.data = reverseBits(result.data, nbits); + return result; +} + +/// Match & decode the typical data section of an IR message. +/// The bytes are stored at result_ptr. The first byte in the result equates to +/// the first byte encountered, and so on. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbytes Nr. of data bytes we expect. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] expectlastspace Do we expect a space at the end of the message? +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance, const int16_t excess, + const bool MSBfirst, const bool expectlastspace) { + // Check if there is enough capture buffer to possibly have the desired bytes. + if (remaining + expectlastspace < (nbytes * 8 * 2) + 1) + return 0; // Nope, so abort. + uint16_t offset = 0; + for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { + bool lastspace = (byte_pos + 1 == nbytes) ? expectlastspace : true; + match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, + zeromark, zerospace, tolerance, excess, + MSBfirst, lastspace); + if (result.success == false) return 0; // Fail + result_ptr[byte_pos] = (uint8_t)result.data; + offset += result.used; + } + return offset; +} + +/// Match & decode a generic/typical IR message. +/// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag +/// `use_bits`. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[out] result_bits_ptr A pointer to where to start storing the bits we +/// decoded. +/// @param[out] result_bytes_ptr A pointer to where to start storing the bytes +/// we decoded. +/// @param[in] use_bits A flag indicating if we are to decode bits or bytes. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_bytes_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + // If we are expecting byte sizes, check it's a factor of 8 or fail. + if (!use_bits && nbits % 8 != 0) return 0; + // Calculate if we expect a trailing space in the data section. + const bool kexpectspace = footermark || (onespace != zerospace); + // Calculate how much remaining buffer is required. + uint16_t min_remaining = nbits * 2 - (kexpectspace ? 0 : 1); + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + uint16_t offset = 0; + + // Header + if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) + return 0; + if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, + excess)) + return 0; + + // Data + if (use_bits) { // Bits. + match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst, kexpectspace); + if (!result.success) return 0; + *result_bits_ptr = result.data; + offset += result.used; + } else { // bytes + uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, + remaining - offset, nbits / 8, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst, kexpectspace); + if (!data_used) return 0; + offset += data_used; + } + // Footer + if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, + excess)) + return 0; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +/// Match & decode a generic/typical <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// +/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +/// Match & decode a generic/typical > 64bit IR message. +/// The bytes are stored at result_ptr. The first byte in the result equates to +/// the first byte encountered, and so on. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint8_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +/// Match & decode a generic/typical constant bit time <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] one Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] zero Nr. of uSeconds in an expected mark signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @note Parameters one + zero add up to the total time for a bit. +/// e.g. mark(one) + space(zero) is a `1`, mark(zero) + space(one) is a `0`. +uint16_t IRrecv::matchGenericConstBitTime(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t one, + const uint32_t zero, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + uint16_t offset = 0; + uint64_t result = 0; + // If we expect a footermark, then this can be processed like normal. + if (footermark) + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, one, zero, zero, one, + footermark, footerspace, atleast, + tolerance, excess, MSBfirst); + // Overwise handle like normal, except for the last bit. and no footer. + uint16_t bits = (nbits > 0) ? nbits - 1 : 0; // Make sure we don't underflow. + offset = _matchGeneric(data_ptr, &result, NULL, true, remaining, bits, + hdrmark, hdrspace, one, zero, zero, one, 0, 0, false, + tolerance, excess, true); + if (!offset) return 0; // Didn't match. + // Now for the last bit. + if (remaining <= offset) return 0; // Not enough buffer. + result <<= 1; + bool last_bit = 0; + // Is the mark a '1' or a `0`? + if (matchMark(*(data_ptr + offset), one, tolerance, excess)) { // 1 + last_bit = 1; + result |= 1; + } else if (matchMark(*(data_ptr + offset), zero, tolerance, excess)) { // 0 + last_bit = 0; + } else { + return 0; // It's neither, so fail. + } + offset++; + uint32_t expected_space = (last_bit ? zero : one) + footerspace; + // If we are not at the end of the buffer, check for at least the expected + // space value. + if (remaining > offset) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), expected_space, tolerance, + excess)) + return false; + } else { + if (!matchSpace(*(data_ptr + offset), expected_space, tolerance)) + return false; + } + offset++; + } + if (!MSBfirst) result = reverseBits(result, nbits); + *result_ptr = result; + return offset; +} + +/// Match & decode a Manchester Code <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// i.e. 1/2 wavelength +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf +uint16_t IRrecv::matchManchester(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t half_period, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst, + const bool GEThomas) { + uint16_t offset = 0; + uint16_t bank = 0; + uint16_t entry = 0; + + // Calculate how much remaining buffer is required. + // Shortest case is nbits. Longest case is 2 * nbits. + uint16_t min_remaining = nbits; + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + + // Header + if (hdrmark) { + entry = *(data_ptr + offset++); + if (!hdrspace) { // If we have no Header Space ... + // Do we have a data 'mark' half period merged with the header mark? + if (matchMark(entry, hdrmark + half_period, + tolerance, excess)) { + // Looks like we do. + bank = entry * kRawTick - hdrmark; + } else if (!matchMark(entry, hdrmark, tolerance, excess)) { + return 0; // It's not a normal header mark, so fail. + } + } else if (!matchMark(entry, hdrmark, tolerance, excess)) { + return 0; // It's not a normal header mark, so fail. + } + } + if (hdrspace) { + entry = *(data_ptr + offset++); + // Check to see if the header space has merged with a data space half period + if (matchSpace(entry, hdrspace + half_period, tolerance, excess)) { + // Looks like we do. + bank = entry * kRawTick - hdrspace; + } else if (!matchSpace(entry, hdrspace, tolerance, excess)) { + return 0; // It's not a normal header space, so fail. + } + } + + if (!match(bank / kRawTick, half_period, tolerance, excess)) bank = 0; + // Data + uint16_t used = matchManchesterData(data_ptr + offset, result_ptr, + remaining - offset, nbits, half_period, + bank, tolerance, excess, MSBfirst, + GEThomas); + if (!used) return 0; // Data did match. + offset += used; + // Footer + if (footermark && + !(matchMark(*(data_ptr + offset), footermark + half_period, + tolerance, excess) || + matchMark(*(data_ptr + offset), footermark, tolerance, excess))) + return 0; + offset++; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess) && + !matchSpace(*(data_ptr + offset), footerspace + half_period, + tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +/// Match & decode a Manchester Code data (<= 64bits. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// i.e. 1/2 wavelength +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] starting_balance Amount of uSeconds to assume exists prior to +/// the current value pointed too. +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf +/// @todo Clean up and optimise this. It is just "get it working code" atm. +uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t half_period, + const uint16_t starting_balance, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst, + const bool GEThomas) { + DPRINTLN("DEBUG: Entered matchManchesterData"); + uint16_t offset = 0; + uint64_t data = 0; + uint16_t nr_half_periods = 0; + const uint16_t expected_half_periods = nbits * 2; + // Flip the bit if we have a starting balance. ie. Carry over from the header. + bool currentBit = starting_balance ? !GEThomas : GEThomas; + const uint16_t raw_half_period = half_period / kRawTick; + + // Calculate how much remaining buffer is required. + // Shortest case is nbits. Longest case is 2 * nbits. + uint16_t min_remaining = nbits; + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) { + DPRINTLN("DEBUG: Ran out of capture buffer!"); + return 0; // Nope, so abort. + } + + // Convert to ticks. Optimisation: Saves on math/extra instructions later. + uint16_t bank = starting_balance / kRawTick; + + // Data + // Loop through the buffer till we run out of buffer, or nr of half periods. + // Possible patterns are: + // short + short = 1 bit (Add the value of the previous bit again) + // short + long + short = 2 bits (Add the previous bit again, then flip & add) + // short + long + long + short = 3 bits (add prev, flip & add, flip & add) + // We can't start with a long. + // + // The general approach is thus: + // Check we have a short interval, next or in the bank. + // If the next timing value is long, act according and reset the bank to + // a short balance. + // or + // If it is short, act accordingly and declare the bank empty. + // Repeat. + while ((offset < remaining || bank) && + nr_half_periods < expected_half_periods) { + // Get the next entry if we haven't anything existing to process. + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); + if (!bank) bank = *(data_ptr + offset++); + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); + // Check if we don't have a short interval. + DPRINTLN("DEBUG: Checking for short interval"); + if (!match(bank, half_period, tolerance, excess)) { + DPRINTLN("DEBUG: It is. Exiting"); + return 0; // Not valid. + } + // We've succeeded in matching half a period, so count it. + nr_half_periods++; + DPRINT("DEBUG: Half Periods = "); + DPRINTLN(nr_half_periods); + // We've now used up our bank, so refill it with the next item, unless we + // are at the end of the capture buffer. + // If we are assume a single half period of "space". + if (offset < remaining) { + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); + bank = *(data_ptr + offset++); + } else if (offset == remaining) { + bank = raw_half_period; + } else { + return 0; // We are out of buffer, so abort! + } + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); + + // Shift the data along and add our new bit. + DPRINT("DEBUG: Adding bit: "); + DPRINTLN((currentBit ? "1" : "0")); + data <<= 1; + data |= currentBit; + + // Check if we have a long interval. + if (match(bank, half_period * 2, tolerance, excess)) { + // It is, so flip the bit we need to append, and remove a half_period of + // time from the bank. + DPRINTLN("DEBUG: long interval detected"); + currentBit = !currentBit; + bank -= raw_half_period; + } else if (match(bank, half_period, tolerance, excess)) { + // It is a short interval, so eat up all the time and move on. + DPRINTLN("DEBUG: short interval detected"); + bank = 0; + } else if (nr_half_periods == expected_half_periods - 1 && + matchAtLeast(bank, half_period, tolerance, excess)) { + // We are at the end of the data & it is a short interval, so eat up all + // the time and move on. + bank = 0; + // Reduce the offset as we are at the end of the data doing a + // matchAtLeast() because we could be processing part of a footer. + offset--; + } else { + // The length isn't what we expected (neither long or short), so bail. + return 0; + } + nr_half_periods++; + } + + // Clean up and process the data. + if (!MSBfirst) data = reverseBits(data, nbits); + // Trim the data to size. + *result_ptr = GETBITS64(data, 0, nbits); + return offset; +} + +#if UNIT_TEST +/// Unit test helper to get access to the params structure. +volatile irparams_t *IRrecv::_getParamsPtr(void) { + return ¶ms; +} +#endif // UNIT_TEST +// End of IRrecv class ------------------- diff --git a/src/IRrecv.h b/src/IRrecv.h index 7adf5eb1b..616eb48b3 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -1,888 +1,894 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2015 Sebastien Warin -// Copyright 2017 David Conran - -#ifndef IRRECV_H_ -#define IRRECV_H_ - -#ifndef UNIT_TEST -#include -#endif -#include -#define __STDC_LIMIT_MACROS -#include -#include "IRremoteESP8266.h" - -// Constants -const uint16_t kHeader = 2; // Usual nr. of header entries. -const uint16_t kFooter = 2; // Usual nr. of footer (stop bits) entries. -const uint16_t kStartOffset = 1; // Usual rawbuf entry to start from. -#define MS_TO_USEC(x) ((x) * 1000U) // Convert milli-Seconds to micro-Seconds. -// Marks tend to be 100us too long, and spaces 100us too short -// when received due to sensor lag. -const uint16_t kMarkExcess = 50; -const uint16_t kRawBuf = 100; // Default length of raw capture buffer -const uint64_t kRepeat = UINT64_MAX; -// Default min size of reported UNKNOWN messages. -const uint16_t kUnknownThreshold = 6; - -// receiver states -const uint8_t kIdleState = 2; -const uint8_t kMarkState = 3; -const uint8_t kSpaceState = 4; -const uint8_t kStopState = 5; -const uint8_t kTolerance = 25; // default percent tolerance in measurements. -const uint8_t kUseDefTol = 255; // Indicate to use the class default tolerance. -const uint16_t kRawTick = 2; // Capture tick to uSec factor. -#define RAWTICK kRawTick // Deprecated. For legacy user code support only. -// How long (ms) before we give up wait for more data? -// Don't exceed kMaxTimeoutMs without a good reason. -// That is the capture buffers maximum value size. (UINT16_MAX / kRawTick) -// Typically messages/protocols tend to repeat around the 100ms timeframe, -// thus we should timeout before that to give us some time to try to decode -// before we need to start capturing a possible new message. -// Typically 15ms suits most applications. However, some protocols demand a -// higher value. e.g. 90ms for XMP-1 and some aircon units. -const uint8_t kTimeoutMs = 15; // In MilliSeconds. -#define TIMEOUT_MS kTimeoutMs // For legacy documentation. -const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1)); - -// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param -const uint32_t kFnvPrime32 = 16777619UL; -const uint32_t kFnvBasis32 = 2166136261UL; - -#ifdef ESP32 -// Which of the ESP32 timers to use by default. -// (3 for most ESP32s, 1 for ESP32-C3s) -#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS -const uint8_t kDefaultESP32Timer = SOC_TIMER_GROUP_TOTAL_TIMERS - 1; -#else // SOC_TIMER_GROUP_TOTAL_TIMERS -const uint8_t kDefaultESP32Timer = 3; -#endif // SOC_TIMER_GROUP_TOTAL_TIMERS -#endif // ESP32 - -#if DECODE_AC -// Hitachi AC is the current largest state size. -const uint16_t kStateSizeMax = kHitachiAc2StateLength; -#else // DECODE_AC -// Just define something (a uint64_t) -const uint16_t kStateSizeMax = sizeof(uint64_t); -#endif // DECODE_AC - -// Types - -/// Information for the interrupt handler -typedef struct { - uint8_t recvpin; // pin for IR data from detector - uint8_t rcvstate; // state machine - uint16_t timer; // state timer, counts 50uS ticks. - uint16_t bufsize; // max. nr. of entries in the capture buffer. - uint16_t *rawbuf; // raw data - // uint16_t is used for rawlen as it saves 3 bytes of iram in the interrupt - // handler. Don't ask why, I don't know. It just does. - uint16_t rawlen; // counter of entries in rawbuf. - uint8_t overflow; // Buffer overflow indicator. - uint8_t timeout; // Nr. of milliSeconds before we give up. -} irparams_t; - -/// Results from a data match -typedef struct { - bool success; // Was the match successful? - uint64_t data; // The data found. - uint16_t used; // How many buffer positions were used. -} match_result_t; - -// Classes - -/// Results returned from the decoder -class decode_results { - public: - decode_type_t decode_type; // NEC, SONY, RC5, UNKNOWN - // value, address, & command are all mutually exclusive with state. - // i.e. They MUST NOT be used at the same time as state, so we can use a union - // structure to save us a handful of valuable bytes of memory. - union { - struct { - uint64_t value; // Decoded value - uint32_t address; // Decoded device address. - uint32_t command; // Decoded command. - }; - uint8_t state[kStateSizeMax]; // Multi-byte results. - }; - uint16_t bits; // Number of bits in decoded value - volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks - uint16_t rawlen; // Number of records in rawbuf. - bool overflow; - bool repeat; // Is the result a repeat code? -}; - -/// Class for receiving IR messages. -class IRrecv { - public: -#if defined(ESP32) - explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, - const uint8_t timeout = kTimeoutMs, - const bool save_buffer = false, - const uint8_t timer_num = kDefaultESP32Timer); // Constructor -#else // ESP32 - explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, - const uint8_t timeout = kTimeoutMs, - const bool save_buffer = false); // Constructor -#endif // ESP32 - ~IRrecv(void); // Destructor - void setTolerance(const uint8_t percent = kTolerance); - uint8_t getTolerance(void); - bool decode(decode_results *results, irparams_t *save = NULL, - uint8_t max_skip = 0, uint16_t noise_floor = 0); - void enableIRIn(const bool pullup = false); - void disableIRIn(void); - void pause(void); - void resume(void); - uint16_t getBufSize(void); -#if DECODE_HASH - void setUnknownThreshold(const uint16_t length); -#endif - bool match(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - bool matchMark(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess); - bool matchMarkRange(const uint32_t measured, const uint32_t desired, - const uint16_t range = 100, - const int16_t excess = kMarkExcess); - bool matchSpace(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess); - bool matchSpaceRange(const uint32_t measured, const uint32_t desired, - const uint16_t range = 100, - const int16_t excess = kMarkExcess); -#ifndef UNIT_TEST - - private: -#endif - irparams_t *irparams_save; - uint8_t _tolerance; -#if defined(ESP32) - uint8_t _timer_num; -#endif // defined(ESP32) -#if DECODE_HASH - uint16_t _unknown_threshold; -#endif -#ifdef UNIT_TEST - volatile irparams_t *_getParamsPtr(void); -#endif // UNIT_TEST - // These are called by decode - uint8_t _validTolerance(const uint8_t percentage); - void copyIrParams(volatile irparams_t *src, irparams_t *dst); - uint16_t compare(const uint16_t oldval, const uint16_t newval); - uint32_t ticksLow(const uint32_t usecs, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - uint32_t ticksHigh(const uint32_t usecs, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - bool matchAtLeast(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - uint16_t _matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_bits_ptr, - uint8_t *result_ptr, - const bool use_bits, - const uint16_t remaining, - const uint16_t required, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool expectlastspace = true); - uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, - const uint16_t remaining, const uint16_t nbytes, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool expectlastspace = true); - uint16_t matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, const uint16_t nbits, - const uint16_t hdrmark, const uint32_t hdrspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr, - const uint16_t remaining, const uint16_t nbits, - const uint16_t hdrmark, const uint32_t hdrspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - uint16_t matchGenericConstBitTime(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t one, - const uint32_t zero, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - uint16_t matchManchesterData(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t half_period, - const uint16_t starting_balance = 0, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool GEThomas = true); - uint16_t matchManchester(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t clock_period, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool GEThomas = true); - void crudeNoiseFilter(decode_results *results, const uint16_t floor = 0); - bool decodeHash(decode_results *results); -#if DECODE_VOLTAS - bool decodeVoltas(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kVoltasBits, - const bool strict = true); -#endif // DECODE_VOLTAS -#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO) - bool decodeNEC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kNECBits, const bool strict = true); -#endif -#if DECODE_ARGO - bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kArgoBits, const bool strict = true); - bool decodeArgoWREM3(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kArgo3AcControlStateLength * 8, - const bool strict = true); -#endif // DECODE_ARGO -#if DECODE_ARRIS - bool decodeArris(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kArrisBits, const bool strict = true); -#endif // DECODE_ARRIS -#if DECODE_SONY - bool decodeSony(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSonyMinBits, - const bool strict = false); -#endif -#if DECODE_SANYO - // DISABLED due to poor quality. - // bool decodeSanyo(decode_results *results, uint16_t offset = kStartOffset, - // uint16_t nbits = kSanyoSA8650BBits, - // bool strict = false); - bool decodeSanyoLC7461(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoLC7461Bits, - const bool strict = true); -#endif -#if DECODE_SANYO_AC - bool decodeSanyoAc(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoAcBits, - const bool strict = true); -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - bool decodeSanyoAc88(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoAc88Bits, - const bool strict = true); -#endif // DECODE_SANYO_AC88 -#if DECODE_SANYO_AC152 - bool decodeSanyoAc152(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoAc152Bits, - const bool strict = true); -#endif // DECODE_SANYO_AC152 -#if DECODE_MITSUBISHI - bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiBits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHI2 - bool decodeMitsubishi2(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiBits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHI_AC - bool decodeMitsubishiAC(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiACBits, - const bool strict = false); -#endif -#if DECODE_MITSUBISHI136 - bool decodeMitsubishi136(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishi136Bits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHI112 - bool decodeMitsubishi112(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishi112Bits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHIHEAVY - bool decodeMitsubishiHeavy(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiHeavy152Bits, - const bool strict = true); -#endif -#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG || DECODE_MWM) - int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, - uint16_t bitTime, const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const uint16_t delta = 0, const uint8_t maxwidth = 3); -#endif -#if DECODE_RC5 - bool decodeRC5(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRC5XBits, - const bool strict = true); -#endif -#if DECODE_RC6 - bool decodeRC6(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRC6Mode0Bits, - const bool strict = false); -#endif -#if DECODE_RCMM - bool decodeRCMM(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRCMMBits, - const bool strict = false); -#endif -#if (DECODE_PANASONIC || DECODE_DENON) - bool decodePanasonic(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kPanasonicBits, - const bool strict = false, - const uint32_t manufacturer = kPanasonicManufacturer); -#endif -#if DECODE_LG - bool decodeLG(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLgBits, - const bool strict = false); -#endif -#if DECODE_INAX - bool decodeInax(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kInaxBits, - const bool strict = true); -#endif // DECODE_INAX -#if DECODE_JVC - bool decodeJVC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kJvcBits, - const bool strict = true); -#endif -#if DECODE_SAMSUNG - bool decodeSAMSUNG(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSamsungBits, - const bool strict = true); -#endif -#if DECODE_SAMSUNG - bool decodeSamsung36(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSamsung36Bits, - const bool strict = true); -#endif -#if DECODE_SAMSUNG_AC - bool decodeSamsungAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSamsungAcBits, - const bool strict = true); -#endif -#if DECODE_WHYNTER - bool decodeWhynter(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kWhynterBits, - const bool strict = true); -#endif -#if DECODE_COOLIX - bool decodeCOOLIX(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCoolixBits, - const bool strict = true); -#endif // DECODE_COOLIX -#if DECODE_COOLIX48 - bool decodeCoolix48(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCoolix48Bits, - const bool strict = true); -#endif // DECODE_COOLIX48 -#if DECODE_DENON - bool decodeDenon(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDenonBits, - const bool strict = true); -#endif -#if DECODE_DISH - bool decodeDISH(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDishBits, - const bool strict = true); -#endif -#if (DECODE_SHARP || DECODE_DENON) - bool decodeSharp(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSharpBits, - const bool strict = true, const bool expansion = true); -#endif -#if DECODE_SHARP_AC - bool decodeSharpAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSharpAcBits, - const bool strict = true); -#endif -#if DECODE_AIWA_RC_T501 - bool decodeAiwaRCT501(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAiwaRcT501Bits, - const bool strict = true); -#endif -#if DECODE_NIKAI - bool decodeNikai(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kNikaiBits, - const bool strict = true); -#endif -#if DECODE_MAGIQUEST - bool decodeMagiQuest(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMagiquestBits, - const bool strict = true); -#endif -#if DECODE_KELVINATOR - bool decodeKelvinator(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kKelvinatorBits, - const bool strict = true); -#endif -#if DECODE_DAIKIN - bool decodeDaikin(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikinBits, - const bool strict = true); -#endif -#if DECODE_DAIKIN64 - bool decodeDaikin64(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin64Bits, - const bool strict = true); -#endif // DECODE_DAIKIN64 -#if DECODE_DAIKIN128 - bool decodeDaikin128(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin128Bits, - const bool strict = true); -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - bool decodeDaikin152(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin152Bits, - const bool strict = true); -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - bool decodeDaikin160(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin160Bits, - const bool strict = true); -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - bool decodeDaikin176(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin176Bits, - const bool strict = true); -#endif // DECODE_DAIKIN176 -#if DECODE_DAIKIN2 - bool decodeDaikin2(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin2Bits, - const bool strict = true); -#endif -#if DECODE_DAIKIN200 - bool decodeDaikin200(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin200Bits, - const bool strict = true); -#endif // DECODE_DAIKIN200 -#if DECODE_DAIKIN216 - bool decodeDaikin216(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin216Bits, - const bool strict = true); -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN312 - bool decodeDaikin312(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin312Bits, - const bool strict = true); -#endif // DECODE_DAIKIN312 -#if DECODE_TOSHIBA_AC - bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kToshibaACBits, - const bool strict = true); -#endif -#if DECODE_TROTEC - bool decodeTrotec(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTrotecBits, - const bool strict = true); -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - bool decodeTrotec3550(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTrotecBits, - const bool strict = true); -#endif // DECODE_TROTEC_3550 -#if DECODE_MIDEA - bool decodeMidea(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMideaBits, - const bool strict = true); -#endif // DECODE_MIDEA -#if DECODE_MIDEA24 - bool decodeMidea24(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMidea24Bits, - const bool strict = true); -#endif // DECODE_MIDEA24 -#if DECODE_FUJITSU_AC - bool decodeFujitsuAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kFujitsuAcBits, - const bool strict = false); -#endif -#if DECODE_LASERTAG - bool decodeLasertag(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLasertagBits, - const bool strict = true); -#endif -#if DECODE_MILESTAG2 - bool decodeMilestag2(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMilesTag2ShotBits, - const bool strict = true); -#endif -#if DECODE_CARRIER_AC - bool decodeCarrierAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAcBits, - const bool strict = true); -#endif // DECODE_CARRIER_AC -#if DECODE_CARRIER_AC40 - bool decodeCarrierAC40(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc40Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC40 -#if DECODE_CARRIER_AC84 - bool decodeCarrierAC84(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc84Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC84 -#if DECODE_CARRIER_AC64 - bool decodeCarrierAC64(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc64Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC64 -#if DECODE_CARRIER_AC128 - bool decodeCarrierAC128(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc128Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC128 -#if DECODE_GOODWEATHER - bool decodeGoodweather(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kGoodweatherBits, - const bool strict = true); -#endif // DECODE_GOODWEATHER -#if DECODE_GORENJE - bool decodeGorenje(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kGorenjeBits, - const bool strict = true); -#endif // DECODE_GORENJE -#if DECODE_GREE - bool decodeGree(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kGreeBits, - const bool strict = true); -#endif -#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \ - DECODE_HAIER_AC176) - bool decodeHaierAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierACBits, - const bool strict = true); -#endif -#if DECODE_HAIER_AC_YRW02 - bool decodeHaierACYRW02(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierACYRW02Bits, - const bool strict = true); -#endif -#if DECODE_HAIER_AC160 - bool decodeHaierAC160(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierAC160Bits, - const bool strict = true); -#endif // DECODE_HAIER_AC160 -#if DECODE_HAIER_AC176 - bool decodeHaierAC176(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierAC176Bits, - const bool strict = true); -#endif // DECODE_HAIER_AC176 -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \ - DECODE_HITACHI_AC344) - bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAcBits, - const bool strict = true, const bool MSBfirst = true); -#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || - // DECODE_HITACHI_AC344) -#if DECODE_HITACHI_AC1 - bool decodeHitachiAC1(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc1Bits, - const bool strict = true); -#endif -#if DECODE_HITACHI_AC3 - bool decodeHitachiAc3(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc3Bits, - const bool strict = true); -#endif // DECODE_HITACHI_AC3 -#if DECODE_HITACHI_AC296 - bool decodeHitachiAc296(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc296Bits, - const bool strict = true); -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC424 - bool decodeHitachiAc424(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc424Bits, - const bool strict = true); -#endif // DECODE_HITACHI_AC424 -#if DECODE_GICABLE - bool decodeGICable(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kGicableBits, - const bool strict = true); -#endif -#if DECODE_WHIRLPOOL_AC - bool decodeWhirlpoolAC(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kWhirlpoolAcBits, - const bool strict = true); -#endif -#if DECODE_LUTRON - bool decodeLutron(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLutronBits, - const bool strict = true); -#endif -#if DECODE_ELECTRA_AC - bool decodeElectraAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kElectraAcBits, - const bool strict = true); -#endif -#if DECODE_PANASONIC_AC - bool decodePanasonicAC(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kPanasonicAcBits, - const bool strict = true); -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - bool decodePanasonicAC32(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kPanasonicAc32Bits, - const bool strict = true); -#endif // DECODE_PANASONIC_AC32 -#if DECODE_PIONEER - bool decodePioneer(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kPioneerBits, - const bool strict = true); -#endif -#if DECODE_MWM - bool decodeMWM(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = 24, - const bool strict = true); -#endif -#if DECODE_VESTEL_AC - bool decodeVestelAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kVestelAcBits, - const bool strict = true); -#endif -#if DECODE_TECO - bool decodeTeco(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTecoBits, - const bool strict = false); -#endif -#if DECODE_LEGOPF - bool decodeLegoPf(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLegoPfBits, - const bool strict = true); -#endif -#if DECODE_NEOCLIMA - bool decodeNeoclima(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kNeoclimaBits, - const bool strict = true); -#endif // DECODE_NEOCLIMA -#if DECODE_AMCOR - bool decodeAmcor(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAmcorBits, - const bool strict = true); -#endif // DECODE_AMCOR -#if DECODE_EPSON - bool decodeEpson(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kEpsonBits, - const bool strict = true); -#endif // DECODE_EPSON -#if DECODE_SYMPHONY - bool decodeSymphony(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSymphonyBits, - const bool strict = true); -#endif // DECODE_SYMPHONY -#if DECODE_AIRWELL - bool decodeAirwell(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAirwellBits, - const bool strict = true); -#endif // DECODE_AIRWELL -#if DECODE_DELONGHI_AC - bool decodeDelonghiAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDelonghiAcBits, - const bool strict = true); -#endif // DECODE_DELONGHI_AC -#if DECODE_DOSHISHA - bool decodeDoshisha(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDoshishaBits, - const bool strict = true); -#endif // DECODE_DOSHISHA -#if DECODE_MULTIBRACKETS - bool decodeMultibrackets(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMultibracketsBits, - const bool strict = true); -#endif // DECODE_MULTIBRACKETS -#if DECODE_TECHNIBEL_AC - bool decodeTechnibelAc(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kTechnibelAcBits, - const bool strict = true); -#endif // DECODE_TECHNIBEL_AC -#if DECODE_CORONA_AC - bool decodeCoronaAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCoronaAcBitsShort, - const bool strict = true); -#endif // DECODE_CORONA_AC -#if DECODE_ZEPEAL - bool decodeZepeal(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kZepealBits, - const bool strict = true); -#endif // DECODE_ZEPEAL -#if DECODE_METZ - bool decodeMetz(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMetzBits, - const bool strict = true); -#endif // DECODE_METZ -#if DECODE_TRANSCOLD - bool decodeTranscold(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTranscoldBits, - const bool strict = true); -#endif // DECODE_TRANSCOLD -#if DECODE_MIRAGE - bool decodeMirage(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMirageBits, - const bool strict = true); -#endif // DECODE_MIRAGE -#if DECODE_ELITESCREENS - bool decodeElitescreens(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kEliteScreensBits, - const bool strict = true); -#endif // DECODE_ELITESCREENS -#if DECODE_ECOCLIM - bool decodeEcoclim(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kEcoclimBits, - const bool strict = true); -#endif // DECODE_ECOCLIM -#if DECODE_XMP - bool decodeXmp(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kXmpBits, const bool strict = true); -#endif // DECODE_XMP -#if DECODE_TRUMA - bool decodeTruma(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTrumaBits, const bool strict = true); -#endif // DECODE_TRUMA -#if DECODE_TEKNOPOINT - bool decodeTeknopoint(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTeknopointBits, - const bool strict = true); -#endif // DECODE_TEKNOPOINT -#if DECODE_KELON - bool decodeKelon(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kKelonBits, const bool strict = true); -#endif // DECODE_KELON -#if DECODE_KELON168 - bool decodeKelon168(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kKelon168Bits, - const bool strict = true); -#endif // DECODE_KELON168 -#if DECODE_BOSE - bool decodeBose(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kBoseBits, const bool strict = true); -#endif // DECODE_BOSE -#if DECODE_RHOSS - bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRhossBits, const bool strict = true); -#endif // DECODE_RHOSS -#if DECODE_AIRTON - bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAirtonBits, - const bool strict = true); -#endif // DECODE_AIRTON -#if DECODE_TOTO - bool decodeToto(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTotoBits, - const bool strict = true); -#endif // DECODE_TOTO -#if DECODE_CLIMABUTLER - bool decodeClimaButler(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kClimaButlerBits, - const bool strict = true); -#endif // DECODE_CLIMABUTLER -#if DECODE_TCL96AC - bool decodeTcl96Ac(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kTcl96AcBits, - const bool strict = true); -#endif // DECODE_TCL96AC -#if DECODE_BOSCH144 - bool decodeBosch144(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kBosch144Bits, - const bool strict = true); -#endif // DECODE_BOSCH144 -#if DECODE_WOWWEE - bool decodeWowwee(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kWowweeBits, - const bool strict = true); -#endif // DECODE_WOWWEE -#if DECODE_YORK - bool decodeYork(decode_results *results, - uint16_t kStartOffset, - const uint16_t kYorkBits, - const bool strict = true); -#endif // DECODE_YORK -}; - -#endif // IRRECV_H_ +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2015 Sebastien Warin +// Copyright 2017 David Conran + +#ifndef IRRECV_H_ +#define IRRECV_H_ + +#ifndef UNIT_TEST +#include +#endif +#include +#define __STDC_LIMIT_MACROS +#include +#include "IRremoteESP8266.h" + +// Constants +const uint16_t kHeader = 2; // Usual nr. of header entries. +const uint16_t kFooter = 2; // Usual nr. of footer (stop bits) entries. +const uint16_t kStartOffset = 1; // Usual rawbuf entry to start from. +#define MS_TO_USEC(x) ((x) * 1000U) // Convert milli-Seconds to micro-Seconds. +// Marks tend to be 100us too long, and spaces 100us too short +// when received due to sensor lag. +const uint16_t kMarkExcess = 50; +const uint16_t kRawBuf = 100; // Default length of raw capture buffer +const uint64_t kRepeat = UINT64_MAX; +// Default min size of reported UNKNOWN messages. +const uint16_t kUnknownThreshold = 6; + +// receiver states +const uint8_t kIdleState = 2; +const uint8_t kMarkState = 3; +const uint8_t kSpaceState = 4; +const uint8_t kStopState = 5; +const uint8_t kTolerance = 25; // default percent tolerance in measurements. +const uint8_t kUseDefTol = 255; // Indicate to use the class default tolerance. +const uint16_t kRawTick = 2; // Capture tick to uSec factor. +#define RAWTICK kRawTick // Deprecated. For legacy user code support only. +// How long (ms) before we give up wait for more data? +// Don't exceed kMaxTimeoutMs without a good reason. +// That is the capture buffers maximum value size. (UINT16_MAX / kRawTick) +// Typically messages/protocols tend to repeat around the 100ms timeframe, +// thus we should timeout before that to give us some time to try to decode +// before we need to start capturing a possible new message. +// Typically 15ms suits most applications. However, some protocols demand a +// higher value. e.g. 90ms for XMP-1 and some aircon units. +const uint8_t kTimeoutMs = 15; // In MilliSeconds. +#define TIMEOUT_MS kTimeoutMs // For legacy documentation. +const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1)); + +// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param +const uint32_t kFnvPrime32 = 16777619UL; +const uint32_t kFnvBasis32 = 2166136261UL; + +#ifdef ESP32 +// Which of the ESP32 timers to use by default. +// (3 for most ESP32s, 1 for ESP32-C3s) +#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS +const uint8_t kDefaultESP32Timer = SOC_TIMER_GROUP_TOTAL_TIMERS - 1; +#else // SOC_TIMER_GROUP_TOTAL_TIMERS +const uint8_t kDefaultESP32Timer = 3; +#endif // SOC_TIMER_GROUP_TOTAL_TIMERS +#endif // ESP32 + +#if DECODE_AC +// Hitachi AC is the current largest state size. +const uint16_t kStateSizeMax = kHitachiAc2StateLength; +#else // DECODE_AC +// Just define something (a uint64_t) +const uint16_t kStateSizeMax = sizeof(uint64_t); +#endif // DECODE_AC + +// Types + +/// Information for the interrupt handler +typedef struct { + uint8_t recvpin; // pin for IR data from detector + uint8_t rcvstate; // state machine + uint16_t timer; // state timer, counts 50uS ticks. + uint16_t bufsize; // max. nr. of entries in the capture buffer. + uint16_t *rawbuf; // raw data + // uint16_t is used for rawlen as it saves 3 bytes of iram in the interrupt + // handler. Don't ask why, I don't know. It just does. + uint16_t rawlen; // counter of entries in rawbuf. + uint8_t overflow; // Buffer overflow indicator. + uint8_t timeout; // Nr. of milliSeconds before we give up. +} irparams_t; + +/// Results from a data match +typedef struct { + bool success; // Was the match successful? + uint64_t data; // The data found. + uint16_t used; // How many buffer positions were used. +} match_result_t; + +// Classes + +/// Results returned from the decoder +class decode_results { + public: + decode_type_t decode_type; // NEC, SONY, RC5, UNKNOWN + // value, address, & command are all mutually exclusive with state. + // i.e. They MUST NOT be used at the same time as state, so we can use a union + // structure to save us a handful of valuable bytes of memory. + union { + struct { + uint64_t value; // Decoded value + uint32_t address; // Decoded device address. + uint32_t command; // Decoded command. + }; + uint8_t state[kStateSizeMax]; // Multi-byte results. + }; + uint16_t bits; // Number of bits in decoded value + volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks + uint16_t rawlen; // Number of records in rawbuf. + bool overflow; + bool repeat; // Is the result a repeat code? +}; + +/// Class for receiving IR messages. +class IRrecv { + public: +#if defined(ESP32) + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false, + const uint8_t timer_num = kDefaultESP32Timer); // Constructor +#else // ESP32 + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false); // Constructor +#endif // ESP32 + ~IRrecv(void); // Destructor + void setTolerance(const uint8_t percent = kTolerance); + uint8_t getTolerance(void); + bool decode(decode_results *results, irparams_t *save = NULL, + uint8_t max_skip = 0, uint16_t noise_floor = 0); + void enableIRIn(const bool pullup = false); + void disableIRIn(void); + void pause(void); + void resume(void); + uint16_t getBufSize(void); +#if DECODE_HASH + void setUnknownThreshold(const uint16_t length); +#endif + bool match(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + bool matchMark(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess); + bool matchMarkRange(const uint32_t measured, const uint32_t desired, + const uint16_t range = 100, + const int16_t excess = kMarkExcess); + bool matchSpace(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess); + bool matchSpaceRange(const uint32_t measured, const uint32_t desired, + const uint16_t range = 100, + const int16_t excess = kMarkExcess); +#ifndef UNIT_TEST + + private: +#endif + irparams_t *irparams_save; + uint8_t _tolerance; +#if defined(ESP32) + uint8_t _timer_num; +#endif // defined(ESP32) +#if DECODE_HASH + uint16_t _unknown_threshold; +#endif +#ifdef UNIT_TEST + volatile irparams_t *_getParamsPtr(void); +#endif // UNIT_TEST + // These are called by decode + uint8_t _validTolerance(const uint8_t percentage); + void copyIrParams(volatile irparams_t *src, irparams_t *dst); + uint16_t compare(const uint16_t oldval, const uint16_t newval); + uint32_t ticksLow(const uint32_t usecs, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + uint32_t ticksHigh(const uint32_t usecs, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + bool matchAtLeast(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + uint16_t _matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t required, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool expectlastspace = true); + uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool expectlastspace = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGenericConstBitTime(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t one, + const uint32_t zero, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchManchesterData(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t half_period, + const uint16_t starting_balance = 0, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool GEThomas = true); + uint16_t matchManchester(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t clock_period, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool GEThomas = true); + void crudeNoiseFilter(decode_results *results, const uint16_t floor = 0); + bool decodeHash(decode_results *results); +#if DECODE_VOLTAS + bool decodeVoltas(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kVoltasBits, + const bool strict = true); +#endif // DECODE_VOLTAS +#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO) + bool decodeNEC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kNECBits, const bool strict = true); +#endif +#if DECODE_ARGO + bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kArgoBits, const bool strict = true); + bool decodeArgoWREM3(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kArgo3AcControlStateLength * 8, + const bool strict = true); +#endif // DECODE_ARGO +#if DECODE_ARRIS + bool decodeArris(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kArrisBits, const bool strict = true); +#endif // DECODE_ARRIS +#if DECODE_SONY + bool decodeSony(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSonyMinBits, + const bool strict = false); +#endif +#if DECODE_SANYO + // DISABLED due to poor quality. + // bool decodeSanyo(decode_results *results, uint16_t offset = kStartOffset, + // uint16_t nbits = kSanyoSA8650BBits, + // bool strict = false); + bool decodeSanyoLC7461(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoLC7461Bits, + const bool strict = true); +#endif +#if DECODE_SANYO_AC + bool decodeSanyoAc(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAcBits, + const bool strict = true); +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + bool decodeSanyoAc88(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAc88Bits, + const bool strict = true); +#endif // DECODE_SANYO_AC88 +#if DECODE_SANYO_AC152 + bool decodeSanyoAc152(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAc152Bits, + const bool strict = true); +#endif // DECODE_SANYO_AC152 +#if DECODE_MITSUBISHI + bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiBits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHI2 + bool decodeMitsubishi2(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiBits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHI_AC + bool decodeMitsubishiAC(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiACBits, + const bool strict = false); +#endif +#if DECODE_MITSUBISHI136 + bool decodeMitsubishi136(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishi136Bits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHI112 + bool decodeMitsubishi112(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishi112Bits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHIHEAVY + bool decodeMitsubishiHeavy(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiHeavy152Bits, + const bool strict = true); +#endif +#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG || DECODE_MWM) + int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, + uint16_t bitTime, const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const uint16_t delta = 0, const uint8_t maxwidth = 3); +#endif +#if DECODE_RC5 + bool decodeRC5(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRC5XBits, + const bool strict = true); +#endif +#if DECODE_RC6 + bool decodeRC6(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRC6Mode0Bits, + const bool strict = false); +#endif +#if DECODE_RCMM + bool decodeRCMM(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRCMMBits, + const bool strict = false); +#endif +#if (DECODE_PANASONIC || DECODE_DENON) + bool decodePanasonic(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kPanasonicBits, + const bool strict = false, + const uint32_t manufacturer = kPanasonicManufacturer); +#endif +#if DECODE_LG + bool decodeLG(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLgBits, + const bool strict = false); +#endif +#if DECODE_INAX + bool decodeInax(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kInaxBits, + const bool strict = true); +#endif // DECODE_INAX +#if DECODE_JVC + bool decodeJVC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kJvcBits, + const bool strict = true); +#endif +#if DECODE_SAMSUNG + bool decodeSAMSUNG(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSamsungBits, + const bool strict = true); +#endif +#if DECODE_SAMSUNG + bool decodeSamsung36(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSamsung36Bits, + const bool strict = true); +#endif +#if DECODE_SAMSUNG_AC + bool decodeSamsungAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSamsungAcBits, + const bool strict = true); +#endif +#if DECODE_WHYNTER + bool decodeWhynter(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kWhynterBits, + const bool strict = true); +#endif +#if DECODE_COOLIX + bool decodeCOOLIX(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCoolixBits, + const bool strict = true); +#endif // DECODE_COOLIX +#if DECODE_COOLIX48 + bool decodeCoolix48(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCoolix48Bits, + const bool strict = true); +#endif // DECODE_COOLIX48 +#if DECODE_DENON + bool decodeDenon(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDenonBits, + const bool strict = true); +#endif +#if DECODE_DISH + bool decodeDISH(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDishBits, + const bool strict = true); +#endif +#if (DECODE_SHARP || DECODE_DENON) + bool decodeSharp(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSharpBits, + const bool strict = true, const bool expansion = true); +#endif +#if DECODE_SHARP_AC + bool decodeSharpAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSharpAcBits, + const bool strict = true); +#endif +#if DECODE_AIWA_RC_T501 + bool decodeAiwaRCT501(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAiwaRcT501Bits, + const bool strict = true); +#endif +#if DECODE_NIKAI + bool decodeNikai(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kNikaiBits, + const bool strict = true); +#endif +#if DECODE_MAGIQUEST + bool decodeMagiQuest(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMagiquestBits, + const bool strict = true); +#endif +#if DECODE_KELVINATOR + bool decodeKelvinator(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kKelvinatorBits, + const bool strict = true); +#endif +#if DECODE_DAIKIN + bool decodeDaikin(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikinBits, + const bool strict = true); +#endif +#if DECODE_DAIKIN64 + bool decodeDaikin64(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin64Bits, + const bool strict = true); +#endif // DECODE_DAIKIN64 +#if DECODE_DAIKIN128 + bool decodeDaikin128(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin128Bits, + const bool strict = true); +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + bool decodeDaikin152(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin152Bits, + const bool strict = true); +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + bool decodeDaikin160(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin160Bits, + const bool strict = true); +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + bool decodeDaikin176(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin176Bits, + const bool strict = true); +#endif // DECODE_DAIKIN176 +#if DECODE_DAIKIN2 + bool decodeDaikin2(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin2Bits, + const bool strict = true); +#endif +#if DECODE_DAIKIN200 + bool decodeDaikin200(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin200Bits, + const bool strict = true); +#endif // DECODE_DAIKIN200 +#if DECODE_DAIKIN216 + bool decodeDaikin216(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin216Bits, + const bool strict = true); +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN312 + bool decodeDaikin312(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin312Bits, + const bool strict = true); +#endif // DECODE_DAIKIN312 +#if DECODE_TOSHIBA_AC + bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kToshibaACBits, + const bool strict = true); +#endif +#if DECODE_TROTEC + bool decodeTrotec(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTrotecBits, + const bool strict = true); +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + bool decodeTrotec3550(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTrotecBits, + const bool strict = true); +#endif // DECODE_TROTEC_3550 +#if DECODE_MIDEA + bool decodeMidea(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMideaBits, + const bool strict = true); +#endif // DECODE_MIDEA +#if DECODE_MIDEA24 + bool decodeMidea24(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMidea24Bits, + const bool strict = true); +#endif // DECODE_MIDEA24 +#if DECODE_FUJITSU_AC + bool decodeFujitsuAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kFujitsuAcBits, + const bool strict = false); +#endif +#if DECODE_FUJITSU_AC264 + bool decodeFujitsuAC264(decode_results* results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kFujitsuAc264Bits, + const bool strict = true); +#endif // DECODE_FUJITSU_AC264 +#if DECODE_LASERTAG + bool decodeLasertag(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLasertagBits, + const bool strict = true); +#endif +#if DECODE_MILESTAG2 + bool decodeMilestag2(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMilesTag2ShotBits, + const bool strict = true); +#endif +#if DECODE_CARRIER_AC + bool decodeCarrierAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAcBits, + const bool strict = true); +#endif // DECODE_CARRIER_AC +#if DECODE_CARRIER_AC40 + bool decodeCarrierAC40(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc40Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC40 +#if DECODE_CARRIER_AC84 + bool decodeCarrierAC84(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc84Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC84 +#if DECODE_CARRIER_AC64 + bool decodeCarrierAC64(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc64Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC64 +#if DECODE_CARRIER_AC128 + bool decodeCarrierAC128(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc128Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC128 +#if DECODE_GOODWEATHER + bool decodeGoodweather(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kGoodweatherBits, + const bool strict = true); +#endif // DECODE_GOODWEATHER +#if DECODE_GORENJE + bool decodeGorenje(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kGorenjeBits, + const bool strict = true); +#endif // DECODE_GORENJE +#if DECODE_GREE + bool decodeGree(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kGreeBits, + const bool strict = true); +#endif +#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \ + DECODE_HAIER_AC176) + bool decodeHaierAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierACBits, + const bool strict = true); +#endif +#if DECODE_HAIER_AC_YRW02 + bool decodeHaierACYRW02(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierACYRW02Bits, + const bool strict = true); +#endif +#if DECODE_HAIER_AC160 + bool decodeHaierAC160(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierAC160Bits, + const bool strict = true); +#endif // DECODE_HAIER_AC160 +#if DECODE_HAIER_AC176 + bool decodeHaierAC176(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierAC176Bits, + const bool strict = true); +#endif // DECODE_HAIER_AC176 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \ + DECODE_HITACHI_AC344) + bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAcBits, + const bool strict = true, const bool MSBfirst = true); +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || + // DECODE_HITACHI_AC344) +#if DECODE_HITACHI_AC1 + bool decodeHitachiAC1(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc1Bits, + const bool strict = true); +#endif +#if DECODE_HITACHI_AC3 + bool decodeHitachiAc3(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc3Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC3 +#if DECODE_HITACHI_AC296 + bool decodeHitachiAc296(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc296Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC424 + bool decodeHitachiAc424(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc424Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC424 +#if DECODE_GICABLE + bool decodeGICable(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kGicableBits, + const bool strict = true); +#endif +#if DECODE_WHIRLPOOL_AC + bool decodeWhirlpoolAC(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kWhirlpoolAcBits, + const bool strict = true); +#endif +#if DECODE_LUTRON + bool decodeLutron(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLutronBits, + const bool strict = true); +#endif +#if DECODE_ELECTRA_AC + bool decodeElectraAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kElectraAcBits, + const bool strict = true); +#endif +#if DECODE_PANASONIC_AC + bool decodePanasonicAC(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kPanasonicAcBits, + const bool strict = true); +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + bool decodePanasonicAC32(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kPanasonicAc32Bits, + const bool strict = true); +#endif // DECODE_PANASONIC_AC32 +#if DECODE_PIONEER + bool decodePioneer(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kPioneerBits, + const bool strict = true); +#endif +#if DECODE_MWM + bool decodeMWM(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = 24, + const bool strict = true); +#endif +#if DECODE_VESTEL_AC + bool decodeVestelAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kVestelAcBits, + const bool strict = true); +#endif +#if DECODE_TECO + bool decodeTeco(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTecoBits, + const bool strict = false); +#endif +#if DECODE_LEGOPF + bool decodeLegoPf(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLegoPfBits, + const bool strict = true); +#endif +#if DECODE_NEOCLIMA + bool decodeNeoclima(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kNeoclimaBits, + const bool strict = true); +#endif // DECODE_NEOCLIMA +#if DECODE_AMCOR + bool decodeAmcor(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAmcorBits, + const bool strict = true); +#endif // DECODE_AMCOR +#if DECODE_EPSON + bool decodeEpson(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kEpsonBits, + const bool strict = true); +#endif // DECODE_EPSON +#if DECODE_SYMPHONY + bool decodeSymphony(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSymphonyBits, + const bool strict = true); +#endif // DECODE_SYMPHONY +#if DECODE_AIRWELL + bool decodeAirwell(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAirwellBits, + const bool strict = true); +#endif // DECODE_AIRWELL +#if DECODE_DELONGHI_AC + bool decodeDelonghiAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDelonghiAcBits, + const bool strict = true); +#endif // DECODE_DELONGHI_AC +#if DECODE_DOSHISHA + bool decodeDoshisha(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDoshishaBits, + const bool strict = true); +#endif // DECODE_DOSHISHA +#if DECODE_MULTIBRACKETS + bool decodeMultibrackets(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMultibracketsBits, + const bool strict = true); +#endif // DECODE_MULTIBRACKETS +#if DECODE_TECHNIBEL_AC + bool decodeTechnibelAc(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kTechnibelAcBits, + const bool strict = true); +#endif // DECODE_TECHNIBEL_AC +#if DECODE_CORONA_AC + bool decodeCoronaAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCoronaAcBitsShort, + const bool strict = true); +#endif // DECODE_CORONA_AC +#if DECODE_ZEPEAL + bool decodeZepeal(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kZepealBits, + const bool strict = true); +#endif // DECODE_ZEPEAL +#if DECODE_METZ + bool decodeMetz(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMetzBits, + const bool strict = true); +#endif // DECODE_METZ +#if DECODE_TRANSCOLD + bool decodeTranscold(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTranscoldBits, + const bool strict = true); +#endif // DECODE_TRANSCOLD +#if DECODE_MIRAGE + bool decodeMirage(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMirageBits, + const bool strict = true); +#endif // DECODE_MIRAGE +#if DECODE_ELITESCREENS + bool decodeElitescreens(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kEliteScreensBits, + const bool strict = true); +#endif // DECODE_ELITESCREENS +#if DECODE_ECOCLIM + bool decodeEcoclim(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kEcoclimBits, + const bool strict = true); +#endif // DECODE_ECOCLIM +#if DECODE_XMP + bool decodeXmp(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kXmpBits, const bool strict = true); +#endif // DECODE_XMP +#if DECODE_TRUMA + bool decodeTruma(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTrumaBits, const bool strict = true); +#endif // DECODE_TRUMA +#if DECODE_TEKNOPOINT + bool decodeTeknopoint(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTeknopointBits, + const bool strict = true); +#endif // DECODE_TEKNOPOINT +#if DECODE_KELON + bool decodeKelon(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kKelonBits, const bool strict = true); +#endif // DECODE_KELON +#if DECODE_KELON168 + bool decodeKelon168(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kKelon168Bits, + const bool strict = true); +#endif // DECODE_KELON168 +#if DECODE_BOSE + bool decodeBose(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kBoseBits, const bool strict = true); +#endif // DECODE_BOSE +#if DECODE_RHOSS + bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRhossBits, const bool strict = true); +#endif // DECODE_RHOSS +#if DECODE_AIRTON + bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAirtonBits, + const bool strict = true); +#endif // DECODE_AIRTON +#if DECODE_TOTO + bool decodeToto(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTotoBits, + const bool strict = true); +#endif // DECODE_TOTO +#if DECODE_CLIMABUTLER + bool decodeClimaButler(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kClimaButlerBits, + const bool strict = true); +#endif // DECODE_CLIMABUTLER +#if DECODE_TCL96AC + bool decodeTcl96Ac(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kTcl96AcBits, + const bool strict = true); +#endif // DECODE_TCL96AC +#if DECODE_BOSCH144 + bool decodeBosch144(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kBosch144Bits, + const bool strict = true); +#endif // DECODE_BOSCH144 +#if DECODE_WOWWEE + bool decodeWowwee(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kWowweeBits, + const bool strict = true); +#endif // DECODE_WOWWEE +#if DECODE_YORK + bool decodeYork(decode_results *results, + uint16_t kStartOffset, + const uint16_t kYorkBits, + const bool strict = true); +#endif // DECODE_YORK +}; + +#endif // IRRECV_H_ diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 949de1ecf..06512fbf4 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -1,1524 +1,1539 @@ - /*************************************************** - * IRremote for ESP8266 - * - * Based on the IRremote library for Arduino by Ken Shirriff - * Version 0.11 August, 2009 - * Copyright 2009 Ken Shirriff - * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html - * - * Edited by Mitra to add new controller SANYO - * - * Interrupt code based on NECIRrcv by Joe Knapp - * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 - * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ - * - * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) - * LG added by Darryl Smith (based on the JVC protocol) - * Whynter A/C ARC-110WD added by Francesco Meschia - * Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit - * Denon: sendDenon, decodeDenon added by Massimiliano Pinto - (from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp) - * Kelvinator A/C and Sherwood added by crankyoldgit - * Mitsubishi (TV) sending added by crankyoldgit - * Pronto code sending added by crankyoldgit - * Mitsubishi & Toshiba A/C added by crankyoldgit - * (derived from https://github.com/r45635/HVAC-IR-Control) - * DISH decode by marcosamarinho - * Gree Heatpump sending added by Ville Skyttä (scop) - * (derived from https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp) - * Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for sending IR code on ESP8266 - * Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266 - * - * Updated by sillyfrog for Daikin, adopted from - * (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/) - * Fujitsu A/C code added by jonnygraham - * Trotec AC code by stufisher - * Carrier & Haier AC code by crankyoldgit - * Vestel AC code by Erdem U. Altınyurt - * Teco AC code by Fabien Valthier (hcoohb) - * Mitsubishi 112 AC Code by kuchel77 - * Kelon AC code by Davide Depau (Depau) - * - * GPL license, all text above must be included in any redistribution - ****************************************************/ - -#ifndef IRREMOTEESP8266_H_ -#define IRREMOTEESP8266_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifdef UNIT_TEST -#include -#include -#endif // UNIT_TEST - -// Library Version Information -// Major version number (X.x.x) -#define _IRREMOTEESP8266_VERSION_MAJOR 2 -// Minor version number (x.X.x) -#define _IRREMOTEESP8266_VERSION_MINOR 8 -// Patch version number (x.x.X) -#define _IRREMOTEESP8266_VERSION_PATCH 6 -// Macro to convert version info into an integer -#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \ - (((major) << 16) | ((minor) << 8) | (patch)) -// Macro to convert literal into a string -#define MKSTR_HELPER(x) #x -#define MKSTR(x) MKSTR_HELPER(x) -// Integer version -#define _IRREMOTEESP8266_VERSION _IRREMOTEESP8266_VERSION_VAL(\ - _IRREMOTEESP8266_VERSION_MAJOR, \ - _IRREMOTEESP8266_VERSION_MINOR, \ - _IRREMOTEESP8266_VERSION_PATCH) -// String version -#define _IRREMOTEESP8266_VERSION_STR MKSTR(_IRREMOTEESP8266_VERSION_MAJOR) "." \ - MKSTR(_IRREMOTEESP8266_VERSION_MINOR) "." \ - MKSTR(_IRREMOTEESP8266_VERSION_PATCH) -// String version (DEPRECATED) -#define _IRREMOTEESP8266_VERSION_ _IRREMOTEESP8266_VERSION_STR - -// Set the language & locale for the library. See the `locale` dir for options. -#ifndef _IR_LOCALE_ -#define _IR_LOCALE_ en-AU -#endif // _IR_LOCALE_ - -// Do we enable all the protocols by default (true), or disable them (false)? -// This allows users of the library to disable or enable all protocols at -// compile-time with `-D_IR_ENABLE_DEFAULT_=true` or -// `-D_IR_ENABLE_DEFAULT_=false` compiler flags respectively. -// Everything is included by default. -// e.g. If you only want to enable use of he NEC protocol to save program space, -// you would use something like: -// `-D_IR_ENABLE_DEFAULT_=false -DDECODE_NEC=true -DSEND_NEC=true` -// -// or alter your 'platform.ini' file accordingly: -// ``` -// build_flags = -D_IR_ENABLE_DEFAULT_=false -// -DDECODE_NEC=true -// -DSEND_NEC=true -// ``` -// If you want to enable support for every protocol *except* _decoding_ the -// Kelvinator protocol, you would use: -// `-DDECODE_KELVINATOR=false` -#ifndef _IR_ENABLE_DEFAULT_ -#define _IR_ENABLE_DEFAULT_ true // Unless set externally, the default is on. -#endif // _IR_ENABLE_DEFAULT_ - -// Supported IR protocols -// Each protocol you include costs memory and, during decode, costs time -// Disable (set to false) all the protocols you do not need/want! -// The Air Conditioner protocols are the most expensive memory-wise. -// - -// Semi-unique code for unknown messages -#ifndef DECODE_HASH -#define DECODE_HASH _IR_ENABLE_DEFAULT_ -#endif // DECODE_HASH - -#ifndef SEND_RAW -#define SEND_RAW _IR_ENABLE_DEFAULT_ -#endif // SEND_RAW - -#ifndef DECODE_NEC -#define DECODE_NEC _IR_ENABLE_DEFAULT_ -#endif // DECODE_NEC -#ifndef SEND_NEC -#define SEND_NEC _IR_ENABLE_DEFAULT_ -#endif // SEND_NEC - -#ifndef DECODE_SHERWOOD -#define DECODE_SHERWOOD false // Not applicable. Actually is DECODE_NEC -#endif // DECODE_SHERWOOD -#ifndef SEND_SHERWOOD -#define SEND_SHERWOOD _IR_ENABLE_DEFAULT_ -#endif // SEND_SHERWOOD - -#ifndef DECODE_RC5 -#define DECODE_RC5 _IR_ENABLE_DEFAULT_ -#endif // DECODE_RC5 -#ifndef SEND_RC5 -#define SEND_RC5 _IR_ENABLE_DEFAULT_ -#endif // SEND_RC5 - -#ifndef DECODE_RC6 -#define DECODE_RC6 _IR_ENABLE_DEFAULT_ -#endif // DECODE_RC6 -#ifndef SEND_RC6 -#define SEND_RC6 _IR_ENABLE_DEFAULT_ -#endif // SEND_RC6 - -#ifndef DECODE_RCMM -#define DECODE_RCMM _IR_ENABLE_DEFAULT_ -#endif // DECODE_RCMM -#ifndef SEND_RCMM -#define SEND_RCMM _IR_ENABLE_DEFAULT_ -#endif // SEND_RCMM - -#ifndef DECODE_SONY -#define DECODE_SONY _IR_ENABLE_DEFAULT_ -#endif // DECODE_SONY -#ifndef SEND_SONY -#define SEND_SONY _IR_ENABLE_DEFAULT_ -#endif // SEND_SONY - -#ifndef DECODE_PANASONIC -#define DECODE_PANASONIC _IR_ENABLE_DEFAULT_ -#endif // DECODE_PANASONIC -#ifndef SEND_PANASONIC -#define SEND_PANASONIC _IR_ENABLE_DEFAULT_ -#endif // SEND_PANASONIC - -#ifndef DECODE_JVC -#define DECODE_JVC _IR_ENABLE_DEFAULT_ -#endif // DECODE_JVC -#ifndef SEND_JVC -#define SEND_JVC _IR_ENABLE_DEFAULT_ -#endif // SEND_JVC - -#ifndef DECODE_SAMSUNG -#define DECODE_SAMSUNG _IR_ENABLE_DEFAULT_ -#endif // DECODE_SAMSUNG -#ifndef SEND_SAMSUNG -#define SEND_SAMSUNG _IR_ENABLE_DEFAULT_ -#endif // SEND_SAMSUNG - -#ifndef DECODE_SAMSUNG36 -#define DECODE_SAMSUNG36 _IR_ENABLE_DEFAULT_ -#endif // DECODE_SAMSUNG36 -#ifndef SEND_SAMSUNG36 -#define SEND_SAMSUNG36 _IR_ENABLE_DEFAULT_ -#endif // SEND_SAMSUNG36 - -#ifndef DECODE_SAMSUNG_AC -#define DECODE_SAMSUNG_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_SAMSUNG_AC -#ifndef SEND_SAMSUNG_AC -#define SEND_SAMSUNG_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_SAMSUNG_AC - -#ifndef DECODE_WHYNTER -#define DECODE_WHYNTER _IR_ENABLE_DEFAULT_ -#endif // DECODE_WHYNTER -#ifndef SEND_WHYNTER -#define SEND_WHYNTER _IR_ENABLE_DEFAULT_ -#endif // SEND_WHYNTER - -#ifndef DECODE_AIWA_RC_T501 -#define DECODE_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ -#endif // DECODE_AIWA_RC_T501 -#ifndef SEND_AIWA_RC_T501 -#define SEND_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ -#endif // SEND_AIWA_RC_T501 - -#ifndef DECODE_LG -#define DECODE_LG _IR_ENABLE_DEFAULT_ -#endif // DECODE_LG -#ifndef SEND_LG -#define SEND_LG _IR_ENABLE_DEFAULT_ -#endif // SEND_LG - -#ifndef DECODE_SANYO -#define DECODE_SANYO _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO -#ifndef SEND_SANYO -#define SEND_SANYO _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO - -#ifndef DECODE_SANYO_AC -#define DECODE_SANYO_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO_AC -#ifndef SEND_SANYO_AC -#define SEND_SANYO_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO_AC - -#ifndef DECODE_SANYO_AC88 -#define DECODE_SANYO_AC88 _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO_AC88 -#ifndef SEND_SANYO_AC88 -#define SEND_SANYO_AC88 _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO_AC88 - -#ifndef DECODE_SANYO_AC152 -#define DECODE_SANYO_AC152 _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO_AC152 -#ifndef SEND_SANYO_AC152 -#define SEND_SANYO_AC152 _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO_AC152 - -#ifndef DECODE_MITSUBISHI -#define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI -#ifndef SEND_MITSUBISHI -#define SEND_MITSUBISHI _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI - -#ifndef DECODE_MITSUBISHI2 -#define DECODE_MITSUBISHI2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI2 -#ifndef SEND_MITSUBISHI2 -#define SEND_MITSUBISHI2 _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI2 - -#ifndef DECODE_DISH -#define DECODE_DISH _IR_ENABLE_DEFAULT_ -#endif // DECODE_DISH -#ifndef SEND_DISH -#define SEND_DISH _IR_ENABLE_DEFAULT_ -#endif // SEND_DISH - -#ifndef DECODE_SHARP -#define DECODE_SHARP _IR_ENABLE_DEFAULT_ -#endif // DECODE_SHARP -#ifndef SEND_SHARP -#define SEND_SHARP _IR_ENABLE_DEFAULT_ -#endif // SEND_SHARP - -#ifndef DECODE_SHARP_AC -#define DECODE_SHARP_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_SHARP_AC -#ifndef SEND_SHARP_AC -#define SEND_SHARP_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_SHARP_AC - -#ifndef DECODE_DENON -#define DECODE_DENON _IR_ENABLE_DEFAULT_ -#endif // DECODE_DENON -#ifndef SEND_DENON -#define SEND_DENON _IR_ENABLE_DEFAULT_ -#endif // SEND_DENON - -#ifndef DECODE_KELVINATOR -#define DECODE_KELVINATOR _IR_ENABLE_DEFAULT_ -#endif // DECODE_KELVINATOR -#ifndef SEND_KELVINATOR -#define SEND_KELVINATOR _IR_ENABLE_DEFAULT_ -#endif // SEND_KELVINATOR - -#ifndef DECODE_MITSUBISHI_AC -#define DECODE_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI_AC -#ifndef SEND_MITSUBISHI_AC -#define SEND_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI_AC - -#ifndef DECODE_MITSUBISHI136 -#define DECODE_MITSUBISHI136 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI136 -#ifndef SEND_MITSUBISHI136 -#define SEND_MITSUBISHI136 _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI136 - -#ifndef DECODE_MITSUBISHI112 -#define DECODE_MITSUBISHI112 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI112 -#ifndef SEND_MITSUBISHI112 -#define SEND_MITSUBISHI112 _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI112 - -#ifndef DECODE_FUJITSU_AC -#define DECODE_FUJITSU_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_FUJITSU_AC -#ifndef SEND_FUJITSU_AC -#define SEND_FUJITSU_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_FUJITSU_AC - -#ifndef DECODE_INAX -#define DECODE_INAX _IR_ENABLE_DEFAULT_ -#endif // DECODE_INAX -#ifndef SEND_INAX -#define SEND_INAX _IR_ENABLE_DEFAULT_ -#endif // SEND_INAX - -#ifndef DECODE_DAIKIN -#define DECODE_DAIKIN _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN -#ifndef SEND_DAIKIN -#define SEND_DAIKIN _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN - -#ifndef DECODE_COOLIX -#define DECODE_COOLIX _IR_ENABLE_DEFAULT_ -#endif // DECODE_COOLIX -#ifndef SEND_COOLIX -#define SEND_COOLIX _IR_ENABLE_DEFAULT_ -#endif // SEND_COOLIX - -#ifndef DECODE_COOLIX48 -#define DECODE_COOLIX48 _IR_ENABLE_DEFAULT_ -#endif // DECODE_COOLIX48 -#ifndef SEND_COOLIX48 -#define SEND_COOLIX48 _IR_ENABLE_DEFAULT_ -#endif // SEND_COOLIX48 - -#ifndef DECODE_GLOBALCACHE -#define DECODE_GLOBALCACHE false // Not applicable. -#endif // DECODE_GLOBALCACHE -#ifndef SEND_GLOBALCACHE -#define SEND_GLOBALCACHE _IR_ENABLE_DEFAULT_ -#endif // SEND_GLOBALCACHE - -#ifndef DECODE_GOODWEATHER -#define DECODE_GOODWEATHER _IR_ENABLE_DEFAULT_ -#endif // DECODE_GOODWEATHER -#ifndef SEND_GOODWEATHER -#define SEND_GOODWEATHER _IR_ENABLE_DEFAULT_ -#endif // SEND_GOODWEATHER - -#ifndef DECODE_GREE -#define DECODE_GREE _IR_ENABLE_DEFAULT_ -#endif // DECODE_GREE -#ifndef SEND_GREE -#define SEND_GREE _IR_ENABLE_DEFAULT_ -#endif // SEND_GREE - -#ifndef DECODE_PRONTO -#define DECODE_PRONTO false // Not applicable. -#endif // DECODE_PRONTO -#ifndef SEND_PRONTO -#define SEND_PRONTO _IR_ENABLE_DEFAULT_ -#endif // SEND_PRONTO - -#ifndef DECODE_ARGO -#define DECODE_ARGO _IR_ENABLE_DEFAULT_ -#endif // DECODE_ARGO -#ifndef SEND_ARGO -#define SEND_ARGO _IR_ENABLE_DEFAULT_ -#endif // SEND_ARGO - -#ifndef DECODE_TROTEC -#define DECODE_TROTEC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TROTEC -#ifndef SEND_TROTEC -#define SEND_TROTEC _IR_ENABLE_DEFAULT_ -#endif // SEND_TROTEC - -#ifndef DECODE_TROTEC_3550 -#define DECODE_TROTEC_3550 _IR_ENABLE_DEFAULT_ -#endif // DECODE_TROTEC_3550 -#ifndef SEND_TROTEC_3550 -#define SEND_TROTEC_3550 _IR_ENABLE_DEFAULT_ -#endif // SEND_TROTEC_3550 - -#ifndef DECODE_NIKAI -#define DECODE_NIKAI _IR_ENABLE_DEFAULT_ -#endif // DECODE_NIKAI -#ifndef SEND_NIKAI -#define SEND_NIKAI _IR_ENABLE_DEFAULT_ -#endif // SEND_NIKAI - -#ifndef DECODE_TOSHIBA_AC -#define DECODE_TOSHIBA_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TOSHIBA_AC -#ifndef SEND_TOSHIBA_AC -#define SEND_TOSHIBA_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TOSHIBA_AC - -#ifndef DECODE_MAGIQUEST -#define DECODE_MAGIQUEST _IR_ENABLE_DEFAULT_ -#endif // DECODE_MAGIQUEST -#ifndef SEND_MAGIQUEST -#define SEND_MAGIQUEST _IR_ENABLE_DEFAULT_ -#endif // SEND_MAGIQUEST - -#ifndef DECODE_MIDEA -#define DECODE_MIDEA _IR_ENABLE_DEFAULT_ -#endif // DECODE_MIDEA -#ifndef SEND_MIDEA -#define SEND_MIDEA _IR_ENABLE_DEFAULT_ -#endif // SEND_MIDEA - -#ifndef DECODE_MIDEA24 -#define DECODE_MIDEA24 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MIDEA24 -#ifndef SEND_MIDEA24 -#define SEND_MIDEA24 _IR_ENABLE_DEFAULT_ -#endif // SEND_MIDEA24 - -#ifndef DECODE_LASERTAG -#define DECODE_LASERTAG _IR_ENABLE_DEFAULT_ -#endif // DECODE_LASERTAG -#ifndef SEND_LASERTAG -#define SEND_LASERTAG _IR_ENABLE_DEFAULT_ -#endif // SEND_LASERTAG - -#ifndef DECODE_CARRIER_AC -#define DECODE_CARRIER_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC -#ifndef SEND_CARRIER_AC -#define SEND_CARRIER_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC - -#ifndef DECODE_CARRIER_AC40 -#define DECODE_CARRIER_AC40 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC40 -#ifndef SEND_CARRIER_AC40 -#define SEND_CARRIER_AC40 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC40 - -#ifndef DECODE_CARRIER_AC64 -#define DECODE_CARRIER_AC64 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC64 -#ifndef SEND_CARRIER_AC64 -#define SEND_CARRIER_AC64 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC64 - -#ifndef DECODE_CARRIER_AC128 -#define DECODE_CARRIER_AC128 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC128 -#ifndef SEND_CARRIER_AC128 -#define SEND_CARRIER_AC128 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC128 - -#ifndef DECODE_HAIER_AC -#define DECODE_HAIER_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC -#ifndef SEND_HAIER_AC -#define SEND_HAIER_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC - -#ifndef DECODE_HITACHI_AC -#define DECODE_HITACHI_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC -#ifndef SEND_HITACHI_AC -#define SEND_HITACHI_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC - -#ifndef DECODE_HITACHI_AC1 -#define DECODE_HITACHI_AC1 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC1 -#ifndef SEND_HITACHI_AC1 -#define SEND_HITACHI_AC1 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC1 - -#ifndef DECODE_HITACHI_AC2 -#define DECODE_HITACHI_AC2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC2 -#ifndef SEND_HITACHI_AC2 -#define SEND_HITACHI_AC2 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC2 - -#ifndef DECODE_HITACHI_AC3 -#define DECODE_HITACHI_AC3 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC3 -#ifndef SEND_HITACHI_AC3 -#define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC3 - -#ifndef DECODE_HITACHI_AC264 -#define DECODE_HITACHI_AC264 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC264 -#ifndef SEND_HITACHI_AC264 -#define SEND_HITACHI_AC264 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC264 - -#ifndef DECODE_HITACHI_AC296 -#define DECODE_HITACHI_AC296 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC296 -#ifndef SEND_HITACHI_AC296 -#define SEND_HITACHI_AC296 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC296 - -#ifndef DECODE_HITACHI_AC344 -#define DECODE_HITACHI_AC344 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC344 -#ifndef SEND_HITACHI_AC344 -#define SEND_HITACHI_AC344 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC344 - -#ifndef DECODE_HITACHI_AC424 -#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC424 -#ifndef SEND_HITACHI_AC424 -#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC424 - -#ifndef DECODE_GICABLE -#define DECODE_GICABLE _IR_ENABLE_DEFAULT_ -#endif // DECODE_GICABLE -#ifndef SEND_GICABLE -#define SEND_GICABLE _IR_ENABLE_DEFAULT_ -#endif // SEND_GICABLE - -#ifndef DECODE_HAIER_AC_YRW02 -#define DECODE_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC_YRW02 -#ifndef SEND_HAIER_AC_YRW02 -#define SEND_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC_YRW02 - -#ifndef DECODE_WHIRLPOOL_AC -#define DECODE_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_WHIRLPOOL_AC -#ifndef SEND_WHIRLPOOL_AC -#define SEND_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_WHIRLPOOL_AC - -#ifndef DECODE_LUTRON -#define DECODE_LUTRON _IR_ENABLE_DEFAULT_ -#endif // DECODE_LUTRON -#ifndef SEND_LUTRON -#define SEND_LUTRON _IR_ENABLE_DEFAULT_ -#endif // SEND_LUTRON - -#ifndef DECODE_ELECTRA_AC -#define DECODE_ELECTRA_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_ELECTRA_AC -#ifndef SEND_ELECTRA_AC -#define SEND_ELECTRA_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_ELECTRA_AC - -#ifndef DECODE_PANASONIC_AC -#define DECODE_PANASONIC_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_PANASONIC_AC -#ifndef SEND_PANASONIC_AC -#define SEND_PANASONIC_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_PANASONIC_AC - -#ifndef DECODE_PANASONIC_AC32 -#define DECODE_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ -#endif // DECODE_PANASONIC_AC32 -#ifndef SEND_PANASONIC_AC32 -#define SEND_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ -#endif // SEND_PANASONIC_AC32 - -#ifndef DECODE_MWM -#define DECODE_MWM _IR_ENABLE_DEFAULT_ -#endif // DECODE_MWM -#ifndef SEND_MWM -#define SEND_MWM _IR_ENABLE_DEFAULT_ -#endif // SEND_MWM - -#ifndef DECODE_PIONEER -#define DECODE_PIONEER _IR_ENABLE_DEFAULT_ -#endif // DECODE_PIONEER -#ifndef SEND_PIONEER -#define SEND_PIONEER _IR_ENABLE_DEFAULT_ -#endif // SEND_PIONEER - -#ifndef DECODE_DAIKIN2 -#define DECODE_DAIKIN2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN2 -#ifndef SEND_DAIKIN2 -#define SEND_DAIKIN2 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN2 - -#ifndef DECODE_VESTEL_AC -#define DECODE_VESTEL_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_VESTEL_AC -#ifndef SEND_VESTEL_AC -#define SEND_VESTEL_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_VESTEL_AC - -#ifndef DECODE_TECO -#define DECODE_TECO _IR_ENABLE_DEFAULT_ -#endif // DECODE_TECO -#ifndef SEND_TECO -#define SEND_TECO _IR_ENABLE_DEFAULT_ -#endif // SEND_TECO - -#ifndef DECODE_TCL96AC -#define DECODE_TCL96AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TCL96AC -#ifndef SEND_TCL96AC -#define SEND_TCL96AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TCL96AC - -#ifndef DECODE_TCL112AC -#define DECODE_TCL112AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TCL112AC -#ifndef SEND_TCL112AC -#define SEND_TCL112AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TCL112AC - -#ifndef DECODE_LEGOPF -#define DECODE_LEGOPF _IR_ENABLE_DEFAULT_ -#endif // DECODE_LEGOPF -#ifndef SEND_LEGOPF -#define SEND_LEGOPF _IR_ENABLE_DEFAULT_ -#endif // SEND_LEGOPF - -#ifndef DECODE_MITSUBISHIHEAVY -#define DECODE_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHIHEAVY -#ifndef SEND_MITSUBISHIHEAVY -#define SEND_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHIHEAVY - -#ifndef DECODE_DAIKIN216 -#define DECODE_DAIKIN216 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN216 -#ifndef SEND_DAIKIN216 -#define SEND_DAIKIN216 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN216 - -#ifndef DECODE_DAIKIN160 -#define DECODE_DAIKIN160 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN160 -#ifndef SEND_DAIKIN160 -#define SEND_DAIKIN160 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN160 - -#ifndef DECODE_NEOCLIMA -#define DECODE_NEOCLIMA _IR_ENABLE_DEFAULT_ -#endif // DECODE_NEOCLIMA -#ifndef SEND_NEOCLIMA -#define SEND_NEOCLIMA _IR_ENABLE_DEFAULT_ -#endif // SEND_NEOCLIMA - -#ifndef DECODE_DAIKIN176 -#define DECODE_DAIKIN176 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN176 -#ifndef SEND_DAIKIN176 -#define SEND_DAIKIN176 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN176 - -#ifndef DECODE_DAIKIN128 -#define DECODE_DAIKIN128 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN128 -#ifndef SEND_DAIKIN128 -#define SEND_DAIKIN128 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN128 - -#ifndef DECODE_AMCOR -#define DECODE_AMCOR _IR_ENABLE_DEFAULT_ -#endif // DECODE_AMCOR -#ifndef SEND_AMCOR -#define SEND_AMCOR _IR_ENABLE_DEFAULT_ -#endif // SEND_AMCOR - -#ifndef DECODE_DAIKIN152 -#define DECODE_DAIKIN152 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN152 -#ifndef SEND_DAIKIN152 -#define SEND_DAIKIN152 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN152 - -#ifndef DECODE_EPSON -#define DECODE_EPSON _IR_ENABLE_DEFAULT_ -#endif // DECODE_EPSON -#ifndef SEND_EPSON -#define SEND_EPSON _IR_ENABLE_DEFAULT_ -#endif // SEND_EPSON - -#ifndef DECODE_SYMPHONY -#define DECODE_SYMPHONY _IR_ENABLE_DEFAULT_ -#endif // DECODE_SYMPHONY -#ifndef SEND_SYMPHONY -#define SEND_SYMPHONY _IR_ENABLE_DEFAULT_ -#endif // SEND_SYMPHONY - -#ifndef DECODE_DAIKIN64 -#define DECODE_DAIKIN64 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN64 -#ifndef SEND_DAIKIN64 -#define SEND_DAIKIN64 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN64 - -#ifndef DECODE_AIRWELL -#define DECODE_AIRWELL _IR_ENABLE_DEFAULT_ -#endif // DECODE_AIRWELL -#ifndef SEND_AIRWELL -#define SEND_AIRWELL _IR_ENABLE_DEFAULT_ -#endif // SEND_AIRWELL - -#ifndef DECODE_DELONGHI_AC -#define DECODE_DELONGHI_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_DELONGHI_AC -#ifndef SEND_DELONGHI_AC -#define SEND_DELONGHI_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_DELONGHI_AC - -#ifndef DECODE_DOSHISHA -#define DECODE_DOSHISHA _IR_ENABLE_DEFAULT_ -#endif // DECODE_DOSHISHA -#ifndef SEND_DOSHISHA -#define SEND_DOSHISHA _IR_ENABLE_DEFAULT_ -#endif // SEND_DOSHISHA - -#ifndef DECODE_MULTIBRACKETS -#define DECODE_MULTIBRACKETS _IR_ENABLE_DEFAULT_ -#endif // DECODE_MULTIBRACKETS -#ifndef SEND_MULTIBRACKETS -#define SEND_MULTIBRACKETS _IR_ENABLE_DEFAULT_ -#endif // SEND_MULTIBRACKETS - -#ifndef DECODE_TECHNIBEL_AC -#define DECODE_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TECHNIBEL_AC -#ifndef SEND_TECHNIBEL_AC -#define SEND_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TECHNIBEL_AC - -#ifndef DECODE_CORONA_AC -#define DECODE_CORONA_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_CORONA_AC -#ifndef SEND_CORONA_AC -#define SEND_CORONA_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_CORONA_AC - -#ifndef DECODE_ZEPEAL -#define DECODE_ZEPEAL _IR_ENABLE_DEFAULT_ -#endif // DECODE_ZEPEAL -#ifndef SEND_ZEPEAL -#define SEND_ZEPEAL _IR_ENABLE_DEFAULT_ -#endif // SEND_ZEPEAL - -#ifndef DECODE_VOLTAS -#define DECODE_VOLTAS _IR_ENABLE_DEFAULT_ -#endif // DECODE_VOLTAS -#ifndef SEND_VOLTAS -#define SEND_VOLTAS _IR_ENABLE_DEFAULT_ -#endif // SEND_VOLTAS - -#ifndef DECODE_METZ -#define DECODE_METZ _IR_ENABLE_DEFAULT_ -#endif // DECODE_METZ -#ifndef SEND_METZ -#define SEND_METZ _IR_ENABLE_DEFAULT_ -#endif // SEND_METZ - -#ifndef DECODE_TRANSCOLD -#define DECODE_TRANSCOLD _IR_ENABLE_DEFAULT_ -#endif // DECODE_TRANSCOLD -#ifndef SEND_TRANSCOLD -#define SEND_TRANSCOLD _IR_ENABLE_DEFAULT_ -#endif // SEND_TRANSCOLD - -#ifndef DECODE_MIRAGE -#define DECODE_MIRAGE _IR_ENABLE_DEFAULT_ -#endif // DECODE_MIRAGE -#ifndef SEND_MIRAGE -#define SEND_MIRAGE _IR_ENABLE_DEFAULT_ -#endif // SEND_MIRAGE - -#ifndef DECODE_ELITESCREENS -#define DECODE_ELITESCREENS _IR_ENABLE_DEFAULT_ -#endif // DECODE_ELITESCREENS -#ifndef SEND_ELITESCREENS -#define SEND_ELITESCREENS _IR_ENABLE_DEFAULT_ -#endif // SEND_ELITESCREENS - -#ifndef DECODE_MILESTAG2 -#define DECODE_MILESTAG2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MILESTAG2 -#ifndef SEND_MILESTAG2 -#define SEND_MILESTAG2 _IR_ENABLE_DEFAULT_ -#endif // SEND_MILESTAG2 - -#ifndef DECODE_ECOCLIM -#define DECODE_ECOCLIM _IR_ENABLE_DEFAULT_ -#endif // DECODE_ECOCLIM -#ifndef SEND_ECOCLIM -#define SEND_ECOCLIM _IR_ENABLE_DEFAULT_ -#endif // SEND_ECOCLIM - -#ifndef DECODE_XMP -#define DECODE_XMP _IR_ENABLE_DEFAULT_ -#endif // DECODE_XMP -#ifndef SEND_XMP -#define SEND_XMP _IR_ENABLE_DEFAULT_ -#endif // SEND_XMP - -#ifndef DECODE_TRUMA -#define DECODE_TRUMA _IR_ENABLE_DEFAULT_ -#endif // DECODE_TRUMA -#ifndef SEND_TRUMA -#define SEND_TRUMA _IR_ENABLE_DEFAULT_ -#endif // SEND_TRUMA - -#ifndef DECODE_HAIER_AC176 -#define DECODE_HAIER_AC176 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC176 -#ifndef SEND_HAIER_AC176 -#define SEND_HAIER_AC176 _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC176 - -#ifndef DECODE_TEKNOPOINT -#define DECODE_TEKNOPOINT _IR_ENABLE_DEFAULT_ -#endif // DECODE_TEKNOPOINT -#ifndef SEND_TEKNOPOINT -#define SEND_TEKNOPOINT _IR_ENABLE_DEFAULT_ -#endif // SEND_TEKNOPOINT - -#ifndef DECODE_KELON -#define DECODE_KELON _IR_ENABLE_DEFAULT_ -#endif // DECODE_KELON -#ifndef SEND_KELON -#define SEND_KELON _IR_ENABLE_DEFAULT_ -#endif // SEND_KELON - -#ifndef DECODE_BOSE -#define DECODE_BOSE _IR_ENABLE_DEFAULT_ -#endif // DECODE_BOSE -#ifndef SEND_BOSE -#define SEND_BOSE _IR_ENABLE_DEFAULT_ -#endif // SEND_BOSE - -#ifndef DECODE_ARRIS -#define DECODE_ARRIS _IR_ENABLE_DEFAULT_ -#endif // DECODE_ARRIS -#ifndef SEND_ARRIS -#define SEND_ARRIS _IR_ENABLE_DEFAULT_ -#endif // SEND_ARRIS - -#ifndef DECODE_RHOSS -#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ -#endif // DECODE_RHOSS -#ifndef SEND_RHOSS -#define SEND_RHOSS _IR_ENABLE_DEFAULT_ -#endif // SEND_RHOSS - -#ifndef DECODE_AIRTON -#define DECODE_AIRTON _IR_ENABLE_DEFAULT_ -#endif // DECODE_AIRTON -#ifndef SEND_AIRTON -#define SEND_AIRTON _IR_ENABLE_DEFAULT_ -#endif // SEND_AIRTON - -#ifndef DECODE_KELON168 -#define DECODE_KELON168 _IR_ENABLE_DEFAULT_ -#endif // DECODE_KELON168 -#ifndef SEND_KELON168 -#define SEND_KELON168 _IR_ENABLE_DEFAULT_ -#endif // SEND_KELON168 - -#ifndef DECODE_DAIKIN200 -#define DECODE_DAIKIN200 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN200 -#ifndef SEND_DAIKIN200 -#define SEND_DAIKIN200 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN200 - -#ifndef DECODE_HAIER_AC160 -#define DECODE_HAIER_AC160 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC160 -#ifndef SEND_HAIER_AC160 -#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC160 - -#ifndef DECODE_TOTO -#define DECODE_TOTO _IR_ENABLE_DEFAULT_ -#endif // DECODE_TOTO -#ifndef SEND_TOTO -#define SEND_TOTO _IR_ENABLE_DEFAULT_ -#endif // SEND_TOTO - -#ifndef DECODE_CLIMABUTLER -#define DECODE_CLIMABUTLER _IR_ENABLE_DEFAULT_ -#endif // DECODE_CLIMABUTLER -#ifndef SEND_CLIMABUTLER -#define SEND_CLIMABUTLER _IR_ENABLE_DEFAULT_ -#endif // SEND_CLIMABUTLER - -#ifndef DECODE_BOSCH144 -#define DECODE_BOSCH144 _IR_ENABLE_DEFAULT_ -#endif // DECODE_BOSCH144 -#ifndef SEND_BOSCH144 -#define SEND_BOSCH144 _IR_ENABLE_DEFAULT_ -#endif // SEND_BOSCH144 - -#ifndef DECODE_DAIKIN312 -#define DECODE_DAIKIN312 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN312 -#ifndef SEND_DAIKIN312 -#define SEND_DAIKIN312 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN312 - -#ifndef DECODE_GORENJE -#define DECODE_GORENJE _IR_ENABLE_DEFAULT_ -#endif // DECODE_GORENJE -#ifndef SEND_GORENJE -#define SEND_GORENJE _IR_ENABLE_DEFAULT_ -#endif // SEND_GORENJE - -#ifndef DECODE_WOWWEE -#define DECODE_WOWWEE _IR_ENABLE_DEFAULT_ -#endif // DECODE_WOWWEE -#ifndef SEND_WOWWEE -#define SEND_WOWWEE _IR_ENABLE_DEFAULT_ -#endif // SEND_WOWWEE - -#ifndef DECODE_CARRIER_AC84 -#define DECODE_CARRIER_AC84 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC84 -#ifndef SEND_CARRIER_AC84 -#define SEND_CARRIER_AC84 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC84 - -#ifndef DECODE_YORK -#define DECODE_YORK _IR_ENABLE_DEFAULT_ -#endif // DECODE_YORK -#ifndef SEND_YORK -#define SEND_YORK _IR_ENABLE_DEFAULT_ -#endif // SEND_YORK - -#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ - DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ - DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ - DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ - DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ - DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ - DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ - DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ - DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ - DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \ - DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \ - DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \ - DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \ - DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \ - DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \ - DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \ - DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \ - DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \ - DECODE_CARRIER_AC84 || DECODE_YORK || \ - false) - // Add any DECODE to the above if it uses result->state (see kStateSizeMax) - // you might also want to add the protocol to hasACState function -#define DECODE_AC true // We need some common infrastructure for decoding A/Cs. -#else -#define DECODE_AC false // We don't need that infrastructure. -#endif - -// Use millisecond 'delay()' calls where we can to avoid tripping the WDT. -// Note: If you plan to send IR messages in the callbacks of the AsyncWebserver -// library, you need to set ALLOW_DELAY_CALLS to false. -// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/430 -#ifndef ALLOW_DELAY_CALLS -#define ALLOW_DELAY_CALLS true -#endif // ALLOW_DELAY_CALLS - -// Enable a run-time settable high-pass filter on captured data **before** -// trying any protocol decoding. -// i.e. Try to remove/merge any really short pulses detected in the raw data. -// Note: Even when this option is enabled, it is _off_ by default, and requires -// a user who knows what they are doing to enable it. -// The option to disable this feature is here if your project is _really_ -// tight on resources. i.e. Saves a small handful of bytes and cpu time. -// WARNING: If you use this feature at runtime, you can no longer trust the -// **raw** data captured. It will now have been slightly **cooked**! -// DANGER: If you set the `noise_floor` value too high, it **WILL** break -// decoding of some protocols. You have been warned. Here Be Dragons! -// -// See: `irrecv::decode()` in IRrecv.cpp for more info. -#ifndef ENABLE_NOISE_FILTER_OPTION -#define ENABLE_NOISE_FILTER_OPTION true -#endif // ENABLE_NOISE_FILTER_OPTION - -/// Enumerator for defining and numbering of supported IR protocol. -/// @note Always add to the end of the list and should never remove entries -/// or change order. Projects may save the type number for later usage -/// so numbering should always stay the same. -enum decode_type_t { - UNKNOWN = -1, - UNUSED = 0, - RC5, - RC6, - NEC, - SONY, - PANASONIC, // (5) - JVC, - SAMSUNG, - WHYNTER, - AIWA_RC_T501, - LG, // (10) - SANYO, - MITSUBISHI, - DISH, - SHARP, - COOLIX, // (15) - DAIKIN, - DENON, - KELVINATOR, - SHERWOOD, - MITSUBISHI_AC, // (20) - RCMM, - SANYO_LC7461, - RC5X, - GREE, - PRONTO, // Technically not a protocol, but an encoding. (25) - NEC_LIKE, - ARGO, - TROTEC, - NIKAI, - RAW, // Technically not a protocol, but an encoding. (30) - GLOBALCACHE, // Technically not a protocol, but an encoding. - TOSHIBA_AC, - FUJITSU_AC, - MIDEA, - MAGIQUEST, // (35) - LASERTAG, - CARRIER_AC, - HAIER_AC, - MITSUBISHI2, - HITACHI_AC, // (40) - HITACHI_AC1, - HITACHI_AC2, - GICABLE, - HAIER_AC_YRW02, - WHIRLPOOL_AC, // (45) - SAMSUNG_AC, - LUTRON, - ELECTRA_AC, - PANASONIC_AC, - PIONEER, // (50) - LG2, - MWM, - DAIKIN2, - VESTEL_AC, - TECO, // (55) - SAMSUNG36, - TCL112AC, - LEGOPF, - MITSUBISHI_HEAVY_88, - MITSUBISHI_HEAVY_152, // 60 - DAIKIN216, - SHARP_AC, - GOODWEATHER, - INAX, - DAIKIN160, // 65 - NEOCLIMA, - DAIKIN176, - DAIKIN128, - AMCOR, - DAIKIN152, // 70 - MITSUBISHI136, - MITSUBISHI112, - HITACHI_AC424, - SONY_38K, - EPSON, // 75 - SYMPHONY, - HITACHI_AC3, - DAIKIN64, - AIRWELL, - DELONGHI_AC, // 80 - DOSHISHA, - MULTIBRACKETS, - CARRIER_AC40, - CARRIER_AC64, - HITACHI_AC344, // 85 - CORONA_AC, - MIDEA24, - ZEPEAL, - SANYO_AC, - VOLTAS, // 90 - METZ, - TRANSCOLD, - TECHNIBEL_AC, - MIRAGE, - ELITESCREENS, // 95 - PANASONIC_AC32, - MILESTAG2, - ECOCLIM, - XMP, - TRUMA, // 100 - HAIER_AC176, - TEKNOPOINT, - KELON, - TROTEC_3550, - SANYO_AC88, // 105 - BOSE, - ARRIS, - RHOSS, - AIRTON, - COOLIX48, // 110 - HITACHI_AC264, - KELON168, - HITACHI_AC296, - DAIKIN200, - HAIER_AC160, // 115 - CARRIER_AC128, - TOTO, - CLIMABUTLER, - TCL96AC, - BOSCH144, // 120 - SANYO_AC152, - DAIKIN312, - GORENJE, - WOWWEE, - CARRIER_AC84, // 125 - YORK, - // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = YORK, -}; - -// Message lengths & required repeat values -const uint16_t kNoRepeat = 0; -const uint16_t kSingleRepeat = 1; - -const uint16_t kAirtonBits = 56; -const uint16_t kAirtonDefaultRepeat = kNoRepeat; -const uint16_t kAirwellBits = 34; -const uint16_t kAirwellMinRepeats = 2; -const uint16_t kAiwaRcT501Bits = 15; -const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; -const uint16_t kAlokaBits = 32; -const uint16_t kAmcorStateLength = 8; -const uint16_t kAmcorBits = kAmcorStateLength * 8; -const uint16_t kAmcorDefaultRepeat = kSingleRepeat; -const uint16_t kArgoStateLength = 12; -const uint16_t kArgoShortStateLength = 4; -const uint16_t kArgoBits = kArgoStateLength * 8; -const uint16_t kArgoShortBits = kArgoShortStateLength * 8; -const uint16_t kArgo3AcControlStateLength = 6; // Bytes -const uint16_t kArgo3iFeelReportStateLength = 2; // Bytes -const uint16_t kArgo3TimerStateLength = 9; // Bytes -const uint16_t kArgo3ConfigStateLength = 4; // Bytes -const uint16_t kArgoDefaultRepeat = kNoRepeat; -const uint16_t kArrisBits = 32; -const uint16_t kBosch144StateLength = 18; -const uint16_t kBosch144Bits = kBosch144StateLength * 8; -const uint16_t kCoolixBits = 24; -const uint16_t kCoolix48Bits = kCoolixBits * 2; -const uint16_t kCoolixDefaultRepeat = kSingleRepeat; -const uint16_t kCarrierAcBits = 32; -const uint16_t kCarrierAcMinRepeat = kNoRepeat; -const uint16_t kCarrierAc40Bits = 40; -const uint16_t kCarrierAc40MinRepeat = 2; -const uint16_t kCarrierAc64Bits = 64; -const uint16_t kCarrierAc64MinRepeat = kNoRepeat; -const uint16_t kCarrierAc84StateLength = 11; -const uint16_t kCarrierAc84Bits = kCarrierAc84StateLength * 8 - 4; -const uint16_t kCarrierAc84MinRepeat = kNoRepeat; -const uint16_t kCarrierAc128StateLength = 16; -const uint16_t kCarrierAc128Bits = kCarrierAc128StateLength * 8; -const uint16_t kCarrierAc128MinRepeat = kNoRepeat; -const uint16_t kCoronaAcStateLengthShort = 7; -const uint16_t kCoronaAcStateLength = kCoronaAcStateLengthShort * 3; -const uint16_t kCoronaAcBitsShort = kCoronaAcStateLengthShort * 8; -const uint16_t kCoronaAcBits = kCoronaAcStateLength * 8; -const uint16_t kDaikinStateLength = 35; -const uint16_t kDaikinBits = kDaikinStateLength * 8; -const uint16_t kDaikinStateLengthShort = kDaikinStateLength - 8; -const uint16_t kDaikinBitsShort = kDaikinStateLengthShort * 8; -const uint16_t kDaikinDefaultRepeat = kNoRepeat; -const uint16_t kDaikin2StateLength = 39; -const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; -const uint16_t kDaikin2DefaultRepeat = kNoRepeat; -const uint16_t kDaikin64Bits = 64; -const uint16_t kDaikin64DefaultRepeat = kNoRepeat; -const uint16_t kDaikin160StateLength = 20; -const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; -const uint16_t kDaikin160DefaultRepeat = kNoRepeat; -const uint16_t kDaikin128StateLength = 16; -const uint16_t kDaikin128Bits = kDaikin128StateLength * 8; -const uint16_t kDaikin128DefaultRepeat = kNoRepeat; -const uint16_t kDaikin152StateLength = 19; -const uint16_t kDaikin152Bits = kDaikin152StateLength * 8; -const uint16_t kDaikin152DefaultRepeat = kNoRepeat; -const uint16_t kDaikin176StateLength = 22; -const uint16_t kDaikin176Bits = kDaikin176StateLength * 8; -const uint16_t kDaikin176DefaultRepeat = kNoRepeat; -const uint16_t kDaikin200StateLength = 25; -const uint16_t kDaikin200Bits = kDaikin200StateLength * 8; -const uint16_t kDaikin200DefaultRepeat = kNoRepeat; -const uint16_t kDaikin216StateLength = 27; -const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; -const uint16_t kDaikin216DefaultRepeat = kNoRepeat; -const uint16_t kDaikin312StateLength = 39; -const uint16_t kDaikin312Bits = kDaikin312StateLength * 8; -const uint16_t kDaikin312DefaultRepeat = kNoRepeat; -const uint16_t kDelonghiAcBits = 64; -const uint16_t kDelonghiAcDefaultRepeat = kNoRepeat; -const uint16_t kTechnibelAcBits = 56; -const uint16_t kTechnibelAcDefaultRepeat = kNoRepeat; -const uint16_t kDenonBits = 15; -const uint16_t kDenon48Bits = 48; -const uint16_t kDenonLegacyBits = 14; -const uint16_t kDishBits = 16; -const uint16_t kDishMinRepeat = 3; -const uint16_t kDoshishaBits = 40; -const uint16_t kEcoclimBits = 56; -const uint16_t kEcoclimShortBits = 15; -const uint16_t kEpsonBits = 32; -const uint16_t kEpsonMinRepeat = 2; -const uint16_t kElectraAcStateLength = 13; -const uint16_t kElectraAcBits = kElectraAcStateLength * 8; -const uint16_t kElectraAcMinRepeat = kNoRepeat; -const uint16_t kEliteScreensBits = 32; -const uint16_t kEliteScreensDefaultRepeat = kSingleRepeat; -const uint16_t kFujitsuAcMinRepeat = kNoRepeat; -const uint16_t kFujitsuAcStateLength = 16; -const uint16_t kFujitsuAcStateLengthShort = 7; -const uint16_t kFujitsuAcBits = kFujitsuAcStateLength * 8; -const uint16_t kFujitsuAcMinBits = (kFujitsuAcStateLengthShort - 1) * 8; -const uint16_t kGicableBits = 16; -const uint16_t kGicableMinRepeat = kSingleRepeat; -const uint16_t kGoodweatherBits = 48; -const uint16_t kGoodweatherMinRepeat = kNoRepeat; -const uint16_t kGorenjeBits = 8; -const uint16_t kGreeStateLength = 8; -const uint16_t kGreeBits = kGreeStateLength * 8; -const uint16_t kGreeDefaultRepeat = kNoRepeat; -const uint16_t kHaierACStateLength = 9; -const uint16_t kHaierACBits = kHaierACStateLength * 8; -const uint16_t kHaierAcDefaultRepeat = kNoRepeat; -const uint16_t kHaierACYRW02StateLength = 14; -const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8; -const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat; -const uint16_t kHaierAC160StateLength = 20; -const uint16_t kHaierAC160Bits = kHaierAC160StateLength * 8; -const uint16_t kHaierAc160DefaultRepeat = kNoRepeat; -const uint16_t kHaierAC176StateLength = 22; -const uint16_t kHaierAC176Bits = kHaierAC176StateLength * 8; -const uint16_t kHaierAc176DefaultRepeat = kNoRepeat; -const uint16_t kHitachiAcStateLength = 28; -const uint16_t kHitachiAcBits = kHitachiAcStateLength * 8; -const uint16_t kHitachiAcDefaultRepeat = kNoRepeat; -const uint16_t kHitachiAc1StateLength = 13; -const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; -const uint16_t kHitachiAc2StateLength = 53; -const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; -const uint16_t kHitachiAc3StateLength = 27; -const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8; -const uint16_t kHitachiAc3MinStateLength = 15; -const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8; -const uint16_t kHitachiAc264StateLength = 33; -const uint16_t kHitachiAc264Bits = kHitachiAc264StateLength * 8; -const uint16_t kHitachiAc296StateLength = 37; -const uint16_t kHitachiAc296Bits = kHitachiAc296StateLength * 8; -const uint16_t kHitachiAc344StateLength = 43; -const uint16_t kHitachiAc344Bits = kHitachiAc344StateLength * 8; -const uint16_t kHitachiAc424StateLength = 53; -const uint16_t kHitachiAc424Bits = kHitachiAc424StateLength * 8; -const uint16_t kInaxBits = 24; -const uint16_t kInaxMinRepeat = kSingleRepeat; -const uint16_t kJvcBits = 16; -const uint16_t kKelonBits = 48; -const uint16_t kKelon168StateLength = 21; -const uint16_t kKelon168Bits = kKelon168StateLength * 8; -const uint16_t kKelvinatorStateLength = 16; -const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; -const uint16_t kKelvinatorDefaultRepeat = kNoRepeat; -const uint16_t kLasertagBits = 13; -const uint16_t kLasertagMinRepeat = kNoRepeat; -const uint16_t kLegoPfBits = 16; -const uint16_t kLegoPfMinRepeat = kNoRepeat; -const uint16_t kLgBits = 28; -const uint16_t kLg32Bits = 32; -const uint16_t kLgDefaultRepeat = kNoRepeat; -const uint16_t kLutronBits = 35; -const uint16_t kMagiquestBits = 56; -const uint16_t kMetzBits = 19; -const uint16_t kMetzMinRepeat = kNoRepeat; -const uint16_t kMideaBits = 48; -const uint16_t kMideaMinRepeat = kNoRepeat; -const uint16_t kMidea24Bits = 24; -const uint16_t kMidea24MinRepeat = kSingleRepeat; -const uint16_t kMirageStateLength = 15; -const uint16_t kMirageBits = kMirageStateLength * 8; -const uint16_t kMirageMinRepeat = kNoRepeat; -const uint16_t kMitsubishiBits = 16; -// TODO(anyone): Verify that the Mitsubishi repeat is really needed. -// Based on marcosamarinho's code. -const uint16_t kMitsubishiMinRepeat = kSingleRepeat; -const uint16_t kMitsubishiACStateLength = 18; -const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8; -const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; -const uint16_t kMitsubishi136StateLength = 17; -const uint16_t kMitsubishi136Bits = kMitsubishi136StateLength * 8; -const uint16_t kMitsubishi136MinRepeat = kNoRepeat; -const uint16_t kMitsubishi112StateLength = 14; -const uint16_t kMitsubishi112Bits = kMitsubishi112StateLength * 8; -const uint16_t kMitsubishi112MinRepeat = kNoRepeat; -const uint16_t kMitsubishiHeavy88StateLength = 11; -const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8; -const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; -const uint16_t kMitsubishiHeavy152StateLength = 19; -const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; -const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; -const uint16_t kMultibracketsBits = 8; -const uint16_t kMultibracketsDefaultRepeat = kSingleRepeat; -const uint16_t kNikaiBits = 24; -const uint16_t kNECBits = 32; -const uint16_t kNeoclimaStateLength = 12; -const uint16_t kNeoclimaBits = kNeoclimaStateLength * 8; -const uint16_t kNeoclimaMinRepeat = kNoRepeat; -const uint16_t kPanasonicBits = 48; -const uint32_t kPanasonicManufacturer = 0x4004; -const uint32_t kPanasonic40Manufacturer = 0x34; -const uint16_t kPanasonic40Bits = 40; -const uint16_t kPanasonicAcStateLength = 27; -const uint16_t kPanasonicAcStateShortLength = 16; -const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8; -const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8; -const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat; -const uint16_t kPanasonicAc32Bits = 32; -const uint16_t kPioneerBits = 64; -const uint16_t kProntoMinLength = 6; -const uint16_t kRC5RawBits = 14; -const uint16_t kRC5Bits = kRC5RawBits - 2; -const uint16_t kRC5XBits = kRC5RawBits - 1; -const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. -const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. -const uint16_t kRCMMBits = 24; -const uint16_t kSamsungBits = 32; -const uint16_t kSamsung36Bits = 36; -const uint16_t kSamsungAcStateLength = 14; -const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8; -const uint16_t kSamsungAcExtendedStateLength = 21; -const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8; -const uint16_t kSamsungAcDefaultRepeat = kNoRepeat; -const uint16_t kSanyoAcStateLength = 9; -const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8; -const uint16_t kSanyoAc88StateLength = 11; -const uint16_t kSanyoAc88Bits = kSanyoAc88StateLength * 8; -const uint16_t kSanyoAc88MinRepeat = 2; -const uint16_t kSanyoAc152StateLength = 19; -const uint16_t kSanyoAc152Bits = kSanyoAc152StateLength * 8; -const uint16_t kSanyoAc152MinRepeat = kNoRepeat; -const uint16_t kSanyoSA8650BBits = 12; -const uint16_t kSanyoLC7461AddressBits = 13; -const uint16_t kSanyoLC7461CommandBits = 8; -const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits + - kSanyoLC7461CommandBits) * 2; -const uint8_t kSharpAddressBits = 5; -const uint8_t kSharpCommandBits = 8; -const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15 -const uint16_t kSharpAcStateLength = 13; -const uint16_t kSharpAcBits = kSharpAcStateLength * 8; // 104 -const uint16_t kSharpAcDefaultRepeat = kNoRepeat; -const uint8_t kSherwoodBits = kNECBits; -const uint16_t kSherwoodMinRepeat = kSingleRepeat; -const uint16_t kSony12Bits = 12; -const uint16_t kSony15Bits = 15; -const uint16_t kSony20Bits = 20; -const uint16_t kSonyMinBits = 12; -const uint16_t kSonyMinRepeat = 2; -const uint16_t kSymphonyBits = 12; -const uint16_t kSymphonyDefaultRepeat = 3; -const uint16_t kTcl96AcStateLength = 12; -const uint16_t kTcl96AcBits = kTcl96AcStateLength * 8; -const uint16_t kTcl96AcDefaultRepeat = kNoRepeat; -const uint16_t kTcl112AcStateLength = 14; -const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; -const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; -const uint16_t kTecoBits = 35; -const uint16_t kTecoDefaultRepeat = kNoRepeat; -const uint16_t kTeknopointStateLength = 14; -const uint16_t kTeknopointBits = kTeknopointStateLength * 8; -const uint16_t kToshibaACStateLength = 9; -const uint16_t kToshibaACBits = kToshibaACStateLength * 8; -const uint16_t kToshibaACMinRepeat = kSingleRepeat; -const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2; -const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8; -const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1; -const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8; -const uint16_t kTotoBits = 24; -const uint16_t kTotoShortBits = kTotoBits; -const uint16_t kTotoLongBits = kTotoShortBits * 2; -const uint16_t kTotoDefaultRepeat = kSingleRepeat; -const uint16_t kTranscoldBits = 24; -const uint16_t kTranscoldDefaultRepeat = kNoRepeat; -const uint16_t kTrotecStateLength = 9; -const uint16_t kTrotecBits = kTrotecStateLength * 8; -const uint16_t kTrotecDefaultRepeat = kNoRepeat; -const uint16_t kTrumaBits = 56; -const uint16_t kWhirlpoolAcStateLength = 21; -const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; -const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; -const uint16_t kWhynterBits = 32; -const uint16_t kWowweeBits = 11; -const uint16_t kWowweeDefaultRepeat = kNoRepeat; -const uint8_t kVestelAcBits = 56; -const uint16_t kXmpBits = 64; -const uint16_t kZepealBits = 16; -const uint16_t kZepealMinRepeat = 4; -const uint16_t kVoltasBits = 80; -const uint16_t kVoltasStateLength = 10; -const uint16_t kMilesTag2ShotBits = 14; -const uint16_t kMilesTag2MsgBits = 24; -const uint16_t kMilesMinRepeat = 0; -const uint16_t kBoseBits = 16; -const uint16_t kRhossStateLength = 12; -const uint16_t kRhossBits = kRhossStateLength * 8; -const uint16_t kRhossDefaultRepeat = 0; -const uint16_t kClimaButlerBits = 52; -const uint16_t kYorkBits = 136; -const uint16_t kYorkStateLength = 17; - - -// Legacy defines. (Deprecated) -#define AIWA_RC_T501_BITS kAiwaRcT501Bits -#define ARGO_COMMAND_LENGTH kArgoStateLength -#define COOLIX_BITS kCoolixBits -#define CARRIER_AC_BITS kCarrierAcBits -#define DAIKIN_COMMAND_LENGTH kDaikinStateLength -#define DENON_BITS kDenonBits -#define DENON_48_BITS kDenon48Bits -#define DENON_LEGACY_BITS kDenonLegacyBits -#define DISH_BITS kDishBits -#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat -#define FUJITSU_AC_STATE_LENGTH kFujitsuAcStateLength -#define FUJITSU_AC_STATE_LENGTH_SHORT kFujitsuAcStateLengthShort -#define FUJITSU_AC_BITS kFujitsuAcBits -#define FUJITSU_AC_MIN_BITS kFujitsuAcMinBits -#define GICABLE_BITS kGicableBits -#define GREE_STATE_LENGTH kGreeStateLength -#define HAIER_AC_STATE_LENGTH kHaierACStateLength -#define HAIER_AC_YRW02_STATE_LENGTH kHaierACYRW02StateLength -#define HITACHI_AC_STATE_LENGTH kHitachiAcStateLength -#define HITACHI_AC_BITS kHitachiAcBits -#define HITACHI_AC1_STATE_LENGTH kHitachiAc1StateLength -#define HITACHI_AC1_BITS kHitachiAc1Bits -#define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength -#define HITACHI_AC2_BITS kHitachiAc2Bits -#define HITACHI_AC296_STATE_LENGTH kHitachiAc296StateLength -#define HITACHI_AC296_BITS kHitachiAc296Bits -#define JVC_BITS kJvcBits -#define KELVINATOR_STATE_LENGTH kKelvinatorStateLength -#define LASERTAG_BITS kLasertagBits -#define LG_BITS kLgBits -#define LG32_BITS kLg32Bits -#define MAGIQUEST_BITS kMagiquestBits -#define MIDEA_BITS kMideaBits -#define MITSUBISHI_BITS kMitsubishiBits -#define MITSUBISHI_AC_STATE_LENGTH kMitsubishiACStateLength -#define NEC_BITS kNECBits -#define NIKAI_BITS kNikaiBits -#define PANASONIC_BITS kPanasonicBits -#define RC5_BITS kRC5Bits -#define RC5X_BITS kRC5XBits -#define RC6_MODE0_BITS kRC6Mode0Bits -#define RC6_36_BITS kRC6_36Bits -#define RCMM_BITS kRCMMBits -#define SANYO_LC7461_BITS kSanyoLC7461Bits -#define SAMSUNG_BITS kSamsungBits -#define SANYO_SA8650B_BITS kSanyoSA8650BBits -#define SHARP_BITS kSharpBits -#define SHERWOOD_BITS kSherwoodBits -#define SONY_12_BITS kSony12Bits -#define SONY_15_BITS kSony15Bits -#define SONY_20_BITS kSony20Bits -#define TOSHIBA_AC_STATE_LENGTH kToshibaACStateLength -#define TROTEC_COMMAND_LENGTH kTrotecStateLength -#define WHYNTER_BITS kWhynterBits - -// Turn on Debugging information by uncommenting the following line. -// #define DEBUG 1 - -#ifdef DEBUG -#ifdef UNIT_TEST -#define DPRINT(x) do { std::cout << x; } while (0) -#define DPRINTLN(x) do { std::cout << x << std::endl; } while (0) -#endif // UNIT_TEST -#ifdef ARDUINO -#define DPRINT(x) do { Serial.print(x); } while (0) -#define DPRINTLN(x) do { Serial.println(x); } while (0) -#endif // ARDUINO -#else // DEBUG -#define DPRINT(x) -#define DPRINTLN(x) -#endif // DEBUG - -#ifdef UNIT_TEST -#ifndef F -// Create a no-op F() macro so the code base still compiles outside of the -// Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out -// the code base. That macro stores constants in Flash (PROGMEM) memory. -// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/667 -#define F(x) x -#endif // F -typedef std::string String; -#endif // UNIT_TEST - -#endif // IRREMOTEESP8266_H_ + /*************************************************** + * IRremote for ESP8266 + * + * Based on the IRremote library for Arduino by Ken Shirriff + * Version 0.11 August, 2009 + * Copyright 2009 Ken Shirriff + * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html + * + * Edited by Mitra to add new controller SANYO + * + * Interrupt code based on NECIRrcv by Joe Knapp + * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 + * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ + * + * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) + * LG added by Darryl Smith (based on the JVC protocol) + * Whynter A/C ARC-110WD added by Francesco Meschia + * Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit + * Denon: sendDenon, decodeDenon added by Massimiliano Pinto + (from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp) + * Kelvinator A/C and Sherwood added by crankyoldgit + * Mitsubishi (TV) sending added by crankyoldgit + * Pronto code sending added by crankyoldgit + * Mitsubishi & Toshiba A/C added by crankyoldgit + * (derived from https://github.com/r45635/HVAC-IR-Control) + * DISH decode by marcosamarinho + * Gree Heatpump sending added by Ville Skyttä (scop) + * (derived from https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp) + * Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for sending IR code on ESP8266 + * Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266 + * + * Updated by sillyfrog for Daikin, adopted from + * (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/) + * Fujitsu A/C code added by jonnygraham + * Trotec AC code by stufisher + * Carrier & Haier AC code by crankyoldgit + * Vestel AC code by Erdem U. Altınyurt + * Teco AC code by Fabien Valthier (hcoohb) + * Mitsubishi 112 AC Code by kuchel77 + * Kelon AC code by Davide Depau (Depau) + * + * GPL license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef IRREMOTEESP8266_H_ +#define IRREMOTEESP8266_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifdef UNIT_TEST +#include +#include +#endif // UNIT_TEST + +// Library Version Information +// Major version number (X.x.x) +#define _IRREMOTEESP8266_VERSION_MAJOR 2 +// Minor version number (x.X.x) +#define _IRREMOTEESP8266_VERSION_MINOR 8 +// Patch version number (x.x.X) +#define _IRREMOTEESP8266_VERSION_PATCH 6 +// Macro to convert version info into an integer +#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \ + (((major) << 16) | ((minor) << 8) | (patch)) +// Macro to convert literal into a string +#define MKSTR_HELPER(x) #x +#define MKSTR(x) MKSTR_HELPER(x) +// Integer version +#define _IRREMOTEESP8266_VERSION _IRREMOTEESP8266_VERSION_VAL(\ + _IRREMOTEESP8266_VERSION_MAJOR, \ + _IRREMOTEESP8266_VERSION_MINOR, \ + _IRREMOTEESP8266_VERSION_PATCH) +// String version +#define _IRREMOTEESP8266_VERSION_STR MKSTR(_IRREMOTEESP8266_VERSION_MAJOR) "." \ + MKSTR(_IRREMOTEESP8266_VERSION_MINOR) "." \ + MKSTR(_IRREMOTEESP8266_VERSION_PATCH) +// String version (DEPRECATED) +#define _IRREMOTEESP8266_VERSION_ _IRREMOTEESP8266_VERSION_STR + +// Set the language & locale for the library. See the `locale` dir for options. +#ifndef _IR_LOCALE_ +#define _IR_LOCALE_ en-AU +#endif // _IR_LOCALE_ + +// Do we enable all the protocols by default (true), or disable them (false)? +// This allows users of the library to disable or enable all protocols at +// compile-time with `-D_IR_ENABLE_DEFAULT_=true` or +// `-D_IR_ENABLE_DEFAULT_=false` compiler flags respectively. +// Everything is included by default. +// e.g. If you only want to enable use of he NEC protocol to save program space, +// you would use something like: +// `-D_IR_ENABLE_DEFAULT_=false -DDECODE_NEC=true -DSEND_NEC=true` +// +// or alter your 'platform.ini' file accordingly: +// ``` +// build_flags = -D_IR_ENABLE_DEFAULT_=false +// -DDECODE_NEC=true +// -DSEND_NEC=true +// ``` +// If you want to enable support for every protocol *except* _decoding_ the +// Kelvinator protocol, you would use: +// `-DDECODE_KELVINATOR=false` +#ifndef _IR_ENABLE_DEFAULT_ +#define _IR_ENABLE_DEFAULT_ true // Unless set externally, the default is on. +#endif // _IR_ENABLE_DEFAULT_ + +// Supported IR protocols +// Each protocol you include costs memory and, during decode, costs time +// Disable (set to false) all the protocols you do not need/want! +// The Air Conditioner protocols are the most expensive memory-wise. +// + +// Semi-unique code for unknown messages +#ifndef DECODE_HASH +#define DECODE_HASH _IR_ENABLE_DEFAULT_ +#endif // DECODE_HASH + +#ifndef SEND_RAW +#define SEND_RAW _IR_ENABLE_DEFAULT_ +#endif // SEND_RAW + +#ifndef DECODE_NEC +#define DECODE_NEC _IR_ENABLE_DEFAULT_ +#endif // DECODE_NEC +#ifndef SEND_NEC +#define SEND_NEC _IR_ENABLE_DEFAULT_ +#endif // SEND_NEC + +#ifndef DECODE_SHERWOOD +#define DECODE_SHERWOOD false // Not applicable. Actually is DECODE_NEC +#endif // DECODE_SHERWOOD +#ifndef SEND_SHERWOOD +#define SEND_SHERWOOD _IR_ENABLE_DEFAULT_ +#endif // SEND_SHERWOOD + +#ifndef DECODE_RC5 +#define DECODE_RC5 _IR_ENABLE_DEFAULT_ +#endif // DECODE_RC5 +#ifndef SEND_RC5 +#define SEND_RC5 _IR_ENABLE_DEFAULT_ +#endif // SEND_RC5 + +#ifndef DECODE_RC6 +#define DECODE_RC6 _IR_ENABLE_DEFAULT_ +#endif // DECODE_RC6 +#ifndef SEND_RC6 +#define SEND_RC6 _IR_ENABLE_DEFAULT_ +#endif // SEND_RC6 + +#ifndef DECODE_RCMM +#define DECODE_RCMM _IR_ENABLE_DEFAULT_ +#endif // DECODE_RCMM +#ifndef SEND_RCMM +#define SEND_RCMM _IR_ENABLE_DEFAULT_ +#endif // SEND_RCMM + +#ifndef DECODE_SONY +#define DECODE_SONY _IR_ENABLE_DEFAULT_ +#endif // DECODE_SONY +#ifndef SEND_SONY +#define SEND_SONY _IR_ENABLE_DEFAULT_ +#endif // SEND_SONY + +#ifndef DECODE_PANASONIC +#define DECODE_PANASONIC _IR_ENABLE_DEFAULT_ +#endif // DECODE_PANASONIC +#ifndef SEND_PANASONIC +#define SEND_PANASONIC _IR_ENABLE_DEFAULT_ +#endif // SEND_PANASONIC + +#ifndef DECODE_JVC +#define DECODE_JVC _IR_ENABLE_DEFAULT_ +#endif // DECODE_JVC +#ifndef SEND_JVC +#define SEND_JVC _IR_ENABLE_DEFAULT_ +#endif // SEND_JVC + +#ifndef DECODE_SAMSUNG +#define DECODE_SAMSUNG _IR_ENABLE_DEFAULT_ +#endif // DECODE_SAMSUNG +#ifndef SEND_SAMSUNG +#define SEND_SAMSUNG _IR_ENABLE_DEFAULT_ +#endif // SEND_SAMSUNG + +#ifndef DECODE_SAMSUNG36 +#define DECODE_SAMSUNG36 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SAMSUNG36 +#ifndef SEND_SAMSUNG36 +#define SEND_SAMSUNG36 _IR_ENABLE_DEFAULT_ +#endif // SEND_SAMSUNG36 + +#ifndef DECODE_SAMSUNG_AC +#define DECODE_SAMSUNG_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_SAMSUNG_AC +#ifndef SEND_SAMSUNG_AC +#define SEND_SAMSUNG_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_SAMSUNG_AC + +#ifndef DECODE_WHYNTER +#define DECODE_WHYNTER _IR_ENABLE_DEFAULT_ +#endif // DECODE_WHYNTER +#ifndef SEND_WHYNTER +#define SEND_WHYNTER _IR_ENABLE_DEFAULT_ +#endif // SEND_WHYNTER + +#ifndef DECODE_AIWA_RC_T501 +#define DECODE_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIWA_RC_T501 +#ifndef SEND_AIWA_RC_T501 +#define SEND_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ +#endif // SEND_AIWA_RC_T501 + +#ifndef DECODE_LG +#define DECODE_LG _IR_ENABLE_DEFAULT_ +#endif // DECODE_LG +#ifndef SEND_LG +#define SEND_LG _IR_ENABLE_DEFAULT_ +#endif // SEND_LG + +#ifndef DECODE_SANYO +#define DECODE_SANYO _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO +#ifndef SEND_SANYO +#define SEND_SANYO _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO + +#ifndef DECODE_SANYO_AC +#define DECODE_SANYO_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC +#ifndef SEND_SANYO_AC +#define SEND_SANYO_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC + +#ifndef DECODE_SANYO_AC88 +#define DECODE_SANYO_AC88 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC88 +#ifndef SEND_SANYO_AC88 +#define SEND_SANYO_AC88 _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC88 + +#ifndef DECODE_SANYO_AC152 +#define DECODE_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC152 +#ifndef SEND_SANYO_AC152 +#define SEND_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC152 + +#ifndef DECODE_MITSUBISHI +#define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI +#ifndef SEND_MITSUBISHI +#define SEND_MITSUBISHI _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI + +#ifndef DECODE_MITSUBISHI2 +#define DECODE_MITSUBISHI2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI2 +#ifndef SEND_MITSUBISHI2 +#define SEND_MITSUBISHI2 _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI2 + +#ifndef DECODE_DISH +#define DECODE_DISH _IR_ENABLE_DEFAULT_ +#endif // DECODE_DISH +#ifndef SEND_DISH +#define SEND_DISH _IR_ENABLE_DEFAULT_ +#endif // SEND_DISH + +#ifndef DECODE_SHARP +#define DECODE_SHARP _IR_ENABLE_DEFAULT_ +#endif // DECODE_SHARP +#ifndef SEND_SHARP +#define SEND_SHARP _IR_ENABLE_DEFAULT_ +#endif // SEND_SHARP + +#ifndef DECODE_SHARP_AC +#define DECODE_SHARP_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_SHARP_AC +#ifndef SEND_SHARP_AC +#define SEND_SHARP_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_SHARP_AC + +#ifndef DECODE_DENON +#define DECODE_DENON _IR_ENABLE_DEFAULT_ +#endif // DECODE_DENON +#ifndef SEND_DENON +#define SEND_DENON _IR_ENABLE_DEFAULT_ +#endif // SEND_DENON + +#ifndef DECODE_KELVINATOR +#define DECODE_KELVINATOR _IR_ENABLE_DEFAULT_ +#endif // DECODE_KELVINATOR +#ifndef SEND_KELVINATOR +#define SEND_KELVINATOR _IR_ENABLE_DEFAULT_ +#endif // SEND_KELVINATOR + +#ifndef DECODE_MITSUBISHI_AC +#define DECODE_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI_AC +#ifndef SEND_MITSUBISHI_AC +#define SEND_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI_AC + +#ifndef DECODE_MITSUBISHI136 +#define DECODE_MITSUBISHI136 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI136 +#ifndef SEND_MITSUBISHI136 +#define SEND_MITSUBISHI136 _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI136 + +#ifndef DECODE_MITSUBISHI112 +#define DECODE_MITSUBISHI112 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI112 +#ifndef SEND_MITSUBISHI112 +#define SEND_MITSUBISHI112 _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI112 + +#ifndef DECODE_FUJITSU_AC +#define DECODE_FUJITSU_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_FUJITSU_AC +#ifndef SEND_FUJITSU_AC +#define SEND_FUJITSU_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_FUJITSU_AC + +#ifndef DECODE_INAX +#define DECODE_INAX _IR_ENABLE_DEFAULT_ +#endif // DECODE_INAX +#ifndef SEND_INAX +#define SEND_INAX _IR_ENABLE_DEFAULT_ +#endif // SEND_INAX + +#ifndef DECODE_DAIKIN +#define DECODE_DAIKIN _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN +#ifndef SEND_DAIKIN +#define SEND_DAIKIN _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN + +#ifndef DECODE_COOLIX +#define DECODE_COOLIX _IR_ENABLE_DEFAULT_ +#endif // DECODE_COOLIX +#ifndef SEND_COOLIX +#define SEND_COOLIX _IR_ENABLE_DEFAULT_ +#endif // SEND_COOLIX + +#ifndef DECODE_COOLIX48 +#define DECODE_COOLIX48 _IR_ENABLE_DEFAULT_ +#endif // DECODE_COOLIX48 +#ifndef SEND_COOLIX48 +#define SEND_COOLIX48 _IR_ENABLE_DEFAULT_ +#endif // SEND_COOLIX48 + +#ifndef DECODE_GLOBALCACHE +#define DECODE_GLOBALCACHE false // Not applicable. +#endif // DECODE_GLOBALCACHE +#ifndef SEND_GLOBALCACHE +#define SEND_GLOBALCACHE _IR_ENABLE_DEFAULT_ +#endif // SEND_GLOBALCACHE + +#ifndef DECODE_GOODWEATHER +#define DECODE_GOODWEATHER _IR_ENABLE_DEFAULT_ +#endif // DECODE_GOODWEATHER +#ifndef SEND_GOODWEATHER +#define SEND_GOODWEATHER _IR_ENABLE_DEFAULT_ +#endif // SEND_GOODWEATHER + +#ifndef DECODE_GREE +#define DECODE_GREE _IR_ENABLE_DEFAULT_ +#endif // DECODE_GREE +#ifndef SEND_GREE +#define SEND_GREE _IR_ENABLE_DEFAULT_ +#endif // SEND_GREE + +#ifndef DECODE_PRONTO +#define DECODE_PRONTO false // Not applicable. +#endif // DECODE_PRONTO +#ifndef SEND_PRONTO +#define SEND_PRONTO _IR_ENABLE_DEFAULT_ +#endif // SEND_PRONTO + +#ifndef DECODE_ARGO +#define DECODE_ARGO _IR_ENABLE_DEFAULT_ +#endif // DECODE_ARGO +#ifndef SEND_ARGO +#define SEND_ARGO _IR_ENABLE_DEFAULT_ +#endif // SEND_ARGO + +#ifndef DECODE_TROTEC +#define DECODE_TROTEC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TROTEC +#ifndef SEND_TROTEC +#define SEND_TROTEC _IR_ENABLE_DEFAULT_ +#endif // SEND_TROTEC + +#ifndef DECODE_TROTEC_3550 +#define DECODE_TROTEC_3550 _IR_ENABLE_DEFAULT_ +#endif // DECODE_TROTEC_3550 +#ifndef SEND_TROTEC_3550 +#define SEND_TROTEC_3550 _IR_ENABLE_DEFAULT_ +#endif // SEND_TROTEC_3550 + +#ifndef DECODE_NIKAI +#define DECODE_NIKAI _IR_ENABLE_DEFAULT_ +#endif // DECODE_NIKAI +#ifndef SEND_NIKAI +#define SEND_NIKAI _IR_ENABLE_DEFAULT_ +#endif // SEND_NIKAI + +#ifndef DECODE_TOSHIBA_AC +#define DECODE_TOSHIBA_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TOSHIBA_AC +#ifndef SEND_TOSHIBA_AC +#define SEND_TOSHIBA_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TOSHIBA_AC + +#ifndef DECODE_MAGIQUEST +#define DECODE_MAGIQUEST _IR_ENABLE_DEFAULT_ +#endif // DECODE_MAGIQUEST +#ifndef SEND_MAGIQUEST +#define SEND_MAGIQUEST _IR_ENABLE_DEFAULT_ +#endif // SEND_MAGIQUEST + +#ifndef DECODE_MIDEA +#define DECODE_MIDEA _IR_ENABLE_DEFAULT_ +#endif // DECODE_MIDEA +#ifndef SEND_MIDEA +#define SEND_MIDEA _IR_ENABLE_DEFAULT_ +#endif // SEND_MIDEA + +#ifndef DECODE_MIDEA24 +#define DECODE_MIDEA24 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MIDEA24 +#ifndef SEND_MIDEA24 +#define SEND_MIDEA24 _IR_ENABLE_DEFAULT_ +#endif // SEND_MIDEA24 + +#ifndef DECODE_LASERTAG +#define DECODE_LASERTAG _IR_ENABLE_DEFAULT_ +#endif // DECODE_LASERTAG +#ifndef SEND_LASERTAG +#define SEND_LASERTAG _IR_ENABLE_DEFAULT_ +#endif // SEND_LASERTAG + +#ifndef DECODE_CARRIER_AC +#define DECODE_CARRIER_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC +#ifndef SEND_CARRIER_AC +#define SEND_CARRIER_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC + +#ifndef DECODE_CARRIER_AC40 +#define DECODE_CARRIER_AC40 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC40 +#ifndef SEND_CARRIER_AC40 +#define SEND_CARRIER_AC40 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC40 + +#ifndef DECODE_CARRIER_AC64 +#define DECODE_CARRIER_AC64 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC64 +#ifndef SEND_CARRIER_AC64 +#define SEND_CARRIER_AC64 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC64 + +#ifndef DECODE_CARRIER_AC128 +#define DECODE_CARRIER_AC128 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC128 +#ifndef SEND_CARRIER_AC128 +#define SEND_CARRIER_AC128 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC128 + +#ifndef DECODE_HAIER_AC +#define DECODE_HAIER_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC +#ifndef SEND_HAIER_AC +#define SEND_HAIER_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC + +#ifndef DECODE_HITACHI_AC +#define DECODE_HITACHI_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC +#ifndef SEND_HITACHI_AC +#define SEND_HITACHI_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC + +#ifndef DECODE_HITACHI_AC1 +#define DECODE_HITACHI_AC1 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC1 +#ifndef SEND_HITACHI_AC1 +#define SEND_HITACHI_AC1 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC1 + +#ifndef DECODE_HITACHI_AC2 +#define DECODE_HITACHI_AC2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC2 +#ifndef SEND_HITACHI_AC2 +#define SEND_HITACHI_AC2 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC2 + +#ifndef DECODE_HITACHI_AC3 +#define DECODE_HITACHI_AC3 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC3 +#ifndef SEND_HITACHI_AC3 +#define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC3 + +#ifndef DECODE_HITACHI_AC264 +#define DECODE_HITACHI_AC264 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC264 +#ifndef SEND_HITACHI_AC264 +#define SEND_HITACHI_AC264 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC264 + +#ifndef DECODE_HITACHI_AC296 +#define DECODE_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC296 +#ifndef SEND_HITACHI_AC296 +#define SEND_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC296 + +#ifndef DECODE_HITACHI_AC344 +#define DECODE_HITACHI_AC344 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC344 +#ifndef SEND_HITACHI_AC344 +#define SEND_HITACHI_AC344 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC344 + +#ifndef DECODE_HITACHI_AC424 +#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC424 +#ifndef SEND_HITACHI_AC424 +#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC424 + +#ifndef DECODE_GICABLE +#define DECODE_GICABLE _IR_ENABLE_DEFAULT_ +#endif // DECODE_GICABLE +#ifndef SEND_GICABLE +#define SEND_GICABLE _IR_ENABLE_DEFAULT_ +#endif // SEND_GICABLE + +#ifndef DECODE_HAIER_AC_YRW02 +#define DECODE_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC_YRW02 +#ifndef SEND_HAIER_AC_YRW02 +#define SEND_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC_YRW02 + +#ifndef DECODE_WHIRLPOOL_AC +#define DECODE_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_WHIRLPOOL_AC +#ifndef SEND_WHIRLPOOL_AC +#define SEND_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_WHIRLPOOL_AC + +#ifndef DECODE_LUTRON +#define DECODE_LUTRON _IR_ENABLE_DEFAULT_ +#endif // DECODE_LUTRON +#ifndef SEND_LUTRON +#define SEND_LUTRON _IR_ENABLE_DEFAULT_ +#endif // SEND_LUTRON + +#ifndef DECODE_ELECTRA_AC +#define DECODE_ELECTRA_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_ELECTRA_AC +#ifndef SEND_ELECTRA_AC +#define SEND_ELECTRA_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_ELECTRA_AC + +#ifndef DECODE_PANASONIC_AC +#define DECODE_PANASONIC_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_PANASONIC_AC +#ifndef SEND_PANASONIC_AC +#define SEND_PANASONIC_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_PANASONIC_AC + +#ifndef DECODE_PANASONIC_AC32 +#define DECODE_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ +#endif // DECODE_PANASONIC_AC32 +#ifndef SEND_PANASONIC_AC32 +#define SEND_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ +#endif // SEND_PANASONIC_AC32 + +#ifndef DECODE_MWM +#define DECODE_MWM _IR_ENABLE_DEFAULT_ +#endif // DECODE_MWM +#ifndef SEND_MWM +#define SEND_MWM _IR_ENABLE_DEFAULT_ +#endif // SEND_MWM + +#ifndef DECODE_PIONEER +#define DECODE_PIONEER _IR_ENABLE_DEFAULT_ +#endif // DECODE_PIONEER +#ifndef SEND_PIONEER +#define SEND_PIONEER _IR_ENABLE_DEFAULT_ +#endif // SEND_PIONEER + +#ifndef DECODE_DAIKIN2 +#define DECODE_DAIKIN2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN2 +#ifndef SEND_DAIKIN2 +#define SEND_DAIKIN2 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN2 + +#ifndef DECODE_VESTEL_AC +#define DECODE_VESTEL_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_VESTEL_AC +#ifndef SEND_VESTEL_AC +#define SEND_VESTEL_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_VESTEL_AC + +#ifndef DECODE_TECO +#define DECODE_TECO _IR_ENABLE_DEFAULT_ +#endif // DECODE_TECO +#ifndef SEND_TECO +#define SEND_TECO _IR_ENABLE_DEFAULT_ +#endif // SEND_TECO + +#ifndef DECODE_TCL96AC +#define DECODE_TCL96AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TCL96AC +#ifndef SEND_TCL96AC +#define SEND_TCL96AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TCL96AC + +#ifndef DECODE_TCL112AC +#define DECODE_TCL112AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TCL112AC +#ifndef SEND_TCL112AC +#define SEND_TCL112AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TCL112AC + +#ifndef DECODE_LEGOPF +#define DECODE_LEGOPF _IR_ENABLE_DEFAULT_ +#endif // DECODE_LEGOPF +#ifndef SEND_LEGOPF +#define SEND_LEGOPF _IR_ENABLE_DEFAULT_ +#endif // SEND_LEGOPF + +#ifndef DECODE_MITSUBISHIHEAVY +#define DECODE_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHIHEAVY +#ifndef SEND_MITSUBISHIHEAVY +#define SEND_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHIHEAVY + +#ifndef DECODE_DAIKIN216 +#define DECODE_DAIKIN216 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN216 +#ifndef SEND_DAIKIN216 +#define SEND_DAIKIN216 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN216 + +#ifndef DECODE_DAIKIN160 +#define DECODE_DAIKIN160 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN160 +#ifndef SEND_DAIKIN160 +#define SEND_DAIKIN160 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN160 + +#ifndef DECODE_NEOCLIMA +#define DECODE_NEOCLIMA _IR_ENABLE_DEFAULT_ +#endif // DECODE_NEOCLIMA +#ifndef SEND_NEOCLIMA +#define SEND_NEOCLIMA _IR_ENABLE_DEFAULT_ +#endif // SEND_NEOCLIMA + +#ifndef DECODE_DAIKIN176 +#define DECODE_DAIKIN176 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN176 +#ifndef SEND_DAIKIN176 +#define SEND_DAIKIN176 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN176 + +#ifndef DECODE_DAIKIN128 +#define DECODE_DAIKIN128 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN128 +#ifndef SEND_DAIKIN128 +#define SEND_DAIKIN128 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN128 + +#ifndef DECODE_AMCOR +#define DECODE_AMCOR _IR_ENABLE_DEFAULT_ +#endif // DECODE_AMCOR +#ifndef SEND_AMCOR +#define SEND_AMCOR _IR_ENABLE_DEFAULT_ +#endif // SEND_AMCOR + +#ifndef DECODE_DAIKIN152 +#define DECODE_DAIKIN152 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN152 +#ifndef SEND_DAIKIN152 +#define SEND_DAIKIN152 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN152 + +#ifndef DECODE_EPSON +#define DECODE_EPSON _IR_ENABLE_DEFAULT_ +#endif // DECODE_EPSON +#ifndef SEND_EPSON +#define SEND_EPSON _IR_ENABLE_DEFAULT_ +#endif // SEND_EPSON + +#ifndef DECODE_SYMPHONY +#define DECODE_SYMPHONY _IR_ENABLE_DEFAULT_ +#endif // DECODE_SYMPHONY +#ifndef SEND_SYMPHONY +#define SEND_SYMPHONY _IR_ENABLE_DEFAULT_ +#endif // SEND_SYMPHONY + +#ifndef DECODE_DAIKIN64 +#define DECODE_DAIKIN64 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN64 +#ifndef SEND_DAIKIN64 +#define SEND_DAIKIN64 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN64 + +#ifndef DECODE_AIRWELL +#define DECODE_AIRWELL _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIRWELL +#ifndef SEND_AIRWELL +#define SEND_AIRWELL _IR_ENABLE_DEFAULT_ +#endif // SEND_AIRWELL + +#ifndef DECODE_DELONGHI_AC +#define DECODE_DELONGHI_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_DELONGHI_AC +#ifndef SEND_DELONGHI_AC +#define SEND_DELONGHI_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_DELONGHI_AC + +#ifndef DECODE_DOSHISHA +#define DECODE_DOSHISHA _IR_ENABLE_DEFAULT_ +#endif // DECODE_DOSHISHA +#ifndef SEND_DOSHISHA +#define SEND_DOSHISHA _IR_ENABLE_DEFAULT_ +#endif // SEND_DOSHISHA + +#ifndef DECODE_MULTIBRACKETS +#define DECODE_MULTIBRACKETS _IR_ENABLE_DEFAULT_ +#endif // DECODE_MULTIBRACKETS +#ifndef SEND_MULTIBRACKETS +#define SEND_MULTIBRACKETS _IR_ENABLE_DEFAULT_ +#endif // SEND_MULTIBRACKETS + +#ifndef DECODE_TECHNIBEL_AC +#define DECODE_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TECHNIBEL_AC +#ifndef SEND_TECHNIBEL_AC +#define SEND_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TECHNIBEL_AC + +#ifndef DECODE_CORONA_AC +#define DECODE_CORONA_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_CORONA_AC +#ifndef SEND_CORONA_AC +#define SEND_CORONA_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_CORONA_AC + +#ifndef DECODE_ZEPEAL +#define DECODE_ZEPEAL _IR_ENABLE_DEFAULT_ +#endif // DECODE_ZEPEAL +#ifndef SEND_ZEPEAL +#define SEND_ZEPEAL _IR_ENABLE_DEFAULT_ +#endif // SEND_ZEPEAL + +#ifndef DECODE_VOLTAS +#define DECODE_VOLTAS _IR_ENABLE_DEFAULT_ +#endif // DECODE_VOLTAS +#ifndef SEND_VOLTAS +#define SEND_VOLTAS _IR_ENABLE_DEFAULT_ +#endif // SEND_VOLTAS + +#ifndef DECODE_METZ +#define DECODE_METZ _IR_ENABLE_DEFAULT_ +#endif // DECODE_METZ +#ifndef SEND_METZ +#define SEND_METZ _IR_ENABLE_DEFAULT_ +#endif // SEND_METZ + +#ifndef DECODE_TRANSCOLD +#define DECODE_TRANSCOLD _IR_ENABLE_DEFAULT_ +#endif // DECODE_TRANSCOLD +#ifndef SEND_TRANSCOLD +#define SEND_TRANSCOLD _IR_ENABLE_DEFAULT_ +#endif // SEND_TRANSCOLD + +#ifndef DECODE_MIRAGE +#define DECODE_MIRAGE _IR_ENABLE_DEFAULT_ +#endif // DECODE_MIRAGE +#ifndef SEND_MIRAGE +#define SEND_MIRAGE _IR_ENABLE_DEFAULT_ +#endif // SEND_MIRAGE + +#ifndef DECODE_ELITESCREENS +#define DECODE_ELITESCREENS _IR_ENABLE_DEFAULT_ +#endif // DECODE_ELITESCREENS +#ifndef SEND_ELITESCREENS +#define SEND_ELITESCREENS _IR_ENABLE_DEFAULT_ +#endif // SEND_ELITESCREENS + +#ifndef DECODE_MILESTAG2 +#define DECODE_MILESTAG2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MILESTAG2 +#ifndef SEND_MILESTAG2 +#define SEND_MILESTAG2 _IR_ENABLE_DEFAULT_ +#endif // SEND_MILESTAG2 + +#ifndef DECODE_ECOCLIM +#define DECODE_ECOCLIM _IR_ENABLE_DEFAULT_ +#endif // DECODE_ECOCLIM +#ifndef SEND_ECOCLIM +#define SEND_ECOCLIM _IR_ENABLE_DEFAULT_ +#endif // SEND_ECOCLIM + +#ifndef DECODE_XMP +#define DECODE_XMP _IR_ENABLE_DEFAULT_ +#endif // DECODE_XMP +#ifndef SEND_XMP +#define SEND_XMP _IR_ENABLE_DEFAULT_ +#endif // SEND_XMP + +#ifndef DECODE_TRUMA +#define DECODE_TRUMA _IR_ENABLE_DEFAULT_ +#endif // DECODE_TRUMA +#ifndef SEND_TRUMA +#define SEND_TRUMA _IR_ENABLE_DEFAULT_ +#endif // SEND_TRUMA + +#ifndef DECODE_HAIER_AC176 +#define DECODE_HAIER_AC176 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC176 +#ifndef SEND_HAIER_AC176 +#define SEND_HAIER_AC176 _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC176 + +#ifndef DECODE_TEKNOPOINT +#define DECODE_TEKNOPOINT _IR_ENABLE_DEFAULT_ +#endif // DECODE_TEKNOPOINT +#ifndef SEND_TEKNOPOINT +#define SEND_TEKNOPOINT _IR_ENABLE_DEFAULT_ +#endif // SEND_TEKNOPOINT + +#ifndef DECODE_KELON +#define DECODE_KELON _IR_ENABLE_DEFAULT_ +#endif // DECODE_KELON +#ifndef SEND_KELON +#define SEND_KELON _IR_ENABLE_DEFAULT_ +#endif // SEND_KELON + +#ifndef DECODE_BOSE +#define DECODE_BOSE _IR_ENABLE_DEFAULT_ +#endif // DECODE_BOSE +#ifndef SEND_BOSE +#define SEND_BOSE _IR_ENABLE_DEFAULT_ +#endif // SEND_BOSE + +#ifndef DECODE_ARRIS +#define DECODE_ARRIS _IR_ENABLE_DEFAULT_ +#endif // DECODE_ARRIS +#ifndef SEND_ARRIS +#define SEND_ARRIS _IR_ENABLE_DEFAULT_ +#endif // SEND_ARRIS + +#ifndef DECODE_RHOSS +#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ +#endif // DECODE_RHOSS +#ifndef SEND_RHOSS +#define SEND_RHOSS _IR_ENABLE_DEFAULT_ +#endif // SEND_RHOSS + +#ifndef DECODE_AIRTON +#define DECODE_AIRTON _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIRTON +#ifndef SEND_AIRTON +#define SEND_AIRTON _IR_ENABLE_DEFAULT_ +#endif // SEND_AIRTON + +#ifndef DECODE_KELON168 +#define DECODE_KELON168 _IR_ENABLE_DEFAULT_ +#endif // DECODE_KELON168 +#ifndef SEND_KELON168 +#define SEND_KELON168 _IR_ENABLE_DEFAULT_ +#endif // SEND_KELON168 + +#ifndef DECODE_DAIKIN200 +#define DECODE_DAIKIN200 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN200 +#ifndef SEND_DAIKIN200 +#define SEND_DAIKIN200 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN200 + +#ifndef DECODE_HAIER_AC160 +#define DECODE_HAIER_AC160 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC160 +#ifndef SEND_HAIER_AC160 +#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC160 + +#ifndef DECODE_TOTO +#define DECODE_TOTO _IR_ENABLE_DEFAULT_ +#endif // DECODE_TOTO +#ifndef SEND_TOTO +#define SEND_TOTO _IR_ENABLE_DEFAULT_ +#endif // SEND_TOTO + +#ifndef DECODE_CLIMABUTLER +#define DECODE_CLIMABUTLER _IR_ENABLE_DEFAULT_ +#endif // DECODE_CLIMABUTLER +#ifndef SEND_CLIMABUTLER +#define SEND_CLIMABUTLER _IR_ENABLE_DEFAULT_ +#endif // SEND_CLIMABUTLER + +#ifndef DECODE_BOSCH144 +#define DECODE_BOSCH144 _IR_ENABLE_DEFAULT_ +#endif // DECODE_BOSCH144 +#ifndef SEND_BOSCH144 +#define SEND_BOSCH144 _IR_ENABLE_DEFAULT_ +#endif // SEND_BOSCH144 + +#ifndef DECODE_DAIKIN312 +#define DECODE_DAIKIN312 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN312 +#ifndef SEND_DAIKIN312 +#define SEND_DAIKIN312 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN312 + +#ifndef DECODE_GORENJE +#define DECODE_GORENJE _IR_ENABLE_DEFAULT_ +#endif // DECODE_GORENJE +#ifndef SEND_GORENJE +#define SEND_GORENJE _IR_ENABLE_DEFAULT_ +#endif // SEND_GORENJE + +#ifndef DECODE_WOWWEE +#define DECODE_WOWWEE _IR_ENABLE_DEFAULT_ +#endif // DECODE_WOWWEE +#ifndef SEND_WOWWEE +#define SEND_WOWWEE _IR_ENABLE_DEFAULT_ +#endif // SEND_WOWWEE + +#ifndef DECODE_CARRIER_AC84 +#define DECODE_CARRIER_AC84 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC84 +#ifndef SEND_CARRIER_AC84 +#define SEND_CARRIER_AC84 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC84 + +#ifndef DECODE_YORK +#define DECODE_YORK _IR_ENABLE_DEFAULT_ +#endif // DECODE_YORK +#ifndef SEND_YORK +#define SEND_YORK _IR_ENABLE_DEFAULT_ +#endif // SEND_YORK + +#ifndef DECODE_FUJITSU_AC264 +#define DECODE_FUJITSU_AC264 _IR_ENABLE_DEFAULT_ +#endif // DECODE_FUJITSU_AC264 +#ifndef SEND_FUJITSU_AC264 +#define SEND_FUJITSU_AC264 _IR_ENABLE_DEFAULT_ +#endif // SEND_FUJITSU_AC264 + +#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ + DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ + DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ + DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ + DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ + DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ + DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ + DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ + DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ + DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \ + DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \ + DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \ + DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \ + DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \ + DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \ + DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \ + DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \ + DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \ + DECODE_CARRIER_AC84 || DECODE_YORK || DECODE_FUJITSU_AC264 || \ + false) + // Add any DECODE to the above if it uses result->state (see kStateSizeMax) + // you might also want to add the protocol to hasACState function +#define DECODE_AC true // We need some common infrastructure for decoding A/Cs. +#else +#define DECODE_AC false // We don't need that infrastructure. +#endif + +// Use millisecond 'delay()' calls where we can to avoid tripping the WDT. +// Note: If you plan to send IR messages in the callbacks of the AsyncWebserver +// library, you need to set ALLOW_DELAY_CALLS to false. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/430 +#ifndef ALLOW_DELAY_CALLS +#define ALLOW_DELAY_CALLS true +#endif // ALLOW_DELAY_CALLS + +// Enable a run-time settable high-pass filter on captured data **before** +// trying any protocol decoding. +// i.e. Try to remove/merge any really short pulses detected in the raw data. +// Note: Even when this option is enabled, it is _off_ by default, and requires +// a user who knows what they are doing to enable it. +// The option to disable this feature is here if your project is _really_ +// tight on resources. i.e. Saves a small handful of bytes and cpu time. +// WARNING: If you use this feature at runtime, you can no longer trust the +// **raw** data captured. It will now have been slightly **cooked**! +// DANGER: If you set the `noise_floor` value too high, it **WILL** break +// decoding of some protocols. You have been warned. Here Be Dragons! +// +// See: `irrecv::decode()` in IRrecv.cpp for more info. +#ifndef ENABLE_NOISE_FILTER_OPTION +#define ENABLE_NOISE_FILTER_OPTION true +#endif // ENABLE_NOISE_FILTER_OPTION + +/// Enumerator for defining and numbering of supported IR protocol. +/// @note Always add to the end of the list and should never remove entries +/// or change order. Projects may save the type number for later usage +/// so numbering should always stay the same. +enum decode_type_t { + UNKNOWN = -1, + UNUSED = 0, + RC5, + RC6, + NEC, + SONY, + PANASONIC, // (5) + JVC, + SAMSUNG, + WHYNTER, + AIWA_RC_T501, + LG, // (10) + SANYO, + MITSUBISHI, + DISH, + SHARP, + COOLIX, // (15) + DAIKIN, + DENON, + KELVINATOR, + SHERWOOD, + MITSUBISHI_AC, // (20) + RCMM, + SANYO_LC7461, + RC5X, + GREE, + PRONTO, // Technically not a protocol, but an encoding. (25) + NEC_LIKE, + ARGO, + TROTEC, + NIKAI, + RAW, // Technically not a protocol, but an encoding. (30) + GLOBALCACHE, // Technically not a protocol, but an encoding. + TOSHIBA_AC, + FUJITSU_AC, + MIDEA, + MAGIQUEST, // (35) + LASERTAG, + CARRIER_AC, + HAIER_AC, + MITSUBISHI2, + HITACHI_AC, // (40) + HITACHI_AC1, + HITACHI_AC2, + GICABLE, + HAIER_AC_YRW02, + WHIRLPOOL_AC, // (45) + SAMSUNG_AC, + LUTRON, + ELECTRA_AC, + PANASONIC_AC, + PIONEER, // (50) + LG2, + MWM, + DAIKIN2, + VESTEL_AC, + TECO, // (55) + SAMSUNG36, + TCL112AC, + LEGOPF, + MITSUBISHI_HEAVY_88, + MITSUBISHI_HEAVY_152, // 60 + DAIKIN216, + SHARP_AC, + GOODWEATHER, + INAX, + DAIKIN160, // 65 + NEOCLIMA, + DAIKIN176, + DAIKIN128, + AMCOR, + DAIKIN152, // 70 + MITSUBISHI136, + MITSUBISHI112, + HITACHI_AC424, + SONY_38K, + EPSON, // 75 + SYMPHONY, + HITACHI_AC3, + DAIKIN64, + AIRWELL, + DELONGHI_AC, // 80 + DOSHISHA, + MULTIBRACKETS, + CARRIER_AC40, + CARRIER_AC64, + HITACHI_AC344, // 85 + CORONA_AC, + MIDEA24, + ZEPEAL, + SANYO_AC, + VOLTAS, // 90 + METZ, + TRANSCOLD, + TECHNIBEL_AC, + MIRAGE, + ELITESCREENS, // 95 + PANASONIC_AC32, + MILESTAG2, + ECOCLIM, + XMP, + TRUMA, // 100 + HAIER_AC176, + TEKNOPOINT, + KELON, + TROTEC_3550, + SANYO_AC88, // 105 + BOSE, + ARRIS, + RHOSS, + AIRTON, + COOLIX48, // 110 + HITACHI_AC264, + KELON168, + HITACHI_AC296, + DAIKIN200, + HAIER_AC160, // 115 + CARRIER_AC128, + TOTO, + CLIMABUTLER, + TCL96AC, + BOSCH144, // 120 + SANYO_AC152, + DAIKIN312, + GORENJE, + WOWWEE, + CARRIER_AC84, // 125 + YORK, + FUJITSU_AC264, + // Add new entries before this one, and update it to point to the last entry. + kLastDecodeType = FUJITSU_AC264, +}; + +// Message lengths & required repeat values +const uint16_t kNoRepeat = 0; +const uint16_t kSingleRepeat = 1; + +const uint16_t kAirtonBits = 56; +const uint16_t kAirtonDefaultRepeat = kNoRepeat; +const uint16_t kAirwellBits = 34; +const uint16_t kAirwellMinRepeats = 2; +const uint16_t kAiwaRcT501Bits = 15; +const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; +const uint16_t kAlokaBits = 32; +const uint16_t kAmcorStateLength = 8; +const uint16_t kAmcorBits = kAmcorStateLength * 8; +const uint16_t kAmcorDefaultRepeat = kSingleRepeat; +const uint16_t kArgoStateLength = 12; +const uint16_t kArgoShortStateLength = 4; +const uint16_t kArgoBits = kArgoStateLength * 8; +const uint16_t kArgoShortBits = kArgoShortStateLength * 8; +const uint16_t kArgo3AcControlStateLength = 6; // Bytes +const uint16_t kArgo3iFeelReportStateLength = 2; // Bytes +const uint16_t kArgo3TimerStateLength = 9; // Bytes +const uint16_t kArgo3ConfigStateLength = 4; // Bytes +const uint16_t kArgoDefaultRepeat = kNoRepeat; +const uint16_t kArrisBits = 32; +const uint16_t kBosch144StateLength = 18; +const uint16_t kBosch144Bits = kBosch144StateLength * 8; +const uint16_t kCoolixBits = 24; +const uint16_t kCoolix48Bits = kCoolixBits * 2; +const uint16_t kCoolixDefaultRepeat = kSingleRepeat; +const uint16_t kCarrierAcBits = 32; +const uint16_t kCarrierAcMinRepeat = kNoRepeat; +const uint16_t kCarrierAc40Bits = 40; +const uint16_t kCarrierAc40MinRepeat = 2; +const uint16_t kCarrierAc64Bits = 64; +const uint16_t kCarrierAc64MinRepeat = kNoRepeat; +const uint16_t kCarrierAc84StateLength = 11; +const uint16_t kCarrierAc84Bits = kCarrierAc84StateLength * 8 - 4; +const uint16_t kCarrierAc84MinRepeat = kNoRepeat; +const uint16_t kCarrierAc128StateLength = 16; +const uint16_t kCarrierAc128Bits = kCarrierAc128StateLength * 8; +const uint16_t kCarrierAc128MinRepeat = kNoRepeat; +const uint16_t kCoronaAcStateLengthShort = 7; +const uint16_t kCoronaAcStateLength = kCoronaAcStateLengthShort * 3; +const uint16_t kCoronaAcBitsShort = kCoronaAcStateLengthShort * 8; +const uint16_t kCoronaAcBits = kCoronaAcStateLength * 8; +const uint16_t kDaikinStateLength = 35; +const uint16_t kDaikinBits = kDaikinStateLength * 8; +const uint16_t kDaikinStateLengthShort = kDaikinStateLength - 8; +const uint16_t kDaikinBitsShort = kDaikinStateLengthShort * 8; +const uint16_t kDaikinDefaultRepeat = kNoRepeat; +const uint16_t kDaikin2StateLength = 39; +const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; +const uint16_t kDaikin2DefaultRepeat = kNoRepeat; +const uint16_t kDaikin64Bits = 64; +const uint16_t kDaikin64DefaultRepeat = kNoRepeat; +const uint16_t kDaikin160StateLength = 20; +const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; +const uint16_t kDaikin160DefaultRepeat = kNoRepeat; +const uint16_t kDaikin128StateLength = 16; +const uint16_t kDaikin128Bits = kDaikin128StateLength * 8; +const uint16_t kDaikin128DefaultRepeat = kNoRepeat; +const uint16_t kDaikin152StateLength = 19; +const uint16_t kDaikin152Bits = kDaikin152StateLength * 8; +const uint16_t kDaikin152DefaultRepeat = kNoRepeat; +const uint16_t kDaikin176StateLength = 22; +const uint16_t kDaikin176Bits = kDaikin176StateLength * 8; +const uint16_t kDaikin176DefaultRepeat = kNoRepeat; +const uint16_t kDaikin200StateLength = 25; +const uint16_t kDaikin200Bits = kDaikin200StateLength * 8; +const uint16_t kDaikin200DefaultRepeat = kNoRepeat; +const uint16_t kDaikin216StateLength = 27; +const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; +const uint16_t kDaikin216DefaultRepeat = kNoRepeat; +const uint16_t kDaikin312StateLength = 39; +const uint16_t kDaikin312Bits = kDaikin312StateLength * 8; +const uint16_t kDaikin312DefaultRepeat = kNoRepeat; +const uint16_t kDelonghiAcBits = 64; +const uint16_t kDelonghiAcDefaultRepeat = kNoRepeat; +const uint16_t kTechnibelAcBits = 56; +const uint16_t kTechnibelAcDefaultRepeat = kNoRepeat; +const uint16_t kDenonBits = 15; +const uint16_t kDenon48Bits = 48; +const uint16_t kDenonLegacyBits = 14; +const uint16_t kDishBits = 16; +const uint16_t kDishMinRepeat = 3; +const uint16_t kDoshishaBits = 40; +const uint16_t kEcoclimBits = 56; +const uint16_t kEcoclimShortBits = 15; +const uint16_t kEpsonBits = 32; +const uint16_t kEpsonMinRepeat = 2; +const uint16_t kElectraAcStateLength = 13; +const uint16_t kElectraAcBits = kElectraAcStateLength * 8; +const uint16_t kElectraAcMinRepeat = kNoRepeat; +const uint16_t kEliteScreensBits = 32; +const uint16_t kEliteScreensDefaultRepeat = kSingleRepeat; +const uint16_t kFujitsuAcMinRepeat = kNoRepeat; +const uint16_t kFujitsuAcStateLength = 16; +const uint16_t kFujitsuAcStateLengthShort = 7; +const uint16_t kFujitsuAcBits = kFujitsuAcStateLength * 8; +const uint16_t kFujitsuAcMinBits = (kFujitsuAcStateLengthShort - 1) * 8; +const uint16_t kFujitsuAc264DefaultRepeat = kNoRepeat; +const uint16_t kFujitsuAc264StateLength = 33; +const uint16_t kFujitsuAc264StateLengthMiddle = 16; +const uint16_t kFujitsuAc264StateLengthShort = 7; +const uint16_t kFujitsuAc264Bits = kFujitsuAc264StateLength * 8; +const uint16_t kFujitsuAc264BitsMiddle = kFujitsuAc264StateLengthMiddle * 8; +const uint16_t kFujitsuAc264BitsShort = kFujitsuAc264StateLengthShort * 8; +const uint16_t kGicableBits = 16; +const uint16_t kGicableMinRepeat = kSingleRepeat; +const uint16_t kGoodweatherBits = 48; +const uint16_t kGoodweatherMinRepeat = kNoRepeat; +const uint16_t kGorenjeBits = 8; +const uint16_t kGreeStateLength = 8; +const uint16_t kGreeBits = kGreeStateLength * 8; +const uint16_t kGreeDefaultRepeat = kNoRepeat; +const uint16_t kHaierACStateLength = 9; +const uint16_t kHaierACBits = kHaierACStateLength * 8; +const uint16_t kHaierAcDefaultRepeat = kNoRepeat; +const uint16_t kHaierACYRW02StateLength = 14; +const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8; +const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat; +const uint16_t kHaierAC160StateLength = 20; +const uint16_t kHaierAC160Bits = kHaierAC160StateLength * 8; +const uint16_t kHaierAc160DefaultRepeat = kNoRepeat; +const uint16_t kHaierAC176StateLength = 22; +const uint16_t kHaierAC176Bits = kHaierAC176StateLength * 8; +const uint16_t kHaierAc176DefaultRepeat = kNoRepeat; +const uint16_t kHitachiAcStateLength = 28; +const uint16_t kHitachiAcBits = kHitachiAcStateLength * 8; +const uint16_t kHitachiAcDefaultRepeat = kNoRepeat; +const uint16_t kHitachiAc1StateLength = 13; +const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; +const uint16_t kHitachiAc2StateLength = 53; +const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; +const uint16_t kHitachiAc3StateLength = 27; +const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8; +const uint16_t kHitachiAc3MinStateLength = 15; +const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8; +const uint16_t kHitachiAc264StateLength = 33; +const uint16_t kHitachiAc264Bits = kHitachiAc264StateLength * 8; +const uint16_t kHitachiAc296StateLength = 37; +const uint16_t kHitachiAc296Bits = kHitachiAc296StateLength * 8; +const uint16_t kHitachiAc344StateLength = 43; +const uint16_t kHitachiAc344Bits = kHitachiAc344StateLength * 8; +const uint16_t kHitachiAc424StateLength = 53; +const uint16_t kHitachiAc424Bits = kHitachiAc424StateLength * 8; +const uint16_t kInaxBits = 24; +const uint16_t kInaxMinRepeat = kSingleRepeat; +const uint16_t kJvcBits = 16; +const uint16_t kKelonBits = 48; +const uint16_t kKelon168StateLength = 21; +const uint16_t kKelon168Bits = kKelon168StateLength * 8; +const uint16_t kKelvinatorStateLength = 16; +const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; +const uint16_t kKelvinatorDefaultRepeat = kNoRepeat; +const uint16_t kLasertagBits = 13; +const uint16_t kLasertagMinRepeat = kNoRepeat; +const uint16_t kLegoPfBits = 16; +const uint16_t kLegoPfMinRepeat = kNoRepeat; +const uint16_t kLgBits = 28; +const uint16_t kLg32Bits = 32; +const uint16_t kLgDefaultRepeat = kNoRepeat; +const uint16_t kLutronBits = 35; +const uint16_t kMagiquestBits = 56; +const uint16_t kMetzBits = 19; +const uint16_t kMetzMinRepeat = kNoRepeat; +const uint16_t kMideaBits = 48; +const uint16_t kMideaMinRepeat = kNoRepeat; +const uint16_t kMidea24Bits = 24; +const uint16_t kMidea24MinRepeat = kSingleRepeat; +const uint16_t kMirageStateLength = 15; +const uint16_t kMirageBits = kMirageStateLength * 8; +const uint16_t kMirageMinRepeat = kNoRepeat; +const uint16_t kMitsubishiBits = 16; +// TODO(anyone): Verify that the Mitsubishi repeat is really needed. +// Based on marcosamarinho's code. +const uint16_t kMitsubishiMinRepeat = kSingleRepeat; +const uint16_t kMitsubishiACStateLength = 18; +const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8; +const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; +const uint16_t kMitsubishi136StateLength = 17; +const uint16_t kMitsubishi136Bits = kMitsubishi136StateLength * 8; +const uint16_t kMitsubishi136MinRepeat = kNoRepeat; +const uint16_t kMitsubishi112StateLength = 14; +const uint16_t kMitsubishi112Bits = kMitsubishi112StateLength * 8; +const uint16_t kMitsubishi112MinRepeat = kNoRepeat; +const uint16_t kMitsubishiHeavy88StateLength = 11; +const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8; +const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; +const uint16_t kMitsubishiHeavy152StateLength = 19; +const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; +const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; +const uint16_t kMultibracketsBits = 8; +const uint16_t kMultibracketsDefaultRepeat = kSingleRepeat; +const uint16_t kNikaiBits = 24; +const uint16_t kNECBits = 32; +const uint16_t kNeoclimaStateLength = 12; +const uint16_t kNeoclimaBits = kNeoclimaStateLength * 8; +const uint16_t kNeoclimaMinRepeat = kNoRepeat; +const uint16_t kPanasonicBits = 48; +const uint32_t kPanasonicManufacturer = 0x4004; +const uint32_t kPanasonic40Manufacturer = 0x34; +const uint16_t kPanasonic40Bits = 40; +const uint16_t kPanasonicAcStateLength = 27; +const uint16_t kPanasonicAcStateShortLength = 16; +const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8; +const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8; +const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat; +const uint16_t kPanasonicAc32Bits = 32; +const uint16_t kPioneerBits = 64; +const uint16_t kProntoMinLength = 6; +const uint16_t kRC5RawBits = 14; +const uint16_t kRC5Bits = kRC5RawBits - 2; +const uint16_t kRC5XBits = kRC5RawBits - 1; +const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. +const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. +const uint16_t kRCMMBits = 24; +const uint16_t kSamsungBits = 32; +const uint16_t kSamsung36Bits = 36; +const uint16_t kSamsungAcStateLength = 14; +const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8; +const uint16_t kSamsungAcExtendedStateLength = 21; +const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8; +const uint16_t kSamsungAcDefaultRepeat = kNoRepeat; +const uint16_t kSanyoAcStateLength = 9; +const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8; +const uint16_t kSanyoAc88StateLength = 11; +const uint16_t kSanyoAc88Bits = kSanyoAc88StateLength * 8; +const uint16_t kSanyoAc88MinRepeat = 2; +const uint16_t kSanyoAc152StateLength = 19; +const uint16_t kSanyoAc152Bits = kSanyoAc152StateLength * 8; +const uint16_t kSanyoAc152MinRepeat = kNoRepeat; +const uint16_t kSanyoSA8650BBits = 12; +const uint16_t kSanyoLC7461AddressBits = 13; +const uint16_t kSanyoLC7461CommandBits = 8; +const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits + + kSanyoLC7461CommandBits) * 2; +const uint8_t kSharpAddressBits = 5; +const uint8_t kSharpCommandBits = 8; +const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15 +const uint16_t kSharpAcStateLength = 13; +const uint16_t kSharpAcBits = kSharpAcStateLength * 8; // 104 +const uint16_t kSharpAcDefaultRepeat = kNoRepeat; +const uint8_t kSherwoodBits = kNECBits; +const uint16_t kSherwoodMinRepeat = kSingleRepeat; +const uint16_t kSony12Bits = 12; +const uint16_t kSony15Bits = 15; +const uint16_t kSony20Bits = 20; +const uint16_t kSonyMinBits = 12; +const uint16_t kSonyMinRepeat = 2; +const uint16_t kSymphonyBits = 12; +const uint16_t kSymphonyDefaultRepeat = 3; +const uint16_t kTcl96AcStateLength = 12; +const uint16_t kTcl96AcBits = kTcl96AcStateLength * 8; +const uint16_t kTcl96AcDefaultRepeat = kNoRepeat; +const uint16_t kTcl112AcStateLength = 14; +const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; +const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; +const uint16_t kTecoBits = 35; +const uint16_t kTecoDefaultRepeat = kNoRepeat; +const uint16_t kTeknopointStateLength = 14; +const uint16_t kTeknopointBits = kTeknopointStateLength * 8; +const uint16_t kToshibaACStateLength = 9; +const uint16_t kToshibaACBits = kToshibaACStateLength * 8; +const uint16_t kToshibaACMinRepeat = kSingleRepeat; +const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2; +const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8; +const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1; +const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8; +const uint16_t kTotoBits = 24; +const uint16_t kTotoShortBits = kTotoBits; +const uint16_t kTotoLongBits = kTotoShortBits * 2; +const uint16_t kTotoDefaultRepeat = kSingleRepeat; +const uint16_t kTranscoldBits = 24; +const uint16_t kTranscoldDefaultRepeat = kNoRepeat; +const uint16_t kTrotecStateLength = 9; +const uint16_t kTrotecBits = kTrotecStateLength * 8; +const uint16_t kTrotecDefaultRepeat = kNoRepeat; +const uint16_t kTrumaBits = 56; +const uint16_t kWhirlpoolAcStateLength = 21; +const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; +const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; +const uint16_t kWhynterBits = 32; +const uint16_t kWowweeBits = 11; +const uint16_t kWowweeDefaultRepeat = kNoRepeat; +const uint8_t kVestelAcBits = 56; +const uint16_t kXmpBits = 64; +const uint16_t kZepealBits = 16; +const uint16_t kZepealMinRepeat = 4; +const uint16_t kVoltasBits = 80; +const uint16_t kVoltasStateLength = 10; +const uint16_t kMilesTag2ShotBits = 14; +const uint16_t kMilesTag2MsgBits = 24; +const uint16_t kMilesMinRepeat = 0; +const uint16_t kBoseBits = 16; +const uint16_t kRhossStateLength = 12; +const uint16_t kRhossBits = kRhossStateLength * 8; +const uint16_t kRhossDefaultRepeat = 0; +const uint16_t kClimaButlerBits = 52; +const uint16_t kYorkBits = 136; +const uint16_t kYorkStateLength = 17; + + +// Legacy defines. (Deprecated) +#define AIWA_RC_T501_BITS kAiwaRcT501Bits +#define ARGO_COMMAND_LENGTH kArgoStateLength +#define COOLIX_BITS kCoolixBits +#define CARRIER_AC_BITS kCarrierAcBits +#define DAIKIN_COMMAND_LENGTH kDaikinStateLength +#define DENON_BITS kDenonBits +#define DENON_48_BITS kDenon48Bits +#define DENON_LEGACY_BITS kDenonLegacyBits +#define DISH_BITS kDishBits +#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat +#define FUJITSU_AC_STATE_LENGTH kFujitsuAcStateLength +#define FUJITSU_AC_STATE_LENGTH_SHORT kFujitsuAcStateLengthShort +#define FUJITSU_AC_BITS kFujitsuAcBits +#define FUJITSU_AC_MIN_BITS kFujitsuAcMinBits +#define GICABLE_BITS kGicableBits +#define GREE_STATE_LENGTH kGreeStateLength +#define HAIER_AC_STATE_LENGTH kHaierACStateLength +#define HAIER_AC_YRW02_STATE_LENGTH kHaierACYRW02StateLength +#define HITACHI_AC_STATE_LENGTH kHitachiAcStateLength +#define HITACHI_AC_BITS kHitachiAcBits +#define HITACHI_AC1_STATE_LENGTH kHitachiAc1StateLength +#define HITACHI_AC1_BITS kHitachiAc1Bits +#define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength +#define HITACHI_AC2_BITS kHitachiAc2Bits +#define HITACHI_AC296_STATE_LENGTH kHitachiAc296StateLength +#define HITACHI_AC296_BITS kHitachiAc296Bits +#define JVC_BITS kJvcBits +#define KELVINATOR_STATE_LENGTH kKelvinatorStateLength +#define LASERTAG_BITS kLasertagBits +#define LG_BITS kLgBits +#define LG32_BITS kLg32Bits +#define MAGIQUEST_BITS kMagiquestBits +#define MIDEA_BITS kMideaBits +#define MITSUBISHI_BITS kMitsubishiBits +#define MITSUBISHI_AC_STATE_LENGTH kMitsubishiACStateLength +#define NEC_BITS kNECBits +#define NIKAI_BITS kNikaiBits +#define PANASONIC_BITS kPanasonicBits +#define RC5_BITS kRC5Bits +#define RC5X_BITS kRC5XBits +#define RC6_MODE0_BITS kRC6Mode0Bits +#define RC6_36_BITS kRC6_36Bits +#define RCMM_BITS kRCMMBits +#define SANYO_LC7461_BITS kSanyoLC7461Bits +#define SAMSUNG_BITS kSamsungBits +#define SANYO_SA8650B_BITS kSanyoSA8650BBits +#define SHARP_BITS kSharpBits +#define SHERWOOD_BITS kSherwoodBits +#define SONY_12_BITS kSony12Bits +#define SONY_15_BITS kSony15Bits +#define SONY_20_BITS kSony20Bits +#define TOSHIBA_AC_STATE_LENGTH kToshibaACStateLength +#define TROTEC_COMMAND_LENGTH kTrotecStateLength +#define WHYNTER_BITS kWhynterBits + +// Turn on Debugging information by uncommenting the following line. +// #define DEBUG 1 + +#ifdef DEBUG +#ifdef UNIT_TEST +#define DPRINT(x) do { std::cout << x; } while (0) +#define DPRINTLN(x) do { std::cout << x << std::endl; } while (0) +#endif // UNIT_TEST +#ifdef ARDUINO +#define DPRINT(x) do { Serial.print(x); } while (0) +#define DPRINTLN(x) do { Serial.println(x); } while (0) +#endif // ARDUINO +#else // DEBUG +#define DPRINT(x) +#define DPRINTLN(x) +#endif // DEBUG + +#ifdef UNIT_TEST +#ifndef F +// Create a no-op F() macro so the code base still compiles outside of the +// Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out +// the code base. That macro stores constants in Flash (PROGMEM) memory. +// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/667 +#define F(x) x +#endif // F +typedef std::string String; +#endif // UNIT_TEST + +#endif // IRREMOTEESP8266_H_ diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 10e440b32..bb7c1852f 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -1,1441 +1,1448 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2017,2019 David Conran - -#include "IRsend.h" -#ifndef UNIT_TEST -#include -#else -#define __STDC_LIMIT_MACROS -#include -#endif -#include -#ifdef UNIT_TEST -#include -#endif -#include "IRtimer.h" - -/// Constructor for an IRsend object. -/// @param[in] IRsendPin Which GPIO pin to use when sending an IR command. -/// @param[in] inverted Optional flag to invert the output. (default = false) -/// e.g. LED is illuminated when GPIO is LOW rather than HIGH. -/// @warning Setting `inverted` to something other than the default could -/// easily destroy your IR LED if you are overdriving it. -/// Unless you *REALLY* know what you are doing, don't change this. -/// @param[in] use_modulation Do we do frequency modulation during transmission? -/// i.e. If not, assume a 100% duty cycle. Ignore attempts to change the -/// duty cycle etc. -IRsend::IRsend(uint16_t IRsendPin, bool inverted, bool use_modulation) - : IRpin(IRsendPin), periodOffset(kPeriodOffset) { - if (inverted) { - outputOn = LOW; - outputOff = HIGH; - } else { - outputOn = HIGH; - outputOff = LOW; - } - modulation = use_modulation; - if (modulation) - _dutycycle = kDutyDefault; - else - _dutycycle = kDutyMax; -} - -/// Enable the pin for output. -void IRsend::begin() { -#ifndef UNIT_TEST - pinMode(IRpin, OUTPUT); -#endif - ledOff(); // Ensure the LED is in a known safe state when we start. -} - -/// Turn off the IR LED. -void IRsend::ledOff() { -#ifndef UNIT_TEST - digitalWrite(IRpin, outputOff); -#endif -} - -/// Turn on the IR LED. -void IRsend::ledOn() { -#ifndef UNIT_TEST - digitalWrite(IRpin, outputOn); -#endif -} - -/// Calculate the period for a given frequency. -/// @param[in] hz Frequency in Hz. -/// @param[in] use_offset Should we use the calculated offset or not? -/// @return nr. of uSeconds. -/// @note (T = 1/f) -uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { - if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. - uint32_t period = - (1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz). - // Apply the offset and ensure we don't result in a <= 0 value. - if (use_offset) - return std::max((uint32_t)1, period + periodOffset); - else - return std::max((uint32_t)1, period); -} - -/// Set the output frequency modulation and duty cycle. -/// @param[in] freq The freq we want to modulate at. -/// Assumes < 1000 means kHz else Hz. -/// @param[in] duty Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// This is ignored if modulation is disabled at object instantiation. -/// @note Integer timing functions & math mean we can't do fractions of -/// microseconds timing. Thus minor changes to the freq & duty values may have -/// limited effect. You've been warned. -void IRsend::enableIROut(uint32_t freq, uint8_t duty) { - // Set the duty cycle to use if we want freq. modulation. - if (modulation) { - _dutycycle = std::min(duty, kDutyMax); - } else { - _dutycycle = kDutyMax; - } - if (freq < 1000) // Were we given kHz? Supports the old call usage. - freq *= 1000; -#ifdef UNIT_TEST - _freq_unittest = freq; -#endif // UNIT_TEST - uint32_t period = calcUSecPeriod(freq); - // Nr. of uSeconds the LED will be on per pulse. - onTimePeriod = (period * _dutycycle) / kDutyMax; - // Nr. of uSeconds the LED will be off per pulse. - offTimePeriod = period - onTimePeriod; -} - -#if ALLOW_DELAY_CALLS -/// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds(). -/// @param[in] usec Nr. of uSeconds to delay for. -void IRsend::_delayMicroseconds(uint32_t usec) { - // delayMicroseconds() is only accurate to 16383us. - // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds - if (usec <= kMaxAccurateUsecDelay) { -#ifndef UNIT_TEST - delayMicroseconds(usec); -#endif - } else { -#ifndef UNIT_TEST - // Invoke a delay(), where possible, to avoid triggering the WDT. - delay(usec / 1000UL); // Delay for as many whole milliseconds as we can. - // Delay the remaining sub-millisecond. - delayMicroseconds(static_cast(usec % 1000UL)); -#endif - } -} -#else // ALLOW_DELAY_CALLS -/// A version of delayMicroseconds() that handles large values and does NOT use -/// the watch-dog friendly delay() calls where appropriate. -/// @note Use this only if you know what you are doing as it may cause the WDT -/// to reset the ESP8266. -void IRsend::_delayMicroseconds(uint32_t usec) { - for (; usec > kMaxAccurateUsecDelay; usec -= kMaxAccurateUsecDelay) -#ifndef UNIT_TEST - delayMicroseconds(kMaxAccurateUsecDelay); - delayMicroseconds(static_cast(usec)); -#endif // UNIT_TEST -} -#endif // ALLOW_DELAY_CALLS - -/// Modulate the IR LED for the given period (usec) and at the duty cycle set. -/// @param[in] usec The period of time to modulate the IR LED for, in -/// microseconds. -/// @return Nr. of pulses actually sent. -/// @note -/// The ESP8266 has no good way to do hardware PWM, so we have to do it all -/// in software. There is a horrible kludge/brilliant hack to use the second -/// serial TX line to do fairly accurate hardware PWM, but it is only -/// available on a single specific GPIO and only available on some modules. -/// e.g. It's not available on the ESP-01 module. -/// Hence, for greater compatibility & choice, we don't use that method. -/// Ref: -/// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ -uint16_t IRsend::mark(uint16_t usec) { - // Handle the simple case of no required frequency modulation. - if (!modulation || _dutycycle >= 100) { - ledOn(); - _delayMicroseconds(usec); - ledOff(); - return 1; - } - - // Not simple, so do it assuming frequency modulation. - uint16_t counter = 0; - IRtimer usecTimer = IRtimer(); - // Cache the time taken so far. This saves us calling time, and we can be - // assured that we can't have odd math problems. i.e. unsigned under/overflow. - uint32_t elapsed = usecTimer.elapsed(); - - while (elapsed < usec) { // Loop until we've met/exceeded our required time. - ledOn(); - // Calculate how long we should pulse on for. - // e.g. Are we to close to the end of our requested mark time (usec)? - _delayMicroseconds(std::min((uint32_t)onTimePeriod, usec - elapsed)); - ledOff(); - counter++; - if (elapsed + onTimePeriod >= usec) - return counter; // LED is now off & we've passed our allotted time. - // Wait for the lesser of the rest of the duty cycle, or the time remaining. - _delayMicroseconds( - std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod)); - elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. - } - return counter; -} - -/// Turn the pin (LED) off for a given time. -/// Sends an IR space for the specified number of microseconds. -/// A space is no output, so the PWM output is disabled. -/// @param[in] time Time in microseconds (us). -void IRsend::space(uint32_t time) { - ledOff(); - if (time == 0) return; - _delayMicroseconds(time); -} - -/// Calculate & set any offsets to account for execution times during sending. -/// -/// @param[in] hz The frequency to calibrate at >= 1000Hz. Default is 38000Hz. -/// @return The calculated period offset (in uSeconds) which is now in use. -/// e.g. -5. -/// @note This will generate an 65535us mark() IR LED signal. -/// This only needs to be called once, if at all. -int8_t IRsend::calibrate(uint16_t hz) { - if (hz < 1000) // Were we given kHz? Supports the old call usage. - hz *= 1000; - periodOffset = 0; // Turn off any existing offset while we calibrate. - enableIROut(hz); - IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. - uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) - uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. - // While it shouldn't be necessary, assume at least 1 pulse, to avoid a - // divide by 0 situation. - pulses = std::max(pulses, (uint16_t)1U); - uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. - // Assuming 38kHz for the example calculations: - // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. - // e.g. 65535.0us / 26us = 2520.5769 - // This should have caused approx 2520 loops through the main loop in mark(). - // The average over that many interations should give us a reasonable - // approximation at what offset we need to use to account for instruction - // execution times. - // - // Calculate the actual period from the actual time & the actual pulses - // generated. - double_t actualPeriod = (double_t)timeTaken / (double_t)pulses; - // Store the difference between the actual time per period vs. calculated. - periodOffset = (int8_t)((double_t)calcPeriod - actualPeriod); - return periodOffset; -} - -/// Generic method for sending data that is common to most protocols. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -void IRsend::sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, - uint32_t zerospace, uint64_t data, uint16_t nbits, - bool MSBfirst) { - if (nbits == 0) // If we are asked to send nothing, just return. - return; - if (MSBfirst) { // Send the MSB first. - // Send 0's until we get down to a bit size we can actually manage. - while (nbits > sizeof(data) * 8) { - mark(zeromark); - space(zerospace); - nbits--; - } - // Send the supplied data. - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) { // Send a 1 - mark(onemark); - space(onespace); - } else { // Send a 0 - mark(zeromark); - space(zerospace); - } - } else { // Send the Least Significant Bit (LSB) first / MSB last. - for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1) - if (data & 1) { // Send a 1 - mark(onemark); - space(onespace); - } else { // Send a 0 - mark(zeromark); - space(zerospace); - } - } -} - -/// Generic method for sending simple protocol messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint64_t data, const uint16_t nbits, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle) { - sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace, - footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat, - dutycycle); -} - -/// Generic method for sending simple protocol messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] mesgtime Min. nr. of usecs a single message needs to be. -/// This is effectively the min. total length of a single message. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint32_t mesgtime, const uint64_t data, - const uint16_t nbits, const uint16_t frequency, - const bool MSBfirst, const uint16_t repeat, - const uint8_t dutycycle) { - // Setup - enableIROut(frequency, dutycycle); - IRtimer usecs = IRtimer(); - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - usecs.reset(); - - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - - // Data - sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst); - - // Footer - if (footermark) mark(footermark); - uint32_t elapsed = usecs.elapsed(); - // Avoid potential unsigned integer underflow. e.g. when mesgtime is 0. - if (elapsed >= mesgtime) - space(gap); - else - space(std::max(gap, mesgtime - elapsed)); - } -} - -/// Generic method for sending simple protocol messages. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] dataptr Pointer to the data to be transmitted. -/// @param[in] nbytes Nr. of bytes of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint8_t *dataptr, const uint16_t nbytes, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle) { - // Setup - enableIROut(frequency, dutycycle); - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - - // Data - for (uint16_t i = 0; i < nbytes; i++) - sendData(onemark, onespace, zeromark, zerospace, *(dataptr + i), 8, - MSBfirst); - - // Footer - if (footermark) mark(footermark); - space(gap); - } -} - -/// Generic method for sending Manchester code data. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// (1/2 wavelength) -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). -void IRsend::sendManchesterData(const uint16_t half_period, - const uint64_t data, - const uint16_t nbits, const bool MSBfirst, - const bool GEThomas) { - if (nbits == 0) return; // Nothing to send. - uint16_t bits = nbits; - uint64_t copy = (GEThomas) ? data : ~data; - - if (MSBfirst) { // Send the MSB first. - // Send 0's until we get down to a bit size we can actually manage. - if (bits > (sizeof(data) * 8)) { - sendManchesterData(half_period, 0ULL, bits - sizeof(data) * 8, MSBfirst, - GEThomas); - bits = sizeof(data) * 8; - } - // Send the supplied data. - for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) - if (copy & mask) { - mark(half_period); - space(half_period); - } else { - space(half_period); - mark(half_period); - } - } else { // Send the Least Significant Bit (LSB) first / MSB last. - for (bits = 0; bits < nbits; bits++, copy >>= 1) - if (copy & 1) { - mark(half_period); - space(half_period); - } else { - space(half_period); - mark(half_period); - } - } -} - -/// Generic method for sending Manchester code messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// (1/2 wavelength) -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Min. nr. of usecs for the led to be off after the footer -/// mark. This is effectively the absolute minimum gap between messages. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendManchester(const uint16_t headermark, - const uint32_t headerspace, - const uint16_t half_period, - const uint16_t footermark, const uint32_t gap, - const uint64_t data, const uint16_t nbits, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle, - const bool GEThomas) { - // Setup - enableIROut(frequency, dutycycle); - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - // Data - sendManchesterData(half_period, data, nbits, MSBfirst, GEThomas); - // Footer - if (footermark) mark(footermark); - if (gap) space(gap); - } -} - -#if SEND_RAW -/// Send a raw IRremote message. -/// -/// @param[in] buf An array of uint16_t's that has microseconds elements. -/// @param[in] len Nr. of elements in the buf[] array. -/// @param[in] hz Frequency to send the message at. (kHz < 1000; Hz >= 1000) -/// @note Even elements are Mark times (On), Odd elements are Space times (Off). -/// Ref: -/// examples/IRrecvDumpV2/IRrecvDumpV2.ino (or later) -void IRsend::sendRaw(const uint16_t buf[], const uint16_t len, - const uint16_t hz) { - // Set IR carrier frequency - enableIROut(hz); - for (uint16_t i = 0; i < len; i++) { - if (i & 1) { // Odd bit. - space(buf[i]); - } else { // Even bit. - mark(buf[i]); - } - } - ledOff(); // We potentially have ended with a mark(), so turn of the LED. -} -#endif // SEND_RAW - -/// Get the minimum number of repeats for a given protocol. -/// @param[in] protocol Protocol number/type of the message you want to send. -/// @return The number of repeats required. -uint16_t IRsend::minRepeats(const decode_type_t protocol) { - switch (protocol) { - // Single repeats - case AIWA_RC_T501: - case AMCOR: - case COOLIX: - case COOLIX48: - case ELITESCREENS: - case GICABLE: - case INAX: - case MIDEA24: - case MITSUBISHI: - case MITSUBISHI2: - case MITSUBISHI_AC: - case MULTIBRACKETS: - case SHERWOOD: - case TOSHIBA_AC: - case TOTO: - return kSingleRepeat; - // Special - case AIRWELL: - return kAirwellMinRepeats; - case CARRIER_AC40: - return kCarrierAc40MinRepeat; - case DISH: - return kDishMinRepeat; - case EPSON: - return kEpsonMinRepeat; - case SANYO_AC88: - return kSanyoAc88MinRepeat; - case SONY: - return kSonyMinRepeat; - case SONY_38K: - return kSonyMinRepeat + 1; - case SYMPHONY: - return kSymphonyDefaultRepeat; - case ZEPEAL: - return kZepealMinRepeat; - default: - return kNoRepeat; - } -} - -/// Get the default number of bits for a given protocol. -/// @param[in] protocol Protocol number/type you want the default bit size for. -/// @return The number of bits. -uint16_t IRsend::defaultBits(const decode_type_t protocol) { - switch (protocol) { - case MULTIBRACKETS: - case GORENJE: - return 8; - case WOWWEE: - return 11; - case RC5: - case SYMPHONY: - return 12; - case LASERTAG: - case RC5X: - return 13; - case AIWA_RC_T501: - case DENON: - case SHARP: - return 15; - case BOSE: - case DISH: - case GICABLE: - case JVC: - case LEGOPF: - case MITSUBISHI: - case MITSUBISHI2: - case ZEPEAL: - return 16; - case METZ: - return 19; - case RC6: - case SONY: - case SONY_38K: - return 20; - case COOLIX: - case INAX: - case MIDEA24: - case NIKAI: - case RCMM: - case TOTO: - case TRANSCOLD: - return 24; - case LG: - case LG2: - return 28; - case ARRIS: - case CARRIER_AC: - case ELITESCREENS: - case EPSON: - case NEC: - case NEC_LIKE: - case PANASONIC_AC32: - case SAMSUNG: - case SHERWOOD: - case WHYNTER: - return 32; - case AIRWELL: - return 34; - case LUTRON: - case TECO: - return 35; - case SAMSUNG36: - return 36; - case CARRIER_AC40: - return kCarrierAc40Bits; // 40 - case DOSHISHA: - return kDoshishaBits; // 40 - case SANYO_LC7461: - return kSanyoLC7461Bits; // 42 - case COOLIX48: - case GOODWEATHER: - case KELON: - case MIDEA: - case PANASONIC: - return 48; - case CLIMABUTLER: - return kClimaButlerBits; // 52 - case AIRTON: - case ECOCLIM: - case MAGIQUEST: - case VESTEL_AC: - case TECHNIBEL_AC: - case TRUMA: - return 56; - case AMCOR: - case CARRIER_AC64: - case DELONGHI_AC: - case PIONEER: - return 64; - case ARGO: - return kArgoBits; - case BOSCH144: - return kBosch144Bits; - case CORONA_AC: - return kCoronaAcBits; - case CARRIER_AC84: - return kCarrierAc84Bits; - case CARRIER_AC128: - return kCarrierAc128Bits; - case DAIKIN: - return kDaikinBits; - case DAIKIN128: - return kDaikin128Bits; - case DAIKIN152: - return kDaikin152Bits; - case DAIKIN160: - return kDaikin160Bits; - case DAIKIN176: - return kDaikin176Bits; - case DAIKIN2: - return kDaikin2Bits; - case DAIKIN200: - return kDaikin200Bits; - case DAIKIN216: - return kDaikin216Bits; - case DAIKIN312: - return kDaikin312Bits; - case DAIKIN64: - return kDaikin64Bits; - case ELECTRA_AC: - return kElectraAcBits; - case GREE: - return kGreeBits; - case HAIER_AC: - return kHaierACBits; - case HAIER_AC_YRW02: - return kHaierACYRW02Bits; - case HAIER_AC160: - return kHaierAC160Bits; - case HAIER_AC176: - return kHaierAC176Bits; - case HITACHI_AC: - return kHitachiAcBits; - case HITACHI_AC1: - return kHitachiAc1Bits; - case HITACHI_AC2: - return kHitachiAc2Bits; - case HITACHI_AC3: - return kHitachiAc3Bits; - case HITACHI_AC264: - return kHitachiAc264Bits; - case HITACHI_AC296: - return kHitachiAc296Bits; - case HITACHI_AC344: - return kHitachiAc344Bits; - case HITACHI_AC424: - return kHitachiAc424Bits; - case KELON168: - return kKelon168Bits; - case KELVINATOR: - return kKelvinatorBits; - case MILESTAG2: - return kMilesTag2ShotBits; - case MIRAGE: - return kMirageBits; - case MITSUBISHI_AC: - return kMitsubishiACBits; - case MITSUBISHI136: - return kMitsubishi136Bits; - case MITSUBISHI112: - return kMitsubishi112Bits; - case MITSUBISHI_HEAVY_152: - return kMitsubishiHeavy152Bits; - case MITSUBISHI_HEAVY_88: - return kMitsubishiHeavy88Bits; - case NEOCLIMA: - return kNeoclimaBits; - case PANASONIC_AC: - return kPanasonicAcBits; - case RHOSS: - return kRhossBits; - case SAMSUNG_AC: - return kSamsungAcBits; - case SANYO_AC: - return kSanyoAcBits; - case SANYO_AC88: - return kSanyoAc88Bits; - case SANYO_AC152: - return kSanyoAc152Bits; - case SHARP_AC: - return kSharpAcBits; - case TCL96AC: - return kTcl96AcBits; - case TCL112AC: - return kTcl112AcBits; - case TEKNOPOINT: - return kTeknopointBits; - case TOSHIBA_AC: - return kToshibaACBits; - case TROTEC: - case TROTEC_3550: - return kTrotecBits; - case VOLTAS: - return kVoltasBits; - case WHIRLPOOL_AC: - return kWhirlpoolAcBits; - case XMP: - return kXmpBits; - case YORK: - return kYorkBits; - // No default amount of bits. - case FUJITSU_AC: - case MWM: - default: - return 0; - } -} - -/// Send a simple (up to 64 bits) IR message of a given type. -/// An unknown/unsupported type will send nothing. -/// @param[in] type Protocol number/type of the message you want to send. -/// @param[in] data The data you want to send (up to 64 bits). -/// @param[in] nbits How many bits long the message is to be. -/// @param[in] repeat How many repeats to do? -/// @return True if it is a type we can attempt to send, false if not. -bool IRsend::send(const decode_type_t type, const uint64_t data, - const uint16_t nbits, const uint16_t repeat) { - uint16_t min_repeat __attribute__((unused)) = - std::max(IRsend::minRepeats(type), repeat); - switch (type) { -#if SEND_AIRTON - case AIRTON: - sendAirton(data, nbits, min_repeat); - break; -#endif // SEND_AIRTON -#if SEND_AIRWELL - case AIRWELL: - sendAirwell(data, nbits, min_repeat); - break; -#endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: - sendAiwaRCT501(data, nbits, min_repeat); - break; -#endif // SEND_AIWA_RC_T501 -#if SEND_ARRIS - case ARRIS: - sendArris(data, nbits, min_repeat); - break; -#endif // SEND_ARRIS -#if SEND_BOSE - case BOSE: - sendBose(data, nbits, min_repeat); - break; -#endif // SEND_BOSE -#if SEND_CARRIER_AC - case CARRIER_AC: - sendCarrierAC(data, nbits, min_repeat); - break; -#endif -#if SEND_CARRIER_AC40 - case CARRIER_AC40: - sendCarrierAC40(data, nbits, min_repeat); - break; -#endif // SEND_CARRIER_AC40 -#if SEND_CARRIER_AC64 - case CARRIER_AC64: - sendCarrierAC64(data, nbits, min_repeat); - break; -#endif // SEND_CARRIER_AC64 -#if SEND_CLIMABUTLER - case CLIMABUTLER: - sendClimaButler(data, nbits, min_repeat); - break; -#endif // SEND_CLIMABUTLER -#if SEND_COOLIX - case COOLIX: - sendCOOLIX(data, nbits, min_repeat); - break; -#endif // SEND_COOLIX -#if SEND_COOLIX48 - case COOLIX48: - sendCoolix48(data, nbits, min_repeat); - break; -#endif // SEND_COOLIX48 -#if SEND_DAIKIN64 - case DAIKIN64: - sendDaikin64(data, nbits, min_repeat); - break; -#endif -#if SEND_DELONGHI_AC - case DELONGHI_AC: - sendDelonghiAc(data, nbits, min_repeat); - break; -#endif -#if SEND_DENON - case DENON: - sendDenon(data, nbits, min_repeat); - break; -#endif -#if SEND_DISH - case DISH: - sendDISH(data, nbits, min_repeat); - break; -#endif -#if SEND_DOSHISHA - case DOSHISHA: - sendDoshisha(data, nbits, min_repeat); - break; -#endif -#if SEND_ECOCLIM - case ECOCLIM: - sendEcoclim(data, nbits, min_repeat); - break; -#endif // SEND_ECOCLIM -#if SEND_ELITESCREENS - case ELITESCREENS: - sendElitescreens(data, nbits, min_repeat); - break; -#endif // SEND_ELITESCREENS -#if SEND_EPSON - case EPSON: - sendEpson(data, nbits, min_repeat); - break; -#endif -#if SEND_GICABLE - case GICABLE: - sendGICable(data, nbits, min_repeat); - break; -#endif -#if SEND_GOODWEATHER - case GOODWEATHER: - sendGoodweather(data, nbits, min_repeat); - break; -#endif -#if SEND_GORENJE - case GORENJE: - sendGorenje(data, nbits, min_repeat); - break; -#endif -#if SEND_GREE - case GREE: - sendGree(data, nbits, min_repeat); - break; -#endif -#if SEND_INAX - case INAX: - sendInax(data, nbits, min_repeat); - break; -#endif // SEND_INAX -#if SEND_JVC - case JVC: - sendJVC(data, nbits, min_repeat); - break; -#endif -#if SEND_KELON - case KELON: - sendKelon(data, nbits, min_repeat); - break; -#endif // SEND_KELON -#if SEND_LASERTAG - case LASERTAG: - sendLasertag(data, nbits, min_repeat); - break; -#endif -#if SEND_LEGOPF - case LEGOPF: - sendLegoPf(data, nbits, min_repeat); - break; -#endif -#if SEND_LG - case LG: - sendLG(data, nbits, min_repeat); - break; - case LG2: - sendLG2(data, nbits, min_repeat); - break; -#endif -#if SEND_LUTRON - case LUTRON: - sendLutron(data, nbits, min_repeat); - break; -#endif -#if SEND_MAGIQUEST - case MAGIQUEST: - sendMagiQuest(data, nbits, min_repeat); - break; -#endif // SEND_MAGIQUEST -#if SEND_METZ - case METZ: - sendMetz(data, nbits, min_repeat); - break; -#endif // SEND_METZ -#if SEND_MIDEA - case MIDEA: - sendMidea(data, nbits, min_repeat); - break; -#endif // SEND_MIDEA -#if SEND_MIDEA24 - case MIDEA24: - sendMidea24(data, nbits, min_repeat); - break; -#endif // SEND_MIDEA24 -#if SEND_MILESTAG2 - case MILESTAG2: - sendMilestag2(data, nbits, min_repeat); - break; -#endif // SEND_MILESTAG2 -#if SEND_MITSUBISHI - case MITSUBISHI: - sendMitsubishi(data, nbits, min_repeat); - break; -#endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: - sendMitsubishi2(data, nbits, min_repeat); - break; -#endif -#if SEND_MULTIBRACKETS - case MULTIBRACKETS: - sendMultibrackets(data, nbits, min_repeat); - break; -#endif -#if SEND_NIKAI - case NIKAI: - sendNikai(data, nbits, min_repeat); - break; -#endif -#if SEND_NEC - case NEC: - case NEC_LIKE: - sendNEC(data, nbits, min_repeat); - break; -#endif -#if SEND_PANASONIC - case PANASONIC: - sendPanasonic64(data, nbits, min_repeat); - break; -#endif // SEND_PANASONIC -#if SEND_PANASONIC_AC32 - case PANASONIC_AC32: - sendPanasonicAC32(data, nbits, min_repeat); - break; -#endif // SEND_PANASONIC_AC32 -#if SEND_PIONEER - case PIONEER: - sendPioneer(data, nbits, min_repeat); - break; -#endif -#if SEND_RC5 - case RC5: - case RC5X: - sendRC5(data, nbits, min_repeat); - break; -#endif -#if SEND_RC6 - case RC6: - sendRC6(data, nbits, min_repeat); - break; -#endif -#if SEND_RCMM - case RCMM: - sendRCMM(data, nbits, min_repeat); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: - sendSAMSUNG(data, nbits, min_repeat); - break; -#endif -#if SEND_SAMSUNG36 - case SAMSUNG36: - sendSamsung36(data, nbits, min_repeat); - break; -#endif -#if SEND_SANYO - case SANYO_LC7461: - sendSanyoLC7461(data, nbits, min_repeat); - break; -#endif -#if SEND_SHARP - case SHARP: - sendSharpRaw(data, nbits, min_repeat); - break; -#endif -#if SEND_SHERWOOD - case SHERWOOD: - sendSherwood(data, nbits, min_repeat); - break; -#endif -#if SEND_SONY - case SONY: - sendSony(data, nbits, min_repeat); - break; - case SONY_38K: - sendSony38(data, nbits, min_repeat); - break; -#endif -#if SEND_SYMPHONY - case SYMPHONY: - sendSymphony(data, nbits, min_repeat); - break; -#endif -#if SEND_TECHNIBEL_AC - case TECHNIBEL_AC: - sendTechnibelAc(data, nbits, min_repeat); - break; -#endif -#if SEND_TECO - case TECO: - sendTeco(data, nbits, min_repeat); - break; -#endif // SEND_TECO -#if SEND_TOTO - case TOTO: - sendToto(data, nbits, min_repeat); - break; -#endif // SEND_TOTO -#if SEND_TRANSCOLD - case TRANSCOLD: - sendTranscold(data, nbits, min_repeat); - break; -#endif // SEND_TRANSCOLD -#if SEND_TRUMA - case TRUMA: - sendTruma(data, nbits, min_repeat); - break; -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case VESTEL_AC: - sendVestelAc(data, nbits, min_repeat); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: - sendWhynter(data, nbits, min_repeat); - break; -#endif -#if SEND_WOWWEE - case WOWWEE: - sendWowwee(data, nbits, min_repeat); - break; -#endif // SEND_WOWWEE -#if SEND_XMP - case XMP: - sendXmp(data, nbits, min_repeat); - break; -#endif -#if SEND_ZEPEAL - case ZEPEAL: - sendZepeal(data, nbits, min_repeat); - break; -#endif // SEND_ZEPEAL - default: - return false; - } - return true; -} - -/// Send a complex (>= 64 bits) IR message of a given type. -/// An unknown/unsupported type will send nothing. -/// @param[in] type Protocol number/type of the message you want to send. -/// @param[in] state A pointer to the array of bytes that make up the state[]. -/// @param[in] nbytes How many bytes are in the state. -/// @return True if it is a type we can attempt to send, false if not. -bool IRsend::send(const decode_type_t type, const uint8_t *state, - const uint16_t nbytes) { - switch (type) { -#if SEND_VOLTAS - case VOLTAS: - sendVoltas(state, nbytes); - break; -#endif // SEND_VOLTAS -#if SEND_AMCOR - case AMCOR: - sendAmcor(state, nbytes); - break; -#endif -#if SEND_ARGO - case ARGO: - sendArgo(state, nbytes); - break; -#endif // SEND_ARGO -#if SEND_BOSCH144 - case BOSCH144: - sendBosch144(state, nbytes); - break; -#endif // SEND_BOSCH144 -#if SEND_CARRIER_AC84 - case CARRIER_AC84: - sendCarrierAC84(state, nbytes); - break; -#endif // SEND_CARRIER_AC84 -#if SEND_CARRIER_AC128 - case CARRIER_AC128: - sendCarrierAC128(state, nbytes); - break; -#endif // SEND_CARRIER_AC128 -#if SEND_CORONA_AC - case CORONA_AC: - sendCoronaAc(state, nbytes); - break; -#endif // SEND_ARGO -#if SEND_DAIKIN - case DAIKIN: - sendDaikin(state, nbytes); - break; -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - case DAIKIN128: - sendDaikin128(state, nbytes); - break; -#endif // SEND_DAIKIN128 -#if SEND_DAIKIN152 - case DAIKIN152: - sendDaikin152(state, nbytes); - break; -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - case DAIKIN160: - sendDaikin160(state, nbytes); - break; -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - case DAIKIN176: - sendDaikin176(state, nbytes); - break; -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - case DAIKIN2: - sendDaikin2(state, nbytes); - break; -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN200 - case DAIKIN200: - sendDaikin200(state, nbytes); - break; -#endif // SEND_DAIKIN200 -#if SEND_DAIKIN216 - case DAIKIN216: - sendDaikin216(state, nbytes); - break; -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN312 - case DAIKIN312: - sendDaikin312(state, nbytes); - break; -#endif // SEND_DAIKIN312 -#if SEND_ELECTRA_AC - case ELECTRA_AC: - sendElectraAC(state, nbytes); - break; -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - case FUJITSU_AC: - sendFujitsuAC(state, nbytes); - break; -#endif // SEND_FUJITSU_AC -#if SEND_GREE - case GREE: - sendGree(state, nbytes); - break; -#endif // SEND_GREE -#if SEND_HAIER_AC - case HAIER_AC: - sendHaierAC(state, nbytes); - break; -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - sendHaierACYRW02(state, nbytes); - break; -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HAIER_AC160 - case HAIER_AC160: - sendHaierAC160(state, nbytes); - break; -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - case HAIER_AC176: - sendHaierAC176(state, nbytes); - break; -#endif // SEND_HAIER_AC176 -#if SEND_HITACHI_AC - case HITACHI_AC: - sendHitachiAC(state, nbytes); - break; -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - sendHitachiAC1(state, nbytes); - break; -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC2 - case HITACHI_AC2: - sendHitachiAC2(state, nbytes); - break; -#endif // SEND_HITACHI_AC2 -#if SEND_HITACHI_AC3 - case HITACHI_AC3: - sendHitachiAc3(state, nbytes); - break; -#endif // SEND_HITACHI_AC3 -#if SEND_HITACHI_AC264 - case HITACHI_AC264: - sendHitachiAc264(state, nbytes); - break; -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - case HITACHI_AC296: - sendHitachiAc296(state, nbytes); - break; -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - case HITACHI_AC344: - sendHitachiAc344(state, nbytes); - break; -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - case HITACHI_AC424: - sendHitachiAc424(state, nbytes); - break; -#endif // SEND_HITACHI_AC424 -#if SEND_KELON168 - case KELON168: - sendKelon168(state, nbytes); - break; -#endif // SEND_KELON168 -#if SEND_KELVINATOR - case KELVINATOR: - sendKelvinator(state, nbytes); - break; -#endif // SEND_KELVINATOR -#if SEND_MIRAGE - case MIRAGE: - sendMirage(state, nbytes); - break; -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - sendMitsubishiAC(state, nbytes); - break; -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI136 - case MITSUBISHI136: - sendMitsubishi136(state, nbytes); - break; -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHI112 - case MITSUBISHI112: - sendMitsubishi112(state, nbytes); - break; -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: - sendMitsubishiHeavy88(state, nbytes); - break; - case MITSUBISHI_HEAVY_152: - sendMitsubishiHeavy152(state, nbytes); - break; -#endif // SEND_MITSUBISHIHEAVY -#if SEND_MWM - case MWM: - sendMWM(state, nbytes); - break; -#endif // SEND_MWM -#if SEND_NEOCLIMA - case NEOCLIMA: - sendNeoclima(state, nbytes); - break; -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - case PANASONIC_AC: - sendPanasonicAC(state, nbytes); - break; -#endif // SEND_PANASONIC_AC -#if SEND_RHOSS - case RHOSS: - sendRhoss(state, nbytes); - break; -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - sendSamsungAC(state, nbytes); - break; -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - case SANYO_AC: - sendSanyoAc(state, nbytes); - break; -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - case SANYO_AC88: - sendSanyoAc88(state, nbytes); - break; -#endif // SEND_SANYO_AC88 -#if SEND_SANYO_AC152 - case SANYO_AC152: - sendSanyoAc152(state, nbytes); - break; -#endif // SEND_SANYO_AC152 -#if SEND_SHARP_AC - case SHARP_AC: - sendSharpAc(state, nbytes); - break; -#endif // SEND_SHARP_AC -#if SEND_TCL96AC - case TCL96AC: - sendTcl96Ac(state, nbytes); - break; -#endif // SEND_TCL96AC -#if SEND_TCL112AC - case TCL112AC: - sendTcl112Ac(state, nbytes); - break; -#endif // SEND_TCL112AC -#if SEND_TEKNOPOINT - case TEKNOPOINT: - sendTeknopoint(state, nbytes); - break; -#endif // SEND_TEKNOPOINT -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - sendToshibaAC(state, nbytes); - break; -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - case TROTEC: - sendTrotec(state, nbytes); - break; -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - case TROTEC_3550: - sendTrotec3550(state, nbytes); - break; -#endif // SEND_TROTEC_3550 -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - sendWhirlpoolAC(state, nbytes); - break; -#endif // SEND_WHIRLPOOL_AC -#if SEND_YORK - case YORK: - sendYork(state, nbytes); - break; -#endif // SEND_YORK - default: - return false; - } - return true; -} +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2017,2019 David Conran + +#include "IRsend.h" +#ifndef UNIT_TEST +#include +#else +#define __STDC_LIMIT_MACROS +#include +#endif +#include +#ifdef UNIT_TEST +#include +#endif +#include "IRtimer.h" + +/// Constructor for an IRsend object. +/// @param[in] IRsendPin Which GPIO pin to use when sending an IR command. +/// @param[in] inverted Optional flag to invert the output. (default = false) +/// e.g. LED is illuminated when GPIO is LOW rather than HIGH. +/// @warning Setting `inverted` to something other than the default could +/// easily destroy your IR LED if you are overdriving it. +/// Unless you *REALLY* know what you are doing, don't change this. +/// @param[in] use_modulation Do we do frequency modulation during transmission? +/// i.e. If not, assume a 100% duty cycle. Ignore attempts to change the +/// duty cycle etc. +IRsend::IRsend(uint16_t IRsendPin, bool inverted, bool use_modulation) + : IRpin(IRsendPin), periodOffset(kPeriodOffset) { + if (inverted) { + outputOn = LOW; + outputOff = HIGH; + } else { + outputOn = HIGH; + outputOff = LOW; + } + modulation = use_modulation; + if (modulation) + _dutycycle = kDutyDefault; + else + _dutycycle = kDutyMax; +} + +/// Enable the pin for output. +void IRsend::begin() { +#ifndef UNIT_TEST + pinMode(IRpin, OUTPUT); +#endif + ledOff(); // Ensure the LED is in a known safe state when we start. +} + +/// Turn off the IR LED. +void IRsend::ledOff() { +#ifndef UNIT_TEST + digitalWrite(IRpin, outputOff); +#endif +} + +/// Turn on the IR LED. +void IRsend::ledOn() { +#ifndef UNIT_TEST + digitalWrite(IRpin, outputOn); +#endif +} + +/// Calculate the period for a given frequency. +/// @param[in] hz Frequency in Hz. +/// @param[in] use_offset Should we use the calculated offset or not? +/// @return nr. of uSeconds. +/// @note (T = 1/f) +uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { + if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. + uint32_t period = + (1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz). + // Apply the offset and ensure we don't result in a <= 0 value. + if (use_offset) + return std::max((uint32_t)1, period + periodOffset); + else + return std::max((uint32_t)1, period); +} + +/// Set the output frequency modulation and duty cycle. +/// @param[in] freq The freq we want to modulate at. +/// Assumes < 1000 means kHz else Hz. +/// @param[in] duty Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// This is ignored if modulation is disabled at object instantiation. +/// @note Integer timing functions & math mean we can't do fractions of +/// microseconds timing. Thus minor changes to the freq & duty values may have +/// limited effect. You've been warned. +void IRsend::enableIROut(uint32_t freq, uint8_t duty) { + // Set the duty cycle to use if we want freq. modulation. + if (modulation) { + _dutycycle = std::min(duty, kDutyMax); + } else { + _dutycycle = kDutyMax; + } + if (freq < 1000) // Were we given kHz? Supports the old call usage. + freq *= 1000; +#ifdef UNIT_TEST + _freq_unittest = freq; +#endif // UNIT_TEST + uint32_t period = calcUSecPeriod(freq); + // Nr. of uSeconds the LED will be on per pulse. + onTimePeriod = (period * _dutycycle) / kDutyMax; + // Nr. of uSeconds the LED will be off per pulse. + offTimePeriod = period - onTimePeriod; +} + +#if ALLOW_DELAY_CALLS +/// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds(). +/// @param[in] usec Nr. of uSeconds to delay for. +void IRsend::_delayMicroseconds(uint32_t usec) { + // delayMicroseconds() is only accurate to 16383us. + // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds + if (usec <= kMaxAccurateUsecDelay) { +#ifndef UNIT_TEST + delayMicroseconds(usec); +#endif + } else { +#ifndef UNIT_TEST + // Invoke a delay(), where possible, to avoid triggering the WDT. + delay(usec / 1000UL); // Delay for as many whole milliseconds as we can. + // Delay the remaining sub-millisecond. + delayMicroseconds(static_cast(usec % 1000UL)); +#endif + } +} +#else // ALLOW_DELAY_CALLS +/// A version of delayMicroseconds() that handles large values and does NOT use +/// the watch-dog friendly delay() calls where appropriate. +/// @note Use this only if you know what you are doing as it may cause the WDT +/// to reset the ESP8266. +void IRsend::_delayMicroseconds(uint32_t usec) { + for (; usec > kMaxAccurateUsecDelay; usec -= kMaxAccurateUsecDelay) +#ifndef UNIT_TEST + delayMicroseconds(kMaxAccurateUsecDelay); + delayMicroseconds(static_cast(usec)); +#endif // UNIT_TEST +} +#endif // ALLOW_DELAY_CALLS + +/// Modulate the IR LED for the given period (usec) and at the duty cycle set. +/// @param[in] usec The period of time to modulate the IR LED for, in +/// microseconds. +/// @return Nr. of pulses actually sent. +/// @note +/// The ESP8266 has no good way to do hardware PWM, so we have to do it all +/// in software. There is a horrible kludge/brilliant hack to use the second +/// serial TX line to do fairly accurate hardware PWM, but it is only +/// available on a single specific GPIO and only available on some modules. +/// e.g. It's not available on the ESP-01 module. +/// Hence, for greater compatibility & choice, we don't use that method. +/// Ref: +/// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ +uint16_t IRsend::mark(uint16_t usec) { + // Handle the simple case of no required frequency modulation. + if (!modulation || _dutycycle >= 100) { + ledOn(); + _delayMicroseconds(usec); + ledOff(); + return 1; + } + + // Not simple, so do it assuming frequency modulation. + uint16_t counter = 0; + IRtimer usecTimer = IRtimer(); + // Cache the time taken so far. This saves us calling time, and we can be + // assured that we can't have odd math problems. i.e. unsigned under/overflow. + uint32_t elapsed = usecTimer.elapsed(); + + while (elapsed < usec) { // Loop until we've met/exceeded our required time. + ledOn(); + // Calculate how long we should pulse on for. + // e.g. Are we to close to the end of our requested mark time (usec)? + _delayMicroseconds(std::min((uint32_t)onTimePeriod, usec - elapsed)); + ledOff(); + counter++; + if (elapsed + onTimePeriod >= usec) + return counter; // LED is now off & we've passed our allotted time. + // Wait for the lesser of the rest of the duty cycle, or the time remaining. + _delayMicroseconds( + std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod)); + elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. + } + return counter; +} + +/// Turn the pin (LED) off for a given time. +/// Sends an IR space for the specified number of microseconds. +/// A space is no output, so the PWM output is disabled. +/// @param[in] time Time in microseconds (us). +void IRsend::space(uint32_t time) { + ledOff(); + if (time == 0) return; + _delayMicroseconds(time); +} + +/// Calculate & set any offsets to account for execution times during sending. +/// +/// @param[in] hz The frequency to calibrate at >= 1000Hz. Default is 38000Hz. +/// @return The calculated period offset (in uSeconds) which is now in use. +/// e.g. -5. +/// @note This will generate an 65535us mark() IR LED signal. +/// This only needs to be called once, if at all. +int8_t IRsend::calibrate(uint16_t hz) { + if (hz < 1000) // Were we given kHz? Supports the old call usage. + hz *= 1000; + periodOffset = 0; // Turn off any existing offset while we calibrate. + enableIROut(hz); + IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. + uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) + uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. + // While it shouldn't be necessary, assume at least 1 pulse, to avoid a + // divide by 0 situation. + pulses = std::max(pulses, (uint16_t)1U); + uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. + // Assuming 38kHz for the example calculations: + // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. + // e.g. 65535.0us / 26us = 2520.5769 + // This should have caused approx 2520 loops through the main loop in mark(). + // The average over that many interations should give us a reasonable + // approximation at what offset we need to use to account for instruction + // execution times. + // + // Calculate the actual period from the actual time & the actual pulses + // generated. + double_t actualPeriod = (double_t)timeTaken / (double_t)pulses; + // Store the difference between the actual time per period vs. calculated. + periodOffset = (int8_t)((double_t)calcPeriod - actualPeriod); + return periodOffset; +} + +/// Generic method for sending data that is common to most protocols. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +void IRsend::sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, + uint32_t zerospace, uint64_t data, uint16_t nbits, + bool MSBfirst) { + if (nbits == 0) // If we are asked to send nothing, just return. + return; + if (MSBfirst) { // Send the MSB first. + // Send 0's until we get down to a bit size we can actually manage. + while (nbits > sizeof(data) * 8) { + mark(zeromark); + space(zerospace); + nbits--; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) + if (data & mask) { // Send a 1 + mark(onemark); + space(onespace); + } else { // Send a 0 + mark(zeromark); + space(zerospace); + } + } else { // Send the Least Significant Bit (LSB) first / MSB last. + for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1) + if (data & 1) { // Send a 1 + mark(onemark); + space(onespace); + } else { // Send a 0 + mark(zeromark); + space(zerospace); + } + } +} + +/// Generic method for sending simple protocol messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle) { + sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace, + footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat, + dutycycle); +} + +/// Generic method for sending simple protocol messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] mesgtime Min. nr. of usecs a single message needs to be. +/// This is effectively the min. total length of a single message. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint32_t mesgtime, const uint64_t data, + const uint16_t nbits, const uint16_t frequency, + const bool MSBfirst, const uint16_t repeat, + const uint8_t dutycycle) { + // Setup + enableIROut(frequency, dutycycle); + IRtimer usecs = IRtimer(); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + usecs.reset(); + + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst); + + // Footer + if (footermark) mark(footermark); + uint32_t elapsed = usecs.elapsed(); + // Avoid potential unsigned integer underflow. e.g. when mesgtime is 0. + if (elapsed >= mesgtime) + space(gap); + else + space(std::max(gap, mesgtime - elapsed)); + } +} + +/// Generic method for sending simple protocol messages. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] dataptr Pointer to the data to be transmitted. +/// @param[in] nbytes Nr. of bytes of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint8_t *dataptr, const uint16_t nbytes, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle) { + // Setup + enableIROut(frequency, dutycycle); + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + for (uint16_t i = 0; i < nbytes; i++) + sendData(onemark, onespace, zeromark, zerospace, *(dataptr + i), 8, + MSBfirst); + + // Footer + if (footermark) mark(footermark); + space(gap); + } +} + +/// Generic method for sending Manchester code data. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// (1/2 wavelength) +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). +void IRsend::sendManchesterData(const uint16_t half_period, + const uint64_t data, + const uint16_t nbits, const bool MSBfirst, + const bool GEThomas) { + if (nbits == 0) return; // Nothing to send. + uint16_t bits = nbits; + uint64_t copy = (GEThomas) ? data : ~data; + + if (MSBfirst) { // Send the MSB first. + // Send 0's until we get down to a bit size we can actually manage. + if (bits > (sizeof(data) * 8)) { + sendManchesterData(half_period, 0ULL, bits - sizeof(data) * 8, MSBfirst, + GEThomas); + bits = sizeof(data) * 8; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) + if (copy & mask) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } else { // Send the Least Significant Bit (LSB) first / MSB last. + for (bits = 0; bits < nbits; bits++, copy >>= 1) + if (copy & 1) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } +} + +/// Generic method for sending Manchester code messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// (1/2 wavelength) +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Min. nr. of usecs for the led to be off after the footer +/// mark. This is effectively the absolute minimum gap between messages. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendManchester(const uint16_t headermark, + const uint32_t headerspace, + const uint16_t half_period, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle, + const bool GEThomas) { + // Setup + enableIROut(frequency, dutycycle); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + // Data + sendManchesterData(half_period, data, nbits, MSBfirst, GEThomas); + // Footer + if (footermark) mark(footermark); + if (gap) space(gap); + } +} + +#if SEND_RAW +/// Send a raw IRremote message. +/// +/// @param[in] buf An array of uint16_t's that has microseconds elements. +/// @param[in] len Nr. of elements in the buf[] array. +/// @param[in] hz Frequency to send the message at. (kHz < 1000; Hz >= 1000) +/// @note Even elements are Mark times (On), Odd elements are Space times (Off). +/// Ref: +/// examples/IRrecvDumpV2/IRrecvDumpV2.ino (or later) +void IRsend::sendRaw(const uint16_t buf[], const uint16_t len, + const uint16_t hz) { + // Set IR carrier frequency + enableIROut(hz); + for (uint16_t i = 0; i < len; i++) { + if (i & 1) { // Odd bit. + space(buf[i]); + } else { // Even bit. + mark(buf[i]); + } + } + ledOff(); // We potentially have ended with a mark(), so turn of the LED. +} +#endif // SEND_RAW + +/// Get the minimum number of repeats for a given protocol. +/// @param[in] protocol Protocol number/type of the message you want to send. +/// @return The number of repeats required. +uint16_t IRsend::minRepeats(const decode_type_t protocol) { + switch (protocol) { + // Single repeats + case AIWA_RC_T501: + case AMCOR: + case COOLIX: + case COOLIX48: + case ELITESCREENS: + case GICABLE: + case INAX: + case MIDEA24: + case MITSUBISHI: + case MITSUBISHI2: + case MITSUBISHI_AC: + case MULTIBRACKETS: + case SHERWOOD: + case TOSHIBA_AC: + case TOTO: + return kSingleRepeat; + // Special + case AIRWELL: + return kAirwellMinRepeats; + case CARRIER_AC40: + return kCarrierAc40MinRepeat; + case DISH: + return kDishMinRepeat; + case EPSON: + return kEpsonMinRepeat; + case SANYO_AC88: + return kSanyoAc88MinRepeat; + case SONY: + return kSonyMinRepeat; + case SONY_38K: + return kSonyMinRepeat + 1; + case SYMPHONY: + return kSymphonyDefaultRepeat; + case ZEPEAL: + return kZepealMinRepeat; + default: + return kNoRepeat; + } +} + +/// Get the default number of bits for a given protocol. +/// @param[in] protocol Protocol number/type you want the default bit size for. +/// @return The number of bits. +uint16_t IRsend::defaultBits(const decode_type_t protocol) { + switch (protocol) { + case MULTIBRACKETS: + case GORENJE: + return 8; + case WOWWEE: + return 11; + case RC5: + case SYMPHONY: + return 12; + case LASERTAG: + case RC5X: + return 13; + case AIWA_RC_T501: + case DENON: + case SHARP: + return 15; + case BOSE: + case DISH: + case GICABLE: + case JVC: + case LEGOPF: + case MITSUBISHI: + case MITSUBISHI2: + case ZEPEAL: + return 16; + case METZ: + return 19; + case RC6: + case SONY: + case SONY_38K: + return 20; + case COOLIX: + case INAX: + case MIDEA24: + case NIKAI: + case RCMM: + case TOTO: + case TRANSCOLD: + return 24; + case LG: + case LG2: + return 28; + case ARRIS: + case CARRIER_AC: + case ELITESCREENS: + case EPSON: + case NEC: + case NEC_LIKE: + case PANASONIC_AC32: + case SAMSUNG: + case SHERWOOD: + case WHYNTER: + return 32; + case AIRWELL: + return 34; + case LUTRON: + case TECO: + return 35; + case SAMSUNG36: + return 36; + case CARRIER_AC40: + return kCarrierAc40Bits; // 40 + case DOSHISHA: + return kDoshishaBits; // 40 + case SANYO_LC7461: + return kSanyoLC7461Bits; // 42 + case COOLIX48: + case GOODWEATHER: + case KELON: + case MIDEA: + case PANASONIC: + return 48; + case CLIMABUTLER: + return kClimaButlerBits; // 52 + case AIRTON: + case ECOCLIM: + case MAGIQUEST: + case VESTEL_AC: + case TECHNIBEL_AC: + case TRUMA: + return 56; + case AMCOR: + case CARRIER_AC64: + case DELONGHI_AC: + case PIONEER: + return 64; + case ARGO: + return kArgoBits; + case BOSCH144: + return kBosch144Bits; + case CORONA_AC: + return kCoronaAcBits; + case CARRIER_AC84: + return kCarrierAc84Bits; + case CARRIER_AC128: + return kCarrierAc128Bits; + case DAIKIN: + return kDaikinBits; + case DAIKIN128: + return kDaikin128Bits; + case DAIKIN152: + return kDaikin152Bits; + case DAIKIN160: + return kDaikin160Bits; + case DAIKIN176: + return kDaikin176Bits; + case DAIKIN2: + return kDaikin2Bits; + case DAIKIN200: + return kDaikin200Bits; + case DAIKIN216: + return kDaikin216Bits; + case DAIKIN312: + return kDaikin312Bits; + case DAIKIN64: + return kDaikin64Bits; + case ELECTRA_AC: + return kElectraAcBits; + case GREE: + return kGreeBits; + case HAIER_AC: + return kHaierACBits; + case HAIER_AC_YRW02: + return kHaierACYRW02Bits; + case HAIER_AC160: + return kHaierAC160Bits; + case HAIER_AC176: + return kHaierAC176Bits; + case HITACHI_AC: + return kHitachiAcBits; + case HITACHI_AC1: + return kHitachiAc1Bits; + case HITACHI_AC2: + return kHitachiAc2Bits; + case HITACHI_AC3: + return kHitachiAc3Bits; + case HITACHI_AC264: + return kHitachiAc264Bits; + case HITACHI_AC296: + return kHitachiAc296Bits; + case HITACHI_AC344: + return kHitachiAc344Bits; + case HITACHI_AC424: + return kHitachiAc424Bits; + case KELON168: + return kKelon168Bits; + case KELVINATOR: + return kKelvinatorBits; + case MILESTAG2: + return kMilesTag2ShotBits; + case MIRAGE: + return kMirageBits; + case MITSUBISHI_AC: + return kMitsubishiACBits; + case MITSUBISHI136: + return kMitsubishi136Bits; + case MITSUBISHI112: + return kMitsubishi112Bits; + case MITSUBISHI_HEAVY_152: + return kMitsubishiHeavy152Bits; + case MITSUBISHI_HEAVY_88: + return kMitsubishiHeavy88Bits; + case NEOCLIMA: + return kNeoclimaBits; + case PANASONIC_AC: + return kPanasonicAcBits; + case RHOSS: + return kRhossBits; + case SAMSUNG_AC: + return kSamsungAcBits; + case SANYO_AC: + return kSanyoAcBits; + case SANYO_AC88: + return kSanyoAc88Bits; + case SANYO_AC152: + return kSanyoAc152Bits; + case SHARP_AC: + return kSharpAcBits; + case TCL96AC: + return kTcl96AcBits; + case TCL112AC: + return kTcl112AcBits; + case TEKNOPOINT: + return kTeknopointBits; + case TOSHIBA_AC: + return kToshibaACBits; + case TROTEC: + case TROTEC_3550: + return kTrotecBits; + case VOLTAS: + return kVoltasBits; + case WHIRLPOOL_AC: + return kWhirlpoolAcBits; + case XMP: + return kXmpBits; + case YORK: + return kYorkBits; + case FUJITSU_AC264: + return kFujitsuAc264Bits; + // No default amount of bits. + case FUJITSU_AC: + case MWM: + default: + return 0; + } +} + +/// Send a simple (up to 64 bits) IR message of a given type. +/// An unknown/unsupported type will send nothing. +/// @param[in] type Protocol number/type of the message you want to send. +/// @param[in] data The data you want to send (up to 64 bits). +/// @param[in] nbits How many bits long the message is to be. +/// @param[in] repeat How many repeats to do? +/// @return True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat) { + uint16_t min_repeat __attribute__((unused)) = + std::max(IRsend::minRepeats(type), repeat); + switch (type) { +#if SEND_AIRTON + case AIRTON: + sendAirton(data, nbits, min_repeat); + break; +#endif // SEND_AIRTON +#if SEND_AIRWELL + case AIRWELL: + sendAirwell(data, nbits, min_repeat); + break; +#endif +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: + sendAiwaRCT501(data, nbits, min_repeat); + break; +#endif // SEND_AIWA_RC_T501 +#if SEND_ARRIS + case ARRIS: + sendArris(data, nbits, min_repeat); + break; +#endif // SEND_ARRIS +#if SEND_BOSE + case BOSE: + sendBose(data, nbits, min_repeat); + break; +#endif // SEND_BOSE +#if SEND_CARRIER_AC + case CARRIER_AC: + sendCarrierAC(data, nbits, min_repeat); + break; +#endif +#if SEND_CARRIER_AC40 + case CARRIER_AC40: + sendCarrierAC40(data, nbits, min_repeat); + break; +#endif // SEND_CARRIER_AC40 +#if SEND_CARRIER_AC64 + case CARRIER_AC64: + sendCarrierAC64(data, nbits, min_repeat); + break; +#endif // SEND_CARRIER_AC64 +#if SEND_CLIMABUTLER + case CLIMABUTLER: + sendClimaButler(data, nbits, min_repeat); + break; +#endif // SEND_CLIMABUTLER +#if SEND_COOLIX + case COOLIX: + sendCOOLIX(data, nbits, min_repeat); + break; +#endif // SEND_COOLIX +#if SEND_COOLIX48 + case COOLIX48: + sendCoolix48(data, nbits, min_repeat); + break; +#endif // SEND_COOLIX48 +#if SEND_DAIKIN64 + case DAIKIN64: + sendDaikin64(data, nbits, min_repeat); + break; +#endif +#if SEND_DELONGHI_AC + case DELONGHI_AC: + sendDelonghiAc(data, nbits, min_repeat); + break; +#endif +#if SEND_DENON + case DENON: + sendDenon(data, nbits, min_repeat); + break; +#endif +#if SEND_DISH + case DISH: + sendDISH(data, nbits, min_repeat); + break; +#endif +#if SEND_DOSHISHA + case DOSHISHA: + sendDoshisha(data, nbits, min_repeat); + break; +#endif +#if SEND_ECOCLIM + case ECOCLIM: + sendEcoclim(data, nbits, min_repeat); + break; +#endif // SEND_ECOCLIM +#if SEND_ELITESCREENS + case ELITESCREENS: + sendElitescreens(data, nbits, min_repeat); + break; +#endif // SEND_ELITESCREENS +#if SEND_EPSON + case EPSON: + sendEpson(data, nbits, min_repeat); + break; +#endif +#if SEND_GICABLE + case GICABLE: + sendGICable(data, nbits, min_repeat); + break; +#endif +#if SEND_GOODWEATHER + case GOODWEATHER: + sendGoodweather(data, nbits, min_repeat); + break; +#endif +#if SEND_GORENJE + case GORENJE: + sendGorenje(data, nbits, min_repeat); + break; +#endif +#if SEND_GREE + case GREE: + sendGree(data, nbits, min_repeat); + break; +#endif +#if SEND_INAX + case INAX: + sendInax(data, nbits, min_repeat); + break; +#endif // SEND_INAX +#if SEND_JVC + case JVC: + sendJVC(data, nbits, min_repeat); + break; +#endif +#if SEND_KELON + case KELON: + sendKelon(data, nbits, min_repeat); + break; +#endif // SEND_KELON +#if SEND_LASERTAG + case LASERTAG: + sendLasertag(data, nbits, min_repeat); + break; +#endif +#if SEND_LEGOPF + case LEGOPF: + sendLegoPf(data, nbits, min_repeat); + break; +#endif +#if SEND_LG + case LG: + sendLG(data, nbits, min_repeat); + break; + case LG2: + sendLG2(data, nbits, min_repeat); + break; +#endif +#if SEND_LUTRON + case LUTRON: + sendLutron(data, nbits, min_repeat); + break; +#endif +#if SEND_MAGIQUEST + case MAGIQUEST: + sendMagiQuest(data, nbits, min_repeat); + break; +#endif // SEND_MAGIQUEST +#if SEND_METZ + case METZ: + sendMetz(data, nbits, min_repeat); + break; +#endif // SEND_METZ +#if SEND_MIDEA + case MIDEA: + sendMidea(data, nbits, min_repeat); + break; +#endif // SEND_MIDEA +#if SEND_MIDEA24 + case MIDEA24: + sendMidea24(data, nbits, min_repeat); + break; +#endif // SEND_MIDEA24 +#if SEND_MILESTAG2 + case MILESTAG2: + sendMilestag2(data, nbits, min_repeat); + break; +#endif // SEND_MILESTAG2 +#if SEND_MITSUBISHI + case MITSUBISHI: + sendMitsubishi(data, nbits, min_repeat); + break; +#endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: + sendMitsubishi2(data, nbits, min_repeat); + break; +#endif +#if SEND_MULTIBRACKETS + case MULTIBRACKETS: + sendMultibrackets(data, nbits, min_repeat); + break; +#endif +#if SEND_NIKAI + case NIKAI: + sendNikai(data, nbits, min_repeat); + break; +#endif +#if SEND_NEC + case NEC: + case NEC_LIKE: + sendNEC(data, nbits, min_repeat); + break; +#endif +#if SEND_PANASONIC + case PANASONIC: + sendPanasonic64(data, nbits, min_repeat); + break; +#endif // SEND_PANASONIC +#if SEND_PANASONIC_AC32 + case PANASONIC_AC32: + sendPanasonicAC32(data, nbits, min_repeat); + break; +#endif // SEND_PANASONIC_AC32 +#if SEND_PIONEER + case PIONEER: + sendPioneer(data, nbits, min_repeat); + break; +#endif +#if SEND_RC5 + case RC5: + case RC5X: + sendRC5(data, nbits, min_repeat); + break; +#endif +#if SEND_RC6 + case RC6: + sendRC6(data, nbits, min_repeat); + break; +#endif +#if SEND_RCMM + case RCMM: + sendRCMM(data, nbits, min_repeat); + break; +#endif +#if SEND_SAMSUNG + case SAMSUNG: + sendSAMSUNG(data, nbits, min_repeat); + break; +#endif +#if SEND_SAMSUNG36 + case SAMSUNG36: + sendSamsung36(data, nbits, min_repeat); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: + sendSanyoLC7461(data, nbits, min_repeat); + break; +#endif +#if SEND_SHARP + case SHARP: + sendSharpRaw(data, nbits, min_repeat); + break; +#endif +#if SEND_SHERWOOD + case SHERWOOD: + sendSherwood(data, nbits, min_repeat); + break; +#endif +#if SEND_SONY + case SONY: + sendSony(data, nbits, min_repeat); + break; + case SONY_38K: + sendSony38(data, nbits, min_repeat); + break; +#endif +#if SEND_SYMPHONY + case SYMPHONY: + sendSymphony(data, nbits, min_repeat); + break; +#endif +#if SEND_TECHNIBEL_AC + case TECHNIBEL_AC: + sendTechnibelAc(data, nbits, min_repeat); + break; +#endif +#if SEND_TECO + case TECO: + sendTeco(data, nbits, min_repeat); + break; +#endif // SEND_TECO +#if SEND_TOTO + case TOTO: + sendToto(data, nbits, min_repeat); + break; +#endif // SEND_TOTO +#if SEND_TRANSCOLD + case TRANSCOLD: + sendTranscold(data, nbits, min_repeat); + break; +#endif // SEND_TRANSCOLD +#if SEND_TRUMA + case TRUMA: + sendTruma(data, nbits, min_repeat); + break; +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case VESTEL_AC: + sendVestelAc(data, nbits, min_repeat); + break; +#endif +#if SEND_WHYNTER + case WHYNTER: + sendWhynter(data, nbits, min_repeat); + break; +#endif +#if SEND_WOWWEE + case WOWWEE: + sendWowwee(data, nbits, min_repeat); + break; +#endif // SEND_WOWWEE +#if SEND_XMP + case XMP: + sendXmp(data, nbits, min_repeat); + break; +#endif +#if SEND_ZEPEAL + case ZEPEAL: + sendZepeal(data, nbits, min_repeat); + break; +#endif // SEND_ZEPEAL + default: + return false; + } + return true; +} + +/// Send a complex (>= 64 bits) IR message of a given type. +/// An unknown/unsupported type will send nothing. +/// @param[in] type Protocol number/type of the message you want to send. +/// @param[in] state A pointer to the array of bytes that make up the state[]. +/// @param[in] nbytes How many bytes are in the state. +/// @return True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const uint8_t *state, + const uint16_t nbytes) { + switch (type) { +#if SEND_VOLTAS + case VOLTAS: + sendVoltas(state, nbytes); + break; +#endif // SEND_VOLTAS +#if SEND_AMCOR + case AMCOR: + sendAmcor(state, nbytes); + break; +#endif +#if SEND_ARGO + case ARGO: + sendArgo(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_BOSCH144 + case BOSCH144: + sendBosch144(state, nbytes); + break; +#endif // SEND_BOSCH144 +#if SEND_CARRIER_AC84 + case CARRIER_AC84: + sendCarrierAC84(state, nbytes); + break; +#endif // SEND_CARRIER_AC84 +#if SEND_CARRIER_AC128 + case CARRIER_AC128: + sendCarrierAC128(state, nbytes); + break; +#endif // SEND_CARRIER_AC128 +#if SEND_CORONA_AC + case CORONA_AC: + sendCoronaAc(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_DAIKIN + case DAIKIN: + sendDaikin(state, nbytes); + break; +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + sendDaikin128(state, nbytes); + break; +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + case DAIKIN152: + sendDaikin152(state, nbytes); + break; +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + case DAIKIN160: + sendDaikin160(state, nbytes); + break; +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + sendDaikin176(state, nbytes); + break; +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + sendDaikin2(state, nbytes); + break; +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN200 + case DAIKIN200: + sendDaikin200(state, nbytes); + break; +#endif // SEND_DAIKIN200 +#if SEND_DAIKIN216 + case DAIKIN216: + sendDaikin216(state, nbytes); + break; +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN312 + case DAIKIN312: + sendDaikin312(state, nbytes); + break; +#endif // SEND_DAIKIN312 +#if SEND_ELECTRA_AC + case ELECTRA_AC: + sendElectraAC(state, nbytes); + break; +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + sendFujitsuAC(state, nbytes); + break; +#endif // SEND_FUJITSU_AC +#if SEND_FUJITSU_AC264 + case FUJITSU_AC264: + sendFujitsuAC264(state, nbytes); + break; +#endif +#if SEND_GREE + case GREE: + sendGree(state, nbytes); + break; +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + sendHaierAC(state, nbytes); + break; +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + sendHaierACYRW02(state, nbytes); + break; +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HAIER_AC160 + case HAIER_AC160: + sendHaierAC160(state, nbytes); + break; +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + case HAIER_AC176: + sendHaierAC176(state, nbytes); + break; +#endif // SEND_HAIER_AC176 +#if SEND_HITACHI_AC + case HITACHI_AC: + sendHitachiAC(state, nbytes); + break; +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + sendHitachiAC1(state, nbytes); + break; +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC2 + case HITACHI_AC2: + sendHitachiAC2(state, nbytes); + break; +#endif // SEND_HITACHI_AC2 +#if SEND_HITACHI_AC3 + case HITACHI_AC3: + sendHitachiAc3(state, nbytes); + break; +#endif // SEND_HITACHI_AC3 +#if SEND_HITACHI_AC264 + case HITACHI_AC264: + sendHitachiAc264(state, nbytes); + break; +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + sendHitachiAc296(state, nbytes); + break; +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + case HITACHI_AC344: + sendHitachiAc344(state, nbytes); + break; +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + case HITACHI_AC424: + sendHitachiAc424(state, nbytes); + break; +#endif // SEND_HITACHI_AC424 +#if SEND_KELON168 + case KELON168: + sendKelon168(state, nbytes); + break; +#endif // SEND_KELON168 +#if SEND_KELVINATOR + case KELVINATOR: + sendKelvinator(state, nbytes); + break; +#endif // SEND_KELVINATOR +#if SEND_MIRAGE + case MIRAGE: + sendMirage(state, nbytes); + break; +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + sendMitsubishiAC(state, nbytes); + break; +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI136 + case MITSUBISHI136: + sendMitsubishi136(state, nbytes); + break; +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + sendMitsubishi112(state, nbytes); + break; +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + sendMitsubishiHeavy88(state, nbytes); + break; + case MITSUBISHI_HEAVY_152: + sendMitsubishiHeavy152(state, nbytes); + break; +#endif // SEND_MITSUBISHIHEAVY +#if SEND_MWM + case MWM: + sendMWM(state, nbytes); + break; +#endif // SEND_MWM +#if SEND_NEOCLIMA + case NEOCLIMA: + sendNeoclima(state, nbytes); + break; +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + sendPanasonicAC(state, nbytes); + break; +#endif // SEND_PANASONIC_AC +#if SEND_RHOSS + case RHOSS: + sendRhoss(state, nbytes); + break; +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + sendSamsungAC(state, nbytes); + break; +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + sendSanyoAc(state, nbytes); + break; +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + case SANYO_AC88: + sendSanyoAc88(state, nbytes); + break; +#endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + case SANYO_AC152: + sendSanyoAc152(state, nbytes); + break; +#endif // SEND_SANYO_AC152 +#if SEND_SHARP_AC + case SHARP_AC: + sendSharpAc(state, nbytes); + break; +#endif // SEND_SHARP_AC +#if SEND_TCL96AC + case TCL96AC: + sendTcl96Ac(state, nbytes); + break; +#endif // SEND_TCL96AC +#if SEND_TCL112AC + case TCL112AC: + sendTcl112Ac(state, nbytes); + break; +#endif // SEND_TCL112AC +#if SEND_TEKNOPOINT + case TEKNOPOINT: + sendTeknopoint(state, nbytes); + break; +#endif // SEND_TEKNOPOINT +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + sendToshibaAC(state, nbytes); + break; +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + sendTrotec(state, nbytes); + break; +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + case TROTEC_3550: + sendTrotec3550(state, nbytes); + break; +#endif // SEND_TROTEC_3550 +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + sendWhirlpoolAC(state, nbytes); + break; +#endif // SEND_WHIRLPOOL_AC +#if SEND_YORK + case YORK: + sendYork(state, nbytes); + break; +#endif // SEND_YORK + default: + return false; + } + return true; +} diff --git a/src/IRsend.h b/src/IRsend.h index 38491372a..c9f1bbe30 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -1,921 +1,926 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2017 David Conran -#ifndef IRSEND_H_ -#define IRSEND_H_ - -#define __STDC_LIMIT_MACROS -#include -#include "IRremoteESP8266.h" - -// Originally from https://github.com/shirriff/Arduino-IRremote/ -// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for -// sending IR code on ESP8266 - -#if TEST || UNIT_TEST -#define VIRTUAL virtual -#else -#define VIRTUAL -#endif - -// Constants -// Offset (in microseconds) to use in Period time calculations to account for -// code excution time in producing the software PWM signal. -#if defined(ESP32) -// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz -const int8_t kPeriodOffset = -2; -#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens) -// Calculated on an ESP8266 NodeMCU v2 board using: -// v2.6.0 with v2.5.2 ESP core @ 160MHz -const int8_t kPeriodOffset = -2; -#else // (defined(ESP8266) && F_CPU == 160000000L) -// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz -const int8_t kPeriodOffset = -5; -#endif // (defined(ESP8266) && F_CPU == 160000000L) -const uint8_t kDutyDefault = 50; // Percentage -const uint8_t kDutyMax = 100; // Percentage -// delayMicroseconds() is only accurate to 16383us. -// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds -const uint16_t kMaxAccurateUsecDelay = 16383; -// Usecs to wait between messages we don't know the proper gap time. -const uint32_t kDefaultMessageGap = 100000; -/// Placeholder for missing sensor temp value -/// @note Not using "-1" as it may be a valid external temp -const float kNoTempValue = -100.0; - -/// Enumerators and Structures for the Common A/C API. -namespace stdAc { -/// Common A/C settings for A/C operating modes. -enum class opmode_t { - kOff = -1, - kAuto = 0, - kCool = 1, - kHeat = 2, - kDry = 3, - kFan = 4, - // Add new entries before this one, and update it to point to the last entry - kLastOpmodeEnum = kFan, -}; - -/// Common A/C settings for Fan Speeds. -enum class fanspeed_t { - kAuto = 0, - kMin = 1, - kLow = 2, - kMedium = 3, - kHigh = 4, - kMax = 5, - kMediumHigh = 6, - // Add new entries before this one, and update it to point to the last entry - kLastFanspeedEnum = kMediumHigh, -}; - -/// Common A/C settings for Vertical Swing. -enum class swingv_t { - kOff = -1, - kAuto = 0, - kHighest = 1, - kHigh = 2, - kMiddle = 3, - kLow = 4, - kLowest = 5, - kUpperMiddle = 6, - // Add new entries before this one, and update it to point to the last entry - kLastSwingvEnum = kUpperMiddle, -}; - -/// @brief Tyoe of A/C command (if the remote uses different codes for each) -/// @note Most remotes support only a single command or aggregate multiple -/// into one (e.g. control+timer). Use @c kControlCommand in such case -enum class ac_command_t { - kControlCommand = 0, - kSensorTempReport = 1, - kTimerCommand = 2, - kConfigCommand = 3, - // Add new entries before this one, and update it to point to the last entry - kLastAcCommandEnum = kConfigCommand, -}; - -/// Common A/C settings for Horizontal Swing. -enum class swingh_t { - kOff = -1, - kAuto = 0, // a.k.a. On. - kLeftMax = 1, - kLeft = 2, - kMiddle = 3, - kRight = 4, - kRightMax = 5, - kWide = 6, // a.k.a. left & right at the same time. - // Add new entries before this one, and update it to point to the last entry - kLastSwinghEnum = kWide, -}; - -/// Structure to hold a common A/C state. -struct state_t { - decode_type_t protocol = decode_type_t::UNKNOWN; - int16_t model = -1; // `-1` means unused. - bool power = false; - stdAc::opmode_t mode = stdAc::opmode_t::kOff; - float degrees = 25; - bool celsius = true; - stdAc::fanspeed_t fanspeed = stdAc::fanspeed_t::kAuto; - stdAc::swingv_t swingv = stdAc::swingv_t::kOff; - stdAc::swingh_t swingh = stdAc::swingh_t::kOff; - bool quiet = false; - bool turbo = false; - bool econo = false; - bool light = false; - bool filter = false; - bool clean = false; - bool beep = false; - int16_t sleep = -1; // `-1` means off. - int16_t clock = -1; // `-1` means not set. - stdAc::ac_command_t command = stdAc::ac_command_t::kControlCommand; - bool iFeel = false; - float sensorTemperature = kNoTempValue; // `kNoTempValue` means not set. -}; -}; // namespace stdAc - -/// Fujitsu A/C model numbers -enum fujitsu_ac_remote_model_t { - ARRAH2E = 1, ///< (1) AR-RAH2E, AR-RAC1E, AR-RAE1E, AR-RCE1E, AR-RAH2U, - ///< AR-REG1U (Default) - ///< Warning: Use on incorrect models can cause the A/C to lock - ///< up, requring the A/C to be physically powered off to fix. - ///< e.g. AR-RAH1U may lock up with a Swing command. - ARDB1, ///< (2) AR-DB1, AR-DL10 (AR-DL10 swing doesn't work) - ARREB1E, ///< (3) AR-REB1E, AR-RAH1U (Similar to ARRAH2E but no horiz - ///< control) - ARJW2, ///< (4) AR-JW2 (Same as ARDB1 but with horiz control) - ARRY4, ///< (5) AR-RY4 (Same as AR-RAH2E but with clean & filter) - ARREW4E, ///< (6) Similar to ARRAH2E, but with different temp config. -}; - -/// Gree A/C model numbers -enum gree_ac_remote_model_t { - YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default) - YBOFB, // (2) Green, YBOFB2, YAPOF3 - YX1FSF, // (3) Soleus Air window unit (Similar to YAW1F, but with an - // Operation mode of Energy Saver (Econo)) -}; - -/// HAIER_AC176 A/C model numbers -enum haier_ac176_remote_model_t { - V9014557_A = 1, // (1) V9014557 Remote in "A" setting. (Default) - V9014557_B, // (2) V9014557 Remote in "B" setting. -}; - -/// HITACHI_AC1 A/C model numbers -enum hitachi_ac1_remote_model_t { - R_LT0541_HTA_A = 1, // (1) R-LT0541-HTA Remote in "A" setting. (Default) - R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting. -}; - -/// MIRAGE A/C model numbers -enum mirage_ac_remote_model_t { - KKG9AC1 = 1, // (1) KKG9A-C1 Remote. (Default) - KKG29AC1, // (2) KKG29A-C1 Remote. -}; - -/// Panasonic A/C model numbers -enum panasonic_ac_remote_model_t { - kPanasonicUnknown = 0, - kPanasonicLke = 1, - kPanasonicNke = 2, - kPanasonicDke = 3, // PKR too. - kPanasonicJke = 4, - kPanasonicCkp = 5, - kPanasonicRkr = 6, -}; - -/// Sharp A/C model numbers -enum sharp_ac_remote_model_t { - A907 = 1, - A705 = 2, - A903 = 3, // 820 too -}; - -/// TCL (& Teknopoint) A/C model numbers -enum tcl_ac_remote_model_t { - TAC09CHSD = 1, - GZ055BE1 = 2, // Also Teknopoint GZ01-BEJ0-000 -}; - -/// Voltas A/C model numbers -enum voltas_ac_remote_model_t { - kVoltasUnknown = 0, // Full Function - kVoltas122LZF = 1, // (1) 122LZF (No SwingH support) (Default) -}; - -/// Whirlpool A/C model numbers -enum whirlpool_ac_remote_model_t { - DG11J13A = 1, // DG11J1-04 too - DG11J191, -}; - -/// LG A/C model numbers -enum lg_ac_remote_model_t { - GE6711AR2853M = 1, // (1) LG 28-bit Protocol (default) - AKB75215403, // (2) LG2 28-bit Protocol - AKB74955603, // (3) LG2 28-bit Protocol variant - AKB73757604, // (4) LG2 Variant of AKB74955603 - LG6711A20083V, // (5) Same as GE6711AR2853M, but only SwingV toggle. -}; - -/// Argo A/C model numbers -enum argo_ac_remote_model_t { - SAC_WREM2 = 1, // (1) ARGO WREM2 remote (default) - SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd -}; - -// Classes - -/// Class for sending all basic IR protocols. -/// @note Originally from https://github.com/shirriff/Arduino-IRremote/ -/// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for -/// sending IR code on ESP8266 -class IRsend { - public: - explicit IRsend(uint16_t IRsendPin, bool inverted = false, - bool use_modulation = true); - void begin(); - void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault); - VIRTUAL void _delayMicroseconds(uint32_t usec); - VIRTUAL uint16_t mark(uint16_t usec); - VIRTUAL void space(uint32_t usec); - int8_t calibrate(uint16_t hz = 38000U); - void sendRaw(const uint16_t buf[], const uint16_t len, const uint16_t hz); - void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, - uint32_t zerospace, uint64_t data, uint16_t nbits, - bool MSBfirst = true); - void sendManchesterData(const uint16_t half_period, const uint64_t data, - const uint16_t nbits, const bool MSBfirst = true, - const bool GEThomas = true); - void sendManchester(const uint16_t headermark, const uint32_t headerspace, - const uint16_t half_period, const uint16_t footermark, - const uint32_t gap, const uint64_t data, - const uint16_t nbits, const uint16_t frequency = 38, - const bool MSBfirst = true, - const uint16_t repeat = kNoRepeat, - const uint8_t dutycycle = kDutyDefault, - const bool GEThomas = true); - void sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint64_t data, const uint16_t nbits, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle); - void sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint32_t mesgtime, const uint64_t data, - const uint16_t nbits, const uint16_t frequency, - const bool MSBfirst, const uint16_t repeat, - const uint8_t dutycycle); - void sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint8_t *dataptr, const uint16_t nbytes, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle); - static uint16_t minRepeats(const decode_type_t protocol); - static uint16_t defaultBits(const decode_type_t protocol); - bool send(const decode_type_t type, const uint64_t data, - const uint16_t nbits, const uint16_t repeat = kNoRepeat); - bool send(const decode_type_t type, const uint8_t *state, - const uint16_t nbytes); -#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ - SEND_MIDEA24) - void sendNEC(uint64_t data, uint16_t nbits = kNECBits, - uint16_t repeat = kNoRepeat); - uint32_t encodeNEC(uint16_t address, uint16_t command); -#endif -#if SEND_SONY - // sendSony() should typically be called with repeat=2 as Sony devices - // expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes) - // Legacy use of this procedure was to only send a single code so call it with - // repeat=0 for backward compatibility. As of v2.0 it defaults to sending - // a Sony command that will be accepted be a device. - void sendSony(const uint64_t data, const uint16_t nbits = kSony20Bits, - const uint16_t repeat = kSonyMinRepeat); - void sendSony38(const uint64_t data, const uint16_t nbits = kSony20Bits, - const uint16_t repeat = kSonyMinRepeat + 1); - uint32_t encodeSony(const uint16_t nbits, const uint16_t command, - const uint16_t address, const uint16_t extended = 0); -#endif // SEND_SONY -#if SEND_SHERWOOD - void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits, - uint16_t repeat = kSherwoodMinRepeat); -#endif - // `sendSAMSUNG()` is required by `sendLG()` -#if (SEND_SAMSUNG || SEND_LG) - void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits, - const uint16_t repeat = kNoRepeat); - uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command); -#endif // (SEND_SAMSUNG || SEND_LG) -#if SEND_SAMSUNG36 - void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_SAMSUNG_AC - void sendSamsungAC(const unsigned char data[], - const uint16_t nbytes = kSamsungAcStateLength, - const uint16_t repeat = kSamsungAcDefaultRepeat); -#endif -#if SEND_LG - void sendLG(uint64_t data, uint16_t nbits = kLgBits, - uint16_t repeat = kNoRepeat); - void sendLG2(uint64_t data, uint16_t nbits = kLgBits, - uint16_t repeat = kNoRepeat); - uint32_t encodeLG(uint16_t address, uint16_t command); -#endif -#if (SEND_SHARP || SEND_DENON) - uint32_t encodeSharp(const uint16_t address, const uint16_t command, - const uint16_t expansion = 1, const uint16_t check = 0, - const bool MSBfirst = false); - void sendSharp(const uint16_t address, const uint16_t command, - const uint16_t nbits = kSharpBits, - const uint16_t repeat = kNoRepeat); - void sendSharpRaw(const uint64_t data, const uint16_t nbits = kSharpBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_SHARP_AC - void sendSharpAc(const unsigned char data[], - const uint16_t nbytes = kSharpAcStateLength, - const uint16_t repeat = kSharpAcDefaultRepeat); -#endif // SEND_SHARP_AC -#if SEND_JVC - void sendJVC(uint64_t data, uint16_t nbits = kJvcBits, - uint16_t repeat = kNoRepeat); - uint16_t encodeJVC(uint8_t address, uint8_t command); -#endif -#if SEND_DENON - void sendDenon(uint64_t data, uint16_t nbits = kDenonBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_SANYO - uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command); - void sendSanyoLC7461(const uint64_t data, - const uint16_t nbits = kSanyoLC7461Bits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_SANYO_AC - void sendSanyoAc(const uint8_t *data, - const uint16_t nbytes = kSanyoAcStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - void sendSanyoAc88(const uint8_t *data, - const uint16_t nbytes = kSanyoAc88StateLength, - const uint16_t repeat = kSanyoAc88MinRepeat); -#endif // SEND_SANYO_AC88 -#if SEND_SANYO_AC152 - void sendSanyoAc152(const uint8_t *data, - const uint16_t nbytes = kSanyoAc152StateLength, - const uint16_t repeat = kSanyoAc152MinRepeat); -#endif // SEND_SANYO_AC152 -#if SEND_DISH - // sendDISH() should typically be called with repeat=3 as DISH devices - // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) - // Legacy use of this procedure was only to send a single code - // so use repeat=0 for backward compatibility. - void sendDISH(uint64_t data, uint16_t nbits = kDishBits, - uint16_t repeat = kDishMinRepeat); -#endif -#if (SEND_PANASONIC || SEND_DENON) - void sendPanasonic64(const uint64_t data, - const uint16_t nbits = kPanasonicBits, - const uint16_t repeat = kNoRepeat); - void sendPanasonic(const uint16_t address, const uint32_t data, - const uint16_t nbits = kPanasonicBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodePanasonic(const uint16_t manufacturer, const uint8_t device, - const uint8_t subdevice, const uint8_t function); -#endif -#if SEND_RC5 - void sendRC5(const uint64_t data, uint16_t nbits = kRC5XBits, - const uint16_t repeat = kNoRepeat); - uint16_t encodeRC5(const uint8_t address, const uint8_t command, - const bool key_released = false); - uint16_t encodeRC5X(const uint8_t address, const uint8_t command, - const bool key_released = false); - uint64_t toggleRC5(const uint64_t data); -#endif -#if SEND_RC6 - void sendRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits, - const uint16_t repeat = kNoRepeat); - uint64_t encodeRC6(const uint32_t address, const uint8_t command, - const uint16_t mode = kRC6Mode0Bits); - uint64_t toggleRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits); -#endif -#if SEND_RCMM - void sendRCMM(uint64_t data, uint16_t nbits = kRCMMBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_COOLIX - void sendCOOLIX(const uint64_t data, const uint16_t nbits = kCoolixBits, - const uint16_t repeat = kCoolixDefaultRepeat); -#endif // SEND_COOLIX -#if SEND_COOLIX48 - void sendCoolix48(const uint64_t data, const uint16_t nbits = kCoolix48Bits, - const uint16_t repeat = kCoolixDefaultRepeat); -#endif // SEND_COOLIX48 -#if SEND_WHYNTER - void sendWhynter(const uint64_t data, const uint16_t nbits = kWhynterBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_MIRAGE - void sendMirage(const unsigned char data[], - const uint16_t nbytes = kMirageStateLength, - const uint16_t repeat = kMirageMinRepeat); -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI - void sendMitsubishi(uint64_t data, uint16_t nbits = kMitsubishiBits, - uint16_t repeat = kMitsubishiMinRepeat); -#endif -#if SEND_MITSUBISHI136 - void sendMitsubishi136(const unsigned char data[], - const uint16_t nbytes = kMitsubishi136StateLength, - const uint16_t repeat = kMitsubishi136MinRepeat); -#endif -#if SEND_MITSUBISHI112 - void sendMitsubishi112(const unsigned char data[], - const uint16_t nbytes = kMitsubishi112StateLength, - const uint16_t repeat = kMitsubishi112MinRepeat); -#endif -#if SEND_MITSUBISHI2 - void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits, - uint16_t repeat = kMitsubishiMinRepeat); -#endif -#if SEND_MITSUBISHI_AC - void sendMitsubishiAC(const unsigned char data[], - const uint16_t nbytes = kMitsubishiACStateLength, - const uint16_t repeat = kMitsubishiACMinRepeat); -#endif -#if SEND_MITSUBISHIHEAVY - void sendMitsubishiHeavy88( - const unsigned char data[], - const uint16_t nbytes = kMitsubishiHeavy88StateLength, - const uint16_t repeat = kMitsubishiHeavy88MinRepeat); - void sendMitsubishiHeavy152( - const unsigned char data[], - const uint16_t nbytes = kMitsubishiHeavy152StateLength, - const uint16_t repeat = kMitsubishiHeavy152MinRepeat); -#endif -#if SEND_FUJITSU_AC - void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat = kFujitsuAcMinRepeat); -#endif -#if SEND_INAX - void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits, - const uint16_t repeat = kInaxMinRepeat); -#endif // SEND_INAX -#if SEND_GLOBALCACHE - void sendGC(uint16_t buf[], uint16_t len); -#endif -#if SEND_KELVINATOR - void sendKelvinator(const unsigned char data[], - const uint16_t nbytes = kKelvinatorStateLength, - const uint16_t repeat = kKelvinatorDefaultRepeat); -#endif -#if SEND_DAIKIN - void sendDaikin(const unsigned char data[], - const uint16_t nbytes = kDaikinStateLength, - const uint16_t repeat = kDaikinDefaultRepeat); -#endif -#if SEND_DAIKIN64 - void sendDaikin64(const uint64_t data, const uint16_t nbits = kDaikin64Bits, - const uint16_t repeat = kDaikin64DefaultRepeat); -#endif // SEND_DAIKIN64 -#if SEND_DAIKIN128 - void sendDaikin128(const unsigned char data[], - const uint16_t nbytes = kDaikin128StateLength, - const uint16_t repeat = kDaikin128DefaultRepeat); -#endif // SEND_DAIKIN128 -#if SEND_DAIKIN152 - void sendDaikin152(const unsigned char data[], - const uint16_t nbytes = kDaikin152StateLength, - const uint16_t repeat = kDaikin152DefaultRepeat); -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - void sendDaikin160(const unsigned char data[], - const uint16_t nbytes = kDaikin160StateLength, - const uint16_t repeat = kDaikin160DefaultRepeat); -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - void sendDaikin176(const unsigned char data[], - const uint16_t nbytes = kDaikin176StateLength, - const uint16_t repeat = kDaikin176DefaultRepeat); -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - void sendDaikin2(const unsigned char data[], - const uint16_t nbytes = kDaikin2StateLength, - const uint16_t repeat = kDaikin2DefaultRepeat); -#endif -#if SEND_DAIKIN200 - void sendDaikin200(const unsigned char data[], - const uint16_t nbytes = kDaikin200StateLength, - const uint16_t repeat = kDaikin200DefaultRepeat); -#endif // SEND_DAIKIN200 -#if SEND_DAIKIN216 - void sendDaikin216(const unsigned char data[], - const uint16_t nbytes = kDaikin216StateLength, - const uint16_t repeat = kDaikin216DefaultRepeat); -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN312 - void sendDaikin312(const unsigned char data[], - const uint16_t nbytes = kDaikin312StateLength, - const uint16_t repeat = kDaikin312DefaultRepeat); -#endif // SEND_DAIKIN312 -#if SEND_AIWA_RC_T501 - void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, - uint16_t repeat = kAiwaRcT501MinRepeats); -#endif -#if SEND_GREE - void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits, - const uint16_t repeat = kGreeDefaultRepeat); - void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength, - const uint16_t repeat = kGreeDefaultRepeat); -#endif -#if SEND_GOODWEATHER - void sendGoodweather(const uint64_t data, - const uint16_t nbits = kGoodweatherBits, - const uint16_t repeat = kGoodweatherMinRepeat); -#endif // SEND_GOODWEATHER -#if SEND_GORENJE - void sendGorenje(const uint64_t data, const uint16_t nbits = kGorenjeBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_GORENJE -#if SEND_PRONTO - void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); -#endif -#if SEND_ARGO - void sendArgo(const unsigned char data[], - const uint16_t nbytes = kArgoStateLength, - const uint16_t repeat = kArgoDefaultRepeat, - bool sendFooter = false); - void sendArgoWREM3(const unsigned char data[], - const uint16_t nbytes = kArgoStateLength, - const uint16_t repeat = kArgoDefaultRepeat); -#endif // SEND_ARGO -#if SEND_TROTEC - void sendTrotec(const unsigned char data[], - const uint16_t nbytes = kTrotecStateLength, - const uint16_t repeat = kTrotecDefaultRepeat); -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - void sendTrotec3550(const unsigned char data[], - const uint16_t nbytes = kTrotecStateLength, - const uint16_t repeat = kTrotecDefaultRepeat); -#endif // SEND_TROTEC_3550 -#if SEND_NIKAI - void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_TOSHIBA_AC - void sendToshibaAC(const uint8_t data[], - const uint16_t nbytes = kToshibaACStateLength, - const uint16_t repeat = kToshibaACMinRepeat); -#endif -#if SEND_MIDEA - void sendMidea(uint64_t data, uint16_t nbits = kMideaBits, - uint16_t repeat = kMideaMinRepeat); -#endif // SEND_MIDEA -#if SEND_MIDEA24 - void sendMidea24(const uint64_t data, const uint16_t nbits = kMidea24Bits, - const uint16_t repeat = kMidea24MinRepeat); -#endif // SEND_MIDEA24 -#if SEND_MAGIQUEST - void sendMagiQuest(const uint64_t data, const uint16_t nbits = kMagiquestBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodeMagiQuest(const uint32_t wand_id, const uint16_t magnitude); -#endif -#if SEND_LASERTAG - void sendLasertag(uint64_t data, uint16_t nbits = kLasertagBits, - uint16_t repeat = kLasertagMinRepeat); -#endif -#if SEND_CARRIER_AC - void sendCarrierAC(uint64_t data, uint16_t nbits = kCarrierAcBits, - uint16_t repeat = kCarrierAcMinRepeat); -#endif -#if SEND_CARRIER_AC40 - void sendCarrierAC40(uint64_t data, uint16_t nbits = kCarrierAc40Bits, - uint16_t repeat = kCarrierAc40MinRepeat); -#endif -#if SEND_CARRIER_AC64 - void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits, - uint16_t repeat = kCarrierAc64MinRepeat); -#endif -#if SEND_CARRIER_AC84 - void sendCarrierAC84(const uint8_t data[], - const uint16_t nbytes = kCarrierAc84StateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_CARRIER_AC84 -#if SEND_CARRIER_AC128 - void sendCarrierAC128(const uint8_t data[], - uint16_t nbytes = kCarrierAc128StateLength, - uint16_t repeat = kCarrierAc128MinRepeat); -#endif // SEND_CARRIER_AC128 -#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) - void sendHaierAC(const unsigned char data[], - const uint16_t nbytes = kHaierACStateLength, - const uint16_t repeat = kHaierAcDefaultRepeat); -#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) -#if SEND_HAIER_AC_YRW02 - void sendHaierACYRW02(const unsigned char data[], - const uint16_t nbytes = kHaierACYRW02StateLength, - const uint16_t repeat = kHaierAcYrw02DefaultRepeat); -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HAIER_AC160 - void sendHaierAC160(const unsigned char data[], - const uint16_t nbytes = kHaierAC160StateLength, - const uint16_t repeat = kHaierAc160DefaultRepeat); -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - void sendHaierAC176(const unsigned char data[], - const uint16_t nbytes = kHaierAC176StateLength, - const uint16_t repeat = kHaierAc176DefaultRepeat); -#endif // SEND_HAIER_AC176 -#if SEND_HITACHI_AC - void sendHitachiAC(const unsigned char data[], - const uint16_t nbytes = kHitachiAcStateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif -#if SEND_HITACHI_AC1 - void sendHitachiAC1(const unsigned char data[], - const uint16_t nbytes = kHitachiAc1StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif -#if SEND_HITACHI_AC2 - void sendHitachiAC2(const unsigned char data[], - const uint16_t nbytes = kHitachiAc2StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif -#if SEND_HITACHI_AC3 - void sendHitachiAc3(const unsigned char data[], - const uint16_t nbytes, // No default as there as so many - // different sizes - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC3 -#if SEND_HITACHI_AC264 - void sendHitachiAc264(const unsigned char data[], - const uint16_t nbytes = kHitachiAc264StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - void sendHitachiAc296(const unsigned char data[], - const uint16_t nbytes = kHitachiAc296StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - void sendHitachiAc344(const unsigned char data[], - const uint16_t nbytes = kHitachiAc344StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - void sendHitachiAc424(const unsigned char data[], - const uint16_t nbytes = kHitachiAc424StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC424 -#if SEND_GICABLE - void sendGICable(uint64_t data, uint16_t nbits = kGicableBits, - uint16_t repeat = kGicableMinRepeat); -#endif -#if SEND_WHIRLPOOL_AC - void sendWhirlpoolAC(const unsigned char data[], - const uint16_t nbytes = kWhirlpoolAcStateLength, - const uint16_t repeat = kWhirlpoolAcDefaultRepeat); -#endif -#if SEND_LUTRON - void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_ELECTRA_AC - void sendElectraAC(const unsigned char data[], - const uint16_t nbytes = kElectraAcStateLength, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_PANASONIC_AC - void sendPanasonicAC(const unsigned char data[], - const uint16_t nbytes = kPanasonicAcStateLength, - const uint16_t repeat = kPanasonicAcDefaultRepeat); -#endif // SEND_PANASONIC_AC -#if SEND_PANASONIC_AC32 - void sendPanasonicAC32(const uint64_t data, - const uint16_t nbits = kPanasonicAc32Bits, - const uint16_t repeat = kPanasonicAcDefaultRepeat); -#endif // SEND_PANASONIC_AC32 -#if SEND_PIONEER - void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodePioneer(uint16_t address, uint16_t command); -#endif -#if SEND_MWM - void sendMWM(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_VESTEL_AC - void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_TCL96AC - void sendTcl96Ac(const unsigned char data[], - const uint16_t nbytes = kTcl96AcStateLength, - const uint16_t repeat = kTcl96AcDefaultRepeat); -#endif // SEND_TCL96AC -#if SEND_TCL112AC - void sendTcl112Ac(const unsigned char data[], - const uint16_t nbytes = kTcl112AcStateLength, - const uint16_t repeat = kTcl112AcDefaultRepeat); -#endif // SEND_TCL112AC -#if SEND_TECO - void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_LEGOPF - void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, - const uint16_t repeat = kLegoPfMinRepeat); -#endif -#if SEND_NEOCLIMA - void sendNeoclima(const unsigned char data[], - const uint16_t nbytes = kNeoclimaStateLength, - const uint16_t repeat = kNeoclimaMinRepeat); -#endif // SEND_NEOCLIMA -#if SEND_AMCOR - void sendAmcor(const unsigned char data[], - const uint16_t nbytes = kAmcorStateLength, - const uint16_t repeat = kAmcorDefaultRepeat); -#endif // SEND_AMCOR -#if SEND_EPSON - void sendEpson(uint64_t data, uint16_t nbits = kEpsonBits, - uint16_t repeat = kEpsonMinRepeat); -#endif -#if SEND_SYMPHONY - void sendSymphony(uint64_t data, uint16_t nbits = kSymphonyBits, - uint16_t repeat = kSymphonyDefaultRepeat); -#endif -#if SEND_AIRWELL - void sendAirwell(uint64_t data, uint16_t nbits = kAirwellBits, - uint16_t repeat = kAirwellMinRepeats); -#endif -#if SEND_DELONGHI_AC - void sendDelonghiAc(uint64_t data, uint16_t nbits = kDelonghiAcBits, - uint16_t repeat = kDelonghiAcDefaultRepeat); -#endif -#if SEND_DOSHISHA - void sendDoshisha(const uint64_t data, uint16_t nbits = kDoshishaBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodeDoshisha(const uint8_t command, const uint8_t channel = 0); -#endif // SEND_DOSHISHA -#if SEND_MULTIBRACKETS - void sendMultibrackets(const uint64_t data, - const uint16_t nbits = kMultibracketsBits, - const uint16_t repeat = kMultibracketsDefaultRepeat); -#endif -#if SEND_TECHNIBEL_AC - void sendTechnibelAc(uint64_t data, uint16_t nbits = kTechnibelAcBits, - uint16_t repeat = kTechnibelAcDefaultRepeat); -#endif -#if SEND_CORONA_AC - void sendCoronaAc(const uint8_t data[], - const uint16_t nbytes = kCoronaAcStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_CORONA_AC -#if SEND_ZEPEAL - void sendZepeal(const uint64_t data, - const uint16_t nbits = kZepealBits, - const uint16_t repeat = kZepealMinRepeat); -#endif // SEND_ZEPEAL -#if SEND_VOLTAS - void sendVoltas(const unsigned char data[], - const uint16_t nbytes = kVoltasStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_VOLTAS -#if SEND_METZ - void sendMetz(const uint64_t data, - const uint16_t nbits = kMetzBits, - const uint16_t repeat = kMetzMinRepeat); - static uint32_t encodeMetz(const uint8_t address, const uint8_t command, - const bool toggle = false); -#endif // SEND_METZ -#if SEND_TRANSCOLD - void sendTranscold(const uint64_t data, const uint16_t nbits = kTranscoldBits, - const uint16_t repeat = kTranscoldDefaultRepeat); -#endif // SEND_TRANSCOLD -#if SEND_ELITESCREENS - void sendElitescreens(const uint64_t data, - const uint16_t nbits = kEliteScreensBits, - const uint16_t repeat = kEliteScreensDefaultRepeat); -#endif // SEND_ELITESCREENS -#if SEND_MILESTAG2 - // Since There 2 types of transmissions - // (14bits for Shooting by default, you can set 24 bit for msg delivery) - void sendMilestag2(const uint64_t data, - const uint16_t nbits = kMilesTag2ShotBits, - const uint16_t repeat = kMilesMinRepeat); -#endif // SEND_MILESTAG2 -#if SEND_ECOCLIM - void sendEcoclim(const uint64_t data, const uint16_t nbits = kEcoclimBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_ECOCLIM -#if SEND_XMP - void sendXmp(const uint64_t data, const uint16_t nbits = kXmpBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_XMP -#if SEND_TRUMA - void sendTruma(const uint64_t data, const uint16_t nbits = kTrumaBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_TRUMA -#if SEND_TEKNOPOINT - void sendTeknopoint(const unsigned char data[], - const uint16_t nbytes = kTeknopointStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_TEKNOPOINT -#if SEND_KELON - void sendKelon(const uint64_t data, const uint16_t nbits = kKelonBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_KELON -#if SEND_KELON168 - void sendKelon168(const unsigned char data[], - const uint16_t nbytes = kKelon168StateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_KELON168 -#if SEND_BOSE - void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_BOSE -#if SEND_ARRIS - void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits, - const uint16_t repeat = kNoRepeat); - static uint32_t toggleArrisRelease(const uint32_t data); - static uint32_t encodeArris(const uint32_t command, const bool release); -#endif // SEND_ARRIS -#if SEND_RHOSS - void sendRhoss(const unsigned char data[], - const uint16_t nbytes = kRhossStateLength, - const uint16_t repeat = kRhossDefaultRepeat); -#endif // SEND_RHOSS -#if SEND_AIRTON - void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits, - const uint16_t repeat = kAirtonDefaultRepeat); -#endif // SEND_AIRTON -#if SEND_TOTO - void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits, - const uint16_t repeat = kTotoDefaultRepeat); -#endif // SEND_TOTO -#if SEND_CLIMABUTLER - void sendClimaButler(const uint64_t data, - const uint16_t nbits = kClimaButlerBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_CLIMABUTLER -#if SEND_BOSCH144 - void sendBosch144(const unsigned char data[], - const uint16_t nbytes = kBosch144StateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_BOSCH144 -#if SEND_WOWWEE - void sendWowwee(const uint64_t data, const uint16_t nbits = kWowweeBits, - const uint16_t repeat = kWowweeDefaultRepeat); -#endif // SEND_WOWWEE -#if SEND_YORK - void sendYork(const unsigned char data[], - const uint16_t nbytes = kYorkStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_YORK - - protected: -#ifdef UNIT_TEST -#ifndef HIGH -#define HIGH 0x1 -#endif -#ifndef LOW -#define LOW 0x0 -#endif -#endif // UNIT_TEST - uint8_t outputOn; - uint8_t outputOff; - VIRTUAL void ledOff(); - VIRTUAL void ledOn(); -#ifndef UNIT_TEST - - private: -#else - uint32_t _freq_unittest; -#endif // UNIT_TEST - uint16_t onTimePeriod; - uint16_t offTimePeriod; - uint16_t IRpin; - int8_t periodOffset; - uint8_t _dutycycle; - bool modulation; - uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true); -#if SEND_SONY - void _sendSony(const uint64_t data, const uint16_t nbits, - const uint16_t repeat, const uint16_t freq); -#endif // SEND_SONY -}; - -#endif // IRSEND_H_ +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2017 David Conran +#ifndef IRSEND_H_ +#define IRSEND_H_ + +#define __STDC_LIMIT_MACROS +#include +#include "IRremoteESP8266.h" + +// Originally from https://github.com/shirriff/Arduino-IRremote/ +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for +// sending IR code on ESP8266 + +#if TEST || UNIT_TEST +#define VIRTUAL virtual +#else +#define VIRTUAL +#endif + +// Constants +// Offset (in microseconds) to use in Period time calculations to account for +// code excution time in producing the software PWM signal. +#if defined(ESP32) +// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz +const int8_t kPeriodOffset = -2; +#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens) +// Calculated on an ESP8266 NodeMCU v2 board using: +// v2.6.0 with v2.5.2 ESP core @ 160MHz +const int8_t kPeriodOffset = -2; +#else // (defined(ESP8266) && F_CPU == 160000000L) +// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz +const int8_t kPeriodOffset = -5; +#endif // (defined(ESP8266) && F_CPU == 160000000L) +const uint8_t kDutyDefault = 50; // Percentage +const uint8_t kDutyMax = 100; // Percentage +// delayMicroseconds() is only accurate to 16383us. +// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds +const uint16_t kMaxAccurateUsecDelay = 16383; +// Usecs to wait between messages we don't know the proper gap time. +const uint32_t kDefaultMessageGap = 100000; +/// Placeholder for missing sensor temp value +/// @note Not using "-1" as it may be a valid external temp +const float kNoTempValue = -100.0; + +/// Enumerators and Structures for the Common A/C API. +namespace stdAc { +/// Common A/C settings for A/C operating modes. +enum class opmode_t { + kOff = -1, + kAuto = 0, + kCool = 1, + kHeat = 2, + kDry = 3, + kFan = 4, + // Add new entries before this one, and update it to point to the last entry + kLastOpmodeEnum = kFan, +}; + +/// Common A/C settings for Fan Speeds. +enum class fanspeed_t { + kAuto = 0, + kMin = 1, + kLow = 2, + kMedium = 3, + kHigh = 4, + kMax = 5, + kMediumHigh = 6, + // Add new entries before this one, and update it to point to the last entry + kLastFanspeedEnum = kMediumHigh, +}; + +/// Common A/C settings for Vertical Swing. +enum class swingv_t { + kOff = -1, + kAuto = 0, + kHighest = 1, + kHigh = 2, + kMiddle = 3, + kLow = 4, + kLowest = 5, + kUpperMiddle = 6, + // Add new entries before this one, and update it to point to the last entry + kLastSwingvEnum = kUpperMiddle, +}; + +/// @brief Tyoe of A/C command (if the remote uses different codes for each) +/// @note Most remotes support only a single command or aggregate multiple +/// into one (e.g. control+timer). Use @c kControlCommand in such case +enum class ac_command_t { + kControlCommand = 0, + kSensorTempReport = 1, + kTimerCommand = 2, + kConfigCommand = 3, + // Add new entries before this one, and update it to point to the last entry + kLastAcCommandEnum = kConfigCommand, +}; + +/// Common A/C settings for Horizontal Swing. +enum class swingh_t { + kOff = -1, + kAuto = 0, // a.k.a. On. + kLeftMax = 1, + kLeft = 2, + kMiddle = 3, + kRight = 4, + kRightMax = 5, + kWide = 6, // a.k.a. left & right at the same time. + // Add new entries before this one, and update it to point to the last entry + kLastSwinghEnum = kWide, +}; + +/// Structure to hold a common A/C state. +struct state_t { + decode_type_t protocol = decode_type_t::UNKNOWN; + int16_t model = -1; // `-1` means unused. + bool power = false; + stdAc::opmode_t mode = stdAc::opmode_t::kOff; + float degrees = 25; + bool celsius = true; + stdAc::fanspeed_t fanspeed = stdAc::fanspeed_t::kAuto; + stdAc::swingv_t swingv = stdAc::swingv_t::kOff; + stdAc::swingh_t swingh = stdAc::swingh_t::kOff; + bool quiet = false; + bool turbo = false; + bool econo = false; + bool light = false; + bool filter = false; + bool clean = false; + bool beep = false; + int16_t sleep = -1; // `-1` means off. + int16_t clock = -1; // `-1` means not set. + stdAc::ac_command_t command = stdAc::ac_command_t::kControlCommand; + bool iFeel = false; + float sensorTemperature = kNoTempValue; // `kNoTempValue` means not set. +}; +}; // namespace stdAc + +/// Fujitsu A/C model numbers +enum fujitsu_ac_remote_model_t { + ARRAH2E = 1, ///< (1) AR-RAH2E, AR-RAC1E, AR-RAE1E, AR-RCE1E, AR-RAH2U, + ///< AR-REG1U (Default) + ///< Warning: Use on incorrect models can cause the A/C to lock + ///< up, requring the A/C to be physically powered off to fix. + ///< e.g. AR-RAH1U may lock up with a Swing command. + ARDB1, ///< (2) AR-DB1, AR-DL10 (AR-DL10 swing doesn't work) + ARREB1E, ///< (3) AR-REB1E, AR-RAH1U (Similar to ARRAH2E but no horiz + ///< control) + ARJW2, ///< (4) AR-JW2 (Same as ARDB1 but with horiz control) + ARRY4, ///< (5) AR-RY4 (Same as AR-RAH2E but with clean & filter) + ARREW4E, ///< (6) Similar to ARRAH2E, but with different temp config. +}; + +/// Gree A/C model numbers +enum gree_ac_remote_model_t { + YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default) + YBOFB, // (2) Green, YBOFB2, YAPOF3 + YX1FSF, // (3) Soleus Air window unit (Similar to YAW1F, but with an + // Operation mode of Energy Saver (Econo)) +}; + +/// HAIER_AC176 A/C model numbers +enum haier_ac176_remote_model_t { + V9014557_A = 1, // (1) V9014557 Remote in "A" setting. (Default) + V9014557_B, // (2) V9014557 Remote in "B" setting. +}; + +/// HITACHI_AC1 A/C model numbers +enum hitachi_ac1_remote_model_t { + R_LT0541_HTA_A = 1, // (1) R-LT0541-HTA Remote in "A" setting. (Default) + R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting. +}; + +/// MIRAGE A/C model numbers +enum mirage_ac_remote_model_t { + KKG9AC1 = 1, // (1) KKG9A-C1 Remote. (Default) + KKG29AC1, // (2) KKG29A-C1 Remote. +}; + +/// Panasonic A/C model numbers +enum panasonic_ac_remote_model_t { + kPanasonicUnknown = 0, + kPanasonicLke = 1, + kPanasonicNke = 2, + kPanasonicDke = 3, // PKR too. + kPanasonicJke = 4, + kPanasonicCkp = 5, + kPanasonicRkr = 6, +}; + +/// Sharp A/C model numbers +enum sharp_ac_remote_model_t { + A907 = 1, + A705 = 2, + A903 = 3, // 820 too +}; + +/// TCL (& Teknopoint) A/C model numbers +enum tcl_ac_remote_model_t { + TAC09CHSD = 1, + GZ055BE1 = 2, // Also Teknopoint GZ01-BEJ0-000 +}; + +/// Voltas A/C model numbers +enum voltas_ac_remote_model_t { + kVoltasUnknown = 0, // Full Function + kVoltas122LZF = 1, // (1) 122LZF (No SwingH support) (Default) +}; + +/// Whirlpool A/C model numbers +enum whirlpool_ac_remote_model_t { + DG11J13A = 1, // DG11J1-04 too + DG11J191, +}; + +/// LG A/C model numbers +enum lg_ac_remote_model_t { + GE6711AR2853M = 1, // (1) LG 28-bit Protocol (default) + AKB75215403, // (2) LG2 28-bit Protocol + AKB74955603, // (3) LG2 28-bit Protocol variant + AKB73757604, // (4) LG2 Variant of AKB74955603 + LG6711A20083V, // (5) Same as GE6711AR2853M, but only SwingV toggle. +}; + +/// Argo A/C model numbers +enum argo_ac_remote_model_t { + SAC_WREM2 = 1, // (1) ARGO WREM2 remote (default) + SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd +}; + +// Classes + +/// Class for sending all basic IR protocols. +/// @note Originally from https://github.com/shirriff/Arduino-IRremote/ +/// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for +/// sending IR code on ESP8266 +class IRsend { + public: + explicit IRsend(uint16_t IRsendPin, bool inverted = false, + bool use_modulation = true); + void begin(); + void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault); + VIRTUAL void _delayMicroseconds(uint32_t usec); + VIRTUAL uint16_t mark(uint16_t usec); + VIRTUAL void space(uint32_t usec); + int8_t calibrate(uint16_t hz = 38000U); + void sendRaw(const uint16_t buf[], const uint16_t len, const uint16_t hz); + void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, + uint32_t zerospace, uint64_t data, uint16_t nbits, + bool MSBfirst = true); + void sendManchesterData(const uint16_t half_period, const uint64_t data, + const uint16_t nbits, const bool MSBfirst = true, + const bool GEThomas = true); + void sendManchester(const uint16_t headermark, const uint32_t headerspace, + const uint16_t half_period, const uint16_t footermark, + const uint32_t gap, const uint64_t data, + const uint16_t nbits, const uint16_t frequency = 38, + const bool MSBfirst = true, + const uint16_t repeat = kNoRepeat, + const uint8_t dutycycle = kDutyDefault, + const bool GEThomas = true); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint32_t mesgtime, const uint64_t data, + const uint16_t nbits, const uint16_t frequency, + const bool MSBfirst, const uint16_t repeat, + const uint8_t dutycycle); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint8_t *dataptr, const uint16_t nbytes, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle); + static uint16_t minRepeats(const decode_type_t protocol); + static uint16_t defaultBits(const decode_type_t protocol); + bool send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat = kNoRepeat); + bool send(const decode_type_t type, const uint8_t *state, + const uint16_t nbytes); +#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ + SEND_MIDEA24) + void sendNEC(uint64_t data, uint16_t nbits = kNECBits, + uint16_t repeat = kNoRepeat); + uint32_t encodeNEC(uint16_t address, uint16_t command); +#endif +#if SEND_SONY + // sendSony() should typically be called with repeat=2 as Sony devices + // expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes) + // Legacy use of this procedure was to only send a single code so call it with + // repeat=0 for backward compatibility. As of v2.0 it defaults to sending + // a Sony command that will be accepted be a device. + void sendSony(const uint64_t data, const uint16_t nbits = kSony20Bits, + const uint16_t repeat = kSonyMinRepeat); + void sendSony38(const uint64_t data, const uint16_t nbits = kSony20Bits, + const uint16_t repeat = kSonyMinRepeat + 1); + uint32_t encodeSony(const uint16_t nbits, const uint16_t command, + const uint16_t address, const uint16_t extended = 0); +#endif // SEND_SONY +#if SEND_SHERWOOD + void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits, + uint16_t repeat = kSherwoodMinRepeat); +#endif + // `sendSAMSUNG()` is required by `sendLG()` +#if (SEND_SAMSUNG || SEND_LG) + void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits, + const uint16_t repeat = kNoRepeat); + uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command); +#endif // (SEND_SAMSUNG || SEND_LG) +#if SEND_SAMSUNG36 + void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_SAMSUNG_AC + void sendSamsungAC(const unsigned char data[], + const uint16_t nbytes = kSamsungAcStateLength, + const uint16_t repeat = kSamsungAcDefaultRepeat); +#endif +#if SEND_LG + void sendLG(uint64_t data, uint16_t nbits = kLgBits, + uint16_t repeat = kNoRepeat); + void sendLG2(uint64_t data, uint16_t nbits = kLgBits, + uint16_t repeat = kNoRepeat); + uint32_t encodeLG(uint16_t address, uint16_t command); +#endif +#if (SEND_SHARP || SEND_DENON) + uint32_t encodeSharp(const uint16_t address, const uint16_t command, + const uint16_t expansion = 1, const uint16_t check = 0, + const bool MSBfirst = false); + void sendSharp(const uint16_t address, const uint16_t command, + const uint16_t nbits = kSharpBits, + const uint16_t repeat = kNoRepeat); + void sendSharpRaw(const uint64_t data, const uint16_t nbits = kSharpBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_SHARP_AC + void sendSharpAc(const unsigned char data[], + const uint16_t nbytes = kSharpAcStateLength, + const uint16_t repeat = kSharpAcDefaultRepeat); +#endif // SEND_SHARP_AC +#if SEND_JVC + void sendJVC(uint64_t data, uint16_t nbits = kJvcBits, + uint16_t repeat = kNoRepeat); + uint16_t encodeJVC(uint8_t address, uint8_t command); +#endif +#if SEND_DENON + void sendDenon(uint64_t data, uint16_t nbits = kDenonBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_SANYO + uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command); + void sendSanyoLC7461(const uint64_t data, + const uint16_t nbits = kSanyoLC7461Bits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_SANYO_AC + void sendSanyoAc(const uint8_t *data, + const uint16_t nbytes = kSanyoAcStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + void sendSanyoAc88(const uint8_t *data, + const uint16_t nbytes = kSanyoAc88StateLength, + const uint16_t repeat = kSanyoAc88MinRepeat); +#endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + void sendSanyoAc152(const uint8_t *data, + const uint16_t nbytes = kSanyoAc152StateLength, + const uint16_t repeat = kSanyoAc152MinRepeat); +#endif // SEND_SANYO_AC152 +#if SEND_DISH + // sendDISH() should typically be called with repeat=3 as DISH devices + // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) + // Legacy use of this procedure was only to send a single code + // so use repeat=0 for backward compatibility. + void sendDISH(uint64_t data, uint16_t nbits = kDishBits, + uint16_t repeat = kDishMinRepeat); +#endif +#if (SEND_PANASONIC || SEND_DENON) + void sendPanasonic64(const uint64_t data, + const uint16_t nbits = kPanasonicBits, + const uint16_t repeat = kNoRepeat); + void sendPanasonic(const uint16_t address, const uint32_t data, + const uint16_t nbits = kPanasonicBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodePanasonic(const uint16_t manufacturer, const uint8_t device, + const uint8_t subdevice, const uint8_t function); +#endif +#if SEND_RC5 + void sendRC5(const uint64_t data, uint16_t nbits = kRC5XBits, + const uint16_t repeat = kNoRepeat); + uint16_t encodeRC5(const uint8_t address, const uint8_t command, + const bool key_released = false); + uint16_t encodeRC5X(const uint8_t address, const uint8_t command, + const bool key_released = false); + uint64_t toggleRC5(const uint64_t data); +#endif +#if SEND_RC6 + void sendRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits, + const uint16_t repeat = kNoRepeat); + uint64_t encodeRC6(const uint32_t address, const uint8_t command, + const uint16_t mode = kRC6Mode0Bits); + uint64_t toggleRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits); +#endif +#if SEND_RCMM + void sendRCMM(uint64_t data, uint16_t nbits = kRCMMBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_COOLIX + void sendCOOLIX(const uint64_t data, const uint16_t nbits = kCoolixBits, + const uint16_t repeat = kCoolixDefaultRepeat); +#endif // SEND_COOLIX +#if SEND_COOLIX48 + void sendCoolix48(const uint64_t data, const uint16_t nbits = kCoolix48Bits, + const uint16_t repeat = kCoolixDefaultRepeat); +#endif // SEND_COOLIX48 +#if SEND_WHYNTER + void sendWhynter(const uint64_t data, const uint16_t nbits = kWhynterBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_MIRAGE + void sendMirage(const unsigned char data[], + const uint16_t nbytes = kMirageStateLength, + const uint16_t repeat = kMirageMinRepeat); +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI + void sendMitsubishi(uint64_t data, uint16_t nbits = kMitsubishiBits, + uint16_t repeat = kMitsubishiMinRepeat); +#endif +#if SEND_MITSUBISHI136 + void sendMitsubishi136(const unsigned char data[], + const uint16_t nbytes = kMitsubishi136StateLength, + const uint16_t repeat = kMitsubishi136MinRepeat); +#endif +#if SEND_MITSUBISHI112 + void sendMitsubishi112(const unsigned char data[], + const uint16_t nbytes = kMitsubishi112StateLength, + const uint16_t repeat = kMitsubishi112MinRepeat); +#endif +#if SEND_MITSUBISHI2 + void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits, + uint16_t repeat = kMitsubishiMinRepeat); +#endif +#if SEND_MITSUBISHI_AC + void sendMitsubishiAC(const unsigned char data[], + const uint16_t nbytes = kMitsubishiACStateLength, + const uint16_t repeat = kMitsubishiACMinRepeat); +#endif +#if SEND_MITSUBISHIHEAVY + void sendMitsubishiHeavy88( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy88StateLength, + const uint16_t repeat = kMitsubishiHeavy88MinRepeat); + void sendMitsubishiHeavy152( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy152StateLength, + const uint16_t repeat = kMitsubishiHeavy152MinRepeat); +#endif +#if SEND_FUJITSU_AC + void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kFujitsuAcMinRepeat); +#endif +#if SEND_FUJITSU_AC264 + void sendFujitsuAC264(const unsigned char data[], + const uint16_t nbytes = kFujitsuAc264StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_FUJITSU_AC264 +#if SEND_INAX + void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits, + const uint16_t repeat = kInaxMinRepeat); +#endif // SEND_INAX +#if SEND_GLOBALCACHE + void sendGC(uint16_t buf[], uint16_t len); +#endif +#if SEND_KELVINATOR + void sendKelvinator(const unsigned char data[], + const uint16_t nbytes = kKelvinatorStateLength, + const uint16_t repeat = kKelvinatorDefaultRepeat); +#endif +#if SEND_DAIKIN + void sendDaikin(const unsigned char data[], + const uint16_t nbytes = kDaikinStateLength, + const uint16_t repeat = kDaikinDefaultRepeat); +#endif +#if SEND_DAIKIN64 + void sendDaikin64(const uint64_t data, const uint16_t nbits = kDaikin64Bits, + const uint16_t repeat = kDaikin64DefaultRepeat); +#endif // SEND_DAIKIN64 +#if SEND_DAIKIN128 + void sendDaikin128(const unsigned char data[], + const uint16_t nbytes = kDaikin128StateLength, + const uint16_t repeat = kDaikin128DefaultRepeat); +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + void sendDaikin152(const unsigned char data[], + const uint16_t nbytes = kDaikin152StateLength, + const uint16_t repeat = kDaikin152DefaultRepeat); +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + void sendDaikin160(const unsigned char data[], + const uint16_t nbytes = kDaikin160StateLength, + const uint16_t repeat = kDaikin160DefaultRepeat); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + void sendDaikin176(const unsigned char data[], + const uint16_t nbytes = kDaikin176StateLength, + const uint16_t repeat = kDaikin176DefaultRepeat); +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + void sendDaikin2(const unsigned char data[], + const uint16_t nbytes = kDaikin2StateLength, + const uint16_t repeat = kDaikin2DefaultRepeat); +#endif +#if SEND_DAIKIN200 + void sendDaikin200(const unsigned char data[], + const uint16_t nbytes = kDaikin200StateLength, + const uint16_t repeat = kDaikin200DefaultRepeat); +#endif // SEND_DAIKIN200 +#if SEND_DAIKIN216 + void sendDaikin216(const unsigned char data[], + const uint16_t nbytes = kDaikin216StateLength, + const uint16_t repeat = kDaikin216DefaultRepeat); +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN312 + void sendDaikin312(const unsigned char data[], + const uint16_t nbytes = kDaikin312StateLength, + const uint16_t repeat = kDaikin312DefaultRepeat); +#endif // SEND_DAIKIN312 +#if SEND_AIWA_RC_T501 + void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, + uint16_t repeat = kAiwaRcT501MinRepeats); +#endif +#if SEND_GREE + void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits, + const uint16_t repeat = kGreeDefaultRepeat); + void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength, + const uint16_t repeat = kGreeDefaultRepeat); +#endif +#if SEND_GOODWEATHER + void sendGoodweather(const uint64_t data, + const uint16_t nbits = kGoodweatherBits, + const uint16_t repeat = kGoodweatherMinRepeat); +#endif // SEND_GOODWEATHER +#if SEND_GORENJE + void sendGorenje(const uint64_t data, const uint16_t nbits = kGorenjeBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_GORENJE +#if SEND_PRONTO + void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); +#endif +#if SEND_ARGO + void sendArgo(const unsigned char data[], + const uint16_t nbytes = kArgoStateLength, + const uint16_t repeat = kArgoDefaultRepeat, + bool sendFooter = false); + void sendArgoWREM3(const unsigned char data[], + const uint16_t nbytes = kArgoStateLength, + const uint16_t repeat = kArgoDefaultRepeat); +#endif // SEND_ARGO +#if SEND_TROTEC + void sendTrotec(const unsigned char data[], + const uint16_t nbytes = kTrotecStateLength, + const uint16_t repeat = kTrotecDefaultRepeat); +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + void sendTrotec3550(const unsigned char data[], + const uint16_t nbytes = kTrotecStateLength, + const uint16_t repeat = kTrotecDefaultRepeat); +#endif // SEND_TROTEC_3550 +#if SEND_NIKAI + void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_TOSHIBA_AC + void sendToshibaAC(const uint8_t data[], + const uint16_t nbytes = kToshibaACStateLength, + const uint16_t repeat = kToshibaACMinRepeat); +#endif +#if SEND_MIDEA + void sendMidea(uint64_t data, uint16_t nbits = kMideaBits, + uint16_t repeat = kMideaMinRepeat); +#endif // SEND_MIDEA +#if SEND_MIDEA24 + void sendMidea24(const uint64_t data, const uint16_t nbits = kMidea24Bits, + const uint16_t repeat = kMidea24MinRepeat); +#endif // SEND_MIDEA24 +#if SEND_MAGIQUEST + void sendMagiQuest(const uint64_t data, const uint16_t nbits = kMagiquestBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodeMagiQuest(const uint32_t wand_id, const uint16_t magnitude); +#endif +#if SEND_LASERTAG + void sendLasertag(uint64_t data, uint16_t nbits = kLasertagBits, + uint16_t repeat = kLasertagMinRepeat); +#endif +#if SEND_CARRIER_AC + void sendCarrierAC(uint64_t data, uint16_t nbits = kCarrierAcBits, + uint16_t repeat = kCarrierAcMinRepeat); +#endif +#if SEND_CARRIER_AC40 + void sendCarrierAC40(uint64_t data, uint16_t nbits = kCarrierAc40Bits, + uint16_t repeat = kCarrierAc40MinRepeat); +#endif +#if SEND_CARRIER_AC64 + void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits, + uint16_t repeat = kCarrierAc64MinRepeat); +#endif +#if SEND_CARRIER_AC84 + void sendCarrierAC84(const uint8_t data[], + const uint16_t nbytes = kCarrierAc84StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CARRIER_AC84 +#if SEND_CARRIER_AC128 + void sendCarrierAC128(const uint8_t data[], + uint16_t nbytes = kCarrierAc128StateLength, + uint16_t repeat = kCarrierAc128MinRepeat); +#endif // SEND_CARRIER_AC128 +#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) + void sendHaierAC(const unsigned char data[], + const uint16_t nbytes = kHaierACStateLength, + const uint16_t repeat = kHaierAcDefaultRepeat); +#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) +#if SEND_HAIER_AC_YRW02 + void sendHaierACYRW02(const unsigned char data[], + const uint16_t nbytes = kHaierACYRW02StateLength, + const uint16_t repeat = kHaierAcYrw02DefaultRepeat); +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HAIER_AC160 + void sendHaierAC160(const unsigned char data[], + const uint16_t nbytes = kHaierAC160StateLength, + const uint16_t repeat = kHaierAc160DefaultRepeat); +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + void sendHaierAC176(const unsigned char data[], + const uint16_t nbytes = kHaierAC176StateLength, + const uint16_t repeat = kHaierAc176DefaultRepeat); +#endif // SEND_HAIER_AC176 +#if SEND_HITACHI_AC + void sendHitachiAC(const unsigned char data[], + const uint16_t nbytes = kHitachiAcStateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif +#if SEND_HITACHI_AC1 + void sendHitachiAC1(const unsigned char data[], + const uint16_t nbytes = kHitachiAc1StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif +#if SEND_HITACHI_AC2 + void sendHitachiAC2(const unsigned char data[], + const uint16_t nbytes = kHitachiAc2StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif +#if SEND_HITACHI_AC3 + void sendHitachiAc3(const unsigned char data[], + const uint16_t nbytes, // No default as there as so many + // different sizes + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC3 +#if SEND_HITACHI_AC264 + void sendHitachiAc264(const unsigned char data[], + const uint16_t nbytes = kHitachiAc264StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void sendHitachiAc296(const unsigned char data[], + const uint16_t nbytes = kHitachiAc296StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + void sendHitachiAc344(const unsigned char data[], + const uint16_t nbytes = kHitachiAc344StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + void sendHitachiAc424(const unsigned char data[], + const uint16_t nbytes = kHitachiAc424StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC424 +#if SEND_GICABLE + void sendGICable(uint64_t data, uint16_t nbits = kGicableBits, + uint16_t repeat = kGicableMinRepeat); +#endif +#if SEND_WHIRLPOOL_AC + void sendWhirlpoolAC(const unsigned char data[], + const uint16_t nbytes = kWhirlpoolAcStateLength, + const uint16_t repeat = kWhirlpoolAcDefaultRepeat); +#endif +#if SEND_LUTRON + void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_ELECTRA_AC + void sendElectraAC(const unsigned char data[], + const uint16_t nbytes = kElectraAcStateLength, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_PANASONIC_AC + void sendPanasonicAC(const unsigned char data[], + const uint16_t nbytes = kPanasonicAcStateLength, + const uint16_t repeat = kPanasonicAcDefaultRepeat); +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC32 + void sendPanasonicAC32(const uint64_t data, + const uint16_t nbits = kPanasonicAc32Bits, + const uint16_t repeat = kPanasonicAcDefaultRepeat); +#endif // SEND_PANASONIC_AC32 +#if SEND_PIONEER + void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodePioneer(uint16_t address, uint16_t command); +#endif +#if SEND_MWM + void sendMWM(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_VESTEL_AC + void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_TCL96AC + void sendTcl96Ac(const unsigned char data[], + const uint16_t nbytes = kTcl96AcStateLength, + const uint16_t repeat = kTcl96AcDefaultRepeat); +#endif // SEND_TCL96AC +#if SEND_TCL112AC + void sendTcl112Ac(const unsigned char data[], + const uint16_t nbytes = kTcl112AcStateLength, + const uint16_t repeat = kTcl112AcDefaultRepeat); +#endif // SEND_TCL112AC +#if SEND_TECO + void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_LEGOPF + void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, + const uint16_t repeat = kLegoPfMinRepeat); +#endif +#if SEND_NEOCLIMA + void sendNeoclima(const unsigned char data[], + const uint16_t nbytes = kNeoclimaStateLength, + const uint16_t repeat = kNeoclimaMinRepeat); +#endif // SEND_NEOCLIMA +#if SEND_AMCOR + void sendAmcor(const unsigned char data[], + const uint16_t nbytes = kAmcorStateLength, + const uint16_t repeat = kAmcorDefaultRepeat); +#endif // SEND_AMCOR +#if SEND_EPSON + void sendEpson(uint64_t data, uint16_t nbits = kEpsonBits, + uint16_t repeat = kEpsonMinRepeat); +#endif +#if SEND_SYMPHONY + void sendSymphony(uint64_t data, uint16_t nbits = kSymphonyBits, + uint16_t repeat = kSymphonyDefaultRepeat); +#endif +#if SEND_AIRWELL + void sendAirwell(uint64_t data, uint16_t nbits = kAirwellBits, + uint16_t repeat = kAirwellMinRepeats); +#endif +#if SEND_DELONGHI_AC + void sendDelonghiAc(uint64_t data, uint16_t nbits = kDelonghiAcBits, + uint16_t repeat = kDelonghiAcDefaultRepeat); +#endif +#if SEND_DOSHISHA + void sendDoshisha(const uint64_t data, uint16_t nbits = kDoshishaBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodeDoshisha(const uint8_t command, const uint8_t channel = 0); +#endif // SEND_DOSHISHA +#if SEND_MULTIBRACKETS + void sendMultibrackets(const uint64_t data, + const uint16_t nbits = kMultibracketsBits, + const uint16_t repeat = kMultibracketsDefaultRepeat); +#endif +#if SEND_TECHNIBEL_AC + void sendTechnibelAc(uint64_t data, uint16_t nbits = kTechnibelAcBits, + uint16_t repeat = kTechnibelAcDefaultRepeat); +#endif +#if SEND_CORONA_AC + void sendCoronaAc(const uint8_t data[], + const uint16_t nbytes = kCoronaAcStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CORONA_AC +#if SEND_ZEPEAL + void sendZepeal(const uint64_t data, + const uint16_t nbits = kZepealBits, + const uint16_t repeat = kZepealMinRepeat); +#endif // SEND_ZEPEAL +#if SEND_VOLTAS + void sendVoltas(const unsigned char data[], + const uint16_t nbytes = kVoltasStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_VOLTAS +#if SEND_METZ + void sendMetz(const uint64_t data, + const uint16_t nbits = kMetzBits, + const uint16_t repeat = kMetzMinRepeat); + static uint32_t encodeMetz(const uint8_t address, const uint8_t command, + const bool toggle = false); +#endif // SEND_METZ +#if SEND_TRANSCOLD + void sendTranscold(const uint64_t data, const uint16_t nbits = kTranscoldBits, + const uint16_t repeat = kTranscoldDefaultRepeat); +#endif // SEND_TRANSCOLD +#if SEND_ELITESCREENS + void sendElitescreens(const uint64_t data, + const uint16_t nbits = kEliteScreensBits, + const uint16_t repeat = kEliteScreensDefaultRepeat); +#endif // SEND_ELITESCREENS +#if SEND_MILESTAG2 + // Since There 2 types of transmissions + // (14bits for Shooting by default, you can set 24 bit for msg delivery) + void sendMilestag2(const uint64_t data, + const uint16_t nbits = kMilesTag2ShotBits, + const uint16_t repeat = kMilesMinRepeat); +#endif // SEND_MILESTAG2 +#if SEND_ECOCLIM + void sendEcoclim(const uint64_t data, const uint16_t nbits = kEcoclimBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_ECOCLIM +#if SEND_XMP + void sendXmp(const uint64_t data, const uint16_t nbits = kXmpBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_XMP +#if SEND_TRUMA + void sendTruma(const uint64_t data, const uint16_t nbits = kTrumaBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_TRUMA +#if SEND_TEKNOPOINT + void sendTeknopoint(const unsigned char data[], + const uint16_t nbytes = kTeknopointStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_TEKNOPOINT +#if SEND_KELON + void sendKelon(const uint64_t data, const uint16_t nbits = kKelonBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_KELON +#if SEND_KELON168 + void sendKelon168(const unsigned char data[], + const uint16_t nbytes = kKelon168StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_KELON168 +#if SEND_BOSE + void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_BOSE +#if SEND_ARRIS + void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits, + const uint16_t repeat = kNoRepeat); + static uint32_t toggleArrisRelease(const uint32_t data); + static uint32_t encodeArris(const uint32_t command, const bool release); +#endif // SEND_ARRIS +#if SEND_RHOSS + void sendRhoss(const unsigned char data[], + const uint16_t nbytes = kRhossStateLength, + const uint16_t repeat = kRhossDefaultRepeat); +#endif // SEND_RHOSS +#if SEND_AIRTON + void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits, + const uint16_t repeat = kAirtonDefaultRepeat); +#endif // SEND_AIRTON +#if SEND_TOTO + void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits, + const uint16_t repeat = kTotoDefaultRepeat); +#endif // SEND_TOTO +#if SEND_CLIMABUTLER + void sendClimaButler(const uint64_t data, + const uint16_t nbits = kClimaButlerBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CLIMABUTLER +#if SEND_BOSCH144 + void sendBosch144(const unsigned char data[], + const uint16_t nbytes = kBosch144StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_BOSCH144 +#if SEND_WOWWEE + void sendWowwee(const uint64_t data, const uint16_t nbits = kWowweeBits, + const uint16_t repeat = kWowweeDefaultRepeat); +#endif // SEND_WOWWEE +#if SEND_YORK + void sendYork(const unsigned char data[], + const uint16_t nbytes = kYorkStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_YORK + + protected: +#ifdef UNIT_TEST +#ifndef HIGH +#define HIGH 0x1 +#endif +#ifndef LOW +#define LOW 0x0 +#endif +#endif // UNIT_TEST + uint8_t outputOn; + uint8_t outputOff; + VIRTUAL void ledOff(); + VIRTUAL void ledOn(); +#ifndef UNIT_TEST + + private: +#else + uint32_t _freq_unittest; +#endif // UNIT_TEST + uint16_t onTimePeriod; + uint16_t offTimePeriod; + uint16_t IRpin; + int8_t periodOffset; + uint8_t _dutycycle; + bool modulation; + uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true); +#if SEND_SONY + void _sendSony(const uint64_t data, const uint16_t nbits, + const uint16_t repeat, const uint16_t freq); +#endif // SEND_SONY +}; + +#endif // IRSEND_H_ diff --git a/src/IRtext.cpp b/src/IRtext.cpp index 9cb39b772..8441475b7 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -1,561 +1,563 @@ -// Copyright 2019-2021 - David Conran (@crankyoldgit) - -/// @file IRtext.cpp -/// @warning If you add or remove an entry in this file, you should run: -/// '../tools/generate_irtext_h.sh' to rebuild the `IRtext.h` file. - -#include "IRtext.h" -#ifndef UNIT_TEST -#include -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "i18n.h" - -#include "IRmacros.h" - -#ifndef PROGMEM -#define PROGMEM // Pretend we have the PROGMEM macro even if we really don't. -#endif - -#ifndef FPSTR -#define FPSTR(X) X // Also pretend we have flash-string helper class cast. -#endif - -#define IRTEXT_CONST_BLOB_NAME(NAME)\ - NAME ## Blob - -#define IRTEXT_CONST_BLOB_DECL(NAME)\ - const char IRTEXT_CONST_BLOB_NAME(NAME) [] PROGMEM - -#define IRTEXT_CONST_BLOB_PTR(NAME)\ - IRTEXT_CONST_PTR(NAME) {\ - IRTEXT_CONST_PTR_CAST(IRTEXT_CONST_BLOB_NAME(NAME)) } - -#define IRTEXT_CONST_STRING(NAME, VALUE)\ - static IRTEXT_CONST_BLOB_DECL(NAME) { VALUE };\ - IRTEXT_CONST_PTR(NAME) PROGMEM {\ - IRTEXT_CONST_PTR_CAST(&(IRTEXT_CONST_BLOB_NAME(NAME))[0]) } - -// Common -IRTEXT_CONST_STRING(kUnknownStr, D_STR_UNKNOWN); ///< "Unknown" -IRTEXT_CONST_STRING(kProtocolStr, D_STR_PROTOCOL); ///< "Protocol" -IRTEXT_CONST_STRING(kPowerStr, D_STR_POWER); ///< "Power" -IRTEXT_CONST_STRING(kOnStr, D_STR_ON); ///< "On" -IRTEXT_CONST_STRING(kOffStr, D_STR_OFF); ///< "Off" -IRTEXT_CONST_STRING(k1Str, D_STR_1); ///< "1" -IRTEXT_CONST_STRING(k0Str, D_STR_0); ///< "0" -IRTEXT_CONST_STRING(kModeStr, D_STR_MODE); ///< "Mode" -IRTEXT_CONST_STRING(kToggleStr, D_STR_TOGGLE); ///< "Toggle" -IRTEXT_CONST_STRING(kTurboStr, D_STR_TURBO); ///< "Turbo" -IRTEXT_CONST_STRING(kSuperStr, D_STR_SUPER); ///< "Super" -IRTEXT_CONST_STRING(kSleepStr, D_STR_SLEEP); ///< "Sleep" -IRTEXT_CONST_STRING(kLightStr, D_STR_LIGHT); ///< "Light" -IRTEXT_CONST_STRING(kPowerfulStr, D_STR_POWERFUL); ///< "Powerful" -IRTEXT_CONST_STRING(kQuietStr, D_STR_QUIET); ///< "Quiet" -IRTEXT_CONST_STRING(kEconoStr, D_STR_ECONO); ///< "Econo" -IRTEXT_CONST_STRING(kSwingStr, D_STR_SWING); ///< "Swing" -IRTEXT_CONST_STRING(kSwingHStr, D_STR_SWINGH); ///< "SwingH" -IRTEXT_CONST_STRING(kSwingVStr, D_STR_SWINGV); ///< "SwingV" -IRTEXT_CONST_STRING(kBeepStr, D_STR_BEEP); ///< "Beep" -IRTEXT_CONST_STRING(kZoneFollowStr, D_STR_ZONEFOLLOW); ///< "Zone Follow" -IRTEXT_CONST_STRING(kFixedStr, D_STR_FIXED); ///< "Fixed" -IRTEXT_CONST_STRING(kMouldStr, D_STR_MOULD); ///< "Mould" -IRTEXT_CONST_STRING(kCleanStr, D_STR_CLEAN); ///< "Clean" -IRTEXT_CONST_STRING(kPurifyStr, D_STR_PURIFY); ///< "Purify" -IRTEXT_CONST_STRING(kTimerStr, D_STR_TIMER); ///< "Timer" -IRTEXT_CONST_STRING(kOnTimerStr, D_STR_ONTIMER); ///< "On Timer" -IRTEXT_CONST_STRING(kOffTimerStr, D_STR_OFFTIMER); ///< "Off Timer" -IRTEXT_CONST_STRING(kTimerModeStr, D_STR_TIMERMODE); ///< "Timer Mode" -IRTEXT_CONST_STRING(kClockStr, D_STR_CLOCK); ///< "Clock" -IRTEXT_CONST_STRING(kCommandStr, D_STR_COMMAND); ///< "Command" -IRTEXT_CONST_STRING(kConfigCommandStr, D_STR_CONFIG); ///< "Config" -IRTEXT_CONST_STRING(kControlCommandStr, D_STR_CONTROL); ///< "Control" -IRTEXT_CONST_STRING(kXFanStr, D_STR_XFAN); ///< "XFan" -IRTEXT_CONST_STRING(kHealthStr, D_STR_HEALTH); ///< "Health" -IRTEXT_CONST_STRING(kModelStr, D_STR_MODEL); ///< "Model" -IRTEXT_CONST_STRING(kTempStr, D_STR_TEMP); ///< "Temp" -IRTEXT_CONST_STRING(kIFeelReportStr, D_STR_IFEELREPORT); ///< "IFeel Report" -IRTEXT_CONST_STRING(kIFeelStr, D_STR_IFEEL); ///< "IFeel" -IRTEXT_CONST_STRING(kHumidStr, D_STR_HUMID); ///< "Humid" -IRTEXT_CONST_STRING(kSaveStr, D_STR_SAVE); ///< "Save" -IRTEXT_CONST_STRING(kEyeStr, D_STR_EYE); ///< "Eye" -IRTEXT_CONST_STRING(kFollowStr, D_STR_FOLLOW); ///< "Follow" -IRTEXT_CONST_STRING(kIonStr, D_STR_ION); ///< "Ion" -IRTEXT_CONST_STRING(kFreshStr, D_STR_FRESH); ///< "Fresh" -IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold" -IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button" -IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat" -IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat" -IRTEXT_CONST_STRING(kISeeStr, D_STR_ISEE); ///< "ISee" -IRTEXT_CONST_STRING(kAbsenseDetectStr, D_STR_ABSENSEDETECT); - ///< "AbsenseDetect" -IRTEXT_CONST_STRING(kDirectIndirectModeStr, D_STR_DIRECTINDIRECTMODE); - ///< "Direct/Indirect mode" -IRTEXT_CONST_STRING(kDirectStr, D_STR_DIRECT); ///< "Direct" -IRTEXT_CONST_STRING(kIndirectStr, D_STR_INDIRECT); ///< "Indirect" - -IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night" -IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent" -IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter" -IRTEXT_CONST_STRING(k3DStr, D_STR_3D); ///< "3D" -IRTEXT_CONST_STRING(kCelsiusStr, D_STR_CELSIUS); ///< "Celsius" -IRTEXT_CONST_STRING(kCelsiusFahrenheitStr, D_STR_CELSIUS_FAHRENHEIT); ///< -///< "Celsius/Fahrenheit" -IRTEXT_CONST_STRING(kTempUpStr, D_STR_TEMPUP); ///< "Temp Up" -IRTEXT_CONST_STRING(kTempDownStr, D_STR_TEMPDOWN); ///< "Temp Down" -IRTEXT_CONST_STRING(kStartStr, D_STR_START); ///< "Start" -IRTEXT_CONST_STRING(kStopStr, D_STR_STOP); ///< "Stop" -IRTEXT_CONST_STRING(kMoveStr, D_STR_MOVE); ///< "Move" -IRTEXT_CONST_STRING(kSetStr, D_STR_SET); ///< "Set" -IRTEXT_CONST_STRING(kCancelStr, D_STR_CANCEL); ///< "Cancel" -IRTEXT_CONST_STRING(kUpStr, D_STR_UP); ///< "Up" -IRTEXT_CONST_STRING(kDownStr, D_STR_DOWN); ///< "Down" -IRTEXT_CONST_STRING(kChangeStr, D_STR_CHANGE); ///< "Change" -IRTEXT_CONST_STRING(kComfortStr, D_STR_COMFORT); ///< "Comfort" -IRTEXT_CONST_STRING(kSensorStr, D_STR_SENSOR); ///< "Sensor" -IRTEXT_CONST_STRING(kWeeklyTimerStr, D_STR_WEEKLYTIMER); ///< "WeeklyTimer" -IRTEXT_CONST_STRING(kWifiStr, D_STR_WIFI); ///< "Wifi" -IRTEXT_CONST_STRING(kLastStr, D_STR_LAST); ///< "Last" -IRTEXT_CONST_STRING(kFastStr, D_STR_FAST); ///< "Fast" -IRTEXT_CONST_STRING(kSlowStr, D_STR_SLOW); ///< "Slow" -IRTEXT_CONST_STRING(kAirFlowStr, D_STR_AIRFLOW); ///< "Air Flow" -IRTEXT_CONST_STRING(kStepStr, D_STR_STEP); ///< "Step" -IRTEXT_CONST_STRING(kNAStr, D_STR_NA); ///< "N/A" -IRTEXT_CONST_STRING(kInsideStr, D_STR_INSIDE); ///< "Inside" -IRTEXT_CONST_STRING(kOutsideStr, D_STR_OUTSIDE); ///< "Outside" -IRTEXT_CONST_STRING(kLoudStr, D_STR_LOUD); ///< "Loud" -IRTEXT_CONST_STRING(kLowerStr, D_STR_LOWER); ///< "Lower" -IRTEXT_CONST_STRING(kUpperStr, D_STR_UPPER); ///< "Upper" -IRTEXT_CONST_STRING(kUpperMiddleStr, D_STR_UPPER_MIDDLE); ///< "Upper-Middle" -IRTEXT_CONST_STRING(kBreezeStr, D_STR_BREEZE); ///< "Breeze" -IRTEXT_CONST_STRING(kCirculateStr, D_STR_CIRCULATE); ///< "Circulate" -IRTEXT_CONST_STRING(kCeilingStr, D_STR_CEILING); ///< "Ceiling" -IRTEXT_CONST_STRING(kWallStr, D_STR_WALL); ///< "Wall" -IRTEXT_CONST_STRING(kRoomStr, D_STR_ROOM); ///< "Room" -IRTEXT_CONST_STRING(k6thSenseStr, D_STR_6THSENSE); ///< "6th Sense" -IRTEXT_CONST_STRING(kTypeStr, D_STR_TYPE); ///< "Type" -IRTEXT_CONST_STRING(kSpecialStr, D_STR_SPECIAL); ///< "Special" -IRTEXT_CONST_STRING(kIdStr, D_STR_ID); ///< "Id" / Device Identifier -IRTEXT_CONST_STRING(kVaneStr, D_STR_VANE); ///< "Vane" -IRTEXT_CONST_STRING(kLockStr, D_STR_LOCK); ///< "Lock" - -IRTEXT_CONST_STRING(kAutoStr, D_STR_AUTO); ///< "Auto" -IRTEXT_CONST_STRING(kAutomaticStr, D_STR_AUTOMATIC); ///< "Automatic" -IRTEXT_CONST_STRING(kManualStr, D_STR_MANUAL); ///< "Manual" -IRTEXT_CONST_STRING(kCoolStr, D_STR_COOL); ///< "Cool" -IRTEXT_CONST_STRING(kCoolingStr, D_STR_COOLING); ///< "Cooling" -IRTEXT_CONST_STRING(kHeatStr, D_STR_HEAT); ///< "Heat" -IRTEXT_CONST_STRING(kHeatingStr, D_STR_HEATING); ///< "Heating" -IRTEXT_CONST_STRING(kDryStr, D_STR_DRY); ///< "Dry" -IRTEXT_CONST_STRING(kDryingStr, D_STR_DRYING); ///< "Drying" -IRTEXT_CONST_STRING(kDehumidifyStr, D_STR_DEHUMIDIFY); ///< "Dehumidify" -IRTEXT_CONST_STRING(kFanStr, D_STR_FAN); ///< "Fan" -// The following Fans strings with "only" are required to help with -// HomeAssistant & Google Home Climate integration. For compatibility only. -// Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes -IRTEXT_CONST_STRING(kFanOnlyStr, D_STR_FANONLY); ///< "fan-only" -IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (HA/legacy) -IRTEXT_CONST_STRING(kFanOnlyWithSpaceStr, D_STR_FANSPACEONLY); ///< "Fan Only" -IRTEXT_CONST_STRING(kFanOnlyNoSpaceStr, D_STR_FANONLYNOSPACE); ///< "FanOnly" - -IRTEXT_CONST_STRING(kRecycleStr, D_STR_RECYCLE); ///< "Recycle" - -IRTEXT_CONST_STRING(kMaxStr, D_STR_MAX); ///< "Max" -IRTEXT_CONST_STRING(kMaximumStr, D_STR_MAXIMUM); ///< "Maximum" -IRTEXT_CONST_STRING(kMinStr, D_STR_MIN); ///< "Min" -IRTEXT_CONST_STRING(kMinimumStr, D_STR_MINIMUM); ///< "Minimum" -IRTEXT_CONST_STRING(kMedHighStr, D_STR_MED_HIGH); ///< "Med-high" -IRTEXT_CONST_STRING(kMedStr, D_STR_MED); ///< "Med" -IRTEXT_CONST_STRING(kMediumStr, D_STR_MEDIUM); ///< "Medium" - -IRTEXT_CONST_STRING(kHighestStr, D_STR_HIGHEST); ///< "Highest" -IRTEXT_CONST_STRING(kHighStr, D_STR_HIGH); ///< "High" -IRTEXT_CONST_STRING(kHiStr, D_STR_HI); ///< "Hi" -IRTEXT_CONST_STRING(kMidStr, D_STR_MID); ///< "Mid" -IRTEXT_CONST_STRING(kMiddleStr, D_STR_MIDDLE); ///< "Middle" -IRTEXT_CONST_STRING(kLowStr, D_STR_LOW); ///< "Low" -IRTEXT_CONST_STRING(kLoStr, D_STR_LO); ///< "Lo" -IRTEXT_CONST_STRING(kLowestStr, D_STR_LOWEST); ///< "Lowest" -IRTEXT_CONST_STRING(kMaxRightStr, D_STR_MAXRIGHT); ///< "Max Right" -IRTEXT_CONST_STRING(kMaxRightNoSpaceStr, D_STR_MAXRIGHT_NOSPACE); ///< - ///< "MaxRight" -IRTEXT_CONST_STRING(kRightMaxStr, D_STR_RIGHTMAX); ///< "Right Max" -IRTEXT_CONST_STRING(kRightMaxNoSpaceStr, D_STR_RIGHTMAX_NOSPACE); ///< - ///< "RightMax" -IRTEXT_CONST_STRING(kRightStr, D_STR_RIGHT); ///< "Right" -IRTEXT_CONST_STRING(kLeftStr, D_STR_LEFT); ///< "Left" -IRTEXT_CONST_STRING(kMaxLeftStr, D_STR_MAXLEFT); ///< "Max Left" -IRTEXT_CONST_STRING(kMaxLeftNoSpaceStr, D_STR_MAXLEFT_NOSPACE); ///< "MaxLeft" -IRTEXT_CONST_STRING(kLeftMaxStr, D_STR_LEFTMAX); ///< "Left Max" -IRTEXT_CONST_STRING(kLeftMaxNoSpaceStr, D_STR_LEFTMAX_NOSPACE); ///< "LeftMax" -IRTEXT_CONST_STRING(kWideStr, D_STR_WIDE); ///< "Wide" -IRTEXT_CONST_STRING(kCentreStr, D_STR_CENTRE); ///< "Centre" -IRTEXT_CONST_STRING(kTopStr, D_STR_TOP); ///< "Top" -IRTEXT_CONST_STRING(kBottomStr, D_STR_BOTTOM); ///< "Bottom" - -// Compound words/phrases/descriptions from pre-defined words. -IRTEXT_CONST_STRING(kEconoToggleStr, D_STR_ECONOTOGGLE); ///< "Econo Toggle" -IRTEXT_CONST_STRING(kEyeAutoStr, D_STR_EYEAUTO); ///< "Eye Auto" -IRTEXT_CONST_STRING(kLightToggleStr, D_STR_LIGHTTOGGLE); ///< "Light Toggle" -///< "Outside Quiet" -IRTEXT_CONST_STRING(kOutsideQuietStr, D_STR_OUTSIDEQUIET); -IRTEXT_CONST_STRING(kPowerToggleStr, D_STR_POWERTOGGLE); ///< "Power Toggle" -IRTEXT_CONST_STRING(kPowerButtonStr, D_STR_POWERBUTTON); ///< "Power Button" -IRTEXT_CONST_STRING(kPreviousPowerStr, D_STR_PREVIOUSPOWER); ///< -///< "Previous Power" -IRTEXT_CONST_STRING(kDisplayTempStr, D_STR_DISPLAYTEMP); ///< "Display Temp" -IRTEXT_CONST_STRING(kSensorTempStr, D_STR_SENSORTEMP); ///< "Sensor Temp" -IRTEXT_CONST_STRING(kSleepTimerStr, D_STR_SLEEP_TIMER); ///< "Sleep Timer" -IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode" -IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///< -///< "Swing(V) Toggle" -IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle" -IRTEXT_CONST_STRING(kSetTimerCommandStr, D_STR_SET_TIMER); ///< "Set Timer" -IRTEXT_CONST_STRING(kScheduleStr, D_STR_SCHEDULE); ///< "Schedule" -IRTEXT_CONST_STRING(kChStr, D_STR_CH); ///< "CH#" -IRTEXT_CONST_STRING(kTimerActiveDaysStr, D_STR_TIMER_ACTIVE_DAYS); -///< "TimerActiveDays" -IRTEXT_CONST_STRING(kKeyStr, D_STR_KEY); ///< "Key" -IRTEXT_CONST_STRING(kValueStr, D_STR_VALUE); ///< "Value" - -// Separators & Punctuation -const char kTimeSep = D_CHR_TIME_SEP; ///< ':' -IRTEXT_CONST_STRING(kSpaceLBraceStr, D_STR_SPACELBRACE); ///< " (" -IRTEXT_CONST_STRING(kCommaSpaceStr, D_STR_COMMASPACE); ///< ", " -IRTEXT_CONST_STRING(kColonSpaceStr, D_STR_COLONSPACE); ///< ": " -IRTEXT_CONST_STRING(kDashStr, D_STR_DASH); ///< "-" - -// IRutils -// - Time -IRTEXT_CONST_STRING(kDayStr, D_STR_DAY); ///< "Day" -IRTEXT_CONST_STRING(kDaysStr, D_STR_DAYS); ///< "Days" -IRTEXT_CONST_STRING(kHourStr, D_STR_HOUR); ///< "Hour" -IRTEXT_CONST_STRING(kHoursStr, D_STR_HOURS); ///< "Hours" -IRTEXT_CONST_STRING(kMinuteStr, D_STR_MINUTE); ///< "Minute" -IRTEXT_CONST_STRING(kMinutesStr, D_STR_MINUTES); ///< "Minutes" -IRTEXT_CONST_STRING(kSecondStr, D_STR_SECOND); ///< "Second" -IRTEXT_CONST_STRING(kSecondsStr, D_STR_SECONDS); ///< "Seconds" -IRTEXT_CONST_STRING(kNowStr, D_STR_NOW); ///< "Now" -IRTEXT_CONST_STRING(kThreeLetterDayOfWeekStr, D_STR_THREELETTERDAYS); ///< -///< "SunMonTueWedThuFriSat" -IRTEXT_CONST_STRING(kYesStr, D_STR_YES); ///< "Yes" -IRTEXT_CONST_STRING(kNoStr, D_STR_NO); ///< "No" -IRTEXT_CONST_STRING(kTrueStr, D_STR_TRUE); ///< "True" -IRTEXT_CONST_STRING(kFalseStr, D_STR_FALSE); ///< "False" - -IRTEXT_CONST_STRING(kRepeatStr, D_STR_REPEAT); ///< "Repeat" -IRTEXT_CONST_STRING(kCodeStr, D_STR_CODE); ///< "Code" -IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits" - -// Model Names -IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F" -IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB" -IRTEXT_CONST_STRING(kYx1fsfStr, D_STR_YX1FSF); ///< "YX1FSF" -IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A" -IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B" -IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A" -IRTEXT_CONST_STRING(kRlt0541htabStr, D_STR_RLT0541HTA_B); ///< "R-LT0541-HTA-B" -IRTEXT_CONST_STRING(kArrah2eStr, D_STR_ARRAH2E); ///< "ARRAH2E" -IRTEXT_CONST_STRING(kArdb1Str, D_STR_ARDB1); ///< "ARDB1" -IRTEXT_CONST_STRING(kArreb1eStr, D_STR_ARREB1E); ///< "ARREB1E" -IRTEXT_CONST_STRING(kArjw2Str, D_STR_ARJW2); ///< "ARJW2" -IRTEXT_CONST_STRING(kArry4Str, D_STR_ARRY4); ///< "ARRY4" -IRTEXT_CONST_STRING(kArrew4eStr, D_STR_ARREW4E); ///< "ARREW4E" -IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///< - ///< "GE6711AR2853M" -IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403" -IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603" -IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604" -IRTEXT_CONST_STRING(kLg6711a20083vStr, D_STR_LG6711A20083V); ///< - ///< "LG6711A20083V" -IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1" -IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1" -IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE" -IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE" -IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE" -IRTEXT_CONST_STRING(kPkrStr, D_STR_PKR); ///< "PKR" -IRTEXT_CONST_STRING(kJkeStr, D_STR_JKE); ///< "JKE" -IRTEXT_CONST_STRING(kCkpStr, D_STR_CKP); ///< "CKP" -IRTEXT_CONST_STRING(kRkrStr, D_STR_RKR); ///< "RKR" -IRTEXT_CONST_STRING(kPanasonicLkeStr, D_STR_PANASONICLKE); ///< "PANASONICLKE" -IRTEXT_CONST_STRING(kPanasonicNkeStr, D_STR_PANASONICNKE); ///< "PANASONICNKE" -IRTEXT_CONST_STRING(kPanasonicDkeStr, D_STR_PANASONICDKE); ///< "PANASONICDKE" -IRTEXT_CONST_STRING(kPanasonicPkrStr, D_STR_PANASONICPKR); ///< "PANASONICPKR" -IRTEXT_CONST_STRING(kPanasonicJkeStr, D_STR_PANASONICJKE); ///< "PANASONICJKE" -IRTEXT_CONST_STRING(kPanasonicCkpStr, D_STR_PANASONICCKP); ///< "PANASONICCKP" -IRTEXT_CONST_STRING(kPanasonicRkrStr, D_STR_PANASONICRKR); ///< "PANASONICRKR" -IRTEXT_CONST_STRING(kA907Str, D_STR_A907); ///< "A907" -IRTEXT_CONST_STRING(kA705Str, D_STR_A705); ///< "A705" -IRTEXT_CONST_STRING(kA903Str, D_STR_A903); ///< "A903" -IRTEXT_CONST_STRING(kTac09chsdStr, D_STR_TAC09CHSD); ///< "TAC09CHSD" -IRTEXT_CONST_STRING(kGz055be1Str, D_STR_GZ055BE1); ///< "GZ055BE1" -IRTEXT_CONST_STRING(k122lzfStr, D_STR_122LZF); ///< "122LZF" -IRTEXT_CONST_STRING(kDg11j13aStr, D_STR_DG11J13A); ///< "DG11J13A" -IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104" -IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191" -IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3" -IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3" - -#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as - // a question mark, check for length > 1 - // to show only currently included protocols -// Protocol Names -// Needs to be in decode_type_t order. -IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { - D_STR_UNUSED "\x0" - COND(DECODE_RC5 || SEND_RC5, - D_STR_RC5, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RC6 || SEND_RC6, - D_STR_RC6, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_NEC || SEND_NEC, - D_STR_NEC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SONY || SEND_SONY, - D_STR_SONY, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PANASONIC || SEND_PANASONIC, - D_STR_PANASONIC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_JVC || SEND_JVC, - D_STR_JVC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SAMSUNG || SEND_SAMSUNG, - D_STR_SAMSUNG, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_WHYNTER || SEND_WHYNTER, - D_STR_WHYNTER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AIWA_RC_T501 || SEND_AIWA_RC_T501, - D_STR_AIWA_RC_T501, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LG || SEND_LG, - D_STR_LG, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO || SEND_SANYO, - D_STR_SANYO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI || SEND_MITSUBISHI, - D_STR_MITSUBISHI, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DISH || SEND_DISH, - D_STR_DISH, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SHARP || SEND_SHARP, - D_STR_SHARP, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_COOLIX || SEND_COOLIX, - D_STR_COOLIX, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN || SEND_DAIKIN, - D_STR_DAIKIN, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DENON || SEND_DENON, - D_STR_DENON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_KELVINATOR || SEND_KELVINATOR, - D_STR_KELVINATOR, D_STR_UNSUPPORTED) "\x0" - COND(SEND_SHERWOOD, - D_STR_SHERWOOD, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY - COND(DECODE_MITSUBISHI_AC || SEND_MITSUBISHI_AC, - D_STR_MITSUBISHI_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RCMM || SEND_RCMM, - D_STR_RCMM, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO || SEND_SANYO, - D_STR_SANYO_LC7461, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RC5 || SEND_RC5, - D_STR_RC5X, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GREE || SEND_GREE, - D_STR_GREE, D_STR_UNSUPPORTED) "\x0" - COND(SEND_PRONTO, - D_STR_PRONTO, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY - COND(DECODE_NEC || SEND_NEC, - D_STR_NEC_LIKE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ARGO || SEND_ARGO, - D_STR_ARGO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TROTEC || SEND_TROTEC, - D_STR_TROTEC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_NIKAI || SEND_NIKAI, - D_STR_NIKAI, D_STR_UNSUPPORTED) "\x0" - COND(SEND_RAW, - D_STR_RAW, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY - COND(SEND_GLOBALCACHE, - D_STR_GLOBALCACHE, D_STR_UNSUPPORTED) "\x0" // SEND - COND(DECODE_TOSHIBA_AC || SEND_TOSHIBA_AC, - D_STR_TOSHIBA_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_FUJITSU_AC || SEND_FUJITSU_AC, - D_STR_FUJITSU_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MIDEA || SEND_MIDEA, - D_STR_MIDEA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MAGIQUEST || SEND_MAGIQUEST, - D_STR_MAGIQUEST, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LASERTAG || SEND_LASERTAG, - D_STR_LASERTAG, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC || SEND_CARRIER_AC, - D_STR_CARRIER_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC || SEND_HAIER_AC, - D_STR_HAIER_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI2 || SEND_MITSUBISHI2, - D_STR_MITSUBISHI2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC || SEND_HITACHI_AC, - D_STR_HITACHI_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC1 || SEND_HITACHI_AC1, - D_STR_HITACHI_AC1, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC2 || SEND_HITACHI_AC2, - D_STR_HITACHI_AC2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GICABLE || SEND_GICABLE, - D_STR_GICABLE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC_YRW02 || SEND_HAIER_AC_YRW02, - D_STR_HAIER_AC_YRW02, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_WHIRLPOOL_AC || SEND_WHIRLPOOL_AC, - D_STR_WHIRLPOOL_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SAMSUNG_AC || SEND_SAMSUNG_AC, - D_STR_SAMSUNG_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LUTRON || SEND_LUTRON, - D_STR_LUTRON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ELECTRA_AC || SEND_ELECTRA_AC, - D_STR_ELECTRA_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PANASONIC_AC || SEND_PANASONIC_AC, - D_STR_PANASONIC_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PIONEER || SEND_PIONEER, - D_STR_PIONEER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LG || SEND_LG, - D_STR_LG2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MWM || SEND_MWM, - D_STR_MWM, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN2 || SEND_DAIKIN2, - D_STR_DAIKIN2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_VESTEL_AC || SEND_VESTEL_AC, - D_STR_VESTEL_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TECO || SEND_TECO, - D_STR_TECO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SAMSUNG36 || SEND_SAMSUNG36, - D_STR_SAMSUNG36, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TCL112AC || SEND_TCL112AC, - D_STR_TCL112AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LEGOPF || SEND_LEGOPF, - D_STR_LEGOPF, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, - D_STR_MITSUBISHI_HEAVY_88, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, - D_STR_MITSUBISHI_HEAVY_152, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN216 || SEND_DAIKIN216, - D_STR_DAIKIN216, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SHARP_AC || SEND_SHARP_AC, - D_STR_SHARP_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GOODWEATHER || SEND_GOODWEATHER, - D_STR_GOODWEATHER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_INAX || SEND_INAX, - D_STR_INAX, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN160 || SEND_DAIKIN160, - D_STR_DAIKIN160, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_NEOCLIMA || SEND_NEOCLIMA, - D_STR_NEOCLIMA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN176 || SEND_DAIKIN176, - D_STR_DAIKIN176, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN128 || SEND_DAIKIN128, - D_STR_DAIKIN128, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AMCOR || SEND_AMCOR, - D_STR_AMCOR, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN152 || SEND_DAIKIN152, - D_STR_DAIKIN152, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI136 || SEND_MITSUBISHI136, - D_STR_MITSUBISHI136, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI112 || SEND_MITSUBISHI112, - D_STR_MITSUBISHI112, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC424 || SEND_HITACHI_AC424, - D_STR_HITACHI_AC424, D_STR_UNSUPPORTED) "\x0" - COND(SEND_SONY, - D_STR_SONY_38K, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_EPSON || SEND_EPSON, - D_STR_EPSON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SYMPHONY || SEND_SYMPHONY, - D_STR_SYMPHONY, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC3 || SEND_HITACHI_AC3, - D_STR_HITACHI_AC3, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN64 || SEND_DAIKIN64, - D_STR_DAIKIN64, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AIRWELL || SEND_AIRWELL, - D_STR_AIRWELL, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DELONGHI_AC || SEND_DELONGHI_AC, - D_STR_DELONGHI_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DOSHISHA || SEND_DOSHISHA, - D_STR_DOSHISHA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MULTIBRACKETS || SEND_MULTIBRACKETS, - D_STR_MULTIBRACKETS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC40 || SEND_CARRIER_AC40, - D_STR_CARRIER_AC40, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC64 || SEND_CARRIER_AC64, - D_STR_CARRIER_AC64, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC344 || SEND_HITACHI_AC344, - D_STR_HITACHI_AC344, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CORONA_AC || SEND_CORONA_AC, - D_STR_CORONA_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MIDEA24 || SEND_MIDEA24, - D_STR_MIDEA24, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ZEPEAL || SEND_ZEPEAL, - D_STR_ZEPEAL, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO_AC || SEND_SANYO_AC, - D_STR_SANYO_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_VOLTAS || SEND_VOLTAS, - D_STR_VOLTAS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_METZ || SEND_METZ, - D_STR_METZ, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TRANSCOLD || SEND_TRANSCOLD, - D_STR_TRANSCOLD, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TECHNIBEL_AC || SEND_TECHNIBEL_AC, - D_STR_TECHNIBEL_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MIRAGE || SEND_MIRAGE, - D_STR_MIRAGE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ELITESCREENS || SEND_ELITESCREENS, - D_STR_ELITESCREENS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PANASONIC_AC32 || SEND_PANASONIC_AC32, - D_STR_PANASONIC_AC32, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MILESTAG2 || SEND_MILESTAG2, - D_STR_MILESTAG2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ECOCLIM || SEND_ECOCLIM, - D_STR_ECOCLIM, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_XMP || SEND_XMP, - D_STR_XMP, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TRUMA || SEND_TRUMA, - D_STR_TRUMA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC176 || SEND_HAIER_AC176, - D_STR_HAIER_AC176, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TEKNOPOINT || SEND_TEKNOPOINT, - D_STR_TEKNOPOINT, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_KELON || SEND_KELON, - D_STR_KELON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TROTEC_3550 || SEND_TROTEC_3550, - D_STR_TROTEC_3550, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO_AC88 || SEND_SANYO_AC88, - D_STR_SANYO_AC88, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_BOSE || SEND_BOSE, - D_STR_BOSE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ARRIS || SEND_ARRIS, - D_STR_ARRIS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RHOSS || SEND_RHOSS, - D_STR_RHOSS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AIRTON || SEND_AIRTON, - D_STR_AIRTON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_COOLIX48 || SEND_COOLIX48, - D_STR_COOLIX48, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC264 || SEND_HITACHI_AC264, - D_STR_HITACHI_AC264, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_KELON168 || SEND_KELON168, - D_STR_KELON168, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC296 || SEND_HITACHI_AC296, - D_STR_HITACHI_AC296, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN200 || SEND_DAIKIN200, - D_STR_DAIKIN200, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC160 || SEND_HAIER_AC160, - D_STR_HAIER_AC160, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC128 || SEND_CARRIER_AC128, - D_STR_CARRIER_AC128, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TOTO || SEND_TOTO, - D_STR_TOTO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CLIMABUTLER || SEND_CLIMABUTLER, - D_STR_CLIMABUTLER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TCL96AC || SEND_TCL96AC, - D_STR_TCL96AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_BOSCH144 || SEND_BOSCH144, - D_STR_BOSCH144, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO_AC152 || SEND_SANYO_AC152, - D_STR_SANYO_AC152, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN312 || SEND_DAIKIN312, - D_STR_DAIKIN312, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GORENJE || SEND_GORENJE, - D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_WOWWEE || SEND_WOWWEE, - D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC84 || SEND_CARRIER_AC84, - D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_YORK || SEND_YORK, - D_STR_YORK, D_STR_UNSUPPORTED) "\x0" - ///< New protocol (macro) strings should be added just above this line. - "\x0" ///< This string requires double null termination. -}; -IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr); +// Copyright 2019-2021 - David Conran (@crankyoldgit) + +/// @file IRtext.cpp +/// @warning If you add or remove an entry in this file, you should run: +/// '../tools/generate_irtext_h.sh' to rebuild the `IRtext.h` file. + +#include "IRtext.h" +#ifndef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "i18n.h" + +#include "IRmacros.h" + +#ifndef PROGMEM +#define PROGMEM // Pretend we have the PROGMEM macro even if we really don't. +#endif + +#ifndef FPSTR +#define FPSTR(X) X // Also pretend we have flash-string helper class cast. +#endif + +#define IRTEXT_CONST_BLOB_NAME(NAME)\ + NAME ## Blob + +#define IRTEXT_CONST_BLOB_DECL(NAME)\ + const char IRTEXT_CONST_BLOB_NAME(NAME) [] PROGMEM + +#define IRTEXT_CONST_BLOB_PTR(NAME)\ + IRTEXT_CONST_PTR(NAME) {\ + IRTEXT_CONST_PTR_CAST(IRTEXT_CONST_BLOB_NAME(NAME)) } + +#define IRTEXT_CONST_STRING(NAME, VALUE)\ + static IRTEXT_CONST_BLOB_DECL(NAME) { VALUE };\ + IRTEXT_CONST_PTR(NAME) PROGMEM {\ + IRTEXT_CONST_PTR_CAST(&(IRTEXT_CONST_BLOB_NAME(NAME))[0]) } + +// Common +IRTEXT_CONST_STRING(kUnknownStr, D_STR_UNKNOWN); ///< "Unknown" +IRTEXT_CONST_STRING(kProtocolStr, D_STR_PROTOCOL); ///< "Protocol" +IRTEXT_CONST_STRING(kPowerStr, D_STR_POWER); ///< "Power" +IRTEXT_CONST_STRING(kOnStr, D_STR_ON); ///< "On" +IRTEXT_CONST_STRING(kOffStr, D_STR_OFF); ///< "Off" +IRTEXT_CONST_STRING(k1Str, D_STR_1); ///< "1" +IRTEXT_CONST_STRING(k0Str, D_STR_0); ///< "0" +IRTEXT_CONST_STRING(kModeStr, D_STR_MODE); ///< "Mode" +IRTEXT_CONST_STRING(kToggleStr, D_STR_TOGGLE); ///< "Toggle" +IRTEXT_CONST_STRING(kTurboStr, D_STR_TURBO); ///< "Turbo" +IRTEXT_CONST_STRING(kSuperStr, D_STR_SUPER); ///< "Super" +IRTEXT_CONST_STRING(kSleepStr, D_STR_SLEEP); ///< "Sleep" +IRTEXT_CONST_STRING(kLightStr, D_STR_LIGHT); ///< "Light" +IRTEXT_CONST_STRING(kPowerfulStr, D_STR_POWERFUL); ///< "Powerful" +IRTEXT_CONST_STRING(kQuietStr, D_STR_QUIET); ///< "Quiet" +IRTEXT_CONST_STRING(kEconoStr, D_STR_ECONO); ///< "Econo" +IRTEXT_CONST_STRING(kSwingStr, D_STR_SWING); ///< "Swing" +IRTEXT_CONST_STRING(kSwingHStr, D_STR_SWINGH); ///< "SwingH" +IRTEXT_CONST_STRING(kSwingVStr, D_STR_SWINGV); ///< "SwingV" +IRTEXT_CONST_STRING(kBeepStr, D_STR_BEEP); ///< "Beep" +IRTEXT_CONST_STRING(kZoneFollowStr, D_STR_ZONEFOLLOW); ///< "Zone Follow" +IRTEXT_CONST_STRING(kFixedStr, D_STR_FIXED); ///< "Fixed" +IRTEXT_CONST_STRING(kMouldStr, D_STR_MOULD); ///< "Mould" +IRTEXT_CONST_STRING(kCleanStr, D_STR_CLEAN); ///< "Clean" +IRTEXT_CONST_STRING(kPurifyStr, D_STR_PURIFY); ///< "Purify" +IRTEXT_CONST_STRING(kTimerStr, D_STR_TIMER); ///< "Timer" +IRTEXT_CONST_STRING(kOnTimerStr, D_STR_ONTIMER); ///< "On Timer" +IRTEXT_CONST_STRING(kOffTimerStr, D_STR_OFFTIMER); ///< "Off Timer" +IRTEXT_CONST_STRING(kTimerModeStr, D_STR_TIMERMODE); ///< "Timer Mode" +IRTEXT_CONST_STRING(kClockStr, D_STR_CLOCK); ///< "Clock" +IRTEXT_CONST_STRING(kCommandStr, D_STR_COMMAND); ///< "Command" +IRTEXT_CONST_STRING(kConfigCommandStr, D_STR_CONFIG); ///< "Config" +IRTEXT_CONST_STRING(kControlCommandStr, D_STR_CONTROL); ///< "Control" +IRTEXT_CONST_STRING(kXFanStr, D_STR_XFAN); ///< "XFan" +IRTEXT_CONST_STRING(kHealthStr, D_STR_HEALTH); ///< "Health" +IRTEXT_CONST_STRING(kModelStr, D_STR_MODEL); ///< "Model" +IRTEXT_CONST_STRING(kTempStr, D_STR_TEMP); ///< "Temp" +IRTEXT_CONST_STRING(kIFeelReportStr, D_STR_IFEELREPORT); ///< "IFeel Report" +IRTEXT_CONST_STRING(kIFeelStr, D_STR_IFEEL); ///< "IFeel" +IRTEXT_CONST_STRING(kHumidStr, D_STR_HUMID); ///< "Humid" +IRTEXT_CONST_STRING(kSaveStr, D_STR_SAVE); ///< "Save" +IRTEXT_CONST_STRING(kEyeStr, D_STR_EYE); ///< "Eye" +IRTEXT_CONST_STRING(kFollowStr, D_STR_FOLLOW); ///< "Follow" +IRTEXT_CONST_STRING(kIonStr, D_STR_ION); ///< "Ion" +IRTEXT_CONST_STRING(kFreshStr, D_STR_FRESH); ///< "Fresh" +IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold" +IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button" +IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat" +IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat" +IRTEXT_CONST_STRING(kISeeStr, D_STR_ISEE); ///< "ISee" +IRTEXT_CONST_STRING(kAbsenseDetectStr, D_STR_ABSENSEDETECT); + ///< "AbsenseDetect" +IRTEXT_CONST_STRING(kDirectIndirectModeStr, D_STR_DIRECTINDIRECTMODE); + ///< "Direct/Indirect mode" +IRTEXT_CONST_STRING(kDirectStr, D_STR_DIRECT); ///< "Direct" +IRTEXT_CONST_STRING(kIndirectStr, D_STR_INDIRECT); ///< "Indirect" + +IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night" +IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent" +IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter" +IRTEXT_CONST_STRING(k3DStr, D_STR_3D); ///< "3D" +IRTEXT_CONST_STRING(kCelsiusStr, D_STR_CELSIUS); ///< "Celsius" +IRTEXT_CONST_STRING(kCelsiusFahrenheitStr, D_STR_CELSIUS_FAHRENHEIT); ///< +///< "Celsius/Fahrenheit" +IRTEXT_CONST_STRING(kTempUpStr, D_STR_TEMPUP); ///< "Temp Up" +IRTEXT_CONST_STRING(kTempDownStr, D_STR_TEMPDOWN); ///< "Temp Down" +IRTEXT_CONST_STRING(kStartStr, D_STR_START); ///< "Start" +IRTEXT_CONST_STRING(kStopStr, D_STR_STOP); ///< "Stop" +IRTEXT_CONST_STRING(kMoveStr, D_STR_MOVE); ///< "Move" +IRTEXT_CONST_STRING(kSetStr, D_STR_SET); ///< "Set" +IRTEXT_CONST_STRING(kCancelStr, D_STR_CANCEL); ///< "Cancel" +IRTEXT_CONST_STRING(kUpStr, D_STR_UP); ///< "Up" +IRTEXT_CONST_STRING(kDownStr, D_STR_DOWN); ///< "Down" +IRTEXT_CONST_STRING(kChangeStr, D_STR_CHANGE); ///< "Change" +IRTEXT_CONST_STRING(kComfortStr, D_STR_COMFORT); ///< "Comfort" +IRTEXT_CONST_STRING(kSensorStr, D_STR_SENSOR); ///< "Sensor" +IRTEXT_CONST_STRING(kWeeklyTimerStr, D_STR_WEEKLYTIMER); ///< "WeeklyTimer" +IRTEXT_CONST_STRING(kWifiStr, D_STR_WIFI); ///< "Wifi" +IRTEXT_CONST_STRING(kLastStr, D_STR_LAST); ///< "Last" +IRTEXT_CONST_STRING(kFastStr, D_STR_FAST); ///< "Fast" +IRTEXT_CONST_STRING(kSlowStr, D_STR_SLOW); ///< "Slow" +IRTEXT_CONST_STRING(kAirFlowStr, D_STR_AIRFLOW); ///< "Air Flow" +IRTEXT_CONST_STRING(kStepStr, D_STR_STEP); ///< "Step" +IRTEXT_CONST_STRING(kNAStr, D_STR_NA); ///< "N/A" +IRTEXT_CONST_STRING(kInsideStr, D_STR_INSIDE); ///< "Inside" +IRTEXT_CONST_STRING(kOutsideStr, D_STR_OUTSIDE); ///< "Outside" +IRTEXT_CONST_STRING(kLoudStr, D_STR_LOUD); ///< "Loud" +IRTEXT_CONST_STRING(kLowerStr, D_STR_LOWER); ///< "Lower" +IRTEXT_CONST_STRING(kUpperStr, D_STR_UPPER); ///< "Upper" +IRTEXT_CONST_STRING(kUpperMiddleStr, D_STR_UPPER_MIDDLE); ///< "Upper-Middle" +IRTEXT_CONST_STRING(kBreezeStr, D_STR_BREEZE); ///< "Breeze" +IRTEXT_CONST_STRING(kCirculateStr, D_STR_CIRCULATE); ///< "Circulate" +IRTEXT_CONST_STRING(kCeilingStr, D_STR_CEILING); ///< "Ceiling" +IRTEXT_CONST_STRING(kWallStr, D_STR_WALL); ///< "Wall" +IRTEXT_CONST_STRING(kRoomStr, D_STR_ROOM); ///< "Room" +IRTEXT_CONST_STRING(k6thSenseStr, D_STR_6THSENSE); ///< "6th Sense" +IRTEXT_CONST_STRING(kTypeStr, D_STR_TYPE); ///< "Type" +IRTEXT_CONST_STRING(kSpecialStr, D_STR_SPECIAL); ///< "Special" +IRTEXT_CONST_STRING(kIdStr, D_STR_ID); ///< "Id" / Device Identifier +IRTEXT_CONST_STRING(kVaneStr, D_STR_VANE); ///< "Vane" +IRTEXT_CONST_STRING(kLockStr, D_STR_LOCK); ///< "Lock" + +IRTEXT_CONST_STRING(kAutoStr, D_STR_AUTO); ///< "Auto" +IRTEXT_CONST_STRING(kAutomaticStr, D_STR_AUTOMATIC); ///< "Automatic" +IRTEXT_CONST_STRING(kManualStr, D_STR_MANUAL); ///< "Manual" +IRTEXT_CONST_STRING(kCoolStr, D_STR_COOL); ///< "Cool" +IRTEXT_CONST_STRING(kCoolingStr, D_STR_COOLING); ///< "Cooling" +IRTEXT_CONST_STRING(kHeatStr, D_STR_HEAT); ///< "Heat" +IRTEXT_CONST_STRING(kHeatingStr, D_STR_HEATING); ///< "Heating" +IRTEXT_CONST_STRING(kDryStr, D_STR_DRY); ///< "Dry" +IRTEXT_CONST_STRING(kDryingStr, D_STR_DRYING); ///< "Drying" +IRTEXT_CONST_STRING(kDehumidifyStr, D_STR_DEHUMIDIFY); ///< "Dehumidify" +IRTEXT_CONST_STRING(kFanStr, D_STR_FAN); ///< "Fan" +// The following Fans strings with "only" are required to help with +// HomeAssistant & Google Home Climate integration. For compatibility only. +// Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes +IRTEXT_CONST_STRING(kFanOnlyStr, D_STR_FANONLY); ///< "fan-only" +IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (HA/legacy) +IRTEXT_CONST_STRING(kFanOnlyWithSpaceStr, D_STR_FANSPACEONLY); ///< "Fan Only" +IRTEXT_CONST_STRING(kFanOnlyNoSpaceStr, D_STR_FANONLYNOSPACE); ///< "FanOnly" + +IRTEXT_CONST_STRING(kRecycleStr, D_STR_RECYCLE); ///< "Recycle" + +IRTEXT_CONST_STRING(kMaxStr, D_STR_MAX); ///< "Max" +IRTEXT_CONST_STRING(kMaximumStr, D_STR_MAXIMUM); ///< "Maximum" +IRTEXT_CONST_STRING(kMinStr, D_STR_MIN); ///< "Min" +IRTEXT_CONST_STRING(kMinimumStr, D_STR_MINIMUM); ///< "Minimum" +IRTEXT_CONST_STRING(kMedHighStr, D_STR_MED_HIGH); ///< "Med-high" +IRTEXT_CONST_STRING(kMedStr, D_STR_MED); ///< "Med" +IRTEXT_CONST_STRING(kMediumStr, D_STR_MEDIUM); ///< "Medium" + +IRTEXT_CONST_STRING(kHighestStr, D_STR_HIGHEST); ///< "Highest" +IRTEXT_CONST_STRING(kHighStr, D_STR_HIGH); ///< "High" +IRTEXT_CONST_STRING(kHiStr, D_STR_HI); ///< "Hi" +IRTEXT_CONST_STRING(kMidStr, D_STR_MID); ///< "Mid" +IRTEXT_CONST_STRING(kMiddleStr, D_STR_MIDDLE); ///< "Middle" +IRTEXT_CONST_STRING(kLowStr, D_STR_LOW); ///< "Low" +IRTEXT_CONST_STRING(kLoStr, D_STR_LO); ///< "Lo" +IRTEXT_CONST_STRING(kLowestStr, D_STR_LOWEST); ///< "Lowest" +IRTEXT_CONST_STRING(kMaxRightStr, D_STR_MAXRIGHT); ///< "Max Right" +IRTEXT_CONST_STRING(kMaxRightNoSpaceStr, D_STR_MAXRIGHT_NOSPACE); ///< + ///< "MaxRight" +IRTEXT_CONST_STRING(kRightMaxStr, D_STR_RIGHTMAX); ///< "Right Max" +IRTEXT_CONST_STRING(kRightMaxNoSpaceStr, D_STR_RIGHTMAX_NOSPACE); ///< + ///< "RightMax" +IRTEXT_CONST_STRING(kRightStr, D_STR_RIGHT); ///< "Right" +IRTEXT_CONST_STRING(kLeftStr, D_STR_LEFT); ///< "Left" +IRTEXT_CONST_STRING(kMaxLeftStr, D_STR_MAXLEFT); ///< "Max Left" +IRTEXT_CONST_STRING(kMaxLeftNoSpaceStr, D_STR_MAXLEFT_NOSPACE); ///< "MaxLeft" +IRTEXT_CONST_STRING(kLeftMaxStr, D_STR_LEFTMAX); ///< "Left Max" +IRTEXT_CONST_STRING(kLeftMaxNoSpaceStr, D_STR_LEFTMAX_NOSPACE); ///< "LeftMax" +IRTEXT_CONST_STRING(kWideStr, D_STR_WIDE); ///< "Wide" +IRTEXT_CONST_STRING(kCentreStr, D_STR_CENTRE); ///< "Centre" +IRTEXT_CONST_STRING(kTopStr, D_STR_TOP); ///< "Top" +IRTEXT_CONST_STRING(kBottomStr, D_STR_BOTTOM); ///< "Bottom" + +// Compound words/phrases/descriptions from pre-defined words. +IRTEXT_CONST_STRING(kEconoToggleStr, D_STR_ECONOTOGGLE); ///< "Econo Toggle" +IRTEXT_CONST_STRING(kEyeAutoStr, D_STR_EYEAUTO); ///< "Eye Auto" +IRTEXT_CONST_STRING(kLightToggleStr, D_STR_LIGHTTOGGLE); ///< "Light Toggle" +///< "Outside Quiet" +IRTEXT_CONST_STRING(kOutsideQuietStr, D_STR_OUTSIDEQUIET); +IRTEXT_CONST_STRING(kPowerToggleStr, D_STR_POWERTOGGLE); ///< "Power Toggle" +IRTEXT_CONST_STRING(kPowerButtonStr, D_STR_POWERBUTTON); ///< "Power Button" +IRTEXT_CONST_STRING(kPreviousPowerStr, D_STR_PREVIOUSPOWER); ///< +///< "Previous Power" +IRTEXT_CONST_STRING(kDisplayTempStr, D_STR_DISPLAYTEMP); ///< "Display Temp" +IRTEXT_CONST_STRING(kSensorTempStr, D_STR_SENSORTEMP); ///< "Sensor Temp" +IRTEXT_CONST_STRING(kSleepTimerStr, D_STR_SLEEP_TIMER); ///< "Sleep Timer" +IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode" +IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///< +///< "Swing(V) Toggle" +IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle" +IRTEXT_CONST_STRING(kSetTimerCommandStr, D_STR_SET_TIMER); ///< "Set Timer" +IRTEXT_CONST_STRING(kScheduleStr, D_STR_SCHEDULE); ///< "Schedule" +IRTEXT_CONST_STRING(kChStr, D_STR_CH); ///< "CH#" +IRTEXT_CONST_STRING(kTimerActiveDaysStr, D_STR_TIMER_ACTIVE_DAYS); +///< "TimerActiveDays" +IRTEXT_CONST_STRING(kKeyStr, D_STR_KEY); ///< "Key" +IRTEXT_CONST_STRING(kValueStr, D_STR_VALUE); ///< "Value" + +// Separators & Punctuation +const char kTimeSep = D_CHR_TIME_SEP; ///< ':' +IRTEXT_CONST_STRING(kSpaceLBraceStr, D_STR_SPACELBRACE); ///< " (" +IRTEXT_CONST_STRING(kCommaSpaceStr, D_STR_COMMASPACE); ///< ", " +IRTEXT_CONST_STRING(kColonSpaceStr, D_STR_COLONSPACE); ///< ": " +IRTEXT_CONST_STRING(kDashStr, D_STR_DASH); ///< "-" + +// IRutils +// - Time +IRTEXT_CONST_STRING(kDayStr, D_STR_DAY); ///< "Day" +IRTEXT_CONST_STRING(kDaysStr, D_STR_DAYS); ///< "Days" +IRTEXT_CONST_STRING(kHourStr, D_STR_HOUR); ///< "Hour" +IRTEXT_CONST_STRING(kHoursStr, D_STR_HOURS); ///< "Hours" +IRTEXT_CONST_STRING(kMinuteStr, D_STR_MINUTE); ///< "Minute" +IRTEXT_CONST_STRING(kMinutesStr, D_STR_MINUTES); ///< "Minutes" +IRTEXT_CONST_STRING(kSecondStr, D_STR_SECOND); ///< "Second" +IRTEXT_CONST_STRING(kSecondsStr, D_STR_SECONDS); ///< "Seconds" +IRTEXT_CONST_STRING(kNowStr, D_STR_NOW); ///< "Now" +IRTEXT_CONST_STRING(kThreeLetterDayOfWeekStr, D_STR_THREELETTERDAYS); ///< +///< "SunMonTueWedThuFriSat" +IRTEXT_CONST_STRING(kYesStr, D_STR_YES); ///< "Yes" +IRTEXT_CONST_STRING(kNoStr, D_STR_NO); ///< "No" +IRTEXT_CONST_STRING(kTrueStr, D_STR_TRUE); ///< "True" +IRTEXT_CONST_STRING(kFalseStr, D_STR_FALSE); ///< "False" + +IRTEXT_CONST_STRING(kRepeatStr, D_STR_REPEAT); ///< "Repeat" +IRTEXT_CONST_STRING(kCodeStr, D_STR_CODE); ///< "Code" +IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits" + +// Model Names +IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F" +IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB" +IRTEXT_CONST_STRING(kYx1fsfStr, D_STR_YX1FSF); ///< "YX1FSF" +IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A" +IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B" +IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A" +IRTEXT_CONST_STRING(kRlt0541htabStr, D_STR_RLT0541HTA_B); ///< "R-LT0541-HTA-B" +IRTEXT_CONST_STRING(kArrah2eStr, D_STR_ARRAH2E); ///< "ARRAH2E" +IRTEXT_CONST_STRING(kArdb1Str, D_STR_ARDB1); ///< "ARDB1" +IRTEXT_CONST_STRING(kArreb1eStr, D_STR_ARREB1E); ///< "ARREB1E" +IRTEXT_CONST_STRING(kArjw2Str, D_STR_ARJW2); ///< "ARJW2" +IRTEXT_CONST_STRING(kArry4Str, D_STR_ARRY4); ///< "ARRY4" +IRTEXT_CONST_STRING(kArrew4eStr, D_STR_ARREW4E); ///< "ARREW4E" +IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///< + ///< "GE6711AR2853M" +IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403" +IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603" +IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604" +IRTEXT_CONST_STRING(kLg6711a20083vStr, D_STR_LG6711A20083V); ///< + ///< "LG6711A20083V" +IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1" +IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1" +IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE" +IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE" +IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE" +IRTEXT_CONST_STRING(kPkrStr, D_STR_PKR); ///< "PKR" +IRTEXT_CONST_STRING(kJkeStr, D_STR_JKE); ///< "JKE" +IRTEXT_CONST_STRING(kCkpStr, D_STR_CKP); ///< "CKP" +IRTEXT_CONST_STRING(kRkrStr, D_STR_RKR); ///< "RKR" +IRTEXT_CONST_STRING(kPanasonicLkeStr, D_STR_PANASONICLKE); ///< "PANASONICLKE" +IRTEXT_CONST_STRING(kPanasonicNkeStr, D_STR_PANASONICNKE); ///< "PANASONICNKE" +IRTEXT_CONST_STRING(kPanasonicDkeStr, D_STR_PANASONICDKE); ///< "PANASONICDKE" +IRTEXT_CONST_STRING(kPanasonicPkrStr, D_STR_PANASONICPKR); ///< "PANASONICPKR" +IRTEXT_CONST_STRING(kPanasonicJkeStr, D_STR_PANASONICJKE); ///< "PANASONICJKE" +IRTEXT_CONST_STRING(kPanasonicCkpStr, D_STR_PANASONICCKP); ///< "PANASONICCKP" +IRTEXT_CONST_STRING(kPanasonicRkrStr, D_STR_PANASONICRKR); ///< "PANASONICRKR" +IRTEXT_CONST_STRING(kA907Str, D_STR_A907); ///< "A907" +IRTEXT_CONST_STRING(kA705Str, D_STR_A705); ///< "A705" +IRTEXT_CONST_STRING(kA903Str, D_STR_A903); ///< "A903" +IRTEXT_CONST_STRING(kTac09chsdStr, D_STR_TAC09CHSD); ///< "TAC09CHSD" +IRTEXT_CONST_STRING(kGz055be1Str, D_STR_GZ055BE1); ///< "GZ055BE1" +IRTEXT_CONST_STRING(k122lzfStr, D_STR_122LZF); ///< "122LZF" +IRTEXT_CONST_STRING(kDg11j13aStr, D_STR_DG11J13A); ///< "DG11J13A" +IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104" +IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191" +IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3" +IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3" + +#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as + // a question mark, check for length > 1 + // to show only currently included protocols +// Protocol Names +// Needs to be in decode_type_t order. +IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { + D_STR_UNUSED "\x0" + COND(DECODE_RC5 || SEND_RC5, + D_STR_RC5, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RC6 || SEND_RC6, + D_STR_RC6, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_NEC || SEND_NEC, + D_STR_NEC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SONY || SEND_SONY, + D_STR_SONY, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PANASONIC || SEND_PANASONIC, + D_STR_PANASONIC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_JVC || SEND_JVC, + D_STR_JVC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SAMSUNG || SEND_SAMSUNG, + D_STR_SAMSUNG, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_WHYNTER || SEND_WHYNTER, + D_STR_WHYNTER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AIWA_RC_T501 || SEND_AIWA_RC_T501, + D_STR_AIWA_RC_T501, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LG || SEND_LG, + D_STR_LG, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO || SEND_SANYO, + D_STR_SANYO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI || SEND_MITSUBISHI, + D_STR_MITSUBISHI, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DISH || SEND_DISH, + D_STR_DISH, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SHARP || SEND_SHARP, + D_STR_SHARP, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_COOLIX || SEND_COOLIX, + D_STR_COOLIX, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN || SEND_DAIKIN, + D_STR_DAIKIN, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DENON || SEND_DENON, + D_STR_DENON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_KELVINATOR || SEND_KELVINATOR, + D_STR_KELVINATOR, D_STR_UNSUPPORTED) "\x0" + COND(SEND_SHERWOOD, + D_STR_SHERWOOD, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY + COND(DECODE_MITSUBISHI_AC || SEND_MITSUBISHI_AC, + D_STR_MITSUBISHI_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RCMM || SEND_RCMM, + D_STR_RCMM, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO || SEND_SANYO, + D_STR_SANYO_LC7461, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RC5 || SEND_RC5, + D_STR_RC5X, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GREE || SEND_GREE, + D_STR_GREE, D_STR_UNSUPPORTED) "\x0" + COND(SEND_PRONTO, + D_STR_PRONTO, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY + COND(DECODE_NEC || SEND_NEC, + D_STR_NEC_LIKE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ARGO || SEND_ARGO, + D_STR_ARGO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TROTEC || SEND_TROTEC, + D_STR_TROTEC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_NIKAI || SEND_NIKAI, + D_STR_NIKAI, D_STR_UNSUPPORTED) "\x0" + COND(SEND_RAW, + D_STR_RAW, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY + COND(SEND_GLOBALCACHE, + D_STR_GLOBALCACHE, D_STR_UNSUPPORTED) "\x0" // SEND + COND(DECODE_TOSHIBA_AC || SEND_TOSHIBA_AC, + D_STR_TOSHIBA_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_FUJITSU_AC || SEND_FUJITSU_AC, + D_STR_FUJITSU_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MIDEA || SEND_MIDEA, + D_STR_MIDEA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MAGIQUEST || SEND_MAGIQUEST, + D_STR_MAGIQUEST, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LASERTAG || SEND_LASERTAG, + D_STR_LASERTAG, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC || SEND_CARRIER_AC, + D_STR_CARRIER_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC || SEND_HAIER_AC, + D_STR_HAIER_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI2 || SEND_MITSUBISHI2, + D_STR_MITSUBISHI2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC || SEND_HITACHI_AC, + D_STR_HITACHI_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC1 || SEND_HITACHI_AC1, + D_STR_HITACHI_AC1, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC2 || SEND_HITACHI_AC2, + D_STR_HITACHI_AC2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GICABLE || SEND_GICABLE, + D_STR_GICABLE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC_YRW02 || SEND_HAIER_AC_YRW02, + D_STR_HAIER_AC_YRW02, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_WHIRLPOOL_AC || SEND_WHIRLPOOL_AC, + D_STR_WHIRLPOOL_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SAMSUNG_AC || SEND_SAMSUNG_AC, + D_STR_SAMSUNG_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LUTRON || SEND_LUTRON, + D_STR_LUTRON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ELECTRA_AC || SEND_ELECTRA_AC, + D_STR_ELECTRA_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PANASONIC_AC || SEND_PANASONIC_AC, + D_STR_PANASONIC_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PIONEER || SEND_PIONEER, + D_STR_PIONEER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LG || SEND_LG, + D_STR_LG2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MWM || SEND_MWM, + D_STR_MWM, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN2 || SEND_DAIKIN2, + D_STR_DAIKIN2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_VESTEL_AC || SEND_VESTEL_AC, + D_STR_VESTEL_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TECO || SEND_TECO, + D_STR_TECO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SAMSUNG36 || SEND_SAMSUNG36, + D_STR_SAMSUNG36, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TCL112AC || SEND_TCL112AC, + D_STR_TCL112AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LEGOPF || SEND_LEGOPF, + D_STR_LEGOPF, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, + D_STR_MITSUBISHI_HEAVY_88, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, + D_STR_MITSUBISHI_HEAVY_152, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN216 || SEND_DAIKIN216, + D_STR_DAIKIN216, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SHARP_AC || SEND_SHARP_AC, + D_STR_SHARP_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GOODWEATHER || SEND_GOODWEATHER, + D_STR_GOODWEATHER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_INAX || SEND_INAX, + D_STR_INAX, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN160 || SEND_DAIKIN160, + D_STR_DAIKIN160, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_NEOCLIMA || SEND_NEOCLIMA, + D_STR_NEOCLIMA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN176 || SEND_DAIKIN176, + D_STR_DAIKIN176, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN128 || SEND_DAIKIN128, + D_STR_DAIKIN128, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AMCOR || SEND_AMCOR, + D_STR_AMCOR, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN152 || SEND_DAIKIN152, + D_STR_DAIKIN152, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI136 || SEND_MITSUBISHI136, + D_STR_MITSUBISHI136, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI112 || SEND_MITSUBISHI112, + D_STR_MITSUBISHI112, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC424 || SEND_HITACHI_AC424, + D_STR_HITACHI_AC424, D_STR_UNSUPPORTED) "\x0" + COND(SEND_SONY, + D_STR_SONY_38K, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_EPSON || SEND_EPSON, + D_STR_EPSON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SYMPHONY || SEND_SYMPHONY, + D_STR_SYMPHONY, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC3 || SEND_HITACHI_AC3, + D_STR_HITACHI_AC3, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN64 || SEND_DAIKIN64, + D_STR_DAIKIN64, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AIRWELL || SEND_AIRWELL, + D_STR_AIRWELL, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DELONGHI_AC || SEND_DELONGHI_AC, + D_STR_DELONGHI_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DOSHISHA || SEND_DOSHISHA, + D_STR_DOSHISHA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MULTIBRACKETS || SEND_MULTIBRACKETS, + D_STR_MULTIBRACKETS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC40 || SEND_CARRIER_AC40, + D_STR_CARRIER_AC40, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC64 || SEND_CARRIER_AC64, + D_STR_CARRIER_AC64, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC344 || SEND_HITACHI_AC344, + D_STR_HITACHI_AC344, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CORONA_AC || SEND_CORONA_AC, + D_STR_CORONA_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MIDEA24 || SEND_MIDEA24, + D_STR_MIDEA24, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ZEPEAL || SEND_ZEPEAL, + D_STR_ZEPEAL, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO_AC || SEND_SANYO_AC, + D_STR_SANYO_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_VOLTAS || SEND_VOLTAS, + D_STR_VOLTAS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_METZ || SEND_METZ, + D_STR_METZ, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TRANSCOLD || SEND_TRANSCOLD, + D_STR_TRANSCOLD, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TECHNIBEL_AC || SEND_TECHNIBEL_AC, + D_STR_TECHNIBEL_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MIRAGE || SEND_MIRAGE, + D_STR_MIRAGE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ELITESCREENS || SEND_ELITESCREENS, + D_STR_ELITESCREENS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PANASONIC_AC32 || SEND_PANASONIC_AC32, + D_STR_PANASONIC_AC32, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MILESTAG2 || SEND_MILESTAG2, + D_STR_MILESTAG2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ECOCLIM || SEND_ECOCLIM, + D_STR_ECOCLIM, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_XMP || SEND_XMP, + D_STR_XMP, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TRUMA || SEND_TRUMA, + D_STR_TRUMA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC176 || SEND_HAIER_AC176, + D_STR_HAIER_AC176, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TEKNOPOINT || SEND_TEKNOPOINT, + D_STR_TEKNOPOINT, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_KELON || SEND_KELON, + D_STR_KELON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TROTEC_3550 || SEND_TROTEC_3550, + D_STR_TROTEC_3550, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO_AC88 || SEND_SANYO_AC88, + D_STR_SANYO_AC88, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_BOSE || SEND_BOSE, + D_STR_BOSE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ARRIS || SEND_ARRIS, + D_STR_ARRIS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RHOSS || SEND_RHOSS, + D_STR_RHOSS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AIRTON || SEND_AIRTON, + D_STR_AIRTON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_COOLIX48 || SEND_COOLIX48, + D_STR_COOLIX48, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC264 || SEND_HITACHI_AC264, + D_STR_HITACHI_AC264, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_KELON168 || SEND_KELON168, + D_STR_KELON168, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC296 || SEND_HITACHI_AC296, + D_STR_HITACHI_AC296, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN200 || SEND_DAIKIN200, + D_STR_DAIKIN200, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC160 || SEND_HAIER_AC160, + D_STR_HAIER_AC160, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC128 || SEND_CARRIER_AC128, + D_STR_CARRIER_AC128, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TOTO || SEND_TOTO, + D_STR_TOTO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CLIMABUTLER || SEND_CLIMABUTLER, + D_STR_CLIMABUTLER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TCL96AC || SEND_TCL96AC, + D_STR_TCL96AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_BOSCH144 || SEND_BOSCH144, + D_STR_BOSCH144, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO_AC152 || SEND_SANYO_AC152, + D_STR_SANYO_AC152, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN312 || SEND_DAIKIN312, + D_STR_DAIKIN312, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GORENJE || SEND_GORENJE, + D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_WOWWEE || SEND_WOWWEE, + D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC84 || SEND_CARRIER_AC84, + D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_YORK || SEND_YORK, + D_STR_YORK, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_FUJITSU_AC264 || SEND_FUJITSU_AC264, + D_STR_FUJITSU_AC264, D_STR_UNSUPPORTED) "\x0" + ///< New protocol (macro) strings should be added just above this line. + "\x0" ///< This string requires double null termination. +}; +IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr); diff --git a/src/IRutils.cpp b/src/IRutils.cpp index e9af0b28f..d69580c2e 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -1,1438 +1,1439 @@ -// Copyright 2017-2021 David Conran - -#include "IRutils.h" -#ifndef UNIT_TEST -#include -#endif - -#define __STDC_LIMIT_MACROS -#include -#include -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" - -// On the ESP8266 platform we need to use a set of ..._P functions -// to handle the strings stored in the flash address space. -#ifndef STRCASECMP -#if defined(ESP8266) -#define STRCASECMP(LHS, RHS) \ - strcasecmp_P(LHS, reinterpret_cast(RHS)) -#else // ESP8266 -#define STRCASECMP strcasecmp -#endif // ESP8266 -#endif // STRCASECMP -#ifndef STRLEN -#if defined(ESP8266) -#define STRLEN(PTR) strlen_P(PTR) -#else // ESP8266 -#define STRLEN(PTR) strlen(PTR) -#endif // ESP8266 -#endif // STRLEN -#ifndef FPSTR -#define FPSTR(X) X -#endif // FPSTR - -/// Reverse the order of the requested least significant nr. of bits. -/// @param[in] input Bit pattern/integer to reverse. -/// @param[in] nbits Nr. of bits to reverse. (LSB -> MSB) -/// @return The reversed bit pattern. -uint64_t reverseBits(uint64_t input, uint16_t nbits) { - if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. - // Cap the nr. of bits to rotate to the max nr. of bits in the input. - nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); - uint64_t output = 0; - for (uint16_t i = 0; i < nbits; i++) { - output <<= 1; - output |= (input & 1); - input >>= 1; - } - // Merge any remaining unreversed bits back to the top of the reversed bits. - return (input << nbits) | output; -} - -/// Convert a uint64_t (unsigned long long) to a string. -/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. -/// @param[in] input The value to print -/// @param[in] base The output base. -/// @returns A String representation of the integer. -/// @note Based on Arduino's Print::printNumber() -String uint64ToString(uint64_t input, uint8_t base) { - String result = ""; - // prevent issues if called with base <= 1 - if (base < 2) base = 10; - // Check we have a base that we can actually print. - // i.e. [0-9A-Z] == 36 - if (base > 36) base = 10; - - // Reserve some string space to reduce fragmentation. - // 16 bytes should store a uint64 in hex text which is the likely worst case. - // 64 bytes would be the worst case (base 2). - result.reserve(16); - - do { - char c = input % base; - input /= base; - - if (c < 10) - c += '0'; - else - c += 'A' - 10; - result = c + result; - } while (input); - return result; -} - -/// Convert a int64_t (signed long long) to a string. -/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. -/// @param[in] input The value to print -/// @param[in] base The output base. -/// @returns A String representation of the integer. -String int64ToString(int64_t input, uint8_t base) { - if (input < 0) { - // Using String(kDashStr) to keep compatible with old arduino - // frameworks. Not needed with 3.0.2. - ///> @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1639#issuecomment-944906016 - return String(kDashStr) + uint64ToString(-input, base); - } - return uint64ToString(input, base); -} - -#ifdef ARDUINO -/// Print a uint64_t/unsigned long long to the Serial port -/// Serial.print() can't handle printing long longs. (uint64_t) -/// @param[in] input The value to print -/// @param[in] base The output base. -void serialPrintUint64(uint64_t input, uint8_t base) { - Serial.print(uint64ToString(input, base)); -} -#endif - -/// Convert a C-style string to a decode_type_t. -/// @param[in] str A C-style string containing a protocol name or number. -/// @return A decode_type_t enum. (decode_type_t::UNKNOWN if no match.) -decode_type_t strToDecodeType(const char * const str) { - auto *ptr = reinterpret_cast(kAllProtocolNamesStr); - uint16_t length = STRLEN(ptr); - for (uint16_t i = 0; length; i++) { - if (!STRCASECMP(str, ptr)) return (decode_type_t)i; - ptr += length + 1; - length = STRLEN(ptr); - } - // Handle integer values of the type by converting to a string and back again. - decode_type_t result = strToDecodeType( - typeToString((decode_type_t)atoi(str)).c_str()); - if (result > 0) - return result; - - return decode_type_t::UNKNOWN; -} - -/// Convert a protocol type (enum etc) to a human readable string. -/// @param[in] protocol Nr. (enum) of the protocol. -/// @param[in] isRepeat A flag indicating if it is a repeat message. -/// @return A String containing the protocol name. kUnknownStr if no match. -String typeToString(const decode_type_t protocol, const bool isRepeat) { - String result = ""; - result.reserve(30); // Size of longest protocol name + " (Repeat)" - if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { - result = kUnknownStr; - } else { - auto *ptr = reinterpret_cast(kAllProtocolNamesStr); - for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) { - if (i == protocol) { - result = FPSTR(ptr); - break; - } - ptr += STRLEN(ptr) + 1; - } - } - if (isRepeat) { - result += kSpaceLBraceStr; - result += kRepeatStr; - result += ')'; - } - return result; -} - -/// Does the given protocol use a complex state as part of the decode? -/// @param[in] protocol The decode_type_t protocol we are enquiring about. -/// @return True if the protocol uses a state array. False if just an integer. -bool hasACState(const decode_type_t protocol) { - switch (protocol) { - // This is kept sorted by name - case AMCOR: - case ARGO: - case BOSCH144: - case CARRIER_AC84: - case CARRIER_AC128: - case CORONA_AC: - case DAIKIN: - case DAIKIN128: - case DAIKIN152: - case DAIKIN160: - case DAIKIN176: - case DAIKIN2: - case DAIKIN200: - case DAIKIN216: - case DAIKIN312: - case ELECTRA_AC: - case FUJITSU_AC: - case GREE: - case HAIER_AC: - case HAIER_AC_YRW02: - case HAIER_AC160: - case HAIER_AC176: - case HITACHI_AC: - case HITACHI_AC1: - case HITACHI_AC2: - case HITACHI_AC3: - case HITACHI_AC264: - case HITACHI_AC296: - case HITACHI_AC344: - case HITACHI_AC424: - case KELON168: - case KELVINATOR: - case MIRAGE: - case MITSUBISHI136: - case MITSUBISHI112: - case MITSUBISHI_AC: - case MITSUBISHI_HEAVY_88: - case MITSUBISHI_HEAVY_152: - case MWM: - case NEOCLIMA: - case PANASONIC_AC: - case RHOSS: - case SAMSUNG_AC: - case SANYO_AC: - case SANYO_AC88: - case SANYO_AC152: - case SHARP_AC: - case TCL96AC: - case TCL112AC: - case TEKNOPOINT: - case TOSHIBA_AC: - case TROTEC: - case TROTEC_3550: - case VOLTAS: - case WHIRLPOOL_AC: - case YORK: - return true; - default: - return false; - } -} - -/// Return the corrected length of a 'raw' format array structure -/// after over-large values are converted into multiple entries. -/// @param[in] results A ptr to a decode_results structure. -/// @return The corrected length. -uint16_t getCorrectedRawLength(const decode_results * const results) { - uint16_t extended_length = results->rawlen - 1; - for (uint16_t i = 0; i < results->rawlen - 1; i++) { - uint32_t usecs = results->rawbuf[i] * kRawTick; - // Add two extra entries for multiple larger than UINT16_MAX it is. - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - return extended_length; -} - -/// Return a String containing the key values of a decode_results structure -/// in a C/C++ code style format. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the code-ified result. -String resultToSourceCode(const decode_results * const results) { - String output = ""; - const uint16_t length = getCorrectedRawLength(results); - const bool hasState = hasACState(results->decode_type); - // Reserve some space for the string to reduce heap fragmentation. - // "uint16_t rawData[9999] = {}; // LONGEST_PROTOCOL\n" = ~55 chars. - // "NNNN, " = ~7 chars on average per raw entry - // Protocols with a `state`: - // "uint8_t state[NN] = {};\n" = ~25 chars - // "0xNN, " = 6 chars per byte. - // Protocols without a `state`: - // " DEADBEEFDEADBEEF\n" - // "uint32_t address = 0xDEADBEEF;\n" - // "uint32_t command = 0xDEADBEEF;\n" - // "uint64_t data = 0xDEADBEEFDEADBEEF;" = ~116 chars max. - output.reserve(55 + (length * 7) + hasState ? 25 + (results->bits / 8) * 6 - : 116); - // Start declaration - output += F("uint16_t "); // variable type - output += F("rawData["); // array name - output += uint64ToString(length, 10); - // array size - output += F("] = {"); // Start declaration - - // Dump data - for (uint16_t i = 1; i < results->rawlen; i++) { - uint32_t usecs; - for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; - usecs -= UINT16_MAX) { - output += uint64ToString(UINT16_MAX); - if (i % 2) - output += F(", 0, "); - else - output += F(", 0, "); - } - output += uint64ToString(usecs, 10); - if (i < results->rawlen - 1) - output += kCommaSpaceStr; // ',' not needed on the last one - if (i % 2 == 0) output += ' '; // Extra if it was even. - } - - // End declaration - output += F("};"); - - // Comment - output += F(" // "); - output += typeToString(results->decode_type, results->repeat); - // Only display the value if the decode type doesn't have an A/C state. - if (!hasState) - output += ' ' + uint64ToString(results->value, 16); - output += F("\n"); - - // Now dump "known" codes - if (results->decode_type != UNKNOWN) { - if (hasState) { -#if DECODE_AC - uint16_t nbytes = ceil(static_cast(results->bits) / 8.0); - output += F("uint8_t state["); - output += uint64ToString(nbytes); - output += F("] = {"); - for (uint16_t i = 0; i < nbytes; i++) { - output += F("0x"); - if (results->state[i] < 0x10) output += '0'; - output += uint64ToString(results->state[i], 16); - if (i < nbytes - 1) output += kCommaSpaceStr; - } - output += F("};\n"); -#endif // DECODE_AC - } else { - // Simple protocols - // Some protocols have an address &/or command. - // NOTE: It will ignore the atypical case when a message has been - // decoded but the address & the command are both 0. - if (results->address > 0 || results->command > 0) { - output += F("uint32_t address = 0x"); - output += uint64ToString(results->address, 16); - output += F(";\n"); - output += F("uint32_t command = 0x"); - output += uint64ToString(results->command, 16); - output += F(";\n"); - } - // Most protocols have data - output += F("uint64_t data = 0x"); - output += uint64ToString(results->value, 16); - output += F(";\n"); - } - } - return output; -} - -/// Dump out the decode_results structure. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the legacy information format. -/// @deprecated This is only for those that want this legacy format. -String resultToTimingInfo(const decode_results * const results) { - String output = ""; - String value = ""; - // Reserve some space for the string to reduce heap fragmentation. - // "Raw Timing[NNNN]:\n\n" = 19 chars - // " +123456, " / "-123456, " = ~12 chars on avg per raw entry. - output.reserve(19 + 12 * results->rawlen); // Should be less than this. - value.reserve(6); // Max value should be 2^17 = 131072 - output += F("Raw Timing["); - output += uint64ToString(results->rawlen - 1, 10); - output += F("]:\n"); - - for (uint16_t i = 1; i < results->rawlen; i++) { - if (i % 2 == 0) - output += kDashStr; // even - else - output += F(" +"); // odd - value = uint64ToString(results->rawbuf[i] * kRawTick); - // Space pad the value till it is at least 6 chars long. - while (value.length() < 6) value = ' ' + value; - output += value; - if (i < results->rawlen - 1) - output += kCommaSpaceStr; // ',' not needed for last one - if (!(i % 8)) output += '\n'; // Newline every 8 entries. - } - output += '\n'; - return output; -} - -/// Convert the decode_results structure's value/state to simple hexadecimal. -/// @param[in] result A ptr to a decode_results structure. -/// @return A String containing the output. -String resultToHexidecimal(const decode_results * const result) { - String output = F("0x"); - // Reserve some space for the string to reduce heap fragmentation. - output.reserve(2 * kStateSizeMax + 2); // Should cover worst cases. - if (hasACState(result->decode_type)) { -#if DECODE_AC - for (uint16_t i = 0; result->bits > i * 8; i++) { - if (result->state[i] < 0x10) output += '0'; // Zero pad - output += uint64ToString(result->state[i], 16); - } -#endif // DECODE_AC - } else { - output += uint64ToString(result->value, 16); - } - return output; -} - -/// Dump out the decode_results structure into a human readable format. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the output. -String resultToHumanReadableBasic(const decode_results * const results) { - String output = ""; - // Reserve some space for the string to reduce heap fragmentation. - // "Protocol : LONGEST_PROTOCOL_NAME (Repeat)\n" - // "Code : 0x (NNNN Bits)\n" = 70 chars - output.reserve(2 * kStateSizeMax + 70); // Should cover most cases. - // Show Encoding standard - output += kProtocolStr; - output += F(" : "); - output += typeToString(results->decode_type, results->repeat); - output += '\n'; - - // Show Code & length - output += kCodeStr; - output += F(" : "); - output += resultToHexidecimal(results); - output += kSpaceLBraceStr; - output += uint64ToString(results->bits); - output += ' '; - output += kBitsStr; - output += F(")\n"); - return output; -} - -/// Convert a decode_results into an array suitable for `sendRaw()`. -/// @param[in] decode A ptr to a decode_results structure that contains a mesg. -/// @return A PTR to a dynamically allocated uint16_t sendRaw compatible array. -/// @note The returned array needs to be delete[]'ed/free()'ed (deallocated) -/// after use by caller. -uint16_t* resultToRawArray(const decode_results * const decode) { - uint16_t *result = new uint16_t[getCorrectedRawLength(decode)]; - if (result != NULL) { // The memory was allocated successfully. - // Convert the decode data. - uint16_t pos = 0; - for (uint16_t i = 1; i < decode->rawlen; i++) { - uint32_t usecs = decode->rawbuf[i] * kRawTick; - while (usecs > UINT16_MAX) { // Keep truncating till it fits. - result[pos++] = UINT16_MAX; - result[pos++] = 0; // A 0 in a sendRaw() array basically means skip. - usecs -= UINT16_MAX; - } - result[pos++] = usecs; - } - } - return result; -} - -/// Sum all the bytes of an array and return the least significant 8-bits of -/// the result. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The 8-bit calculated result of all the bytes and init value. -uint8_t sumBytes(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t checksum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; - return checksum; -} - -/// Calculate a rolling XOR of all the bytes of an array. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The 8-bit calculated result of all the bytes and init value. -uint8_t xorBytes(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t checksum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; - return checksum; -} - -/// Count the number of bits of a certain type in an array. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The nr. of bits found of the given type found in the array. -uint16_t countBits(const uint8_t * const start, const uint16_t length, - const bool ones, const uint16_t init) { - uint16_t count = init; - for (uint16_t offset = 0; offset < length; offset++) - for (uint8_t currentbyte = *(start + offset); - currentbyte; - currentbyte >>= 1) - if (currentbyte & 1) count++; - if (ones || length == 0) - return count; - else - return (length * 8) - count; -} - -/// Count the number of bits of a certain type in an Integer. -/// @param[in] data The value you want bits counted for. Starting from the LSB. -/// @param[in] length How many bits to use in the calculation? Starts at the LSB -/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The nr. of bits found of the given type found in the Integer. -uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones, - const uint16_t init) { - uint16_t count = init; - uint8_t bitsSoFar = length; - for (uint64_t remainder = data; remainder && bitsSoFar; - remainder >>= 1, bitsSoFar--) - if (remainder & 1) count++; - if (ones || length == 0) - return count; - else - return length - count; -} - -/// Invert/Flip the bits in an Integer. -/// @param[in] data The Integer that will be inverted. -/// @param[in] nbits How many bits are to be inverted. Starting from the LSB. -/// @return An Integer with the appropriate bits inverted/flipped. -uint64_t invertBits(const uint64_t data, const uint16_t nbits) { - // No change if we are asked to invert no bits. - if (nbits == 0) return data; - uint64_t result = ~data; - // If we are asked to invert all the bits or more than we have, it's simple. - if (nbits >= sizeof(data) * 8) return result; - // Mask off any unwanted bits and return the result. - return (result & ((1ULL << nbits) - 1)); -} - -/// Convert degrees Celsius to degrees Fahrenheit. -float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } - -/// Convert degrees Fahrenheit to degrees Celsius. -float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } - -namespace irutils { - /// Create a String with a colon separated "label: value" pair suitable for - /// Humans. - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addLabeledString(const String value, const String label, - const bool precomma) { - String result = ""; - // ", " + ": " = 4 chars - result.reserve(4 + value.length() + label.length()); - if (precomma) result += kCommaSpaceStr; - result += label; - result += kColonSpaceStr; - return result + value; - } - - /// Create a String with a colon separated flag suitable for Humans. - /// e.g. "Power: On" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addBoolToString(const bool value, const String label, - const bool precomma) { - return addLabeledString(value ? kOnStr : kOffStr, label, precomma); - } - - /// Create a String with a colon separated toggle flag suitable for Humans. - /// e.g. "Light: Toggle", "Light: -" - /// @param[in] toggle The value of the toggle to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addToggleToString(const bool toggle, const String label, - const bool precomma) { - return addLabeledString(toggle ? kToggleStr : kDashStr, label, precomma); - } - - /// Create a String with a colon separated labeled Integer suitable for - /// Humans. - /// e.g. "Foo: 23" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addIntToString(const uint16_t value, const String label, - const bool precomma) { - return addLabeledString(uint64ToString(value), label, precomma); - } - - /// Create a String with a colon separated labeled Integer suitable for - /// Humans. - /// e.g. "Foo: 23" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addSignedIntToString(const int16_t value, const String label, - const bool precomma) { - return addLabeledString(int64ToString(value), label, precomma); - } - - - /// Generate the model string for a given Protocol/Model pair. - /// @param[in] protocol The IR protocol. - /// @param[in] model The model number for that protocol. - /// @return The resulting String. - /// @note After adding a new model you should update IRac::strToModel() too. - String modelToStr(const decode_type_t protocol, const int16_t model) { - switch (protocol) { - case decode_type_t::FUJITSU_AC: - switch (model) { - case fujitsu_ac_remote_model_t::ARRAH2E: return kArrah2eStr; - case fujitsu_ac_remote_model_t::ARDB1: return kArdb1Str; - case fujitsu_ac_remote_model_t::ARREB1E: return kArreb1eStr; - case fujitsu_ac_remote_model_t::ARJW2: return kArjw2Str; - case fujitsu_ac_remote_model_t::ARRY4: return kArry4Str; - case fujitsu_ac_remote_model_t::ARREW4E: return kArrew4eStr; - default: return kUnknownStr; - } - break; - case decode_type_t::GREE: - switch (model) { - case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; - case gree_ac_remote_model_t::YBOFB: return kYbofbStr; - case gree_ac_remote_model_t::YX1FSF: return kYx1fsfStr; - default: return kUnknownStr; - } - break; - case decode_type_t::HAIER_AC176: - switch (model) { - case haier_ac176_remote_model_t::V9014557_A: - return kV9014557AStr; - case haier_ac176_remote_model_t::V9014557_B: - return kV9014557BStr; - default: - return kUnknownStr; - } - break; - case decode_type_t::HITACHI_AC1: - switch (model) { - case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: - return kRlt0541htaaStr; - case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: - return kRlt0541htabStr; - default: - return kUnknownStr; - } - break; - case decode_type_t::LG: - case decode_type_t::LG2: - switch (model) { - case lg_ac_remote_model_t::GE6711AR2853M: return kGe6711ar2853mStr; - case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str; - case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str; - case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str; - case lg_ac_remote_model_t::LG6711A20083V: return kLg6711a20083vStr; - default: return kUnknownStr; - } - break; - case decode_type_t::MIRAGE: - switch (model) { - case mirage_ac_remote_model_t::KKG9AC1: return kKkg9ac1Str; - case mirage_ac_remote_model_t::KKG29AC1: return kKkg29ac1Str; - default: return kUnknownStr; - } - break; - case decode_type_t::PANASONIC_AC: - switch (model) { - case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr; - case panasonic_ac_remote_model_t::kPanasonicNke: return kNkeStr; - case panasonic_ac_remote_model_t::kPanasonicDke: return kDkeStr; - case panasonic_ac_remote_model_t::kPanasonicJke: return kJkeStr; - case panasonic_ac_remote_model_t::kPanasonicCkp: return kCkpStr; - case panasonic_ac_remote_model_t::kPanasonicRkr: return kRkrStr; - default: return kUnknownStr; - } - break; - case decode_type_t::SHARP_AC: - switch (model) { - case sharp_ac_remote_model_t::A907: return kA907Str; - case sharp_ac_remote_model_t::A705: return kA705Str; - case sharp_ac_remote_model_t::A903: return kA903Str; - default: return kUnknownStr; - } - break; - case decode_type_t::TCL112AC: - switch (model) { - case tcl_ac_remote_model_t::TAC09CHSD: return kTac09chsdStr; - case tcl_ac_remote_model_t::GZ055BE1: return kGz055be1Str; - default: return kUnknownStr; - } - break; - case decode_type_t::VOLTAS: - switch (model) { - case voltas_ac_remote_model_t::kVoltas122LZF: return k122lzfStr; - default: return kUnknownStr; - } - break; - case decode_type_t::WHIRLPOOL_AC: - switch (model) { - case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr; - case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str; - default: return kUnknownStr; - } - break; - case decode_type_t::ARGO: - switch (model) { - case argo_ac_remote_model_t::SAC_WREM2: return kArgoWrem2Str; - case argo_ac_remote_model_t::SAC_WREM3: return kArgoWrem3Str; - default: return kUnknownStr; - } - break; - default: return kUnknownStr; - } - } - - /// Create a String of human output for a given protocol model number. - /// e.g. "Model: JKE" - /// @param[in] protocol The IR protocol. - /// @param[in] model The model number for that protocol. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addModelToString(const decode_type_t protocol, const int16_t model, - const bool precomma) { - String result = ""; - // ", Model: NNN (BlahBlahEtc)" = ~40 chars for longest model name. - result.reserve(40); - result += addIntToString(model, kModelStr, precomma); - result += kSpaceLBraceStr; - result += modelToStr(protocol, model); - return result + ')'; - } - - /// Create a String of human output for a given temperature. - /// e.g. "Temp: 25C" - /// @param[in] degrees The temperature in degrees. - /// @param[in] celsius Is the temp Celsius or Fahrenheit. - /// true is C, false is F - /// @param[in] precomma Should the output string start with ", " or not? - /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? - /// @return The resulting String. - String addTempToString(const uint16_t degrees, const bool celsius, - const bool precomma, const bool isSensorTemp) { - String result = addIntToString(degrees, (isSensorTemp)? - kSensorTempStr : kTempStr, precomma); - result += celsius ? 'C' : 'F'; - return result; - } - - /// Create a String of human output for a given temperature. - /// e.g. "Temp: 25.5C" - /// @param[in] degrees The temperature in degrees. - /// @param[in] celsius Is the temp Celsius or Fahrenheit. - /// true is C, false is F - /// @param[in] precomma Should the output string start with ", " or not? - /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? - /// @return The resulting String. - String addTempFloatToString(const float degrees, const bool celsius, - const bool precomma, const bool isSensorTemp) { - String result = ""; - result.reserve(21); // Assuming ", Sensor Temp: XXX.5F" is the largest. - result += addIntToString(degrees, (isSensorTemp)? - kSensorTempStr : kTempStr, precomma); - // Is it a half degree? - if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); - result += celsius ? 'C' : 'F'; - return result; - } - - /// Create a String of human output for the given operating mode. - /// e.g. "Mode: 1 (Cool)" - /// @param[in] mode The operating mode to display. - /// @param[in] automatic The numeric value for Auto mode. - /// @param[in] cool The numeric value for Cool mode. - /// @param[in] heat The numeric value for Heat mode. - /// @param[in] dry The numeric value for Dry mode. - /// @param[in] fan The numeric value for Fan mode. - /// @return The resulting String. - String addModeToString(const uint8_t mode, const uint8_t automatic, - const uint8_t cool, const uint8_t heat, - const uint8_t dry, const uint8_t fan) { - String result = ""; - result.reserve(22); // ", Mode: NNN (UNKNOWN)" - result += addIntToString(mode, kModeStr); - result += kSpaceLBraceStr; - if (mode == automatic) result += kAutoStr; - else if (mode == cool) result += kCoolStr; - else if (mode == heat) result += kHeatStr; - else if (mode == dry) result += kDryStr; - else if (mode == fan) result += kFanStr; - else - result += kUnknownStr; - return result + ')'; - } - - /// Create a String of the 3-letter day of the week from a numerical day of - /// the week. e.g. "Day: 1 (Mon)" - /// @param[in] day_of_week A numerical version of the sequential day of the - /// week. e.g. Saturday = 7 etc. - /// @param[in] offset Days to offset by. - /// e.g. For different day starting the week. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addDayToString(const uint8_t day_of_week, const int8_t offset, - const bool precomma) { - String result = ""; - result.reserve(19); // ", Day: N (UNKNOWN)" - result += addIntToString(day_of_week, kDayStr, precomma); - result += kSpaceLBraceStr; - result += dayToString(day_of_week, offset); - return result + ')'; - } - - /// Create a String of the 3-letter day of the week from a numerical day of - /// the week. e.g. "Mon" - /// @param[in] day_of_week A numerical version of the sequential day of the - /// week. e.g. Sunday = 1, Monday = 2, ..., Saturday = 7 - /// @param[in] offset Days to offset by. - /// e.g. For different day starting the week. - /// @return The resulting String. - String dayToString(const uint8_t day_of_week, const int8_t offset) { - if ((uint8_t)(day_of_week + offset) < 7) -#if UNIT_TEST - return String(kThreeLetterDayOfWeekStr).substr( - (day_of_week + offset) * 3, 3); -#else // UNIT_TEST - return String(kThreeLetterDayOfWeekStr).substring( - (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3); -#endif // UNIT_TEST - else - return kUnknownStr; - } - - /// Create a String of human output for the given fan speed. - /// e.g. "Fan: 0 (Auto)" - /// @param[in] speed The numeric speed of the fan to display. - /// @param[in] high The numeric value for High speed. (second highest) - /// @param[in] low The numeric value for Low speed. - /// @param[in] automatic The numeric value for Auto speed. - /// @param[in] quiet The numeric value for Quiet speed. - /// @param[in] medium The numeric value for Medium speed. - /// @param[in] maximum The numeric value for Highest speed. (if > high) - /// @param[in] medium_high The numeric value for third-highest speed. - /// (if > medium) - /// @return The resulting String. - String addFanToString(const uint8_t speed, const uint8_t high, - const uint8_t low, const uint8_t automatic, - const uint8_t quiet, const uint8_t medium, - const uint8_t maximum, const uint8_t medium_high) { - String result = ""; - result.reserve(21); // ", Fan: NNN (UNKNOWN)" - result += addIntToString(speed, kFanStr); - result += kSpaceLBraceStr; - if (speed == high) result += kHighStr; - else if (speed == low) result += kLowStr; - else if (speed == automatic) result += kAutoStr; - else if (speed == quiet) result += kQuietStr; - else if (speed == medium) result += kMediumStr; - else if (speed == maximum) result += kMaximumStr; - else if (speed == medium_high) result += kMedHighStr; - else - result += kUnknownStr; - return result + ')'; - } - - /// Create a String of human output for the given horizontal swing setting. - /// e.g. "Swing(H): 0 (Auto)" - /// @param[in] position The numeric position of the swing to display. - /// @param[in] automatic The numeric value for Auto position. - /// @param[in] maxleft The numeric value for most left position. - /// @param[in] left The numeric value for Left position. - /// @param[in] middle The numeric value for Middle position. - /// @param[in] right The numeric value for Right position. - /// @param[in] maxright The numeric value for most right position. - /// @param[in] off The numeric value for Off position. - /// @param[in] leftright The numeric value for "left right" position. - /// @param[in] rightleft The numeric value for "right left" position. - /// @param[in] threed The numeric value for 3D setting. - /// @param[in] wide The numeric value for Wide position. - /// @return The resulting String. - String addSwingHToString(const uint8_t position, const uint8_t automatic, - const uint8_t maxleft, const uint8_t left, - const uint8_t middle, - const uint8_t right, const uint8_t maxright, - const uint8_t off, - const uint8_t leftright, const uint8_t rightleft, - const uint8_t threed, const uint8_t wide) { - String result = ""; - result.reserve(30); // ", Swing(H): NNN (Left Right)" - result += addIntToString(position, kSwingHStr); - result += kSpaceLBraceStr; - if (position == automatic) { - result += kAutoStr; - } else if (position == left) { - result += kLeftStr; - } else if (position == middle) { - result += kMiddleStr; - } else if (position == right) { - result += kRightStr; - } else if (position == maxleft) { - result += kMaxLeftStr; - } else if (position == maxright) { - result += kMaxRightStr; - } else if (position == off) { - result += kOffStr; - } else if (position == leftright) { - result += kLeftStr; - result += ' '; - result += kRightStr; - } else if (position == rightleft) { - result += kRightStr; - result += ' '; - result += kLeftStr; - } else if (position == threed) { - result += k3DStr; - } else if (position == wide) { - result += kWideStr; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// Create a String of human output for the given vertical swing setting. - /// e.g. "Swing(V): 0 (Auto)" - /// @param[in] position The numeric position of the swing to display. - /// @param[in] automatic The numeric value for Auto position. - /// @param[in] highest The numeric value for Highest position. - /// @param[in] high The numeric value for High position. - /// @param[in] uppermiddle The numeric value for Upper Middle position. - /// @param[in] middle The numeric value for Middle position. - /// @param[in] lowermiddle The numeric value for Lower Middle position. - /// @param[in] low The numeric value for Low position. - /// @param[in] lowest The numeric value for Low position. - /// @param[in] off The numeric value for Off position. - /// @param[in] swing The numeric value for Swing setting. - /// @param[in] breeze The numeric value for Breeze setting. - /// @param[in] circulate The numeric value for Circulate setting. - /// @return The resulting String. - String addSwingVToString(const uint8_t position, const uint8_t automatic, - const uint8_t highest, const uint8_t high, - const uint8_t uppermiddle, - const uint8_t middle, - const uint8_t lowermiddle, - const uint8_t low, const uint8_t lowest, - const uint8_t off, const uint8_t swing, - const uint8_t breeze, const uint8_t circulate) { - String result = ""; - result.reserve(31); // ", Swing(V): NNN (Upper Middle)" - result += addIntToString(position, kSwingVStr); - result += kSpaceLBraceStr; - if (position == automatic) { - result += kAutoStr; - } else if (position == highest) { - result += kHighestStr; - } else if (position == high) { - result += kHighStr; - } else if (position == middle) { - result += kMiddleStr; - } else if (position == low) { - result += kLowStr; - } else if (position == lowest) { - result += kLowestStr; - } else if (position == off) { - result += kOffStr; - } else if (position == uppermiddle) { - result += kUpperStr; - result += ' '; - result += kMiddleStr; - } else if (position == lowermiddle) { - result += kLowerStr; - result += ' '; - result += kMiddleStr; - } else if (position == swing) { - result += kSwingStr; - } else if (position == breeze) { - result += kBreezeStr; - } else if (position == circulate) { - result += kCirculateStr; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// @brief Create a String of human output for the given timer setting. - /// e.g. "Timer Mode: 2 (Schedule 1)" - /// @param[in] timerMode The numeric value of the timer mode to display. - /// @param[in] noTimer The numeric value for no timer (off) - /// @param[in] delayTimer The numeric value for delay (sleep) timer - /// @param[in] schedule1 The numeric value for schedule timer #1 - /// @param[in] schedule2 The numeric value for schedule timer #2 - /// @param[in] schedule3 The numeric value for schedule timer #3 - /// @param[in] precomma Should the output string start with ", " or not? - /// @return String representation - String addTimerModeToString(const uint8_t timerMode, const uint8_t noTimer, - const uint8_t delayTimer, const uint8_t schedule1, - const uint8_t schedule2, const uint8_t schedule3, - const bool precomma) { - String result = ""; - result.reserve(28); // ", Timer Mode: 2 (Schedule 1)" - result += addIntToString(timerMode, kTimerModeStr, precomma); - result += kSpaceLBraceStr; - if (timerMode == noTimer) { - result += kOffStr; - } else if (timerMode == delayTimer) { - result += kSleepTimerStr; - } else if (timerMode == schedule1) { - result += kScheduleStr; - result += '1'; - } else if (timerMode == schedule2) { - result += kScheduleStr; - result += '2'; - } else if (timerMode == schedule3) { - result += kScheduleStr; - result += '3'; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// @brief Create a String of human output for the given channel - /// e.g. "[CH#0]" - /// @param channel The numeric value of the channel to display. - /// @return String representation - String channelToString(const uint8_t channel) { - String result = ""; - result.reserve(6); // "[CH#4]" - result += "["; - result += kChStr; - result += uint64ToString(channel); - result += "]"; - return result; - } - - /// @brief Create a String of human output for the given command type - /// e.g. "IFeel Report" - /// @param irCommandType The numeric value of the command type to display. - /// @param acControlCmd The numeric value of the "control" (default) command - /// @param iFeelReportCmd The numeric value of the sensor temperature command - /// @param timerCmd The numeric value of the timer config IR command - /// @param configCmd The numeric value of the config param set IR command - /// @return String representation - String irCommandTypeToString(uint8_t irCommandType, uint8_t acControlCmd, - uint8_t iFeelReportCmd, uint8_t timerCmd, - uint8_t configCmd) { - String result = ""; - result.reserve(12); // "IFeel Report" - if (irCommandType == acControlCmd) { - result += kCommandStr; - } else if (irCommandType == iFeelReportCmd) { - result += kIFeelReportStr; - } else if (irCommandType == timerCmd) { - result += kTimerStr; - } else if (irCommandType == configCmd) { - result += kConfigCommandStr; - } else { - result += kUnknownStr; - } - return result; - } - - /// @brief Create a String of the 3-letter day of the week bitmap - // e.g. 0b0000101 is "Sun | Tue" - /// @param[in] daysBitmap The bitmap representing days of week to represent - /// e.g bit[0]=Sunday, bit[1]=Monday, ... - /// @param[in] offset Days to offset by. - /// e.g. For different day starting the week. - /// @return String representation. - String daysBitmaskToString(uint8_t daysBitmap, uint8_t offset) { - String result = ""; - result.reserve(27); // Sun|Mon|Tue|Wed|Thu|Fri|Sat - - for (uint8_t i = 0; i < 7; ++i) { - if (((daysBitmap >> i) & 0b1) == 0b1) { - if (result.length() > 0) { - result += "|"; - } - result += irutils::dayToString(i, offset); - } - } - return result; - } - - /// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. - /// @param[in] unescaped A String containing text to make HTML safe. - /// @return A string that is HTML safe. - String htmlEscape(const String unescaped) { - String result = ""; - uint16_t ulen = unescaped.length(); - result.reserve(ulen); // The result will be at least the size of input. - for (size_t i = 0; i < ulen; i++) { - char c = unescaped[i]; - switch (c) { - // ';!-"<>=&#{}() are all unsafe. - case '\'': result += F("'"); break; - case ';': result += F(";"); break; - case '!': result += F("!"); break; - case '-': result += F("‐"); break; - case '\"': result += F("""); break; - case '<': result += F("<"); break; - case '>': result += F(">"); break; - case '=': result += F("&#equals;"); break; - case '&': result += F("&"); break; - case '#': result += F("#"); break; - case '{': result += F("{"); break; - case '}': result += F("}"); break; - case '(': result += F("("); break; - case ')': result += F(")"); break; - default: result += c; - } - } - return result; - } - - /// Convert a nr. of milliSeconds into a Human-readable string. - /// e.g. "1 Day 6 Hours 34 Minutes 17 Seconds" - /// @param[in] msecs Nr. of milliSeconds (ms). - /// @return A human readable string. - String msToString(uint32_t const msecs) { - uint32_t totalseconds = msecs / 1000; - if (totalseconds == 0) return kNowStr; - - // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. - uint8_t days = totalseconds / (60 * 60 * 24); - uint8_t hours = (totalseconds / (60 * 60)) % 24; - uint8_t minutes = (totalseconds / 60) % 60; - uint8_t seconds = totalseconds % 60; - - String result = ""; - result.reserve(42); // "99 Days, 23 Hours, 59 Minutes, 59 Seconds" - if (days) - result += uint64ToString(days) + ' ' + String((days > 1) ? kDaysStr - : kDayStr); - if (hours) { - if (result.length()) result += ' '; - result += uint64ToString(hours) + ' ' + String((hours > 1) ? kHoursStr - : kHourStr); - } - if (minutes) { - if (result.length()) result += ' '; - result += uint64ToString(minutes) + ' ' + String( - (minutes > 1) ? kMinutesStr : kMinuteStr); - } - if (seconds) { - if (result.length()) result += ' '; - result += uint64ToString(seconds) + ' ' + String( - (seconds > 1) ? kSecondsStr : kSecondStr); - } - return result; - } - - /// Convert a nr. of minutes into a 24h clock format Human-readable string. - /// e.g. "23:59" - /// @param[in] mins Nr. of Minutes. - /// @return A human readable string. - String minsToString(const uint16_t mins) { - String result = ""; - result.reserve(5); // 23:59 is the typical worst case. - if (mins / 60 < 10) result += '0'; // Zero pad the hours - result += uint64ToString(mins / 60) + kTimeSep; - if (mins % 60 < 10) result += '0'; // Zero pad the minutes. - result += uint64ToString(mins % 60); - return result; - } - - /// Sum all the nibbles together in a series of bytes. - /// @param[in] start A ptr to the start of the byte array to calculate over. - /// @param[in] length How many bytes to use in the calculation. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @return The 8-bit calculated result of all the bytes and init value. - uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t sum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) - sum += (*ptr >> 4) + (*ptr & 0xF); - return sum; - } - - /// Sum all the nibbles together in an integer. - /// @param[in] data The integer to be summed. - /// @param[in] count The number of nibbles to sum. Starts from LSB. Max of 16. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @param[in] nibbleonly true, the result is 4 bits. false, it's 8 bits. - /// @return The 4/8-bit calculated result of all the nibbles and init value. - uint8_t sumNibbles(const uint64_t data, const uint8_t count, - const uint8_t init, const bool nibbleonly) { - uint8_t sum = init; - uint64_t copy = data; - const uint8_t nrofnibbles = (count < 16) ? count : (64 / 4); - for (uint8_t i = 0; i < nrofnibbles; i++, copy >>= 4) sum += copy & 0xF; - return nibbleonly ? sum & 0xF : sum; - } - - /// Sum all the bytes together in an integer. - /// @param[in] data The integer to be summed. - /// @param[in] count The number of bytes to sum. Starts from LSB. Max of 8. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @param[in] byteonly true, the result is 8 bits. false, it's 16 bits. - /// @return The 8/16-bit calculated result of all the bytes and init value. - uint16_t sumBytes(const uint64_t data, const uint8_t count, - const uint8_t init, const bool byteonly) { - uint16_t sum = init; - uint64_t copy = data; - const uint8_t nrofbytes = (count < 8) ? count : (64 / 8); - for (uint8_t i = 0; i < nrofbytes; i++, copy >>= 8) sum += (copy & 0xFF); - return byteonly ? sum & 0xFF : sum; - } - - /// Convert a byte of Binary Coded Decimal(BCD) into an Integer. - /// @param[in] bcd The BCD value. - /// @return A normal Integer value. - uint8_t bcdToUint8(const uint8_t bcd) { - if (bcd > 0x99) return 255; // Too big. - return (bcd >> 4) * 10 + (bcd & 0xF); - } - - /// Convert an Integer into a byte of Binary Coded Decimal(BCD). - /// @param[in] integer The number to convert. - /// @return An 8-bit BCD value. - uint8_t uint8ToBcd(const uint8_t integer) { - if (integer > 99) return 255; // Too big. - return ((integer / 10) << 4) + (integer % 10); - } - - /// Return the value of `position`th bit of an Integer. - /// @param[in] data Value to be examined. - /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. - /// @param[in] size Nr. of bits in data. - /// @return The bit's value. - bool getBit(const uint64_t data, const uint8_t position, const uint8_t size) { - if (position >= size) return false; // Outside of range. - return data & (1ULL << position); - } - - /// Return the value of `position`th bit of an Integer. - /// @param[in] data Value to be examined. - /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. - /// @return The bit's value. - bool getBit(const uint8_t data, const uint8_t position) { - if (position >= 8) return false; // Outside of range. - return data & (1 << position); - } - - /// Return the value of an Integer with the `position`th bit changed. - /// @param[in] data Value to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - /// @param[in] size Nr. of bits in data. - /// @return A suitably modified integer. - uint64_t setBit(const uint64_t data, const uint8_t position, const bool on, - const uint8_t size) { - if (position >= size) return data; // Outside of range. - uint64_t mask = 1ULL << position; - if (on) - return data | mask; - else - return data & ~mask; - } - - /// Return the value of an Integer with the `position`th bit changed. - /// @param[in] data Value to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - /// @return A suitably modified integer. - uint8_t setBit(const uint8_t data, const uint8_t position, const bool on) { - if (position >= 8) return data; // Outside of range. - uint8_t mask = 1 << position; - if (on) - return data | mask; - else - return data & ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 8-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint8_t * const data, const uint8_t position, const bool on) { - uint8_t mask = 1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 32-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint32_t * const data, const uint8_t position, const bool on) { - uint32_t mask = (uint32_t)1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 64-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint64_t * const data, const uint8_t position, const bool on) { - uint64_t mask = (uint64_t)1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter an uint8_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint8_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint8_t data) { - if (offset >= 8 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(uint8_t)(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Alter an uint32_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint32_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint32_t data) { - if (offset >= 32 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint32_t mask = UINT32_MAX >> (32 - ((nbits > 32) ? 32 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Alter an uint64_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint64_t data) { - if (offset >= 64 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint64_t mask = UINT64_MAX >> (64 - ((nbits > 64) ? 64 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Create byte pairs where the second byte of the pair is a bit - /// inverted/flipped copy of the first/previous byte of the pair. - /// @param[in,out] ptr A pointer to the start of array to modify. - /// @param[in] length The byte size of the array. - /// @note A length of `<= 1` will do nothing. - /// @return A ptr to the modified array. - uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) { - for (uint16_t i = 1; i < length; i += 2) { - // Code done this way to avoid a compiler warning bug. - uint8_t inv = ~*(ptr + i - 1); - *(ptr + i) = inv; - } - return ptr; - } - - /// Check an array to see if every second byte of a pair is a bit - /// inverted/flipped copy of the first/previous byte of the pair. - /// @param[in] ptr A pointer to the start of array to check. - /// @param[in] length The byte size of the array. - /// @note A length of `<= 1` will always return true. - /// @return true, if every second byte is inverted. Otherwise false. - bool checkInvertedBytePairs(const uint8_t * const ptr, - const uint16_t length) { - for (uint16_t i = 1; i < length; i += 2) { - // Code done this way to avoid a compiler warning bug. - uint8_t inv = ~*(ptr + i - 1); - if (*(ptr + i) != inv) return false; - } - return true; - } - - /// Perform a low level bit manipulation sanity check for the given cpu - /// architecture and the compiler operation. Calls to this should return - /// 0 if everything is as expected, anything else means the library won't work - /// as expected. - /// @return A bit mask value of potential issues. - /// 0: (e.g. 0b00000000) Everything appears okay. - /// 0th bit set: (0b1) Unexpected bit field/packing encountered. - /// Try a different compiler. - /// 1st bit set: (0b10) Unexpected Endianness. Try a different compiler flag - /// or use a CPU different architecture. - /// e.g. A result of 3 (0b11) would mean both a bit field and an Endianness - /// issue has been found. - uint8_t lowLevelSanityCheck(void) { - const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL; - volatile uint32_t EndianTest = 0x12345678; - const uint8_t kBitFieldError = 0b01; - const uint8_t kEndiannessError = 0b10; - uint8_t result = 0; - union bitpackdata { - struct { - uint64_t lowestbit:1; // 0th bit - uint64_t next7bits:7; // 1-7th bits - uint64_t _unused_1:20; // 8-27th bits - // Cross the 32 bit boundary. - uint64_t crossbits:16; // 28-43rd bits - uint64_t _usused_2:18; // 44-61st bits - uint64_t highest2bits:2; // 62-63rd bits - }; - uint64_t all; - }; - - bitpackdata data; - data.lowestbit = true; - data.next7bits = 0b0011100; // 0x1C - data._unused_1 = 0; - data.crossbits = 0x1234; - data._usused_2 = 0; - data.highest2bits = 0b10; // 2 - - if (data.all != kExpectedBitFieldResult) result |= kBitFieldError; - // Check that we are using Little Endian for integers -#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) - if (BYTE_ORDER != LITTLE_ENDIAN) result |= kEndiannessError; -#endif -#if defined(__IEEE_BIG_ENDIAN) || defined(__IEEE_BYTES_BIG_ENDIAN) - result |= kEndiannessError; -#endif - // Brute force check for little endian. - if (*((uint8_t*)(&EndianTest)) != 0x78) // NOLINT(readability/casting) - result |= kEndiannessError; - return result; - } -} // namespace irutils +// Copyright 2017-2021 David Conran + +#include "IRutils.h" +#ifndef UNIT_TEST +#include +#endif + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" + +// On the ESP8266 platform we need to use a set of ..._P functions +// to handle the strings stored in the flash address space. +#ifndef STRCASECMP +#if defined(ESP8266) +#define STRCASECMP(LHS, RHS) \ + strcasecmp_P(LHS, reinterpret_cast(RHS)) +#else // ESP8266 +#define STRCASECMP strcasecmp +#endif // ESP8266 +#endif // STRCASECMP +#ifndef STRLEN +#if defined(ESP8266) +#define STRLEN(PTR) strlen_P(PTR) +#else // ESP8266 +#define STRLEN(PTR) strlen(PTR) +#endif // ESP8266 +#endif // STRLEN +#ifndef FPSTR +#define FPSTR(X) X +#endif // FPSTR + +/// Reverse the order of the requested least significant nr. of bits. +/// @param[in] input Bit pattern/integer to reverse. +/// @param[in] nbits Nr. of bits to reverse. (LSB -> MSB) +/// @return The reversed bit pattern. +uint64_t reverseBits(uint64_t input, uint16_t nbits) { + if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. + // Cap the nr. of bits to rotate to the max nr. of bits in the input. + nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); + uint64_t output = 0; + for (uint16_t i = 0; i < nbits; i++) { + output <<= 1; + output |= (input & 1); + input >>= 1; + } + // Merge any remaining unreversed bits back to the top of the reversed bits. + return (input << nbits) | output; +} + +/// Convert a uint64_t (unsigned long long) to a string. +/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. +/// @param[in] input The value to print +/// @param[in] base The output base. +/// @returns A String representation of the integer. +/// @note Based on Arduino's Print::printNumber() +String uint64ToString(uint64_t input, uint8_t base) { + String result = ""; + // prevent issues if called with base <= 1 + if (base < 2) base = 10; + // Check we have a base that we can actually print. + // i.e. [0-9A-Z] == 36 + if (base > 36) base = 10; + + // Reserve some string space to reduce fragmentation. + // 16 bytes should store a uint64 in hex text which is the likely worst case. + // 64 bytes would be the worst case (base 2). + result.reserve(16); + + do { + char c = input % base; + input /= base; + + if (c < 10) + c += '0'; + else + c += 'A' - 10; + result = c + result; + } while (input); + return result; +} + +/// Convert a int64_t (signed long long) to a string. +/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. +/// @param[in] input The value to print +/// @param[in] base The output base. +/// @returns A String representation of the integer. +String int64ToString(int64_t input, uint8_t base) { + if (input < 0) { + // Using String(kDashStr) to keep compatible with old arduino + // frameworks. Not needed with 3.0.2. + ///> @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1639#issuecomment-944906016 + return String(kDashStr) + uint64ToString(-input, base); + } + return uint64ToString(input, base); +} + +#ifdef ARDUINO +/// Print a uint64_t/unsigned long long to the Serial port +/// Serial.print() can't handle printing long longs. (uint64_t) +/// @param[in] input The value to print +/// @param[in] base The output base. +void serialPrintUint64(uint64_t input, uint8_t base) { + Serial.print(uint64ToString(input, base)); +} +#endif + +/// Convert a C-style string to a decode_type_t. +/// @param[in] str A C-style string containing a protocol name or number. +/// @return A decode_type_t enum. (decode_type_t::UNKNOWN if no match.) +decode_type_t strToDecodeType(const char * const str) { + auto *ptr = reinterpret_cast(kAllProtocolNamesStr); + uint16_t length = STRLEN(ptr); + for (uint16_t i = 0; length; i++) { + if (!STRCASECMP(str, ptr)) return (decode_type_t)i; + ptr += length + 1; + length = STRLEN(ptr); + } + // Handle integer values of the type by converting to a string and back again. + decode_type_t result = strToDecodeType( + typeToString((decode_type_t)atoi(str)).c_str()); + if (result > 0) + return result; + + return decode_type_t::UNKNOWN; +} + +/// Convert a protocol type (enum etc) to a human readable string. +/// @param[in] protocol Nr. (enum) of the protocol. +/// @param[in] isRepeat A flag indicating if it is a repeat message. +/// @return A String containing the protocol name. kUnknownStr if no match. +String typeToString(const decode_type_t protocol, const bool isRepeat) { + String result = ""; + result.reserve(30); // Size of longest protocol name + " (Repeat)" + if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { + result = kUnknownStr; + } else { + auto *ptr = reinterpret_cast(kAllProtocolNamesStr); + for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) { + if (i == protocol) { + result = FPSTR(ptr); + break; + } + ptr += STRLEN(ptr) + 1; + } + } + if (isRepeat) { + result += kSpaceLBraceStr; + result += kRepeatStr; + result += ')'; + } + return result; +} + +/// Does the given protocol use a complex state as part of the decode? +/// @param[in] protocol The decode_type_t protocol we are enquiring about. +/// @return True if the protocol uses a state array. False if just an integer. +bool hasACState(const decode_type_t protocol) { + switch (protocol) { + // This is kept sorted by name + case AMCOR: + case ARGO: + case BOSCH144: + case CARRIER_AC84: + case CARRIER_AC128: + case CORONA_AC: + case DAIKIN: + case DAIKIN128: + case DAIKIN152: + case DAIKIN160: + case DAIKIN176: + case DAIKIN2: + case DAIKIN200: + case DAIKIN216: + case DAIKIN312: + case ELECTRA_AC: + case FUJITSU_AC: + case FUJITSU_AC264: + case GREE: + case HAIER_AC: + case HAIER_AC_YRW02: + case HAIER_AC160: + case HAIER_AC176: + case HITACHI_AC: + case HITACHI_AC1: + case HITACHI_AC2: + case HITACHI_AC3: + case HITACHI_AC264: + case HITACHI_AC296: + case HITACHI_AC344: + case HITACHI_AC424: + case KELON168: + case KELVINATOR: + case MIRAGE: + case MITSUBISHI136: + case MITSUBISHI112: + case MITSUBISHI_AC: + case MITSUBISHI_HEAVY_88: + case MITSUBISHI_HEAVY_152: + case MWM: + case NEOCLIMA: + case PANASONIC_AC: + case RHOSS: + case SAMSUNG_AC: + case SANYO_AC: + case SANYO_AC88: + case SANYO_AC152: + case SHARP_AC: + case TCL96AC: + case TCL112AC: + case TEKNOPOINT: + case TOSHIBA_AC: + case TROTEC: + case TROTEC_3550: + case VOLTAS: + case WHIRLPOOL_AC: + case YORK: + return true; + default: + return false; + } +} + +/// Return the corrected length of a 'raw' format array structure +/// after over-large values are converted into multiple entries. +/// @param[in] results A ptr to a decode_results structure. +/// @return The corrected length. +uint16_t getCorrectedRawLength(const decode_results * const results) { + uint16_t extended_length = results->rawlen - 1; + for (uint16_t i = 0; i < results->rawlen - 1; i++) { + uint32_t usecs = results->rawbuf[i] * kRawTick; + // Add two extra entries for multiple larger than UINT16_MAX it is. + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + return extended_length; +} + +/// Return a String containing the key values of a decode_results structure +/// in a C/C++ code style format. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the code-ified result. +String resultToSourceCode(const decode_results * const results) { + String output = ""; + const uint16_t length = getCorrectedRawLength(results); + const bool hasState = hasACState(results->decode_type); + // Reserve some space for the string to reduce heap fragmentation. + // "uint16_t rawData[9999] = {}; // LONGEST_PROTOCOL\n" = ~55 chars. + // "NNNN, " = ~7 chars on average per raw entry + // Protocols with a `state`: + // "uint8_t state[NN] = {};\n" = ~25 chars + // "0xNN, " = 6 chars per byte. + // Protocols without a `state`: + // " DEADBEEFDEADBEEF\n" + // "uint32_t address = 0xDEADBEEF;\n" + // "uint32_t command = 0xDEADBEEF;\n" + // "uint64_t data = 0xDEADBEEFDEADBEEF;" = ~116 chars max. + output.reserve(55 + (length * 7) + hasState ? 25 + (results->bits / 8) * 6 + : 116); + // Start declaration + output += F("uint16_t "); // variable type + output += F("rawData["); // array name + output += uint64ToString(length, 10); + // array size + output += F("] = {"); // Start declaration + + // Dump data + for (uint16_t i = 1; i < results->rawlen; i++) { + uint32_t usecs; + for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; + usecs -= UINT16_MAX) { + output += uint64ToString(UINT16_MAX); + if (i % 2) + output += F(", 0, "); + else + output += F(", 0, "); + } + output += uint64ToString(usecs, 10); + if (i < results->rawlen - 1) + output += kCommaSpaceStr; // ',' not needed on the last one + if (i % 2 == 0) output += ' '; // Extra if it was even. + } + + // End declaration + output += F("};"); + + // Comment + output += F(" // "); + output += typeToString(results->decode_type, results->repeat); + // Only display the value if the decode type doesn't have an A/C state. + if (!hasState) + output += ' ' + uint64ToString(results->value, 16); + output += F("\n"); + + // Now dump "known" codes + if (results->decode_type != UNKNOWN) { + if (hasState) { +#if DECODE_AC + uint16_t nbytes = ceil(static_cast(results->bits) / 8.0); + output += F("uint8_t state["); + output += uint64ToString(nbytes); + output += F("] = {"); + for (uint16_t i = 0; i < nbytes; i++) { + output += F("0x"); + if (results->state[i] < 0x10) output += '0'; + output += uint64ToString(results->state[i], 16); + if (i < nbytes - 1) output += kCommaSpaceStr; + } + output += F("};\n"); +#endif // DECODE_AC + } else { + // Simple protocols + // Some protocols have an address &/or command. + // NOTE: It will ignore the atypical case when a message has been + // decoded but the address & the command are both 0. + if (results->address > 0 || results->command > 0) { + output += F("uint32_t address = 0x"); + output += uint64ToString(results->address, 16); + output += F(";\n"); + output += F("uint32_t command = 0x"); + output += uint64ToString(results->command, 16); + output += F(";\n"); + } + // Most protocols have data + output += F("uint64_t data = 0x"); + output += uint64ToString(results->value, 16); + output += F(";\n"); + } + } + return output; +} + +/// Dump out the decode_results structure. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the legacy information format. +/// @deprecated This is only for those that want this legacy format. +String resultToTimingInfo(const decode_results * const results) { + String output = ""; + String value = ""; + // Reserve some space for the string to reduce heap fragmentation. + // "Raw Timing[NNNN]:\n\n" = 19 chars + // " +123456, " / "-123456, " = ~12 chars on avg per raw entry. + output.reserve(19 + 12 * results->rawlen); // Should be less than this. + value.reserve(6); // Max value should be 2^17 = 131072 + output += F("Raw Timing["); + output += uint64ToString(results->rawlen - 1, 10); + output += F("]:\n"); + + for (uint16_t i = 1; i < results->rawlen; i++) { + if (i % 2 == 0) + output += kDashStr; // even + else + output += F(" +"); // odd + value = uint64ToString(results->rawbuf[i] * kRawTick); + // Space pad the value till it is at least 6 chars long. + while (value.length() < 6) value = ' ' + value; + output += value; + if (i < results->rawlen - 1) + output += kCommaSpaceStr; // ',' not needed for last one + if (!(i % 8)) output += '\n'; // Newline every 8 entries. + } + output += '\n'; + return output; +} + +/// Convert the decode_results structure's value/state to simple hexadecimal. +/// @param[in] result A ptr to a decode_results structure. +/// @return A String containing the output. +String resultToHexidecimal(const decode_results * const result) { + String output = F("0x"); + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax + 2); // Should cover worst cases. + if (hasACState(result->decode_type)) { +#if DECODE_AC + for (uint16_t i = 0; result->bits > i * 8; i++) { + if (result->state[i] < 0x10) output += '0'; // Zero pad + output += uint64ToString(result->state[i], 16); + } +#endif // DECODE_AC + } else { + output += uint64ToString(result->value, 16); + } + return output; +} + +/// Dump out the decode_results structure into a human readable format. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the output. +String resultToHumanReadableBasic(const decode_results * const results) { + String output = ""; + // Reserve some space for the string to reduce heap fragmentation. + // "Protocol : LONGEST_PROTOCOL_NAME (Repeat)\n" + // "Code : 0x (NNNN Bits)\n" = 70 chars + output.reserve(2 * kStateSizeMax + 70); // Should cover most cases. + // Show Encoding standard + output += kProtocolStr; + output += F(" : "); + output += typeToString(results->decode_type, results->repeat); + output += '\n'; + + // Show Code & length + output += kCodeStr; + output += F(" : "); + output += resultToHexidecimal(results); + output += kSpaceLBraceStr; + output += uint64ToString(results->bits); + output += ' '; + output += kBitsStr; + output += F(")\n"); + return output; +} + +/// Convert a decode_results into an array suitable for `sendRaw()`. +/// @param[in] decode A ptr to a decode_results structure that contains a mesg. +/// @return A PTR to a dynamically allocated uint16_t sendRaw compatible array. +/// @note The returned array needs to be delete[]'ed/free()'ed (deallocated) +/// after use by caller. +uint16_t* resultToRawArray(const decode_results * const decode) { + uint16_t *result = new uint16_t[getCorrectedRawLength(decode)]; + if (result != NULL) { // The memory was allocated successfully. + // Convert the decode data. + uint16_t pos = 0; + for (uint16_t i = 1; i < decode->rawlen; i++) { + uint32_t usecs = decode->rawbuf[i] * kRawTick; + while (usecs > UINT16_MAX) { // Keep truncating till it fits. + result[pos++] = UINT16_MAX; + result[pos++] = 0; // A 0 in a sendRaw() array basically means skip. + usecs -= UINT16_MAX; + } + result[pos++] = usecs; + } + } + return result; +} + +/// Sum all the bytes of an array and return the least significant 8-bits of +/// the result. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The 8-bit calculated result of all the bytes and init value. +uint8_t sumBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t checksum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; + return checksum; +} + +/// Calculate a rolling XOR of all the bytes of an array. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The 8-bit calculated result of all the bytes and init value. +uint8_t xorBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t checksum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; + return checksum; +} + +/// Count the number of bits of a certain type in an array. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The nr. of bits found of the given type found in the array. +uint16_t countBits(const uint8_t * const start, const uint16_t length, + const bool ones, const uint16_t init) { + uint16_t count = init; + for (uint16_t offset = 0; offset < length; offset++) + for (uint8_t currentbyte = *(start + offset); + currentbyte; + currentbyte >>= 1) + if (currentbyte & 1) count++; + if (ones || length == 0) + return count; + else + return (length * 8) - count; +} + +/// Count the number of bits of a certain type in an Integer. +/// @param[in] data The value you want bits counted for. Starting from the LSB. +/// @param[in] length How many bits to use in the calculation? Starts at the LSB +/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The nr. of bits found of the given type found in the Integer. +uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones, + const uint16_t init) { + uint16_t count = init; + uint8_t bitsSoFar = length; + for (uint64_t remainder = data; remainder && bitsSoFar; + remainder >>= 1, bitsSoFar--) + if (remainder & 1) count++; + if (ones || length == 0) + return count; + else + return length - count; +} + +/// Invert/Flip the bits in an Integer. +/// @param[in] data The Integer that will be inverted. +/// @param[in] nbits How many bits are to be inverted. Starting from the LSB. +/// @return An Integer with the appropriate bits inverted/flipped. +uint64_t invertBits(const uint64_t data, const uint16_t nbits) { + // No change if we are asked to invert no bits. + if (nbits == 0) return data; + uint64_t result = ~data; + // If we are asked to invert all the bits or more than we have, it's simple. + if (nbits >= sizeof(data) * 8) return result; + // Mask off any unwanted bits and return the result. + return (result & ((1ULL << nbits) - 1)); +} + +/// Convert degrees Celsius to degrees Fahrenheit. +float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } + +/// Convert degrees Fahrenheit to degrees Celsius. +float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } + +namespace irutils { + /// Create a String with a colon separated "label: value" pair suitable for + /// Humans. + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addLabeledString(const String value, const String label, + const bool precomma) { + String result = ""; + // ", " + ": " = 4 chars + result.reserve(4 + value.length() + label.length()); + if (precomma) result += kCommaSpaceStr; + result += label; + result += kColonSpaceStr; + return result + value; + } + + /// Create a String with a colon separated flag suitable for Humans. + /// e.g. "Power: On" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addBoolToString(const bool value, const String label, + const bool precomma) { + return addLabeledString(value ? kOnStr : kOffStr, label, precomma); + } + + /// Create a String with a colon separated toggle flag suitable for Humans. + /// e.g. "Light: Toggle", "Light: -" + /// @param[in] toggle The value of the toggle to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addToggleToString(const bool toggle, const String label, + const bool precomma) { + return addLabeledString(toggle ? kToggleStr : kDashStr, label, precomma); + } + + /// Create a String with a colon separated labeled Integer suitable for + /// Humans. + /// e.g. "Foo: 23" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addIntToString(const uint16_t value, const String label, + const bool precomma) { + return addLabeledString(uint64ToString(value), label, precomma); + } + + /// Create a String with a colon separated labeled Integer suitable for + /// Humans. + /// e.g. "Foo: 23" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addSignedIntToString(const int16_t value, const String label, + const bool precomma) { + return addLabeledString(int64ToString(value), label, precomma); + } + + + /// Generate the model string for a given Protocol/Model pair. + /// @param[in] protocol The IR protocol. + /// @param[in] model The model number for that protocol. + /// @return The resulting String. + /// @note After adding a new model you should update IRac::strToModel() too. + String modelToStr(const decode_type_t protocol, const int16_t model) { + switch (protocol) { + case decode_type_t::FUJITSU_AC: + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: return kArrah2eStr; + case fujitsu_ac_remote_model_t::ARDB1: return kArdb1Str; + case fujitsu_ac_remote_model_t::ARREB1E: return kArreb1eStr; + case fujitsu_ac_remote_model_t::ARJW2: return kArjw2Str; + case fujitsu_ac_remote_model_t::ARRY4: return kArry4Str; + case fujitsu_ac_remote_model_t::ARREW4E: return kArrew4eStr; + default: return kUnknownStr; + } + break; + case decode_type_t::GREE: + switch (model) { + case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; + case gree_ac_remote_model_t::YBOFB: return kYbofbStr; + case gree_ac_remote_model_t::YX1FSF: return kYx1fsfStr; + default: return kUnknownStr; + } + break; + case decode_type_t::HAIER_AC176: + switch (model) { + case haier_ac176_remote_model_t::V9014557_A: + return kV9014557AStr; + case haier_ac176_remote_model_t::V9014557_B: + return kV9014557BStr; + default: + return kUnknownStr; + } + break; + case decode_type_t::HITACHI_AC1: + switch (model) { + case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: + return kRlt0541htaaStr; + case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: + return kRlt0541htabStr; + default: + return kUnknownStr; + } + break; + case decode_type_t::LG: + case decode_type_t::LG2: + switch (model) { + case lg_ac_remote_model_t::GE6711AR2853M: return kGe6711ar2853mStr; + case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str; + case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str; + case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str; + case lg_ac_remote_model_t::LG6711A20083V: return kLg6711a20083vStr; + default: return kUnknownStr; + } + break; + case decode_type_t::MIRAGE: + switch (model) { + case mirage_ac_remote_model_t::KKG9AC1: return kKkg9ac1Str; + case mirage_ac_remote_model_t::KKG29AC1: return kKkg29ac1Str; + default: return kUnknownStr; + } + break; + case decode_type_t::PANASONIC_AC: + switch (model) { + case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr; + case panasonic_ac_remote_model_t::kPanasonicNke: return kNkeStr; + case panasonic_ac_remote_model_t::kPanasonicDke: return kDkeStr; + case panasonic_ac_remote_model_t::kPanasonicJke: return kJkeStr; + case panasonic_ac_remote_model_t::kPanasonicCkp: return kCkpStr; + case panasonic_ac_remote_model_t::kPanasonicRkr: return kRkrStr; + default: return kUnknownStr; + } + break; + case decode_type_t::SHARP_AC: + switch (model) { + case sharp_ac_remote_model_t::A907: return kA907Str; + case sharp_ac_remote_model_t::A705: return kA705Str; + case sharp_ac_remote_model_t::A903: return kA903Str; + default: return kUnknownStr; + } + break; + case decode_type_t::TCL112AC: + switch (model) { + case tcl_ac_remote_model_t::TAC09CHSD: return kTac09chsdStr; + case tcl_ac_remote_model_t::GZ055BE1: return kGz055be1Str; + default: return kUnknownStr; + } + break; + case decode_type_t::VOLTAS: + switch (model) { + case voltas_ac_remote_model_t::kVoltas122LZF: return k122lzfStr; + default: return kUnknownStr; + } + break; + case decode_type_t::WHIRLPOOL_AC: + switch (model) { + case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr; + case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str; + default: return kUnknownStr; + } + break; + case decode_type_t::ARGO: + switch (model) { + case argo_ac_remote_model_t::SAC_WREM2: return kArgoWrem2Str; + case argo_ac_remote_model_t::SAC_WREM3: return kArgoWrem3Str; + default: return kUnknownStr; + } + break; + default: return kUnknownStr; + } + } + + /// Create a String of human output for a given protocol model number. + /// e.g. "Model: JKE" + /// @param[in] protocol The IR protocol. + /// @param[in] model The model number for that protocol. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addModelToString(const decode_type_t protocol, const int16_t model, + const bool precomma) { + String result = ""; + // ", Model: NNN (BlahBlahEtc)" = ~40 chars for longest model name. + result.reserve(40); + result += addIntToString(model, kModelStr, precomma); + result += kSpaceLBraceStr; + result += modelToStr(protocol, model); + return result + ')'; + } + + /// Create a String of human output for a given temperature. + /// e.g. "Temp: 25C" + /// @param[in] degrees The temperature in degrees. + /// @param[in] celsius Is the temp Celsius or Fahrenheit. + /// true is C, false is F + /// @param[in] precomma Should the output string start with ", " or not? + /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? + /// @return The resulting String. + String addTempToString(const uint16_t degrees, const bool celsius, + const bool precomma, const bool isSensorTemp) { + String result = addIntToString(degrees, (isSensorTemp)? + kSensorTempStr : kTempStr, precomma); + result += celsius ? 'C' : 'F'; + return result; + } + + /// Create a String of human output for a given temperature. + /// e.g. "Temp: 25.5C" + /// @param[in] degrees The temperature in degrees. + /// @param[in] celsius Is the temp Celsius or Fahrenheit. + /// true is C, false is F + /// @param[in] precomma Should the output string start with ", " or not? + /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? + /// @return The resulting String. + String addTempFloatToString(const float degrees, const bool celsius, + const bool precomma, const bool isSensorTemp) { + String result = ""; + result.reserve(21); // Assuming ", Sensor Temp: XXX.5F" is the largest. + result += addIntToString(degrees, (isSensorTemp)? + kSensorTempStr : kTempStr, precomma); + // Is it a half degree? + if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); + result += celsius ? 'C' : 'F'; + return result; + } + + /// Create a String of human output for the given operating mode. + /// e.g. "Mode: 1 (Cool)" + /// @param[in] mode The operating mode to display. + /// @param[in] automatic The numeric value for Auto mode. + /// @param[in] cool The numeric value for Cool mode. + /// @param[in] heat The numeric value for Heat mode. + /// @param[in] dry The numeric value for Dry mode. + /// @param[in] fan The numeric value for Fan mode. + /// @return The resulting String. + String addModeToString(const uint8_t mode, const uint8_t automatic, + const uint8_t cool, const uint8_t heat, + const uint8_t dry, const uint8_t fan) { + String result = ""; + result.reserve(22); // ", Mode: NNN (UNKNOWN)" + result += addIntToString(mode, kModeStr); + result += kSpaceLBraceStr; + if (mode == automatic) result += kAutoStr; + else if (mode == cool) result += kCoolStr; + else if (mode == heat) result += kHeatStr; + else if (mode == dry) result += kDryStr; + else if (mode == fan) result += kFanStr; + else + result += kUnknownStr; + return result + ')'; + } + + /// Create a String of the 3-letter day of the week from a numerical day of + /// the week. e.g. "Day: 1 (Mon)" + /// @param[in] day_of_week A numerical version of the sequential day of the + /// week. e.g. Saturday = 7 etc. + /// @param[in] offset Days to offset by. + /// e.g. For different day starting the week. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addDayToString(const uint8_t day_of_week, const int8_t offset, + const bool precomma) { + String result = ""; + result.reserve(19); // ", Day: N (UNKNOWN)" + result += addIntToString(day_of_week, kDayStr, precomma); + result += kSpaceLBraceStr; + result += dayToString(day_of_week, offset); + return result + ')'; + } + + /// Create a String of the 3-letter day of the week from a numerical day of + /// the week. e.g. "Mon" + /// @param[in] day_of_week A numerical version of the sequential day of the + /// week. e.g. Sunday = 1, Monday = 2, ..., Saturday = 7 + /// @param[in] offset Days to offset by. + /// e.g. For different day starting the week. + /// @return The resulting String. + String dayToString(const uint8_t day_of_week, const int8_t offset) { + if ((uint8_t)(day_of_week + offset) < 7) +#if UNIT_TEST + return String(kThreeLetterDayOfWeekStr).substr( + (day_of_week + offset) * 3, 3); +#else // UNIT_TEST + return String(kThreeLetterDayOfWeekStr).substring( + (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3); +#endif // UNIT_TEST + else + return kUnknownStr; + } + + /// Create a String of human output for the given fan speed. + /// e.g. "Fan: 0 (Auto)" + /// @param[in] speed The numeric speed of the fan to display. + /// @param[in] high The numeric value for High speed. (second highest) + /// @param[in] low The numeric value for Low speed. + /// @param[in] automatic The numeric value for Auto speed. + /// @param[in] quiet The numeric value for Quiet speed. + /// @param[in] medium The numeric value for Medium speed. + /// @param[in] maximum The numeric value for Highest speed. (if > high) + /// @param[in] medium_high The numeric value for third-highest speed. + /// (if > medium) + /// @return The resulting String. + String addFanToString(const uint8_t speed, const uint8_t high, + const uint8_t low, const uint8_t automatic, + const uint8_t quiet, const uint8_t medium, + const uint8_t maximum, const uint8_t medium_high) { + String result = ""; + result.reserve(21); // ", Fan: NNN (UNKNOWN)" + result += addIntToString(speed, kFanStr); + result += kSpaceLBraceStr; + if (speed == high) result += kHighStr; + else if (speed == low) result += kLowStr; + else if (speed == automatic) result += kAutoStr; + else if (speed == quiet) result += kQuietStr; + else if (speed == medium) result += kMediumStr; + else if (speed == maximum) result += kMaximumStr; + else if (speed == medium_high) result += kMedHighStr; + else + result += kUnknownStr; + return result + ')'; + } + + /// Create a String of human output for the given horizontal swing setting. + /// e.g. "Swing(H): 0 (Auto)" + /// @param[in] position The numeric position of the swing to display. + /// @param[in] automatic The numeric value for Auto position. + /// @param[in] maxleft The numeric value for most left position. + /// @param[in] left The numeric value for Left position. + /// @param[in] middle The numeric value for Middle position. + /// @param[in] right The numeric value for Right position. + /// @param[in] maxright The numeric value for most right position. + /// @param[in] off The numeric value for Off position. + /// @param[in] leftright The numeric value for "left right" position. + /// @param[in] rightleft The numeric value for "right left" position. + /// @param[in] threed The numeric value for 3D setting. + /// @param[in] wide The numeric value for Wide position. + /// @return The resulting String. + String addSwingHToString(const uint8_t position, const uint8_t automatic, + const uint8_t maxleft, const uint8_t left, + const uint8_t middle, + const uint8_t right, const uint8_t maxright, + const uint8_t off, + const uint8_t leftright, const uint8_t rightleft, + const uint8_t threed, const uint8_t wide) { + String result = ""; + result.reserve(30); // ", Swing(H): NNN (Left Right)" + result += addIntToString(position, kSwingHStr); + result += kSpaceLBraceStr; + if (position == automatic) { + result += kAutoStr; + } else if (position == left) { + result += kLeftStr; + } else if (position == middle) { + result += kMiddleStr; + } else if (position == right) { + result += kRightStr; + } else if (position == maxleft) { + result += kMaxLeftStr; + } else if (position == maxright) { + result += kMaxRightStr; + } else if (position == off) { + result += kOffStr; + } else if (position == leftright) { + result += kLeftStr; + result += ' '; + result += kRightStr; + } else if (position == rightleft) { + result += kRightStr; + result += ' '; + result += kLeftStr; + } else if (position == threed) { + result += k3DStr; + } else if (position == wide) { + result += kWideStr; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// Create a String of human output for the given vertical swing setting. + /// e.g. "Swing(V): 0 (Auto)" + /// @param[in] position The numeric position of the swing to display. + /// @param[in] automatic The numeric value for Auto position. + /// @param[in] highest The numeric value for Highest position. + /// @param[in] high The numeric value for High position. + /// @param[in] uppermiddle The numeric value for Upper Middle position. + /// @param[in] middle The numeric value for Middle position. + /// @param[in] lowermiddle The numeric value for Lower Middle position. + /// @param[in] low The numeric value for Low position. + /// @param[in] lowest The numeric value for Low position. + /// @param[in] off The numeric value for Off position. + /// @param[in] swing The numeric value for Swing setting. + /// @param[in] breeze The numeric value for Breeze setting. + /// @param[in] circulate The numeric value for Circulate setting. + /// @return The resulting String. + String addSwingVToString(const uint8_t position, const uint8_t automatic, + const uint8_t highest, const uint8_t high, + const uint8_t uppermiddle, + const uint8_t middle, + const uint8_t lowermiddle, + const uint8_t low, const uint8_t lowest, + const uint8_t off, const uint8_t swing, + const uint8_t breeze, const uint8_t circulate) { + String result = ""; + result.reserve(31); // ", Swing(V): NNN (Upper Middle)" + result += addIntToString(position, kSwingVStr); + result += kSpaceLBraceStr; + if (position == automatic) { + result += kAutoStr; + } else if (position == highest) { + result += kHighestStr; + } else if (position == high) { + result += kHighStr; + } else if (position == middle) { + result += kMiddleStr; + } else if (position == low) { + result += kLowStr; + } else if (position == lowest) { + result += kLowestStr; + } else if (position == off) { + result += kOffStr; + } else if (position == uppermiddle) { + result += kUpperStr; + result += ' '; + result += kMiddleStr; + } else if (position == lowermiddle) { + result += kLowerStr; + result += ' '; + result += kMiddleStr; + } else if (position == swing) { + result += kSwingStr; + } else if (position == breeze) { + result += kBreezeStr; + } else if (position == circulate) { + result += kCirculateStr; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// @brief Create a String of human output for the given timer setting. + /// e.g. "Timer Mode: 2 (Schedule 1)" + /// @param[in] timerMode The numeric value of the timer mode to display. + /// @param[in] noTimer The numeric value for no timer (off) + /// @param[in] delayTimer The numeric value for delay (sleep) timer + /// @param[in] schedule1 The numeric value for schedule timer #1 + /// @param[in] schedule2 The numeric value for schedule timer #2 + /// @param[in] schedule3 The numeric value for schedule timer #3 + /// @param[in] precomma Should the output string start with ", " or not? + /// @return String representation + String addTimerModeToString(const uint8_t timerMode, const uint8_t noTimer, + const uint8_t delayTimer, const uint8_t schedule1, + const uint8_t schedule2, const uint8_t schedule3, + const bool precomma) { + String result = ""; + result.reserve(28); // ", Timer Mode: 2 (Schedule 1)" + result += addIntToString(timerMode, kTimerModeStr, precomma); + result += kSpaceLBraceStr; + if (timerMode == noTimer) { + result += kOffStr; + } else if (timerMode == delayTimer) { + result += kSleepTimerStr; + } else if (timerMode == schedule1) { + result += kScheduleStr; + result += '1'; + } else if (timerMode == schedule2) { + result += kScheduleStr; + result += '2'; + } else if (timerMode == schedule3) { + result += kScheduleStr; + result += '3'; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// @brief Create a String of human output for the given channel + /// e.g. "[CH#0]" + /// @param channel The numeric value of the channel to display. + /// @return String representation + String channelToString(const uint8_t channel) { + String result = ""; + result.reserve(6); // "[CH#4]" + result += "["; + result += kChStr; + result += uint64ToString(channel); + result += "]"; + return result; + } + + /// @brief Create a String of human output for the given command type + /// e.g. "IFeel Report" + /// @param irCommandType The numeric value of the command type to display. + /// @param acControlCmd The numeric value of the "control" (default) command + /// @param iFeelReportCmd The numeric value of the sensor temperature command + /// @param timerCmd The numeric value of the timer config IR command + /// @param configCmd The numeric value of the config param set IR command + /// @return String representation + String irCommandTypeToString(uint8_t irCommandType, uint8_t acControlCmd, + uint8_t iFeelReportCmd, uint8_t timerCmd, + uint8_t configCmd) { + String result = ""; + result.reserve(12); // "IFeel Report" + if (irCommandType == acControlCmd) { + result += kCommandStr; + } else if (irCommandType == iFeelReportCmd) { + result += kIFeelReportStr; + } else if (irCommandType == timerCmd) { + result += kTimerStr; + } else if (irCommandType == configCmd) { + result += kConfigCommandStr; + } else { + result += kUnknownStr; + } + return result; + } + + /// @brief Create a String of the 3-letter day of the week bitmap + // e.g. 0b0000101 is "Sun | Tue" + /// @param[in] daysBitmap The bitmap representing days of week to represent + /// e.g bit[0]=Sunday, bit[1]=Monday, ... + /// @param[in] offset Days to offset by. + /// e.g. For different day starting the week. + /// @return String representation. + String daysBitmaskToString(uint8_t daysBitmap, uint8_t offset) { + String result = ""; + result.reserve(27); // Sun|Mon|Tue|Wed|Thu|Fri|Sat + + for (uint8_t i = 0; i < 7; ++i) { + if (((daysBitmap >> i) & 0b1) == 0b1) { + if (result.length() > 0) { + result += "|"; + } + result += irutils::dayToString(i, offset); + } + } + return result; + } + + /// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. + /// @param[in] unescaped A String containing text to make HTML safe. + /// @return A string that is HTML safe. + String htmlEscape(const String unescaped) { + String result = ""; + uint16_t ulen = unescaped.length(); + result.reserve(ulen); // The result will be at least the size of input. + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + switch (c) { + // ';!-"<>=&#{}() are all unsafe. + case '\'': result += F("'"); break; + case ';': result += F(";"); break; + case '!': result += F("!"); break; + case '-': result += F("‐"); break; + case '\"': result += F("""); break; + case '<': result += F("<"); break; + case '>': result += F(">"); break; + case '=': result += F("&#equals;"); break; + case '&': result += F("&"); break; + case '#': result += F("#"); break; + case '{': result += F("{"); break; + case '}': result += F("}"); break; + case '(': result += F("("); break; + case ')': result += F(")"); break; + default: result += c; + } + } + return result; + } + + /// Convert a nr. of milliSeconds into a Human-readable string. + /// e.g. "1 Day 6 Hours 34 Minutes 17 Seconds" + /// @param[in] msecs Nr. of milliSeconds (ms). + /// @return A human readable string. + String msToString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return kNowStr; + + // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. + uint8_t days = totalseconds / (60 * 60 * 24); + uint8_t hours = (totalseconds / (60 * 60)) % 24; + uint8_t minutes = (totalseconds / 60) % 60; + uint8_t seconds = totalseconds % 60; + + String result = ""; + result.reserve(42); // "99 Days, 23 Hours, 59 Minutes, 59 Seconds" + if (days) + result += uint64ToString(days) + ' ' + String((days > 1) ? kDaysStr + : kDayStr); + if (hours) { + if (result.length()) result += ' '; + result += uint64ToString(hours) + ' ' + String((hours > 1) ? kHoursStr + : kHourStr); + } + if (minutes) { + if (result.length()) result += ' '; + result += uint64ToString(minutes) + ' ' + String( + (minutes > 1) ? kMinutesStr : kMinuteStr); + } + if (seconds) { + if (result.length()) result += ' '; + result += uint64ToString(seconds) + ' ' + String( + (seconds > 1) ? kSecondsStr : kSecondStr); + } + return result; + } + + /// Convert a nr. of minutes into a 24h clock format Human-readable string. + /// e.g. "23:59" + /// @param[in] mins Nr. of Minutes. + /// @return A human readable string. + String minsToString(const uint16_t mins) { + String result = ""; + result.reserve(5); // 23:59 is the typical worst case. + if (mins / 60 < 10) result += '0'; // Zero pad the hours + result += uint64ToString(mins / 60) + kTimeSep; + if (mins % 60 < 10) result += '0'; // Zero pad the minutes. + result += uint64ToString(mins % 60); + return result; + } + + /// Sum all the nibbles together in a series of bytes. + /// @param[in] start A ptr to the start of the byte array to calculate over. + /// @param[in] length How many bytes to use in the calculation. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @return The 8-bit calculated result of all the bytes and init value. + uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t sum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) + sum += (*ptr >> 4) + (*ptr & 0xF); + return sum; + } + + /// Sum all the nibbles together in an integer. + /// @param[in] data The integer to be summed. + /// @param[in] count The number of nibbles to sum. Starts from LSB. Max of 16. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @param[in] nibbleonly true, the result is 4 bits. false, it's 8 bits. + /// @return The 4/8-bit calculated result of all the nibbles and init value. + uint8_t sumNibbles(const uint64_t data, const uint8_t count, + const uint8_t init, const bool nibbleonly) { + uint8_t sum = init; + uint64_t copy = data; + const uint8_t nrofnibbles = (count < 16) ? count : (64 / 4); + for (uint8_t i = 0; i < nrofnibbles; i++, copy >>= 4) sum += copy & 0xF; + return nibbleonly ? sum & 0xF : sum; + } + + /// Sum all the bytes together in an integer. + /// @param[in] data The integer to be summed. + /// @param[in] count The number of bytes to sum. Starts from LSB. Max of 8. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @param[in] byteonly true, the result is 8 bits. false, it's 16 bits. + /// @return The 8/16-bit calculated result of all the bytes and init value. + uint16_t sumBytes(const uint64_t data, const uint8_t count, + const uint8_t init, const bool byteonly) { + uint16_t sum = init; + uint64_t copy = data; + const uint8_t nrofbytes = (count < 8) ? count : (64 / 8); + for (uint8_t i = 0; i < nrofbytes; i++, copy >>= 8) sum += (copy & 0xFF); + return byteonly ? sum & 0xFF : sum; + } + + /// Convert a byte of Binary Coded Decimal(BCD) into an Integer. + /// @param[in] bcd The BCD value. + /// @return A normal Integer value. + uint8_t bcdToUint8(const uint8_t bcd) { + if (bcd > 0x99) return 255; // Too big. + return (bcd >> 4) * 10 + (bcd & 0xF); + } + + /// Convert an Integer into a byte of Binary Coded Decimal(BCD). + /// @param[in] integer The number to convert. + /// @return An 8-bit BCD value. + uint8_t uint8ToBcd(const uint8_t integer) { + if (integer > 99) return 255; // Too big. + return ((integer / 10) << 4) + (integer % 10); + } + + /// Return the value of `position`th bit of an Integer. + /// @param[in] data Value to be examined. + /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. + /// @param[in] size Nr. of bits in data. + /// @return The bit's value. + bool getBit(const uint64_t data, const uint8_t position, const uint8_t size) { + if (position >= size) return false; // Outside of range. + return data & (1ULL << position); + } + + /// Return the value of `position`th bit of an Integer. + /// @param[in] data Value to be examined. + /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. + /// @return The bit's value. + bool getBit(const uint8_t data, const uint8_t position) { + if (position >= 8) return false; // Outside of range. + return data & (1 << position); + } + + /// Return the value of an Integer with the `position`th bit changed. + /// @param[in] data Value to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + /// @param[in] size Nr. of bits in data. + /// @return A suitably modified integer. + uint64_t setBit(const uint64_t data, const uint8_t position, const bool on, + const uint8_t size) { + if (position >= size) return data; // Outside of range. + uint64_t mask = 1ULL << position; + if (on) + return data | mask; + else + return data & ~mask; + } + + /// Return the value of an Integer with the `position`th bit changed. + /// @param[in] data Value to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + /// @return A suitably modified integer. + uint8_t setBit(const uint8_t data, const uint8_t position, const bool on) { + if (position >= 8) return data; // Outside of range. + uint8_t mask = 1 << position; + if (on) + return data | mask; + else + return data & ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 8-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint8_t * const data, const uint8_t position, const bool on) { + uint8_t mask = 1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 32-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint32_t * const data, const uint8_t position, const bool on) { + uint32_t mask = (uint32_t)1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 64-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint64_t * const data, const uint8_t position, const bool on) { + uint64_t mask = (uint64_t)1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter an uint8_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint8_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint8_t data) { + if (offset >= 8 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(uint8_t)(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Alter an uint32_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint32_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint32_t data) { + if (offset >= 32 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint32_t mask = UINT32_MAX >> (32 - ((nbits > 32) ? 32 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Alter an uint64_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint64_t data) { + if (offset >= 64 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint64_t mask = UINT64_MAX >> (64 - ((nbits > 64) ? 64 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Create byte pairs where the second byte of the pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in,out] ptr A pointer to the start of array to modify. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will do nothing. + /// @return A ptr to the modified array. + uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + *(ptr + i) = inv; + } + return ptr; + } + + /// Check an array to see if every second byte of a pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in] ptr A pointer to the start of array to check. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will always return true. + /// @return true, if every second byte is inverted. Otherwise false. + bool checkInvertedBytePairs(const uint8_t * const ptr, + const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + if (*(ptr + i) != inv) return false; + } + return true; + } + + /// Perform a low level bit manipulation sanity check for the given cpu + /// architecture and the compiler operation. Calls to this should return + /// 0 if everything is as expected, anything else means the library won't work + /// as expected. + /// @return A bit mask value of potential issues. + /// 0: (e.g. 0b00000000) Everything appears okay. + /// 0th bit set: (0b1) Unexpected bit field/packing encountered. + /// Try a different compiler. + /// 1st bit set: (0b10) Unexpected Endianness. Try a different compiler flag + /// or use a CPU different architecture. + /// e.g. A result of 3 (0b11) would mean both a bit field and an Endianness + /// issue has been found. + uint8_t lowLevelSanityCheck(void) { + const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL; + volatile uint32_t EndianTest = 0x12345678; + const uint8_t kBitFieldError = 0b01; + const uint8_t kEndiannessError = 0b10; + uint8_t result = 0; + union bitpackdata { + struct { + uint64_t lowestbit:1; // 0th bit + uint64_t next7bits:7; // 1-7th bits + uint64_t _unused_1:20; // 8-27th bits + // Cross the 32 bit boundary. + uint64_t crossbits:16; // 28-43rd bits + uint64_t _usused_2:18; // 44-61st bits + uint64_t highest2bits:2; // 62-63rd bits + }; + uint64_t all; + }; + + bitpackdata data; + data.lowestbit = true; + data.next7bits = 0b0011100; // 0x1C + data._unused_1 = 0; + data.crossbits = 0x1234; + data._usused_2 = 0; + data.highest2bits = 0b10; // 2 + + if (data.all != kExpectedBitFieldResult) result |= kBitFieldError; + // Check that we are using Little Endian for integers +#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) + if (BYTE_ORDER != LITTLE_ENDIAN) result |= kEndiannessError; +#endif +#if defined(__IEEE_BIG_ENDIAN) || defined(__IEEE_BYTES_BIG_ENDIAN) + result |= kEndiannessError; +#endif + // Brute force check for little endian. + if (*((uint8_t*)(&EndianTest)) != 0x78) // NOLINT(readability/casting) + result |= kEndiannessError; + return result; + } +} // namespace irutils diff --git a/src/ir_Fujitsu.cpp b/src/ir_Fujitsu.cpp index 0c2a84331..0e4b56f76 100644 --- a/src/ir_Fujitsu.cpp +++ b/src/ir_Fujitsu.cpp @@ -1,1100 +1,2092 @@ -// Copyright 2017 Jonny Graham -// Copyright 2017-2022 David Conran -// Copyright 2021 siriuslzx - -/// @file -/// @brief Support for Fujitsu A/C protocols. -/// Fujitsu A/C support added by Jonny Graham & David Conran -/// @warning Use of incorrect model may cause the A/C unit to lock up. -/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical -/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. -/// The correct model for it is ARREB1E. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 - -#include "ir_Fujitsu.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Ref: -// These values are based on averages of measurements -const uint16_t kFujitsuAcHdrMark = 3324; -const uint16_t kFujitsuAcHdrSpace = 1574; -const uint16_t kFujitsuAcBitMark = 448; -const uint16_t kFujitsuAcOneSpace = 1182; -const uint16_t kFujitsuAcZeroSpace = 390; -const uint16_t kFujitsuAcMinGap = 8100; -const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addFanToString; -using irutils::addTempFloatToString; -using irutils::minsToString; - -#if SEND_FUJITSU_AC -/// Send a Fujitsu A/C formatted message. -/// Status: STABLE / Known Good. -/// @param[in] data The message to be sent. -/// @param[in] nbytes The number of bytes of message to be sent. -/// Typically one of: -/// kFujitsuAcStateLength, -/// kFujitsuAcStateLength - 1, -/// kFujitsuAcStateLengthShort, -/// kFujitsuAcStateLengthShort - 1 -/// @param[in] repeat The number of times the command is to be repeated. -void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC - -// Code to emulate Fujitsu A/C IR remote control unit. - -/// Class Constructor -/// @param[in] pin GPIO to be used when sending. -/// @param[in] model The enum for the model of A/C to be emulated. -/// @param[in] inverted Is the output signal to be inverted? -/// @param[in] use_modulation Is frequency modulation to be used? -IRFujitsuAC::IRFujitsuAC(const uint16_t pin, - const fujitsu_ac_remote_model_t model, - const bool inverted, const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { - setModel(model); - stateReset(); -} - -/// Set the currently emulated model of the A/C. -/// @param[in] model An enum representing the model to support/emulate. -void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { - _model = model; - switch (model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _state_length = kFujitsuAcStateLength - 1; - _state_length_short = kFujitsuAcStateLengthShort - 1; - break; - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - default: - _state_length = kFujitsuAcStateLength; - _state_length_short = kFujitsuAcStateLengthShort; - } -} - -/// Get the currently emulated/detected model of the A/C. -/// @return The enum representing the model of A/C. -fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } - -/// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset(void) { - for (size_t i = 0; i < kFujitsuAcStateLength; i++) { - _.longcode[i] = 0; - } - setTemp(24); - _.Fan = kFujitsuAcFanHigh; - _.Mode = kFujitsuAcModeCool; - _.Swing = kFujitsuAcSwingBoth; - _cmd = kFujitsuAcCmdTurnOn; - _.Filter = false; - _.Clean = false; - _.TimerType = kFujitsuAcStopTimers; - _.OnTimer = 0; - _.OffTimer = 0; - _.longcode[0] = 0x14; - _.longcode[1] = 0x63; - _.longcode[3] = 0x10; - _.longcode[4] = 0x10; - _rawstatemodified = true; -} - -/// Set up hardware to be able to send a message. -void IRFujitsuAC::begin(void) { _irsend.begin(); } - -#if SEND_FUJITSU_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRFujitsuAC::send(const uint16_t repeat) { - _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); -} -#endif // SEND_FUJITSU_AC - -/// Update the length (size) of the state code for the current configuration. -/// @return true, if use long codes; false, use short codes. -bool IRFujitsuAC::updateUseLongOrShort(void) { - bool fullCmd = false; - switch (_cmd) { - case kFujitsuAcCmdTurnOff: // 0x02 - case kFujitsuAcCmdEcono: // 0x09 - case kFujitsuAcCmdPowerful: // 0x39 - case kFujitsuAcCmdStepVert: // 0x6C - case kFujitsuAcCmdToggleSwingVert: // 0x6D - case kFujitsuAcCmdStepHoriz: // 0x79 - case kFujitsuAcCmdToggleSwingHoriz: // 0x7A - _.Cmd = _cmd; - _rawstatemodified = true; - break; - default: - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - _.Cmd = 0xFE; - _rawstatemodified = true; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Cmd = 0xFC; - _rawstatemodified = true; - break; - } - fullCmd = true; - break; - } - return fullCmd; -} - -/// Calculate and set the checksum values for the internal state. -void IRFujitsuAC::checkSum(void) { - _rawstatemodified = true; - if (updateUseLongOrShort()) { // Is it going to be a long code? - // Nr. of bytes in the message after this byte. - _.RestLength = _state_length - 7; - _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; - _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); - - // These values depend on model - if (_model != fujitsu_ac_remote_model_t::ARREB1E && - _model != fujitsu_ac_remote_model_t::ARREW4E) { - _.OutsideQuiet = 0; - if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { - _.TimerType = kFujitsuAcStopTimers; - } - } - if (_model != fujitsu_ac_remote_model_t::ARRY4) { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - break; - default: - _.Clean = false; - } - _.Filter = false; - } - // Set the On/Off/Sleep timer Nr of mins. - _.OffTimer = getOffSleepTimer(); - _.OnTimer = getOnTimer(); - // Enable bit for the Off/Sleep timer - _.OffTimerEnable = _.OffTimer > 0; - // Enable bit for the On timer - _.OnTimerEnable = _.OnTimer > 0; - - uint8_t checksum = 0; - uint8_t checksum_complement = 0; - switch (_model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Swing = kFujitsuAcSwingOff; - checksum = sumBytes(_.longcode, _state_length - 1); - checksum_complement = 0x9B; - break; - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - _.unknown = 1; - // FALL THRU - default: - checksum = sumBytes(_.longcode + _state_length_short, - _state_length - _state_length_short - 1); - } - // and negate the checksum and store it in the last byte. - _.longcode[_state_length - 1] = checksum_complement - checksum; - } else { // short codes - for (size_t i = 0; i < _state_length_short; i++) { - _.shortcode[i] = _.longcode[i]; - } - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - // The last byte is the inverse of penultimate byte - _.shortcode[_state_length_short - 1] = - ~_.shortcode[_state_length_short - 2]; - break; - default: - {}; // We don't need to do anything for the others. - } - } -} - -/// Get the length (size) of the state code for the current configuration. -/// @return The length of the state array required for this config. -uint8_t IRFujitsuAC::getStateLength(void) { - return updateUseLongOrShort() ? _state_length : _state_length_short; -} - -/// Is the current binary state representation a long or a short code? -/// @return true, if long; false, if short. -bool IRFujitsuAC::isLongCode(void) const { - return _.Cmd == 0xFE || _.Cmd == 0xFC; -} - -/// 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* IRFujitsuAC::getRaw(void) { - checkSum(); - return isLongCode() ? _.longcode : _.shortcode; -} - -/// Build the internal state/config from the current (raw) A/C message. -/// @param[in] length Size of the current/used (raw) A/C message array. -void IRFujitsuAC::buildFromState(const uint16_t length) { - switch (length) { - case kFujitsuAcStateLength - 1: - case kFujitsuAcStateLengthShort - 1: - setModel(fujitsu_ac_remote_model_t::ARDB1); - // ARJW2 has horizontal swing. - if (_.Swing > kFujitsuAcSwingVert) - setModel(fujitsu_ac_remote_model_t::ARJW2); - break; - default: - switch (_.Cmd) { - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setModel(fujitsu_ac_remote_model_t::ARREB1E); - break; - default: - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - } - } - switch (_.RestLength) { - case 8: - if (_model != fujitsu_ac_remote_model_t::ARJW2) - setModel(fujitsu_ac_remote_model_t::ARDB1); - break; - case 9: - if (_model != fujitsu_ac_remote_model_t::ARREB1E) - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - break; - } - if (_.Power) - setCmd(kFujitsuAcCmdTurnOn); - else - setCmd(kFujitsuAcCmdStayOn); - // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if - // either the raw Filter or Clean setting is on. - if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean) && - !get10CHeat()) - setModel(fujitsu_ac_remote_model_t::ARRY4); - if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) - setModel(fujitsu_ac_remote_model_t::ARREB1E); - switch (_.Cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setCmd(_.Cmd); - break; - } - if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -/// @param[in] length Size of the newState array. -/// @return true, if successful; Otherwise false. (i.e. size check) -bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAcStateLength) return false; - for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { - if (i < length) - _.longcode[i] = newState[i]; - else - _.longcode[i] = 0; - } - buildFromState(length); - _rawstatemodified = false; - return true; -} - -/// Request the A/C to step the Horizontal Swing. -void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } - -/// Request the A/C to toggle the Horizontal Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingHoriz(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingHoriz); -} - -/// Request the A/C to step the Vertical Swing. -void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } - -/// Request the A/C to toggle the Vertical Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingVert(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingVert); -} - -/// Set the requested (special) command part for the A/C message. -/// @param[in] cmd The special command code. -void IRFujitsuAC::setCmd(const uint8_t cmd) { - switch (cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdTurnOn: - case kFujitsuAcCmdStayOn: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - _cmd = cmd; - break; - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - switch (_model) { - // Only these remotes have horizontal. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - switch (_model) { - // Only these remotes have these commands. - case ARREB1E: - case ARREW4E: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } -} - -/// Set the requested (special) command part for the A/C message. -/// @return The special command code. -uint8_t IRFujitsuAC::getCmd(void) const { - return _cmd; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setPower(const bool on) { - setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); -} - -/// Set the requested power state of the A/C to off. -void IRFujitsuAC::off(void) { setPower(false); } - -/// Set the requested power state of the A/C to on. -void IRFujitsuAC::on(void) { setPower(true); } - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } - -/// Set the Outside Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setOutsideQuiet(const bool on) { - _.OutsideQuiet = on; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Outside Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getOutsideQuiet(void) const { - switch (_model) { - // Only ARREB1E & ARREW4E seems to have this mode. - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - return _.OutsideQuiet; - default: return false; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees. -/// @param[in] useCelsius Use Celsius or Fahrenheit? -void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { - float mintemp; - float maxtemp; - uint8_t offset; - bool _useCelsius; - float _temp; - - switch (_model) { - // These models have native Fahrenheit & Celsius upport. - case fujitsu_ac_remote_model_t::ARREW4E: - _useCelsius = useCelsius; - _temp = temp; - break; - // Make sure everything else uses Celsius. - default: - _useCelsius = true; - _temp = useCelsius ? temp : fahrenheitToCelsius(temp); - } - setCelsius(_useCelsius); - if (_useCelsius) { - mintemp = kFujitsuAcMinTemp; - maxtemp = kFujitsuAcMaxTemp; - offset = kFujitsuAcTempOffsetC; - } else { - mintemp = kFujitsuAcMinTempF; - maxtemp = kFujitsuAcMaxTempF; - offset = kFujitsuAcTempOffsetF; - } - _temp = std::max(mintemp, _temp); - _temp = std::min(maxtemp, _temp); - if (_useCelsius) { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) - _.Temp = (_temp - (offset / 2)) * 2; - else - _.Temp = (_temp - offset) * 4; - } else { - _.Temp = _temp - offset; - } - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees of the currently set units. -float IRFujitsuAC::getTemp(void) const { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) { - if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. - return _.Temp + kFujitsuAcTempOffsetF; - else - return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); - } else { - return _.Temp / 4 + kFujitsuAcMinTemp; - } -} - -/// Set the speed of the fan. -/// @param[in] fanSpeed The desired setting. -void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { - if (fanSpeed > kFujitsuAcFanQuiet) - _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - else - _.Fan = fanSpeed; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRFujitsuAC::setMode(const uint8_t mode) { - if (mode > kFujitsuAcModeHeat) - _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - else - _.Mode = mode; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } - -/// Set the requested swing operation mode of the A/C unit. -/// @param[in] swingMode The swingMode code for the A/C. -/// Vertical, Horizon, or Both. See constants for details. -/// @note Not all models support all possible swing modes. -void IRFujitsuAC::setSwing(const uint8_t swingMode) { - _.Swing = swingMode; - _rawstatemodified = true; - switch (_model) { - // No Horizontal support. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; - break; - // Has Horizontal support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - default: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; - } - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the requested swing operation mode of the A/C unit. -/// @return The contents of the swing state/mode. -uint8_t IRFujitsuAC::getSwing(void) const { return _.Swing; } - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setClean(const bool on) { - _.Clean = on; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getClean(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; - default: return false; - } -} - -/// Set the Filter mode status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setFilter(const bool on) { - _.Filter = on; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Filter mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getFilter(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; - default: return false; - } -} - -/// Set the 10C heat status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::set10CHeat(const bool on) { - switch (_model) { - // Only selected models support this. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - setClean(on); // 10C Heat uses the same bit as Clean - if (on) { - _.Mode = kFujitsuAcModeFan; - _.Power = true; - _.Fan = kFujitsuAcFanAuto; - _.Swing = kFujitsuAcSwingOff; - _rawstatemodified = true; - } - default: - break; - } -} - -/// Get the 10C heat status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::get10CHeat(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && - _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); - default: return false; - } -} - -/// Get the Timer type of the A/C message. -/// @return The current timer type in numeric form. -uint8_t IRFujitsuAC::getTimerType(void) const { - switch (_model) { - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: return _.TimerType; - default: return kFujitsuAcStopTimers; - } -} - -/// Set the Timer type of the A/C message. -/// @param[in] timertype The kind of timer to use for the message. -void IRFujitsuAC::setTimerType(const uint8_t timertype) { - switch (timertype) { - case kFujitsuAcSleepTimer: - case kFujitsuAcOnTimer: - case kFujitsuAcOffTimer: - case kFujitsuAcStopTimers: - _.TimerType = timertype; - break; - default: _.TimerType = kFujitsuAcStopTimers; - } - _rawstatemodified = true; -} - -/// Get the On Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOnTimer(void) const { - if (getTimerType() == kFujitsuAcOnTimer) - return _.OnTimer; - return 0; -} - -/// Set the On Timer setting of the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { - _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. - _rawstatemodified = true; - if (_.OnTimer) { - _.TimerType = kFujitsuAcOnTimer; - } else if (getTimerType() == kFujitsuAcOnTimer) { - _.TimerType = kFujitsuAcStopTimers; - } -} - -/// Get the Off/Sleep Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOffSleepTimer(void) const { - switch (getTimerType()) { - case kFujitsuAcOffTimer: - case kFujitsuAcSleepTimer: return _.OffTimer; - default: return 0; - } -} - -/// Set the Off/Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { - _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. - _rawstatemodified = true; -} - -/// Set the Off Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. - if (nr_mins) - _.TimerType = kFujitsuAcOffTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// Set the Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. - if (nr_mins) - _.TimerType = kFujitsuAcSleepTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = state[length - 1]; - switch (length) { - case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 - return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 - sum = sumBytes(state, length - 1); - sum_complement = 0x9B; - break; - case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E - sum = sumBytes(state + kFujitsuAcStateLengthShort, - length - 1 - kFujitsuAcStateLengthShort); - break; - default: // Includes ARDB1 & ARJW2 short. - return true; // Assume the checksum is valid for other lengths. - } - return checksum == (uint8_t)(sum_complement - sum); // Does it match? -} - -/// Set the device's remote ID number. -/// @param[in] num The ID for the remote. Valid number range is 0 to 3. -void IRFujitsuAC::setId(const uint8_t num) { - _.Id = num; - _rawstatemodified = true; -} - -/// Get the current device's remote ID number. -/// @return The current device's remote ID number. -uint8_t IRFujitsuAC::getId(void) const { return _.Id; } - -/// Set the Temperature units for the A/C. -/// @param[in] on true, use Celsius. false, use Fahrenheit. -void IRFujitsuAC::setCelsius(const bool on) { - _.Fahrenheit = !on; - _rawstatemodified = true; -} - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } - -/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; - case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; - case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; - case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; - default: return kFujitsuAcModeAuto; - } -} - -/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; - case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; - case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; - default: return kFujitsuAcFanAuto; - } -} - -/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; - case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; - case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; - case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; - case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; - case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; - case kFujitsuAcFanQuiet: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to a previous state. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRFujitsuAC::toCommon(const stdAc::state_t *prev) { - stdAc::state_t result{}; - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::FUJITSU_AC; - checkSum(); - result.model = _model; - result.power = getPower(); - // Only update these settings if it is a long message, or we have no previous - // state info for those settings. - if (isLongCode() || prev == NULL) { - result.mode = toCommonMode(_.Mode); - result.celsius = getCelsius(); - { - const float minHeat = result.celsius ? kFujitsuAcMinHeat - : kFujitsuAcMinHeatF; - result.degrees = get10CHeat() ? minHeat : getTemp(); - } - result.fanspeed = toCommonFanSpeed(_.Fan); - uint8_t swing = _.Swing; - switch (result.model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - result.clean = _.Clean; - result.filter = _.Filter; - result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff; - result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto - : stdAc::swingh_t::kOff; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - default: - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - } - } - result.quiet = _.Fan == kFujitsuAcFanQuiet; - result.turbo = _cmd == kFujitsuAcCmdPowerful; - result.econo = _cmd == kFujitsuAcCmdEcono; - // Not supported. - result.light = false; - result.filter = 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 IRFujitsuAC::toString(void) const { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - fujitsu_ac_remote_model_t model = _model; - result += addModelToString(decode_type_t::FUJITSU_AC, model, false); - result += addIntToString(_.Id, kIdStr); - result += addBoolToString(getPower(), kPowerStr); - if (_rawstatemodified || isLongCode()) { - result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, - kFujitsuAcModeHeat, kFujitsuAcModeDry, - kFujitsuAcModeFan); - { - const bool isCelsius = getCelsius(); - const float minHeat = isCelsius ? kFujitsuAcMinHeat : kFujitsuAcMinHeatF; - result += addTempFloatToString(get10CHeat() ? minHeat : getTemp(), - isCelsius); - } - result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, - kFujitsuAcFanAuto, kFujitsuAcFanQuiet, - kFujitsuAcFanMed); - switch (model) { - // These models have no internal swing, clean. or filter state. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - break; - // These models have Clean & Filter, plus Swing (via fall thru) - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - result += addBoolToString(getClean(), kCleanStr); - result += addBoolToString(getFilter(), kFilterStr); - // FALL THRU - default: // e.g. ARREW4E - switch (model) { - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(get10CHeat(), k10CHeatStr); - break; - default: - break; - } - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kFujitsuAcSwingOff: - result += kOffStr; - break; - case kFujitsuAcSwingVert: - result += kSwingVStr; - break; - case kFujitsuAcSwingHoriz: - result += kSwingHStr; - break; - case kFujitsuAcSwingBoth: - result += kSwingVStr; - result += '+'; - result += kSwingHStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - } - } - result += kCommaSpaceStr; - result += kCommandStr; - result += kColonSpaceStr; - switch (_cmd) { - case kFujitsuAcCmdStepHoriz: - result += kStepStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdStepVert: - result += kStepStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdToggleSwingHoriz: - result += kToggleStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdToggleSwingVert: - result += kToggleStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdEcono: - result += kEconoStr; - break; - case kFujitsuAcCmdPowerful: - result += kPowerfulStr; - break; - default: - result += kNAStr; - } - if (_rawstatemodified || isLongCode()) { - uint16_t mins = 0; - String type_str = kTimerStr; - switch (model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); - // FALL THRU - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - switch (getTimerType()) { - case kFujitsuAcOnTimer: - type_str = kOnTimerStr; - mins = getOnTimer(); - break; - case kFujitsuAcOffTimer: - type_str = kOffTimerStr; - mins = getOffSleepTimer(); - break; - case kFujitsuAcSleepTimer: - type_str = kSleepTimerStr; - mins = getOffSleepTimer(); - break; - } - result += addLabeledString(mins ? minsToString(mins) : kOffStr, - type_str); - break; - default: - break; - } - } - return result; -} - -#if DECODE_FUJITSU_AC -/// Decode the supplied Fujitsu AC IR message if possible. -/// Status: STABLE / Working. -/// @param[in,out] results Ptr to the data to decode & where to store the decode -/// 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 A boolean. True if it can decode it, false if it can't. -bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + - offset) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAcBits: - case kFujitsuAcBits - 8: - case kFujitsuAcMinBits: - case kFujitsuAcMinBits + 8: break; - default: return false; // Must be called with the correct nr. of bits. - } - } - - // Header / Some of the Data - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kFujitsuAcMinBits - 8, - kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header - kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - 0, 0, // No Footer (yet) - false, _tolerance + kFujitsuAcExtraTolerance, 0, - false); // LSBF - if (!used) return false; - offset += used; - // Check we have the typical data header. - if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; - dataBitsSoFar += kFujitsuAcMinBits - 8; - - // Keep reading bytes until we either run out of message or state to fill. - match_result_t data_result; - for (uint16_t i = 5; - offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - _tolerance + kFujitsuAcExtraTolerance, 0, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Footer - if (offset > results->rawlen || - !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) - return false; - // The space is optional if we are out of capture. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - results->decode_type = FUJITSU_AC; - results->bits = dataBitsSoFar; - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAcMinBits: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFC) return false; - return true; // Success - case kFujitsuAcMinBits + 8: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFE) return false; - // The last byte needs to be the inverse of the penultimate byte. - if (results->state[5] != (uint8_t)~results->state[6]) return false; - return true; // Success - case kFujitsuAcBits - 8: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFC) return false; - break; - case kFujitsuAcBits: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - return true; // All good. -} -#endif // DECODE_FUJITSU_AC +// Copyright 2017 Jonny Graham +// Copyright 2017-2022 David Conran +// Copyright 2021 siriuslzx +// Copyright 2023 Takeshi Shimizu + +/// @file +/// @brief Support for Fujitsu A/C protocols. +/// Fujitsu A/C support added by Jonny Graham & David Conran +/// @warning Use of incorrect model may cause the A/C unit to lock up. +/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical +/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. +/// The correct model for it is ARREB1E. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 + +#include "ir_Fujitsu.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Ref: +// These values are based on averages of measurements +const uint16_t kFujitsuAcHdrMark = 3324; +const uint16_t kFujitsuAcHdrSpace = 1574; +const uint16_t kFujitsuAcBitMark = 448; +const uint16_t kFujitsuAcOneSpace = 1182; +const uint16_t kFujitsuAcZeroSpace = 390; +const uint16_t kFujitsuAcMinGap = 8100; +const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addFanToString; +using irutils::addTempFloatToString; +using irutils::minsToString; +using irutils::addSignedIntToString; + +#if SEND_FUJITSU_AC +/// Send a Fujitsu A/C formatted message. +/// Status: STABLE / Known Good. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// Typically one of: +/// kFujitsuAcStateLength, +/// kFujitsuAcStateLength - 1, +/// kFujitsuAcStateLengthShort, +/// kFujitsuAcStateLengthShort - 1 +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC + +// Code to emulate Fujitsu A/C IR remote control unit. + +/// Class Constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] model The enum for the model of A/C to be emulated. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRFujitsuAC::IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model, + const bool inverted, const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + setModel(model); + stateReset(); +} + +/// Set the currently emulated model of the A/C. +/// @param[in] model An enum representing the model to support/emulate. +void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { + _model = model; + switch (model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _state_length = kFujitsuAcStateLength - 1; + _state_length_short = kFujitsuAcStateLengthShort - 1; + break; + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + default: + _state_length = kFujitsuAcStateLength; + _state_length_short = kFujitsuAcStateLengthShort; + } +} + +/// Get the currently emulated/detected model of the A/C. +/// @return The enum representing the model of A/C. +fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } + +/// Reset the state of the remote to a known good state/sequence. +void IRFujitsuAC::stateReset(void) { + for (size_t i = 0; i < kFujitsuAcStateLength; i++) { + _.longcode[i] = 0; + } + setTemp(24); + _.Fan = kFujitsuAcFanHigh; + _.Mode = kFujitsuAcModeCool; + _.Swing = kFujitsuAcSwingBoth; + _cmd = kFujitsuAcCmdTurnOn; + _.Filter = false; + _.Clean = false; + _.TimerType = kFujitsuAcStopTimers; + _.OnTimer = 0; + _.OffTimer = 0; + _.longcode[0] = 0x14; + _.longcode[1] = 0x63; + _.longcode[3] = 0x10; + _.longcode[4] = 0x10; + _rawstatemodified = true; +} + +/// Set up hardware to be able to send a message. +void IRFujitsuAC::begin(void) { _irsend.begin(); } + +#if SEND_FUJITSU_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRFujitsuAC::send(const uint16_t repeat) { + _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); +} +#endif // SEND_FUJITSU_AC + +/// Update the length (size) of the state code for the current configuration. +/// @return true, if use long codes; false, use short codes. +bool IRFujitsuAC::updateUseLongOrShort(void) { + bool fullCmd = false; + switch (_cmd) { + case kFujitsuAcCmdTurnOff: // 0x02 + case kFujitsuAcCmdEcono: // 0x09 + case kFujitsuAcCmdPowerful: // 0x39 + case kFujitsuAcCmdStepVert: // 0x6C + case kFujitsuAcCmdToggleSwingVert: // 0x6D + case kFujitsuAcCmdStepHoriz: // 0x79 + case kFujitsuAcCmdToggleSwingHoriz: // 0x7A + _.Cmd = _cmd; + _rawstatemodified = true; + break; + default: + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + _.Cmd = 0xFE; + _rawstatemodified = true; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Cmd = 0xFC; + _rawstatemodified = true; + break; + } + fullCmd = true; + break; + } + return fullCmd; +} + +/// Calculate and set the checksum values for the internal state. +void IRFujitsuAC::checkSum(void) { + _rawstatemodified = true; + if (updateUseLongOrShort()) { // Is it going to be a long code? + // Nr. of bytes in the message after this byte. + _.RestLength = _state_length - 7; + _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; + _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); + + // These values depend on model + if (_model != fujitsu_ac_remote_model_t::ARREB1E && + _model != fujitsu_ac_remote_model_t::ARREW4E) { + _.OutsideQuiet = 0; + if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { + _.TimerType = kFujitsuAcStopTimers; + } + } + if (_model != fujitsu_ac_remote_model_t::ARRY4) { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + break; + default: + _.Clean = false; + } + _.Filter = false; + } + // Set the On/Off/Sleep timer Nr of mins. + _.OffTimer = getOffSleepTimer(); + _.OnTimer = getOnTimer(); + // Enable bit for the Off/Sleep timer + _.OffTimerEnable = _.OffTimer > 0; + // Enable bit for the On timer + _.OnTimerEnable = _.OnTimer > 0; + + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + switch (_model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Swing = kFujitsuAcSwingOff; + checksum = sumBytes(_.longcode, _state_length - 1); + checksum_complement = 0x9B; + break; + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + _.unknown = 1; + // FALL THRU + default: + checksum = sumBytes(_.longcode + _state_length_short, + _state_length - _state_length_short - 1); + } + // and negate the checksum and store it in the last byte. + _.longcode[_state_length - 1] = checksum_complement - checksum; + } else { // short codes + for (size_t i = 0; i < _state_length_short; i++) { + _.shortcode[i] = _.longcode[i]; + } + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + // The last byte is the inverse of penultimate byte + _.shortcode[_state_length_short - 1] = + ~_.shortcode[_state_length_short - 2]; + break; + default: + {}; // We don't need to do anything for the others. + } + } +} + +/// Get the length (size) of the state code for the current configuration. +/// @return The length of the state array required for this config. +uint8_t IRFujitsuAC::getStateLength(void) { + return updateUseLongOrShort() ? _state_length : _state_length_short; +} + +/// Is the current binary state representation a long or a short code? +/// @return true, if long; false, if short. +bool IRFujitsuAC::isLongCode(void) const { + return _.Cmd == 0xFE || _.Cmd == 0xFC; +} + +/// 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* IRFujitsuAC::getRaw(void) { + checkSum(); + return isLongCode() ? _.longcode : _.shortcode; +} + +/// Build the internal state/config from the current (raw) A/C message. +/// @param[in] length Size of the current/used (raw) A/C message array. +void IRFujitsuAC::buildFromState(const uint16_t length) { + switch (length) { + case kFujitsuAcStateLength - 1: + case kFujitsuAcStateLengthShort - 1: + setModel(fujitsu_ac_remote_model_t::ARDB1); + // ARJW2 has horizontal swing. + if (_.Swing > kFujitsuAcSwingVert) + setModel(fujitsu_ac_remote_model_t::ARJW2); + break; + default: + switch (_.Cmd) { + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setModel(fujitsu_ac_remote_model_t::ARREB1E); + break; + default: + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + } + } + switch (_.RestLength) { + case 8: + if (_model != fujitsu_ac_remote_model_t::ARJW2) + setModel(fujitsu_ac_remote_model_t::ARDB1); + break; + case 9: + if (_model != fujitsu_ac_remote_model_t::ARREB1E) + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + break; + } + if (_.Power) + setCmd(kFujitsuAcCmdTurnOn); + else + setCmd(kFujitsuAcCmdStayOn); + // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if + // either the raw Filter or Clean setting is on. + if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean) && + !get10CHeat()) + setModel(fujitsu_ac_remote_model_t::ARRY4); + if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) + setModel(fujitsu_ac_remote_model_t::ARREB1E); + switch (_.Cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setCmd(_.Cmd); + break; + } + if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +/// @param[in] length Size of the newState array. +/// @return true, if successful; Otherwise false. (i.e. size check) +bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > kFujitsuAcStateLength) return false; + for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { + if (i < length) + _.longcode[i] = newState[i]; + else + _.longcode[i] = 0; + } + buildFromState(length); + _rawstatemodified = false; + return true; +} + +/// Request the A/C to step the Horizontal Swing. +void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } + +/// Request the A/C to toggle the Horizontal Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingHoriz(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingHoriz); +} + +/// Request the A/C to step the Vertical Swing. +void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } + +/// Request the A/C to toggle the Vertical Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingVert(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingVert); +} + +/// Set the requested (special) command part for the A/C message. +/// @param[in] cmd The special command code. +void IRFujitsuAC::setCmd(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdTurnOn: + case kFujitsuAcCmdStayOn: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + _cmd = cmd; + break; + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + switch (_model) { + // Only these remotes have horizontal. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + switch (_model) { + // Only these remotes have these commands. + case ARREB1E: + case ARREW4E: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } +} + +/// Set the requested (special) command part for the A/C message. +/// @return The special command code. +uint8_t IRFujitsuAC::getCmd(void) const { + return _cmd; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setPower(const bool on) { + setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); +} + +/// Set the requested power state of the A/C to off. +void IRFujitsuAC::off(void) { setPower(false); } + +/// Set the requested power state of the A/C to on. +void IRFujitsuAC::on(void) { setPower(true); } + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } + +/// Set the Outside Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setOutsideQuiet(const bool on) { + _.OutsideQuiet = on; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Outside Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getOutsideQuiet(void) const { + switch (_model) { + // Only ARREB1E & ARREW4E seems to have this mode. + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + return _.OutsideQuiet; + default: return false; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees. +/// @param[in] useCelsius Use Celsius or Fahrenheit? +void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { + float mintemp; + float maxtemp; + uint8_t offset; + bool _useCelsius; + float _temp; + + switch (_model) { + // These models have native Fahrenheit & Celsius upport. + case fujitsu_ac_remote_model_t::ARREW4E: + _useCelsius = useCelsius; + _temp = temp; + break; + // Make sure everything else uses Celsius. + default: + _useCelsius = true; + _temp = useCelsius ? temp : fahrenheitToCelsius(temp); + } + setCelsius(_useCelsius); + if (_useCelsius) { + mintemp = kFujitsuAcMinTemp; + maxtemp = kFujitsuAcMaxTemp; + offset = kFujitsuAcTempOffsetC; + } else { + mintemp = kFujitsuAcMinTempF; + maxtemp = kFujitsuAcMaxTempF; + offset = kFujitsuAcTempOffsetF; + } + _temp = std::max(mintemp, _temp); + _temp = std::min(maxtemp, _temp); + if (_useCelsius) { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) + _.Temp = (_temp - (offset / 2)) * 2; + else + _.Temp = (_temp - offset) * 4; + } else { + _.Temp = _temp - offset; + } + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees of the currently set units. +float IRFujitsuAC::getTemp(void) const { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) { + if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. + return _.Temp + kFujitsuAcTempOffsetF; + else + return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); + } else { + return _.Temp / 4 + kFujitsuAcMinTemp; + } +} + +/// Set the speed of the fan. +/// @param[in] fanSpeed The desired setting. +void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { + if (fanSpeed > kFujitsuAcFanQuiet) + _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. + else + _.Fan = fanSpeed; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRFujitsuAC::setMode(const uint8_t mode) { + if (mode > kFujitsuAcModeHeat) + _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. + else + _.Mode = mode; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } + +/// Set the requested swing operation mode of the A/C unit. +/// @param[in] swingMode The swingMode code for the A/C. +/// Vertical, Horizon, or Both. See constants for details. +/// @note Not all models support all possible swing modes. +void IRFujitsuAC::setSwing(const uint8_t swingMode) { + _.Swing = swingMode; + _rawstatemodified = true; + switch (_model) { + // No Horizontal support. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; + break; + // Has Horizontal support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + default: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; + } + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the requested swing operation mode of the A/C unit. +/// @return The contents of the swing state/mode. +uint8_t IRFujitsuAC::getSwing(void) const { return _.Swing; } + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setClean(const bool on) { + _.Clean = on; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getClean(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; + default: return false; + } +} + +/// Set the Filter mode status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setFilter(const bool on) { + _.Filter = on; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Filter mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getFilter(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; + default: return false; + } +} + +/// Set the 10C heat status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::set10CHeat(const bool on) { + switch (_model) { + // Only selected models support this. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + setClean(on); // 10C Heat uses the same bit as Clean + if (on) { + _.Mode = kFujitsuAcModeFan; + _.Power = true; + _.Fan = kFujitsuAcFanAuto; + _.Swing = kFujitsuAcSwingOff; + _rawstatemodified = true; + } + default: + break; + } +} + +/// Get the 10C heat status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::get10CHeat(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && + _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); + default: return false; + } +} + +/// Get the Timer type of the A/C message. +/// @return The current timer type in numeric form. +uint8_t IRFujitsuAC::getTimerType(void) const { + switch (_model) { + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: return _.TimerType; + default: return kFujitsuAcStopTimers; + } +} + +/// Set the Timer type of the A/C message. +/// @param[in] timertype The kind of timer to use for the message. +void IRFujitsuAC::setTimerType(const uint8_t timertype) { + switch (timertype) { + case kFujitsuAcSleepTimer: + case kFujitsuAcOnTimer: + case kFujitsuAcOffTimer: + case kFujitsuAcStopTimers: + _.TimerType = timertype; + break; + default: _.TimerType = kFujitsuAcStopTimers; + } + _rawstatemodified = true; +} + +/// Get the On Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOnTimer(void) const { + if (getTimerType() == kFujitsuAcOnTimer) + return _.OnTimer; + return 0; +} + +/// Set the On Timer setting of the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { + _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; + if (_.OnTimer) { + _.TimerType = kFujitsuAcOnTimer; + } else if (getTimerType() == kFujitsuAcOnTimer) { + _.TimerType = kFujitsuAcStopTimers; + } +} + +/// Get the Off/Sleep Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOffSleepTimer(void) const { + switch (getTimerType()) { + case kFujitsuAcOffTimer: + case kFujitsuAcSleepTimer: return _.OffTimer; + default: return 0; + } +} + +/// Set the Off/Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { + _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; +} + +/// Set the Off Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. + if (nr_mins) + _.TimerType = kFujitsuAcOffTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// Set the Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. + if (nr_mins) + _.TimerType = kFujitsuAcSleepTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = state[length - 1]; + switch (length) { + case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 + return state[length - 1] == (uint8_t)~state[length - 2]; + case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 + sum = sumBytes(state, length - 1); + sum_complement = 0x9B; + break; + case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E + sum = sumBytes(state + kFujitsuAcStateLengthShort, + length - 1 - kFujitsuAcStateLengthShort); + break; + default: // Includes ARDB1 & ARJW2 short. + return true; // Assume the checksum is valid for other lengths. + } + return checksum == (uint8_t)(sum_complement - sum); // Does it match? +} + +/// Set the device's remote ID number. +/// @param[in] num The ID for the remote. Valid number range is 0 to 3. +void IRFujitsuAC::setId(const uint8_t num) { + _.Id = num; + _rawstatemodified = true; +} + +/// Get the current device's remote ID number. +/// @return The current device's remote ID number. +uint8_t IRFujitsuAC::getId(void) const { return _.Id; } + +/// Set the Temperature units for the A/C. +/// @param[in] on true, use Celsius. false, use Fahrenheit. +void IRFujitsuAC::setCelsius(const bool on) { + _.Fahrenheit = !on; + _rawstatemodified = true; +} + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } + +/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; + case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; + case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; + case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; + default: return kFujitsuAcModeAuto; + } +} + +/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; + case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; + case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; + default: return kFujitsuAcFanAuto; + } +} + +/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAcFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRFujitsuAC::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result{}; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::FUJITSU_AC; + checkSum(); + result.model = _model; + result.power = getPower(); + // Only update these settings if it is a long message, or we have no previous + // state info for those settings. + if (isLongCode() || prev == NULL) { + result.mode = toCommonMode(_.Mode); + result.celsius = getCelsius(); + { + const float minHeat = result.celsius ? kFujitsuAcMinHeat + : kFujitsuAcMinHeatF; + result.degrees = get10CHeat() ? minHeat : getTemp(); + } + result.fanspeed = toCommonFanSpeed(_.Fan); + uint8_t swing = _.Swing; + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + result.clean = _.Clean; + result.filter = _.Filter; + result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + default: + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + } + } + result.quiet = _.Fan == kFujitsuAcFanQuiet; + result.turbo = _cmd == kFujitsuAcCmdPowerful; + result.econo = _cmd == kFujitsuAcCmdEcono; + // Not supported. + result.light = false; + result.filter = 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 IRFujitsuAC::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + fujitsu_ac_remote_model_t model = _model; + result += addModelToString(decode_type_t::FUJITSU_AC, model, false); + result += addIntToString(_.Id, kIdStr); + result += addBoolToString(getPower(), kPowerStr); + if (_rawstatemodified || isLongCode()) { + result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + { + const bool isCelsius = getCelsius(); + const float minHeat = isCelsius ? kFujitsuAcMinHeat : kFujitsuAcMinHeatF; + result += addTempFloatToString(get10CHeat() ? minHeat : getTemp(), + isCelsius); + } + result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + switch (model) { + // These models have no internal swing, clean. or filter state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + // These models have Clean & Filter, plus Swing (via fall thru) + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getFilter(), kFilterStr); + // FALL THRU + default: // e.g. ARREW4E + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(get10CHeat(), k10CHeatStr); + break; + default: + break; + } + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kFujitsuAcSwingOff: + result += kOffStr; + break; + case kFujitsuAcSwingVert: + result += kSwingVStr; + break; + case kFujitsuAcSwingHoriz: + result += kSwingHStr; + break; + case kFujitsuAcSwingBoth: + result += kSwingVStr; + result += '+'; + result += kSwingHStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + } + } + result += kCommaSpaceStr; + result += kCommandStr; + result += kColonSpaceStr; + switch (_cmd) { + case kFujitsuAcCmdStepHoriz: + result += kStepStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdStepVert: + result += kStepStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdToggleSwingHoriz: + result += kToggleStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdToggleSwingVert: + result += kToggleStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdEcono: + result += kEconoStr; + break; + case kFujitsuAcCmdPowerful: + result += kPowerfulStr; + break; + default: + result += kNAStr; + } + if (_rawstatemodified || isLongCode()) { + uint16_t mins = 0; + String type_str = kTimerStr; + switch (model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); + // FALL THRU + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + switch (getTimerType()) { + case kFujitsuAcOnTimer: + type_str = kOnTimerStr; + mins = getOnTimer(); + break; + case kFujitsuAcOffTimer: + type_str = kOffTimerStr; + mins = getOffSleepTimer(); + break; + case kFujitsuAcSleepTimer: + type_str = kSleepTimerStr; + mins = getOffSleepTimer(); + break; + } + result += addLabeledString(mins ? minsToString(mins) : kOffStr, + type_str); + break; + default: + break; + } + } + return result; +} + +#if DECODE_FUJITSU_AC +/// Decode the supplied Fujitsu AC IR message if possible. +/// Status: STABLE / Working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// 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 A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + + offset) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAcBits: + case kFujitsuAcBits - 8: + case kFujitsuAcMinBits: + case kFujitsuAcMinBits + 8: break; + default: return false; // Must be called with the correct nr. of bits. + } + } + + // Header / Some of the Data + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kFujitsuAcMinBits - 8, + kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header + kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + 0, 0, // No Footer (yet) + false, _tolerance + kFujitsuAcExtraTolerance, 0, + false); // LSBF + if (!used) return false; + offset += used; + // Check we have the typical data header. + if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; + dataBitsSoFar += kFujitsuAcMinBits - 8; + + // Keep reading bytes until we either run out of message or state to fill. + match_result_t data_result; + for (uint16_t i = 5; + offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + _tolerance + kFujitsuAcExtraTolerance, 0, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Footer + if (offset > results->rawlen || + !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) + return false; + // The space is optional if we are out of capture. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) + return false; + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + results->decode_type = FUJITSU_AC; + results->bits = dataBitsSoFar; + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAcMinBits: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFC) return false; + return true; // Success + case kFujitsuAcMinBits + 8: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFE) return false; + // The last byte needs to be the inverse of the penultimate byte. + if (results->state[5] != (uint8_t)~results->state[6]) return false; + return true; // Success + case kFujitsuAcBits - 8: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFC) return false; + break; + case kFujitsuAcBits: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + return true; // All good. +} +#endif // DECODE_FUJITSU_AC + +#if SEND_FUJITSU_AC264 +/// Send a Fujitsu 264 bit A/C formatted message. +/// Status: STABLE / Known Good. +/// @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::sendFujitsuAC264(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC264 + +// Code to emulate Fujitsu 264 bit A/C IR remote control unit. + +/// 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? +IRFujitsuAC264::IRFujitsuAC264(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 IRFujitsuAC264::begin(void) { _irsend.begin(); } + +/// Reset the state of the remote to a known good state/sequence. +void IRFujitsuAC264::stateReset(void) { + for (size_t i = 0; i < kFujitsuAc264StateLength; i++) { + _.raw[i] = 0; + } + _ispoweredon = false; + _isecofan = false; + _isoutsidequiet = false; + _settemp = 0; + setTemp(24); + _cmd = _.Cmd = kFujitsuAc264CmdCool; + _.TempAuto = 0; + _.Mode = kFujitsuAc264ModeCool; + _.FanSpeed = kFujitsuAc264FanSpeedHigh; + _.FanAngle = kFujitsuAc264FanAngleStay; + _.Swing = false; + _.Economy = false; + _.Clean = false; + _.ClockHours = 0; + _.ClockMins = 0; + _.SleepTimerEnable = false; + _.SleepTimer = 0; + _.TimerEnable = kFujitsuAc264OnOffTimerDisable; + _.OnTimer = 0; + _.OffTimer = 0; + _.raw[0] = 0x14; + _.raw[1] = 0x63; + _.raw[2] = 0x00; + _.raw[3] = 0x10; + _.raw[4] = 0x10; +} + +#if SEND_FUJITSU_AC264 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRFujitsuAC264::send(const uint16_t repeat) { + _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); + _settemp = _.Temp; // Preserve the sent setting + _ispoweredon = (_cmd < kFujitsuAc264SpCmdTurnOff); + if (_cmd == kFujitsuAc264SpCmdEcoFanOn) + _isecofan = true; + if (_cmd == kFujitsuAc264SpCmdEcoFanOff) + _isecofan = false; + if (_cmd == kFujitsuAc264SpCmdOutsideQuietOn) + _isoutsidequiet = true; + if (_cmd == kFujitsuAc264SpCmdOutsideQuietOff) + _isoutsidequiet = false; +} +#endif // SEND_FUJITSU_AC264 + +/// 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* IRFujitsuAC264::getRaw(void) { + checkSum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +/// @param[in] length Size of the newState array. +/// @return True, if successful; Otherwise false. (i.e. size check) +bool IRFujitsuAC264::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > kFujitsuAc264StateLength) return false; + for (uint16_t i = 0; i < kFujitsuAc264StateLength; i++) { + if (i < length) + _.raw[i] = newState[i]; + else + _.raw[i] = 0; + } + switch (length) { + case kFujitsuAc264StateLengthShort: + if (std::memcmp(_.raw, kFujitsuAc264StatesTurnOff, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdTurnOff; + if (std::memcmp(_.raw, kFujitsuAc264StatesTogglePowerful, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdTogglePowerful; + if (std::memcmp(_.raw, kFujitsuAc264StatesEcoFanOff, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdEcoFanOff; + if (std::memcmp(_.raw, kFujitsuAc264StatesEcoFanOn, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdEcoFanOn; + break; + case kFujitsuAc264StateLengthMiddle: + if (std::memcmp(_.raw, kFujitsuAc264StatesOutsideQuietOff, + kFujitsuAc264StateLengthMiddle) == 0) + _cmd = kFujitsuAc264SpCmdOutsideQuietOff; + if (std::memcmp(_.raw, kFujitsuAc264StatesOutsideQuietOn, + kFujitsuAc264StateLengthMiddle) == 0) + _cmd = kFujitsuAc264SpCmdOutsideQuietOn; + if (std::memcmp(_.raw, kFujitsuAc264StatesToggleSterilization, + kFujitsuAc264StateLengthMiddle) == 0) + _cmd = kFujitsuAc264SpCmdToggleSterilization; + break; + case kFujitsuAc264StateLength: + setPower(true); + _cmd = _.Cmd; + break; + default: + return false; + } + return true; +} + +/// 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 IRFujitsuAC264::validChecksum(uint8_t state[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = 0; + + if (length == kFujitsuAc264StateLengthShort) { + checksum = state[kFujitsuAc264StateLengthShort - 1]; + sum = state[kFujitsuAc264StateLengthShort - 2]; + sum_complement = 0xFF; + } else if (length == kFujitsuAc264StateLengthMiddle) { + checksum = state[kFujitsuAc264StateLengthMiddle - 1]; + sum = sumBytes(state, kFujitsuAc264StateLengthMiddle - 1); + sum_complement = 0x9E; + // The current command is normal + } else if (length == kFujitsuAc264StateLength) { + checksum = state[kFujitsuAc264StateLength - 1]; + sum = sumBytes(state, kFujitsuAc264StateLength - 1); + sum_complement = 0xAF; + } else { + return false; + } + return checksum == (uint8_t) (sum_complement - sum); // Does it match? +} + +/// Calculate and set the checksum values for the internal state. +void IRFujitsuAC264::checkSum(void) { + if (!isSpecialCommand()) { // The current command is not special + _.raw[5] = 0xFE; + _.RestLength = 0x1A; + _.Protocol = 0x40; + _.raw[13] = 0x00; + _.raw[15] |= 0x12; + _.raw[16] |= 0x06; + _.raw[17] = 0x00; + _.raw[21] |= 0x40; + _.raw[22] |= 0x10; + _.raw[25] = 0x00; + _.raw[26] = 0x00; + _.raw[27] = 0x00; + _.raw[28] |= 0xF0; + _.raw[29] = 0xFF; + _.raw[30] = 0xFF; + + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + checksum = sumBytes(_.raw, kFujitsuAc264StateLength - 1); + checksum_complement = 0xAF; + _.raw[kFujitsuAc264StateLength - 1] = checksum_complement - checksum; + } +} + +/// Is the current command a special command? +/// @return True, if special command (kFujitsuAc264SpCmd*); +/// false, if normal command (kFujitsuAc264Cmd*). +bool IRFujitsuAC264::isSpecialCommand(void) const { + return (_cmd & 0xF0) == 0xF0; +} + +/// Get the length (size) of the state code for the current configuration. +/// @return The length of the state array required for this config. +uint8_t IRFujitsuAC264::getStateLength(void) { + uint8_t stateLength = 0; + + switch (_cmd) { + case kFujitsuAc264SpCmdTurnOff: + case kFujitsuAc264SpCmdTogglePowerful: + case kFujitsuAc264SpCmdEcoFanOff: + case kFujitsuAc264SpCmdEcoFanOn: + stateLength = kFujitsuAc264StateLengthShort; + break; + case kFujitsuAc264SpCmdOutsideQuietOff: + case kFujitsuAc264SpCmdOutsideQuietOn: + case kFujitsuAc264SpCmdToggleSterilization: + stateLength = kFujitsuAc264StateLengthMiddle; + break; + default: + stateLength = kFujitsuAc264StateLength; + break; + } + return stateLength; +} + +/// Set the requested power state of the A/C to on. +/// @note Mode should be set after this function. +void IRFujitsuAC264::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRFujitsuAC264::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on True, the setting is on. false, the setting is off. +/// @note If on = true, a mode should be set after calling this function. +void IRFujitsuAC264::setPower(const bool on) { + if (on) { + _cmd = kFujitsuAc264CmdCool; + } else { + _cmd = kFujitsuAc264SpCmdTurnOff; + std::memcpy(_.raw, kFujitsuAc264StatesTurnOff, + kFujitsuAc264StateLengthShort); + } +} + +/// Get the value of the current power setting. +/// @return True, the setting is on. false, the setting is off. +bool IRFujitsuAC264::getPower(void) const { return _ispoweredon; } + +/// Check if the temperature setting is changed. +/// @return True if the temperature is not changed. +bool IRFujitsuAC264::isTempStayed(void) const { return _settemp == _.Temp; } + +/// Set the temperature. +/// @param[in] temp The temperature in degrees Celcius. +/// @note The fractional part which is truncated to multiple of 0.5. +void IRFujitsuAC264::setTemp(const float temp) { + float _temp; + + if (temp > kFujitsuAc264MaxTemp) + _temp = kFujitsuAc264MaxTemp; + else if ((temp < kFujitsuAc264MinTemp) && (_.Mode != kFujitsuAc264ModeHeat)) + _temp = kFujitsuAc264MinTemp; + else if (temp < kFujitsuAc264MinHeat) + _temp = kFujitsuAc264MinHeat; + else + _temp = temp; + + _.Temp = (_temp - (kFujitsuAc264TempOffsetC / 2)) * 2; + _cmd = _.Cmd = kFujitsuAc264CmdTemp; + _.SubCmd = isTempStayed(); +} + +/// Get the current temperature setting. +/// @return The current setting for temperature in degrees Celcius. +float IRFujitsuAC264::getTemp(void) const { + return static_cast(_.Temp / 2.0) + (kFujitsuAc264TempOffsetC / 2); +} + +/// Set the temperature in auto mode. +/// @param[in] temp The temperature in auto mode in degrees Celcius. +/// @note The fractional part which is truncated to multiple of 0.5. +void IRFujitsuAC264::setTempAuto(const float temp) { + int8_t _tempx10; + + _tempx10 = (int8_t) (temp * 10); + _tempx10 -= _tempx10 % 5; + if (temp > kFujitsuAc264MaxTempAuto) + _tempx10 = kFujitsuAc264MaxTempAuto * 10; + else if (temp < kFujitsuAc264MinTempAuto) + _tempx10 = kFujitsuAc264MinTempAuto * 10; + + _.TempAuto = _tempx10; + _cmd = _.Cmd = kFujitsuAc264CmdTemp; +} + +/// Get the current temperature in auto mode setting. +/// @return The current setting for temp in auto mode in degrees Celcius. +float IRFujitsuAC264::getTempAuto(void) const { + return static_cast(static_cast(_.TempAuto) / 10.0); +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @param[in] weakdry True if dry mode is in weak. +void IRFujitsuAC264::setMode(const uint8_t mode, const bool weakdry) { + switch (mode) { + case kFujitsuAc264ModeAuto: + _.Mode = kFujitsuAc264ModeAuto; + _cmd = _.Cmd = kFujitsuAc264CmdAuto; + break; + case kFujitsuAc264ModeCool: + _.Mode = kFujitsuAc264ModeCool; + _cmd = _.Cmd = kFujitsuAc264CmdCool; + break; + case kFujitsuAc264ModeFan: + _.Mode = kFujitsuAc264ModeFan; + _cmd = _.Cmd = kFujitsuAc264CmdFan; + break; + case kFujitsuAc264ModeHeat: + _.Mode = kFujitsuAc264ModeHeat; + _cmd = _.Cmd = kFujitsuAc264CmdHeat; + break; + case kFujitsuAc264ModeDry: + _.Mode = kFujitsuAc264ModeDry; + _.WeakDry = weakdry; + _cmd = _.Cmd = kFujitsuAc264CmdDry; + break; + default: + _.Mode = kFujitsuAc264ModeAuto; + _cmd = _.Cmd = kFujitsuAc264CmdAuto; + break; + } + _.SubCmd = 1; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRFujitsuAC264::getMode(void) const { return _.Mode; } + +/// Get the weak dry mode setting of the A/C. +/// @return The weak dry mode setting. +bool IRFujitsuAC264::isWeakDry(void) const { return _.WeakDry; } + +/// Set the speed of the fan. +/// @param[in] fanSpeed The desired setting. +void IRFujitsuAC264::setFanSpeed(const uint8_t fanSpeed) { + // Set the fan to auto if out of range. + if ((fanSpeed == kFujitsuAc264FanSpeedQuiet) || + (fanSpeed == kFujitsuAc264FanSpeedLow) || + (fanSpeed == kFujitsuAc264FanSpeedMed) || + (fanSpeed == kFujitsuAc264FanSpeedHigh)) + _.FanSpeed = fanSpeed; + else + _.FanSpeed = kFujitsuAc264FanSpeedAuto; + _cmd = _.Cmd = kFujitsuAc264CmdFanSpeed; + _.SubCmd = 0; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRFujitsuAC264::getFanSpeed(void) const { return _.FanSpeed; } + +/// Set the angle of the fan. +/// @param[in] fanAngle The desired setting. +void IRFujitsuAC264::setFanAngle(const uint8_t fanAngle) { + // Set the fan to stay if out of range. + if ((fanAngle > kFujitsuAc264FanAngle7) || + (fanAngle < kFujitsuAc264FanAngle1)) { + _.FanAngle = kFujitsuAc264FanAngleStay; + } else { + _.FanAngle = fanAngle; + } + _cmd = _.Cmd = kFujitsuAc264CmdFanAngle; + _.SubCmd = 0; +} + +/// Get the current fan angle setting. +/// @return The current fan angle. +uint8_t IRFujitsuAC264::getFanAngle(void) const { return _.FanAngle; } + +/// Set weather the swing of fan is enabled or not. +/// @param[in] on True if swing is enabled, false if disabled. +void IRFujitsuAC264::setSwing(const bool on) { + _.Swing = on; + _.FanAngle = kFujitsuAc264FanAngleStay; // Set the fan to stay. + _cmd = _.Cmd = kFujitsuAc264CmdSwing; + _.SubCmd = 0; +} + +/// Get the requested swing operation mode of the A/C unit. +/// @return True if swing is enabled, false if disabled. +bool IRFujitsuAC264::getSwing(void) const { return _.Swing; } + +/// Set weather economy mode is enabled or not. +/// @param[in] on True if economy mode is enabled, false if disabled. +void IRFujitsuAC264::setEconomy(const bool on) { + _.Economy = on; + _cmd = _.Cmd = kFujitsuAc264CmdEconomy; + _.SubCmd = 0; +} + +/// Get the requested economy mode of the A/C unit. +/// @return True if economy mode is enabled, false if disabled. +bool IRFujitsuAC264::getEconomy(void) const { return _.Economy; } + +/// Set weather clean mode is enabled or not. +/// @param[in] on True if swing is enabled, false if disabled. +void IRFujitsuAC264::setClean(const bool on) { + _.Clean = on; + _cmd = _.Cmd = kFujitsuAc264CmdClean; + _.SubCmd = 0; +} + +/// Get the requested clean mode of the A/C unit. +/// @return True if clean is enabled, false if disabled. +bool IRFujitsuAC264::getClean(void) const { return _.Clean; } + +/// Toggle the sterilization. +/// @note This command is valid only when AC's power is off. +void IRFujitsuAC264::toggleSterilization(void) { + if (getPower()) + return; + _cmd = kFujitsuAc264SpCmdToggleSterilization; + std::memcpy(_.raw, kFujitsuAc264StatesToggleSterilization, + kFujitsuAc264StateLengthMiddle); +} + +/// Set weather outside quiet mode is enabled or not. +/// @param[in] on True if outside quiet is enabled, false if disabled. +/// @note This command is valid only when AC's power is off. +void IRFujitsuAC264::setOutsideQuiet(const bool on) { + if (getPower()) + return; + if (on) { + _cmd = kFujitsuAc264SpCmdOutsideQuietOn; + std::memcpy(_.raw, kFujitsuAc264StatesOutsideQuietOn, + kFujitsuAc264StateLengthMiddle); + } else { + _cmd = kFujitsuAc264SpCmdOutsideQuietOff; + std::memcpy(_.raw, kFujitsuAc264StatesOutsideQuietOff, + kFujitsuAc264StateLengthMiddle); + } +} + +/// Get the requested outside quiet mode of the A/C unit. +/// @return True if outside quiet is enabled, false if disabled. +bool IRFujitsuAC264::getOutsideQuiet(void) const { return _isoutsidequiet; } + +/// Set weather economy fan mode is enabled or not. +/// @param[in] on True if economy fan mode is enabled, false if disabled. +/// @note This command is valid only when AC's power is off. +void IRFujitsuAC264::setEcoFan(const bool on) { + if (getPower()) + return; + if (on) { + _cmd = kFujitsuAc264SpCmdEcoFanOn; + std::memcpy(_.raw, kFujitsuAc264StatesEcoFanOn, + kFujitsuAc264StateLengthShort); + } else { + _cmd = kFujitsuAc264SpCmdEcoFanOff; + std::memcpy(_.raw, kFujitsuAc264StatesEcoFanOff, + kFujitsuAc264StateLengthShort); + } +} + +/// Get the requested economy fan mode of the A/C unit. +/// @return True if economy fan mode is enabled, false if disabled. +bool IRFujitsuAC264::getEcoFan(void) const { return _isecofan; } + +/// Toggle the powerful mode. +/// @note This command is valid only when AC's power is on. +void IRFujitsuAC264::togglePowerful(void) { + if (!getPower()) + return; + _cmd = kFujitsuAc264SpCmdTogglePowerful; + std::memcpy(_.raw, kFujitsuAc264StatesTogglePowerful, + kFujitsuAc264StateLengthShort); +} + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRFujitsuAC264::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + // Hours. + _.ClockHours = mins / 60; + // Minutes. + _.ClockMins = mins % 60; +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRFujitsuAC264::getClock(void) const { + return (_.ClockHours * 60 + _.ClockMins); +} + +/// Set the sleep timer setting of the A/C. +/// @param[in] mins Minutes to set the timer to. 0 means disabled. +void IRFujitsuAC264::setSleepTimer(const uint16_t mins) { + if (mins == 0) { + _.SleepTimerEnable = false; + _.SleepTimer = 0; + _cmd = _.Cmd = kFujitsuAc264CmdCancelSleepTimer; + _.SubCmd = 0; + } else if (mins <= kFujitsuAc264SleepTimerMax) { + _.SleepTimerEnable = true; + _.SleepTimer = 0x800 + mins; + _cmd = _.Cmd = kFujitsuAc264CmdSleepTime; + _.SubCmd = 1; + } +} + +/// Get the sleep timer setting of the A/C. +/// @return Minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC264::getSleepTimer(void) const { + if (_.SleepTimerEnable) + return (_.SleepTimer - 0x800); + return 0; +} + +/// Set the Timer enable of the A/C message. +/// @param[in] timer_enable The kind of timer to enable for the message. +void IRFujitsuAC264::setTimerEnable(const uint8_t timer_enable) { + switch (timer_enable) { + case kFujitsuAc264OnTimerEnable: + _.TimerEnable = timer_enable; + _cmd = _.Cmd = kFujitsuAc264CmdOnTimer; + _.SubCmd = 0; + break; + case kFujitsuAc264OffTimerEnable: + case kFujitsuAc264OnOffTimerEnable: + _.TimerEnable = timer_enable; + _cmd = _.Cmd = kFujitsuAc264CmdOffTimer; + _.SubCmd = 0; + break; + case kFujitsuAc264OnOffTimerDisable: + _.TimerEnable = timer_enable; + _cmd = _.Cmd = kFujitsuAc264CmdCancelOnOffTimer; + _.SubCmd = 0; + break; + default: + _.TimerEnable = kFujitsuAc264OnOffTimerDisable; + _cmd = _.Cmd = kFujitsuAc264CmdCancelOnOffTimer; + _.SubCmd = 0; + break; + } +} + +/// Get the Timer enable of the A/C message. +/// @return The current timer enable in numeric form. +uint8_t IRFujitsuAC264::getTimerEnable(void) const { return _.TimerEnable; } + +/// Set the on timer setting of the A/C. +/// @param[in] mins10 Time in 10 minutes unit, when the A/C will turn on. +/// 0 means 0:00 AM, 1 means 0:10 AM. +void IRFujitsuAC264::setOnTimer(const uint8_t mins10) { + if (mins10 <= kFujitsuAc26OnOffTimerMax) + _.OnTimer = mins10; +} + +/// Get the on timer setting of the A/C. +/// @return Time in 10 minutes unit, when the A/C will turn on. +/// 0 means 0:00 AM, 1 means 0:10 AM. +uint8_t IRFujitsuAC264::getOnTimer(void) const { return _.OnTimer; } + +/// Set the off timer setting of the A/C. +/// @param[in] mins10 Time in 10 minutes unit, when the A/C will turn off. +/// 0 means 0:00 AM, 1 means 0:10 AM. +void IRFujitsuAC264::setOffTimer(const uint8_t mins10) { + if (mins10 <= kFujitsuAc26OnOffTimerMax) + _.OffTimer = mins10; +} + +/// Get the off timer setting of the A/C. +/// @return Time in 10 minutes unit, when the A/C will turn off. +/// 0 means 0:00 AM, 1 means 0:10 AM. +uint8_t IRFujitsuAC264::getOffTimer(void) const { return _.OffTimer; } + +/// Set the requested (normal) command part for the A/C message. +/// @param[in] cmd Command to be set. +/// @note Only normal commands (=!isSpecialCommand()) can be set +/// with this function. +void IRFujitsuAC264::setCmd(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAc264CmdCool: + case kFujitsuAc264CmdHeat: + case kFujitsuAc264CmdDry: + case kFujitsuAc264CmdAuto: + case kFujitsuAc264CmdFan: + case kFujitsuAc264CmdSleepTime: + _cmd = _.Cmd = cmd; + _.SubCmd = 1; + break; + case kFujitsuAc264CmdTemp: + case kFujitsuAc264CmdSwing: + case kFujitsuAc264CmdEconomy: + case kFujitsuAc264CmdClean: + case kFujitsuAc264CmdFanSpeed: + case kFujitsuAc264CmdFanAngle: + case kFujitsuAc264CmdCancelSleepTimer: + case kFujitsuAc264CmdOnTimer: + case kFujitsuAc264CmdOffTimer: + case kFujitsuAc264CmdCancelOnOffTimer: + _cmd = _.Cmd = cmd; + _.SubCmd = 0; + break; + default: + break; + } +} + +/// Get the requested command part for the A/C message. +/// @return The command code. +uint8_t IRFujitsuAC264::getCmd(void) const { + return _cmd; +} + +/// 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 IRFujitsuAC264::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kFujitsuAc264ModeCool; + case stdAc::opmode_t::kHeat: return kFujitsuAc264ModeHeat; + case stdAc::opmode_t::kDry: return kFujitsuAc264ModeDry; + case stdAc::opmode_t::kFan: return kFujitsuAc264ModeFan; + default: return kFujitsuAc264ModeAuto; + } +} + +/// 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 IRFujitsuAC264::convertFanSpeed(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kFujitsuAc264FanSpeedQuiet; + case stdAc::fanspeed_t::kLow: return kFujitsuAc264FanSpeedLow; + case stdAc::fanspeed_t::kMedium: return kFujitsuAc264FanSpeedMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kFujitsuAc264FanSpeedHigh; + default: return kFujitsuAc264FanSpeedAuto; + } +} + +/// 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 IRFujitsuAC264::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAc264ModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAc264ModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAc264ModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAc264ModeFan: 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 IRFujitsuAC264::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAc264FanSpeedHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAc264FanSpeedMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAc264FanSpeedLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAc264FanSpeedQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRFujitsuAC264::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result{}; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::FUJITSU_AC264; + checkSum(); + result.power = _cmd != kFujitsuAc264SpCmdTurnOff; + // Only update these settings if it is not a special command message, + // or we have no previous state info for those settings. + if (!isSpecialCommand() || prev == NULL) { + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.FanSpeed); + result.clean = getClean(); + result.swingv = getSwing()? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.econo = getEconomy(); + result.clock = getClock(); + uint16_t sleep_time = getSleepTimer(); + result.sleep = sleep_time? sleep_time: -1; + } + result.quiet = getEcoFan(); + // Not supported. + result.turbo = false; + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.filter = false; + result.beep = false; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRFujitsuAC264::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + if (isSpecialCommand()) { // Special commands + result += kCommandStr; + result += kColonSpaceStr; + switch (_cmd) { + case kFujitsuAc264SpCmdTurnOff: + result += "Power Off"; + break; + case kFujitsuAc264SpCmdTogglePowerful: + result += kPowerfulStr; + break; + case kFujitsuAc264SpCmdEcoFanOff: + result += "Eco Fan "; + result += kOffStr; + break; + case kFujitsuAc264SpCmdEcoFanOn: + result += "Eco Fan "; + result += kOnStr; + break; + case kFujitsuAc264SpCmdOutsideQuietOff: + result += "Outside Quiet "; + result += kOffStr; + break; + case kFujitsuAc264SpCmdOutsideQuietOn: + result += "Outside Quiet "; + result += kOnStr; + break; + case kFujitsuAc264SpCmdToggleSterilization: + result += "Sterilization"; + break; + default: + result += kNAStr; + } + } else { // Normal commands + result += addBoolToString(true, kPowerStr, false); + // Mode + result += addModeToString(_.Mode, kFujitsuAc264ModeAuto, + kFujitsuAc264ModeCool, kFujitsuAc264ModeHeat, + kFujitsuAc264ModeDry, kFujitsuAc264ModeFan); + // Temp + float degrees = getTemp(); + result += addTempFloatToString(getTemp()); + // Temp in Auto + degrees = getTempAuto(); + String degrees_str = (degrees >= 0)? uint64ToString(degrees): + String(kDashStr) + uint64ToString(-degrees); + result += addLabeledString(degrees_str, "Temp (Auto)"); + if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); + result += 'C'; + // Fan Speed + result += addFanToString(_.FanSpeed, kFujitsuAc264FanSpeedHigh, + kFujitsuAc264FanSpeedLow, kFujitsuAc264FanSpeedAuto, + kFujitsuAc264FanSpeedQuiet, kFujitsuAc264FanSpeedMed); + // Fan Angle + result += addIntToString(_.FanAngle, "Fan Angle"); + result += kSpaceLBraceStr; + switch (_.FanAngle) { + case kFujitsuAc264FanAngle1: + result += kHighestStr; + break; + case kFujitsuAc264FanAngle2: + result += kHighStr; + break; + case kFujitsuAc264FanAngle4: + result += kMiddleStr; + break; + case kFujitsuAc264FanAngle6: + result += kLowStr; + break; + case kFujitsuAc264FanAngle7: + result += kLowestStr; + break; + case kFujitsuAc264FanAngle3: + result += kUpperStr; + result += ' '; + result += kMiddleStr; + break; + case kFujitsuAc264FanAngle5: + result += kLowerStr; + result += ' '; + result += kMiddleStr; + break; + case kFujitsuAc264FanAngleStay: + result += "Stay"; + break; + default: + result += kUnknownStr; + } + result += ')'; + // Swing + result += addBoolToString(getSwing(), kSwingStr); + // Low Power + result += addBoolToString(getEconomy(), "Economy"); + // Clean + result += addBoolToString(getClean(), kCleanStr); + // Cmd + result += kCommaSpaceStr; + result += kCommandStr; + result += kColonSpaceStr; + switch (_.Cmd) { + case kFujitsuAc264CmdCool: + result += kCoolStr; + break; + case kFujitsuAc264CmdHeat: + result += kHeatStr; + break; + case kFujitsuAc264CmdDry: + if (_.WeakDry) + result += "Weak "; + result += kDryStr; + break; + case kFujitsuAc264CmdAuto: + result += kAutoStr; + break; + case kFujitsuAc264CmdFan: + result += kFanStr; + break; + case kFujitsuAc264CmdTemp: + result += kTempStr; + break; + case kFujitsuAc264CmdSwing: + result += kSwingStr; + break; + case kFujitsuAc264CmdSleepTime: + result += kSleepTimerStr; + break; + case kFujitsuAc264CmdEconomy: + result += "Economy"; + break; + case kFujitsuAc264CmdClean: + result += kCleanStr; + break; + case kFujitsuAc264CmdFanSpeed: + result += "Fan Speed"; + break; + case kFujitsuAc264CmdFanAngle: + result += "Fan Angle"; + break; + case kFujitsuAc264CmdCancelSleepTimer: + result += "Cancel Sleep Timer"; + break; + case kFujitsuAc264CmdOffTimer: + result += kOffTimerStr; + break; + case kFujitsuAc264CmdOnTimer: + result += kOnTimerStr; + break; + case kFujitsuAc264CmdCancelOnOffTimer: + result += "Cancel On/Off Timer"; + break; + default: + result += kNAStr; + } + // Clock + uint16_t mins = 0; + mins = getClock(); + result += addLabeledString(mins ? minsToString(mins) : kNAStr, + "Current Time"); + // Sleep Timer + mins = getSleepTimer(); + result += addLabeledString(mins ? minsToString(mins) : kOffStr, + kSleepTimerStr); + // On/Off Timer + uint8_t timer_enable = getTimerEnable(); + mins = getOnTimer(); + result += addLabeledString((timer_enable & kFujitsuAc264OnTimerEnable)? + minsToString(mins * 10): kOffStr, kOnTimerStr); + mins = getOffTimer(); + result += addLabeledString((timer_enable & kFujitsuAc264OffTimerEnable)? + minsToString(mins * 10): kOffStr, kOffTimerStr); + } + return result; +} + +#if DECODE_FUJITSU_AC264 +/// Decode the supplied Fujitsu 264 bit AC IR message if possible. +/// Status: STABLE / Working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// 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 A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeFujitsuAC264(decode_results* results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + uint16_t dataBitsSoFar = 0; + uint8_t restLength = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAc264BitsShort) + kHeader + kFooter - 1 + + offset) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAc264Bits: + case kFujitsuAc264BitsMiddle: + case kFujitsuAc264BitsShort: break; + default: return false; // Must be called with the correct nr. of bits. + } + } + + // Header / Some of the Data + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kFujitsuAc264BitsShort, + kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header + kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + 0, 0, // No Footer (yet) + false, _tolerance + kFujitsuAcExtraTolerance, 0, + false); // LSBF + offset += used; + dataBitsSoFar += kFujitsuAc264BitsShort; + + // Check if it has the Fujitsu AC264 protocol header + if (results->state[0] != 0x14 || results->state[1] != 0x63 || + results->state[2] != 0x00 || results->state[3] != 0x10 || + results->state[4] != 0x10) + return false; + + // Identify which command it is + switch (results->state[5]) { + case 0xFE: // Command length is normal or middle + restLength = results->state[6]; + // check the rest length + if ((restLength != 0x1A) && (restLength != 0x09)) + return false; + break; + case 0x51: // Command length is short + case 0x50: // Command length is short + case 0x39: // Command length is short + case 0x02: // Command length is short + if (results->state[6] == (uint8_t)~results->state[5]) { // checksum + results->decode_type = FUJITSU_AC264; + results->bits = dataBitsSoFar; + return true; + } else { + return false; + } + default: + return false; + } + + // Keep reading bytes until we either run out of message or state to fill. + match_result_t data_result; + for (uint16_t i = kFujitsuAc264StateLengthShort; + offset <= results->rawlen - 16 && i < kFujitsuAc264StateLengthShort + + restLength; i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + _tolerance + kFujitsuAcExtraTolerance, 0, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAc264BitsMiddle: + case kFujitsuAc264Bits: + // Check if the state[5] is matched with the protocol. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + + if (!IRFujitsuAC264::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + results->decode_type = FUJITSU_AC264; + results->bits = dataBitsSoFar; + return true; // All good. +} +#endif // DECODE_FUJITSU_AC264 diff --git a/src/ir_Fujitsu.h b/src/ir_Fujitsu.h index 6e2583a0c..ef8f865bc 100644 --- a/src/ir_Fujitsu.h +++ b/src/ir_Fujitsu.h @@ -1,266 +1,544 @@ -// Copyright 2017 Jonny Graham -// Copyright 2018-2022 David Conran -// Copyright 2021 siriuslzx - -/// @file -/// @brief Support for Fujitsu A/C protocols. -/// Fujitsu A/C support added by Jonny Graham -/// @warning Use of incorrect model may cause the A/C unit to lock up. -/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical -/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. -/// The correct model for it is ARREB1E. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 - -// Supports: -// Brand: Fujitsu, Model: AR-RAH2E remote (ARRAH2E) -// Brand: Fujitsu, Model: ASYG30LFCA A/C (ARRAH2E) -// Brand: Fujitsu General, Model: AR-RCE1E remote (ARRAH2E) -// Brand: Fujitsu General, Model: ASHG09LLCA A/C (ARRAH2E) -// Brand: Fujitsu General, Model: AOHG09LLC A/C (ARRAH2E) -// Brand: Fujitsu, Model: AR-DB1 remote (ARDB1) -// Brand: Fujitsu, Model: AST9RSGCW A/C (ARDB1) -// Brand: Fujitsu, Model: AR-REB1E remote (ARREB1E) -// Brand: Fujitsu, Model: ASYG7LMCA A/C (ARREB1E) -// Brand: Fujitsu, Model: AR-RAE1E remote (ARRAH2E) -// Brand: Fujitsu, Model: AGTV14LAC A/C (ARRAH2E) -// Brand: Fujitsu, Model: AR-RAC1E remote (ARRAH2E) -// Brand: Fujitsu, Model: ASTB09LBC A/C (ARRY4) -// Brand: Fujitsu, Model: AR-RY4 remote (ARRY4) -// Brand: Fujitsu General, Model: AR-JW2 remote (ARJW2) -// Brand: Fujitsu, Model: AR-DL10 remote (ARDB1) -// Brand: Fujitsu, Model: ASU30C1 A/C (ARDB1) -// Brand: Fujitsu, Model: AR-RAH1U remote (ARREB1E) -// Brand: Fujitsu, Model: AR-RAH2U remote (ARRAH2E) -// Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E) -// Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E) -// Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E) -// Brand: Fujitsu, Model: AR-REB4E remote (ARREB1E) -// Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E) -// Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E) -// Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E) -// Brand: Fujitsu, Model: AR-REG1U remote (ARRAH2E) -// Brand: OGeneral, Model: AR-RCL1E remote (ARRAH2E) -// Brand: Fujitsu General, Model: AR-JW17 remote (ARDB1) - -#ifndef IR_FUJITSU_H_ -#define IR_FUJITSU_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifdef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif - -/// Native representation of a Fujitsu A/C message. -union FujitsuProtocol { - struct { - uint8_t longcode[kFujitsuAcStateLength]; ///< The state of the IR remote. - uint8_t shortcode[kFujitsuAcStateLengthShort]; - }; - struct { - // Byte 0~1 - uint64_t :16; // Fixed header - // Byte 2 - uint64_t :4; - uint64_t Id :2; // Device Number/Identifier - uint64_t :2; - // Byte 3-4 - uint64_t :16; - // Byte 5 - uint64_t Cmd :8; // short codes:cmd; long codes:fixed value - // Byte 6 - uint64_t RestLength :8; // Nr. of bytes in the message after this byte. - // Byte 7 - uint64_t Protocol :8; // Seems like a protocol version number. Not sure. - // Byte 8 - uint64_t Power :1; - uint64_t Fahrenheit :1; - uint64_t Temp :6; // Internal representation varies between models. - // Byte 9 - uint64_t Mode :3; - uint64_t Clean :1; // Also 10C Heat in ARREW4E. - uint64_t TimerType :2; - uint64_t :2; - // Byte 10 - uint64_t Fan :3; - uint64_t :1; - uint64_t Swing :2; - uint64_t :2; - // Byte 11~13 - uint64_t OffTimer :11; // Also is the sleep timer value - uint64_t OffTimerEnable :1; - uint64_t OnTimer :11; - uint64_t OnTimerEnable :1; - // Byte 14 - uint64_t :3; - uint64_t Filter :1; - uint64_t :1; - uint64_t unknown :1; - uint64_t :1; - uint64_t OutsideQuiet :1; - // Byte 15 - uint64_t :0; // Checksum - }; -}; - -// Constants -const uint8_t kFujitsuAcModeAuto = 0x0; // 0b000 -const uint8_t kFujitsuAcModeCool = 0x1; // 0b001 -const uint8_t kFujitsuAcModeDry = 0x2; // 0b010 -const uint8_t kFujitsuAcModeFan = 0x3; // 0b011 -const uint8_t kFujitsuAcModeHeat = 0x4; // 0b100 - -const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000 -const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001 -const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010 -const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001 -const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001 -const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100 -const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101 -const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001 -const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010 - -const uint8_t kFujitsuAcFanAuto = 0x00; -const uint8_t kFujitsuAcFanHigh = 0x01; -const uint8_t kFujitsuAcFanMed = 0x02; -const uint8_t kFujitsuAcFanLow = 0x03; -const uint8_t kFujitsuAcFanQuiet = 0x04; - -const float kFujitsuAcMinHeat = 10; // 10C -const float kFujitsuAcMinTemp = 16; // 16C -const float kFujitsuAcMaxTemp = 30; // 30C -const uint8_t kFujitsuAcTempOffsetC = kFujitsuAcMinTemp; -const float kFujitsuAcMinHeatF = 50; // 50F -const float kFujitsuAcMinTempF = 60; // 60F -const float kFujitsuAcMaxTempF = 88; // 88F -const uint8_t kFujitsuAcTempOffsetF = 44; - -const uint8_t kFujitsuAcSwingOff = 0x00; -const uint8_t kFujitsuAcSwingVert = 0x01; -const uint8_t kFujitsuAcSwingHoriz = 0x02; -const uint8_t kFujitsuAcSwingBoth = 0x03; - -const uint8_t kFujitsuAcStopTimers = 0b00; // 0 -const uint8_t kFujitsuAcSleepTimer = 0b01; // 1 -const uint8_t kFujitsuAcOffTimer = 0b10; // 2 -const uint8_t kFujitsuAcOnTimer = 0b11; // 3 -const uint16_t kFujitsuAcTimerMax = 12 * 60; ///< Minutes. - -// Legacy defines. -#define FUJITSU_AC_MODE_AUTO kFujitsuAcModeAuto -#define FUJITSU_AC_MODE_COOL kFujitsuAcModeCool -#define FUJITSU_AC_MODE_DRY kFujitsuAcModeDry -#define FUJITSU_AC_MODE_FAN kFujitsuAcModeFan -#define FUJITSU_AC_MODE_HEAT kFujitsuAcModeHeat -#define FUJITSU_AC_CMD_STAY_ON kFujitsuAcCmdStayOn -#define FUJITSU_AC_CMD_TURN_ON kFujitsuAcCmdTurnOn -#define FUJITSU_AC_CMD_TURN_OFF kFujitsuAcCmdTurnOff -#define FUJITSU_AC_CMD_STEP_HORIZ kFujitsuAcCmdStepHoriz -#define FUJITSU_AC_CMD_STEP_VERT kFujitsuAcCmdStepVert -#define FUJITSU_AC_FAN_AUTO kFujitsuAcFanAuto -#define FUJITSU_AC_FAN_HIGH kFujitsuAcFanHigh -#define FUJITSU_AC_FAN_MED kFujitsuAcFanMed -#define FUJITSU_AC_FAN_LOW kFujitsuAcFanLow -#define FUJITSU_AC_FAN_QUIET kFujitsuAcFanQuiet -#define FUJITSU_AC_MIN_TEMP kFujitsuAcMinTemp -#define FUJITSU_AC_MAX_TEMP kFujitsuAcMaxTemp -#define FUJITSU_AC_SWING_OFF kFujitsuAcSwingOff -#define FUJITSU_AC_SWING_VERT kFujitsuAcSwingVert -#define FUJITSU_AC_SWING_HORIZ kFujitsuAcSwingHoriz -#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth - -/// Class for handling detailed Fujitsu A/C messages. -class IRFujitsuAC { - public: - explicit IRFujitsuAC(const uint16_t pin, - const fujitsu_ac_remote_model_t model = ARRAH2E, - const bool inverted = false, - const bool use_modulation = true); - void setModel(const fujitsu_ac_remote_model_t model); - fujitsu_ac_remote_model_t getModel(void) const; - void stateReset(void); -#if SEND_FUJITSU_AC - void send(const uint16_t repeat = kFujitsuAcMinRepeat); - /// 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_FUJITSU_AC - void begin(void); - void stepHoriz(void); - void toggleSwingHoriz(const bool update = true); - void stepVert(void); - void toggleSwingVert(const bool update = true); - void setCmd(const uint8_t cmd); - uint8_t getCmd(void) const; - void setTemp(const float temp, const bool useCelsius = true); - float getTemp(void) const; - void setFanSpeed(const uint8_t fan); - uint8_t getFanSpeed(void) const; - void setMode(const uint8_t mode); - uint8_t getMode(void) const; - void setSwing(const uint8_t mode); - uint8_t getSwing(void) const; - uint8_t* getRaw(void); - bool setRaw(const uint8_t newState[], const uint16_t length); - uint8_t getStateLength(void); - static bool validChecksum(uint8_t* state, const uint16_t length); - bool isLongCode(void) const; - void setPower(const bool on); - void off(void); - void on(void); - bool getPower(void) const; - void setClean(const bool on); - bool getClean(void) const; - void setFilter(const bool on); - bool getFilter(void) const; - void set10CHeat(const bool on); - bool get10CHeat(void) const; - void setOutsideQuiet(const bool on); - bool getOutsideQuiet(void) const; - uint8_t getTimerType(void) const; - void setTimerType(const uint8_t timertype); - uint16_t getOnTimer(void) const; - void setOnTimer(const uint16_t nr_mins); - uint16_t getOffSleepTimer(void) const; - void setOffTimer(const uint16_t nr_mins); - void setSleepTimer(const uint16_t nr_mins); - void setId(const uint8_t num); - uint8_t getId(void) const; - void setCelsius(const bool on); - bool getCelsius(void) const; - static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFan(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(const stdAc::state_t *prev = NULL); - String toString(void) const; -#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 - FujitsuProtocol _; - uint8_t _cmd; - fujitsu_ac_remote_model_t _model; - uint8_t _state_length; - uint8_t _state_length_short; - bool _rawstatemodified; - void checkSum(void); - bool updateUseLongOrShort(void); - void buildFromState(const uint16_t length); - void setOffSleepTimer(const uint16_t nr_mins); -}; - -#endif // IR_FUJITSU_H_ +// Copyright 2017 Jonny Graham +// Copyright 2018-2022 David Conran +// Copyright 2021 siriuslzx +// Copyright 2023 Takeshi Shimizu + +/// @file +/// @brief Support for Fujitsu A/C protocols. +/// Fujitsu A/C support added by Jonny Graham +/// @warning Use of incorrect model may cause the A/C unit to lock up. +/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical +/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. +/// The correct model for it is ARREB1E. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 + +// Supports: +// Brand: Fujitsu, Model: AR-RAH2E remote (ARRAH2E) +// Brand: Fujitsu, Model: ASYG30LFCA A/C (ARRAH2E) +// Brand: Fujitsu General, Model: AR-RCE1E remote (ARRAH2E) +// Brand: Fujitsu General, Model: ASHG09LLCA A/C (ARRAH2E) +// Brand: Fujitsu General, Model: AOHG09LLC A/C (ARRAH2E) +// Brand: Fujitsu, Model: AR-DB1 remote (ARDB1) +// Brand: Fujitsu, Model: AST9RSGCW A/C (ARDB1) +// Brand: Fujitsu, Model: AR-REB1E remote (ARREB1E) +// Brand: Fujitsu, Model: ASYG7LMCA A/C (ARREB1E) +// Brand: Fujitsu, Model: AR-RAE1E remote (ARRAH2E) +// Brand: Fujitsu, Model: AGTV14LAC A/C (ARRAH2E) +// Brand: Fujitsu, Model: AR-RAC1E remote (ARRAH2E) +// Brand: Fujitsu, Model: ASTB09LBC A/C (ARRY4) +// Brand: Fujitsu, Model: AR-RY4 remote (ARRY4) +// Brand: Fujitsu General, Model: AR-JW2 remote (ARJW2) +// Brand: Fujitsu, Model: AR-DL10 remote (ARDB1) +// Brand: Fujitsu, Model: ASU30C1 A/C (ARDB1) +// Brand: Fujitsu, Model: AR-RAH1U remote (ARREB1E) +// Brand: Fujitsu, Model: AR-RAH2U remote (ARRAH2E) +// Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E) +// Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E) +// Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E) +// Brand: Fujitsu, Model: AR-REB4E remote (ARREB1E) +// Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E) +// Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E) +// Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E) +// Brand: Fujitsu, Model: AR-REG1U remote (ARRAH2E) +// Brand: OGeneral, Model: AR-RCL1E remote (ARRAH2E) +// Brand: Fujitsu General, Model: AR-JW17 remote (ARDB1) +// Brand: Fujitsu, Model: AS-AH402M A/C (FUJITSU_AC264 AR-RLB2J) + +#ifndef IR_FUJITSU_H_ +#define IR_FUJITSU_H_ + +#define __STDC_LIMIT_MACROS +#include +#include +#ifdef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +/// Native representation of a Fujitsu A/C message. +union FujitsuProtocol { + struct { + uint8_t longcode[kFujitsuAcStateLength]; ///< The state of the IR remote. + uint8_t shortcode[kFujitsuAcStateLengthShort]; + }; + struct { + // Byte 0~1 + uint64_t :16; // Fixed header + // Byte 2 + uint64_t :4; + uint64_t Id :2; // Device Number/Identifier + uint64_t :2; + // Byte 3-4 + uint64_t :16; + // Byte 5 + uint64_t Cmd :8; // short codes:cmd; long codes:fixed value + // Byte 6 + uint64_t RestLength :8; // Nr. of bytes in the message after this byte. + // Byte 7 + uint64_t Protocol :8; // Seems like a protocol version number. Not sure. + // Byte 8 + uint64_t Power :1; + uint64_t Fahrenheit :1; + uint64_t Temp :6; // Internal representation varies between models. + // Byte 9 + uint64_t Mode :3; + uint64_t Clean :1; // Also 10C Heat in ARREW4E. + uint64_t TimerType :2; + uint64_t :2; + // Byte 10 + uint64_t Fan :3; + uint64_t :1; + uint64_t Swing :2; + uint64_t :2; + // Byte 11~13 + uint64_t OffTimer :11; // Also is the sleep timer value + uint64_t OffTimerEnable :1; + uint64_t OnTimer :11; + uint64_t OnTimerEnable :1; + // Byte 14 + uint64_t :3; + uint64_t Filter :1; + uint64_t :1; + uint64_t unknown :1; + uint64_t :1; + uint64_t OutsideQuiet :1; + // Byte 15 + uint64_t :0; // Checksum + }; +}; + +// Constants +const uint8_t kFujitsuAcModeAuto = 0x0; // 0b000 +const uint8_t kFujitsuAcModeCool = 0x1; // 0b001 +const uint8_t kFujitsuAcModeDry = 0x2; // 0b010 +const uint8_t kFujitsuAcModeFan = 0x3; // 0b011 +const uint8_t kFujitsuAcModeHeat = 0x4; // 0b100 + +const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000 +const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001 +const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010 +const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001 +const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001 +const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100 +const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101 +const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001 +const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010 + +const uint8_t kFujitsuAcFanAuto = 0x00; +const uint8_t kFujitsuAcFanHigh = 0x01; +const uint8_t kFujitsuAcFanMed = 0x02; +const uint8_t kFujitsuAcFanLow = 0x03; +const uint8_t kFujitsuAcFanQuiet = 0x04; + +const float kFujitsuAcMinHeat = 10; // 10C +const float kFujitsuAcMinTemp = 16; // 16C +const float kFujitsuAcMaxTemp = 30; // 30C +const uint8_t kFujitsuAcTempOffsetC = kFujitsuAcMinTemp; +const float kFujitsuAcMinHeatF = 50; // 50F +const float kFujitsuAcMinTempF = 60; // 60F +const float kFujitsuAcMaxTempF = 88; // 88F +const uint8_t kFujitsuAcTempOffsetF = 44; + +const uint8_t kFujitsuAcSwingOff = 0x00; +const uint8_t kFujitsuAcSwingVert = 0x01; +const uint8_t kFujitsuAcSwingHoriz = 0x02; +const uint8_t kFujitsuAcSwingBoth = 0x03; + +const uint8_t kFujitsuAcStopTimers = 0b00; // 0 +const uint8_t kFujitsuAcSleepTimer = 0b01; // 1 +const uint8_t kFujitsuAcOffTimer = 0b10; // 2 +const uint8_t kFujitsuAcOnTimer = 0b11; // 3 +const uint16_t kFujitsuAcTimerMax = 12 * 60; ///< Minutes. + +/// Native representation of a Fujitsu 264 bit A/C message. +union Fujitsu264Protocol { + struct { + uint8_t raw[kFujitsuAc264StateLength]; ///< The state of the IR remote. + }; + struct { + uint8_t middlecode[kFujitsuAc264StateLengthMiddle]; + uint8_t shortcode[kFujitsuAc264StateLengthShort]; + }; + struct { + // Byte 0~1 + uint8_t :8; // Fixed header + uint8_t :8; // Fixed header + // Byte 2 + uint8_t :8; + // Byte 3-4 + uint8_t :8; + uint8_t :8; + // Byte 5 + uint8_t :8; + // Byte 6 + uint8_t RestLength :8; // Nr. of bytes in the message after this byte. + // Byte 7 + uint8_t Protocol :8; // Seems like a protocol version number. + // Byte 8 + uint64_t SubCmd :1; + uint64_t :1; + uint64_t Temp :6; // Temperature in modes except auto + // Byte 9 + uint64_t Mode :4; + uint64_t SleepTimerEnable :1; + uint64_t :3; + // Byte 10 + uint64_t FanSpeed :4; + uint64_t Swing :1; + uint64_t :3; + // Byte 11~12 + uint64_t SleepTimer :12; + uint64_t :4; + // Byte 13 + uint64_t :8; + // Byte 14 + uint64_t TempAuto :8; // Temperature in auto mode + // Byte 15 + uint64_t Economy :1; + uint64_t :7; + // Byte 16 + uint8_t Clean :1; + uint8_t :7; + // Byte 17 + uint8_t :8; + // Byte 18 + uint8_t Cmd :8; + // Byte 19 + uint8_t ClockHours :8; + // Byte 20 + uint8_t ClockMins :8; + // Byte 21 + uint8_t :5; + uint8_t WeakDry :1; // Valid only when mode = dry + uint8_t :2; + // Byte 22 + uint8_t TimerEnable :4; + uint8_t :4; + // Byte 23 + uint8_t OnTimer :8; + // Byte 24 + uint8_t OffTimer :8; + // Byte 25~27 + uint8_t :8; + uint8_t :8; + uint8_t :8; + // Byte 28 + uint8_t FanAngle :4; + uint8_t :4; + // Byte 29~31 + uint8_t :8; + uint8_t :8; + uint8_t :8; + // Byte 32 + uint8_t CheckSum :8; + }; +}; + +// Constants +const uint8_t kFujitsuAc264ModeAuto = 0x0; // 0b0000 +const uint8_t kFujitsuAc264ModeCool = 0x1; // 0b0001 +const uint8_t kFujitsuAc264ModeFan = 0x3; // 0b0011 +const uint8_t kFujitsuAc264ModeHeat = 0x4; // 0b0100 +const uint8_t kFujitsuAc264ModeDry = 0x5; // 0b0101 + +const uint8_t kFujitsuAc264CmdCool = 0x01; // 0b00000001 +const uint8_t kFujitsuAc264CmdHeat = 0x02; // 0b00000010 +const uint8_t kFujitsuAc264CmdDry = 0x03; // 0b00000011 +const uint8_t kFujitsuAc264CmdAuto = 0x04; // 0b00000100 +const uint8_t kFujitsuAc264CmdFan = 0x05; // 0b00000101 +const uint8_t kFujitsuAc264CmdTemp = 0x07; // 0b00000111 +const uint8_t kFujitsuAc264CmdSwing = 0x0B; // 0b00001011 +const uint8_t kFujitsuAc264CmdSleepTime = 0x0E; // 0b00001110 +const uint8_t kFujitsuAc264CmdEconomy = 0x14; // 0b00010100 +const uint8_t kFujitsuAc264CmdClean = 0x1B; // 0b00011011 +const uint8_t kFujitsuAc264CmdFanSpeed = 0x1E; // 0b00011110 +const uint8_t kFujitsuAc264CmdFanAngle = 0x22; // 0b00100010 +const uint8_t kFujitsuAc264CmdCancelSleepTimer = 0x30; // 0b00110000 +const uint8_t kFujitsuAc264CmdOnTimer = 0x39; // 0b00111001 +const uint8_t kFujitsuAc264CmdOffTimer = 0x3A; // 0b00111010 +const uint8_t kFujitsuAc264CmdCancelOnOffTimer = 0x3B; // 0b00111011 + +const uint8_t kFujitsuAc264SpCmdTogglePowerful = 0xF0; // 0b11110000 +const uint8_t kFujitsuAc264SpCmdTurnOff = 0xF1; // 0b11110001 +const uint8_t kFujitsuAc264SpCmdEcoFanOff = 0xF2; // 0b11110010 +const uint8_t kFujitsuAc264SpCmdEcoFanOn = 0xF3; // 0b11110011 +const uint8_t kFujitsuAc264SpCmdOutsideQuietOff = 0xF4; // 0b11110100 +const uint8_t kFujitsuAc264SpCmdOutsideQuietOn = 0xF5; // 0b11110101 +const uint8_t kFujitsuAc264SpCmdToggleSterilization = 0xF6; // 0b11110110 + +const uint8_t kFujitsuAc264FanSpeedAuto = 0x0; // 0b0000 +const uint8_t kFujitsuAc264FanSpeedQuiet = 0x1; // 0b0001 +const uint8_t kFujitsuAc264FanSpeedLow = 0x3; // 0b0011 +const uint8_t kFujitsuAc264FanSpeedMed = 0x6; // 0b0110 +const uint8_t kFujitsuAc264FanSpeedHigh = 0x8; // 0b1000 + +const uint8_t kFujitsuAc264FanAngle1 = 0x1; // 0b0001 +const uint8_t kFujitsuAc264FanAngle2 = 0x2; // 0b0010 +const uint8_t kFujitsuAc264FanAngle3 = 0x3; // 0b0011 +const uint8_t kFujitsuAc264FanAngle4 = 0x4; // 0b0100 +const uint8_t kFujitsuAc264FanAngle5 = 0x5; // 0b0101 +const uint8_t kFujitsuAc264FanAngle6 = 0x6; // 0b0110 +const uint8_t kFujitsuAc264FanAngle7 = 0x7; // 0b0111 +const uint8_t kFujitsuAc264FanAngleStay = 0xF; // 0b1111 + +const uint8_t kFujitsuAc264MinHeat = 16; // 16C +const uint8_t kFujitsuAc264MinTemp = 18; // 18C +const uint8_t kFujitsuAc264MaxTemp = 30; // 30C +const uint8_t kFujitsuAc264TempOffsetC = 16; +const int8_t kFujitsuAc264MinTempAuto = -2; // -2C +const int8_t kFujitsuAc264MaxTempAuto = 2; // 2C + +const uint16_t kFujitsuAc264SleepTimerMax = 12 * 60; ///< Minutes. +const uint8_t kFujitsuAc264OnOffTimerDisable = 0x0; // 0b0000 +const uint8_t kFujitsuAc264OnTimerEnable = 0x1; // 0b0001 +const uint8_t kFujitsuAc264OffTimerEnable = 0x2; // 0b0010 +const uint8_t kFujitsuAc264OnOffTimerEnable = 0x3; // 0b0011 +const uint16_t kFujitsuAc26OnOffTimerMax = 24 * 6 - 1; ///< 10 Minutes. + +/// Special command for Power Off +const uint8_t kFujitsuAc264StatesTurnOff + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD}; +/// Special command for Toggle Powerful +const uint8_t kFujitsuAc264StatesTogglePowerful + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; +/// Special command for Eco Fan Off +const uint8_t kFujitsuAc264StatesEcoFanOff + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x51, 0xAE}; +/// Special command for Eco Fan On +const uint8_t kFujitsuAc264StatesEcoFanOn + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x50, 0xAF}; +/// Special command for Outside Quiet Off +/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, +/// but has different meaning. +const uint8_t kFujitsuAc264StatesOutsideQuietOff + [kFujitsuAc264StateLengthMiddle] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x01, 0x00, 0x00, 0xFE, 0xBF, 0x00, 0x41}; +/// Special command for Outside Quiet Off +/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, +/// but has different meaning. +const uint8_t kFujitsuAc264StatesOutsideQuietOn + [kFujitsuAc264StateLengthMiddle] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x00, 0x00, 0x00, 0xFF, 0xBF, 0x00, 0x41}; +/// Special command for Toggle Sterilization +/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, +/// but has different meaning. +const uint8_t kFujitsuAc264StatesToggleSterilization + [kFujitsuAc264StateLengthMiddle] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x60, 0x03, 0x00, 0x00, 0xFC, 0x9F, 0x00, 0x41}; + +// Legacy defines. +#define FUJITSU_AC_MODE_AUTO kFujitsuAcModeAuto +#define FUJITSU_AC_MODE_COOL kFujitsuAcModeCool +#define FUJITSU_AC_MODE_DRY kFujitsuAcModeDry +#define FUJITSU_AC_MODE_FAN kFujitsuAcModeFan +#define FUJITSU_AC_MODE_HEAT kFujitsuAcModeHeat +#define FUJITSU_AC_CMD_STAY_ON kFujitsuAcCmdStayOn +#define FUJITSU_AC_CMD_TURN_ON kFujitsuAcCmdTurnOn +#define FUJITSU_AC_CMD_TURN_OFF kFujitsuAcCmdTurnOff +#define FUJITSU_AC_CMD_STEP_HORIZ kFujitsuAcCmdStepHoriz +#define FUJITSU_AC_CMD_STEP_VERT kFujitsuAcCmdStepVert +#define FUJITSU_AC_FAN_AUTO kFujitsuAcFanAuto +#define FUJITSU_AC_FAN_HIGH kFujitsuAcFanHigh +#define FUJITSU_AC_FAN_MED kFujitsuAcFanMed +#define FUJITSU_AC_FAN_LOW kFujitsuAcFanLow +#define FUJITSU_AC_FAN_QUIET kFujitsuAcFanQuiet +#define FUJITSU_AC_MIN_TEMP kFujitsuAcMinTemp +#define FUJITSU_AC_MAX_TEMP kFujitsuAcMaxTemp +#define FUJITSU_AC_SWING_OFF kFujitsuAcSwingOff +#define FUJITSU_AC_SWING_VERT kFujitsuAcSwingVert +#define FUJITSU_AC_SWING_HORIZ kFujitsuAcSwingHoriz +#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth + +/// Class for handling detailed Fujitsu A/C messages. +class IRFujitsuAC { + public: + explicit IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model = ARRAH2E, + const bool inverted = false, + const bool use_modulation = true); + void setModel(const fujitsu_ac_remote_model_t model); + fujitsu_ac_remote_model_t getModel(void) const; + void stateReset(void); +#if SEND_FUJITSU_AC + void send(const uint16_t repeat = kFujitsuAcMinRepeat); + /// 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_FUJITSU_AC + void begin(void); + void stepHoriz(void); + void toggleSwingHoriz(const bool update = true); + void stepVert(void); + void toggleSwingVert(const bool update = true); + void setCmd(const uint8_t cmd); + uint8_t getCmd(void) const; + void setTemp(const float temp, const bool useCelsius = true); + float getTemp(void) const; + void setFanSpeed(const uint8_t fan); + uint8_t getFanSpeed(void) const; + void setMode(const uint8_t mode); + uint8_t getMode(void) const; + void setSwing(const uint8_t mode); + uint8_t getSwing(void) const; + uint8_t* getRaw(void); + bool setRaw(const uint8_t newState[], const uint16_t length); + uint8_t getStateLength(void); + static bool validChecksum(uint8_t* state, const uint16_t length); + bool isLongCode(void) const; + void setPower(const bool on); + void off(void); + void on(void); + bool getPower(void) const; + void setClean(const bool on); + bool getClean(void) const; + void setFilter(const bool on); + bool getFilter(void) const; + void set10CHeat(const bool on); + bool get10CHeat(void) const; + void setOutsideQuiet(const bool on); + bool getOutsideQuiet(void) const; + uint8_t getTimerType(void) const; + void setTimerType(const uint8_t timertype); + uint16_t getOnTimer(void) const; + void setOnTimer(const uint16_t nr_mins); + uint16_t getOffSleepTimer(void) const; + void setOffTimer(const uint16_t nr_mins); + void setSleepTimer(const uint16_t nr_mins); + void setId(const uint8_t num); + uint8_t getId(void) const; + void setCelsius(const bool on); + bool getCelsius(void) const; + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(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(const stdAc::state_t *prev = NULL); + String toString(void) const; +#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 + FujitsuProtocol _; + uint8_t _cmd; + fujitsu_ac_remote_model_t _model; + uint8_t _state_length; + uint8_t _state_length_short; + bool _rawstatemodified; + void checkSum(void); + bool updateUseLongOrShort(void); + void buildFromState(const uint16_t length); + void setOffSleepTimer(const uint16_t nr_mins); +}; + +/// Class for handling detailed Fujitsu 264 bit A/C messages. +class IRFujitsuAC264 { + public: + explicit IRFujitsuAC264(const uint16_t pin, + const bool inverted = false, + const bool use_modulation = true); +#if SEND_FUJITSU_AC264 + void send(const uint16_t repeat = kFujitsuAc264DefaultRepeat); + /// 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_FUJITSU_AC264 + void begin(void); + uint8_t* getRaw(void); + bool setRaw(const uint8_t newState[], const uint16_t length); + static bool validChecksum(uint8_t state[], + const uint16_t length = kFujitsuAc264StateLength); + + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void) const; + bool isTempStayed(void) const; + void setTemp(const float temp); + float getTemp(void) const; + void setTempAuto(const float temp); + float getTempAuto(void) const; + void setMode(const uint8_t mode, const bool weakdry = false); + uint8_t getMode(void) const; + bool isWeakDry(void) const; + void setFanSpeed(const uint8_t fanSpeed); + uint8_t getFanSpeed(void) const; + void setFanAngle(const uint8_t fanAngle); + uint8_t getFanAngle(void) const; + void setSwing(const bool on); + bool getSwing(void) const; + void setEconomy(const bool on); + bool getEconomy(void) const; + void setClean(const bool on); + bool getClean(void) const; + + void toggleSterilization(void); + void setOutsideQuiet(const bool on); + bool getOutsideQuiet(void) const; + void setEcoFan(const bool on); + bool getEcoFan(void) const; + void togglePowerful(void); + + void setClock(const uint16_t mins_since_midnight); + uint16_t getClock(void) const; + void setSleepTimer(const uint16_t mins); + uint16_t getSleepTimer(void) const; + void setTimerEnable(const uint8_t timer_enable); + uint8_t getTimerEnable(void) const; + void setOnTimer(const uint8_t mins10); + uint8_t getOnTimer(void) const; + void setOffTimer(const uint8_t mins10); + uint8_t getOffTimer(void) const; + + void setCmd(const uint8_t cmd); + uint8_t getCmd(void) const; + uint8_t getStateLength(void); + + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFanSpeed(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(const stdAc::state_t *prev = NULL); + String toString(void) const; + +#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 + Fujitsu264Protocol _; + uint8_t _cmd; + bool _ispoweredon; + bool _isecofan; + bool _isoutsidequiet; + uint8_t _settemp; + void stateReset(void); + void checkSum(void); + bool isSpecialCommand(void) const; +}; + +#endif // IR_FUJITSU_H_ diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 438cc5da3..77cfc97d3 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -1,1148 +1,1151 @@ -// Copyright 2019 - David Conran (@crankyoldgit) -// The default text to use throughout the library. -// The library will use this text if no locale (_IR_LOCALE_) is set or if -// the locale doesn't define particular values. -// If they are defined, this file should NOT override them. -// -// This file should contain a #define for every translateable/locale dependant -// string used by the library. Language specific files don't have to include -// everything. -// -// NOTE: ASCII/UTF-8 characters only. Unicode is NOT supported. -// -// The defaults are English (AU) / en-AU. Australia (AU) is pretty much the same -// as English (UK) for this libraries use case. -#ifndef LOCALE_DEFAULTS_H_ -#define LOCALE_DEFAULTS_H_ - -#ifndef D_STR_UNKNOWN -#define D_STR_UNKNOWN "UNKNOWN" -#endif // D_STR_UNKNOWN -#ifndef D_STR_PROTOCOL -#define D_STR_PROTOCOL "Protocol" -#endif // D_STR_PROTOCOL -#ifndef D_STR_POWER -#define D_STR_POWER "Power" -#endif // D_STR_POWER -#ifndef D_STR_PREVIOUS -#define D_STR_PREVIOUS "Previous" -#endif // D_STR_PREVIOUS -#ifndef D_STR_ON -#define D_STR_ON "On" -#endif // D_STR_ON -#ifndef D_STR_1 -#define D_STR_1 "1" -#endif // D_STR_1 -#ifndef D_STR_OFF -#define D_STR_OFF "Off" -#endif // D_STR_OFF -#ifndef D_STR_0 -#define D_STR_0 "0" -#endif // D_STR_0 -#ifndef D_STR_MODE -#define D_STR_MODE "Mode" -#endif // D_STR_MODE -#ifndef D_STR_TOGGLE -#define D_STR_TOGGLE "Toggle" -#endif // D_STR_TOGGLE -#ifndef D_STR_TURBO -#define D_STR_TURBO "Turbo" -#endif // D_STR_TURBO -#ifndef D_STR_SUPER -#define D_STR_SUPER "Super" -#endif // D_STR_SUPER -#ifndef D_STR_SLEEP -#define D_STR_SLEEP "Sleep" -#endif // D_STR_SLEEP -#ifndef D_STR_LIGHT -#define D_STR_LIGHT "Light" -#endif // D_STR_LIGHT -#ifndef D_STR_POWERFUL -#define D_STR_POWERFUL "Powerful" -#endif // D_STR_POWERFUL -#ifndef D_STR_QUIET -#define D_STR_QUIET "Quiet" -#endif // D_STR_QUIET -#ifndef D_STR_ECONO -#define D_STR_ECONO "Econo" -#endif // D_STR_ECONO -#ifndef D_STR_SWING -#define D_STR_SWING "Swing" -#endif // D_STR_SWING -#ifndef D_STR_SWINGH -#define D_STR_SWINGH D_STR_SWING"(H)" // Set `D_STR_SWING` first! -#endif // D_STR_SWINGH -#ifndef D_STR_SWINGV -#define D_STR_SWINGV D_STR_SWING"(V)" // Set `D_STR_SWING` first! -#endif // D_STR_SWINGV -#ifndef D_STR_BEEP -#define D_STR_BEEP "Beep" -#endif // D_STR_BEEP -#ifndef D_STR_MOULD -#define D_STR_MOULD "Mould" -#endif // D_STR_MOULD -#ifndef D_STR_CLEAN -#define D_STR_CLEAN "Clean" -#endif // D_STR_CLEAN -#ifndef D_STR_PURIFY -#define D_STR_PURIFY "Purify" -#endif // D_STR_PURIFY -#ifndef D_STR_TIMER -#define D_STR_TIMER "Timer" -#endif // D_STR_TIMER -#ifndef D_STR_ONTIMER -#define D_STR_ONTIMER D_STR_ON " " D_STR_TIMER // Set `D_STR_ON` first! -#endif // D_STR_ONTIMER -#ifndef D_STR_OFFTIMER -#define D_STR_OFFTIMER D_STR_OFF " " D_STR_TIMER // Set `D_STR_OFF` first! -#endif // D_STR_OFFTIMER -#ifndef D_STR_TIMERMODE -#define D_STR_TIMERMODE D_STR_TIMER " " D_STR_MODE // Set `D_STR_MODE` first! -#endif // D_STR_TIMERMODE -#ifndef D_STR_CLOCK -#define D_STR_CLOCK "Clock" -#endif // D_STR_CLOCK -#ifndef D_STR_COMMAND -#define D_STR_COMMAND "Command" -#endif // D_STR_COMMAND -#ifndef D_STR_XFAN -#define D_STR_XFAN "XFan" -#endif // D_STR_XFAN -#ifndef D_STR_HEALTH -#define D_STR_HEALTH "Health" -#endif // D_STR_HEALTH -#ifndef D_STR_MODEL -#define D_STR_MODEL "Model" -#endif // D_STR_MODEL -#ifndef D_STR_TEMP -#define D_STR_TEMP "Temp" -#endif // D_STR_TEMP -#ifndef D_STR_IFEEL -#define D_STR_IFEEL "IFeel" -#endif // D_STR_IFEEL -#ifndef D_STR_ISEE -#define D_STR_ISEE "ISee" -#endif // D_STR_ISEE -#ifndef D_STR_HUMID -#define D_STR_HUMID "Humid" -#endif // D_STR_HUMID -#ifndef D_STR_SAVE -#define D_STR_SAVE "Save" -#endif // D_STR_SAVE -#ifndef D_STR_EYE -#define D_STR_EYE "Eye" -#endif // D_STR_EYE -#ifndef D_STR_FOLLOW -#define D_STR_FOLLOW "Follow" -#endif // D_STR_FOLLOW -#ifndef D_STR_ION -#define D_STR_ION "Ion" -#endif // D_STR_ION -#ifndef D_STR_FRESH -#define D_STR_FRESH "Fresh" -#endif // D_STR_FRESH -#ifndef D_STR_HOLD -#define D_STR_HOLD "Hold" -#endif // D_STR_HOLD -#ifndef D_STR_8C_HEAT -#define D_STR_8C_HEAT "8C " D_STR_HEAT // Set `D_STR_HEAT` first! -#endif // D_STR_8C_HEAT -#ifndef D_STR_10C_HEAT -#define D_STR_10C_HEAT "10C " D_STR_HEAT // Set `D_STR_HEAT` first! -#endif // D_STR_10C_HEAT -#ifndef D_STR_BUTTON -#define D_STR_BUTTON "Button" -#endif // D_STR_BUTTON -#ifndef D_STR_NIGHT -#define D_STR_NIGHT "Night" -#endif // D_STR_NIGHT -#ifndef D_STR_SILENT -#define D_STR_SILENT "Silent" -#endif // D_STR_SILENT -#ifndef D_STR_FILTER -#define D_STR_FILTER "Filter" -#endif // D_STR_FILTER -#ifndef D_STR_3D -#define D_STR_3D "3D" -#endif // D_STR_3D -#ifndef D_STR_CELSIUS -#define D_STR_CELSIUS "Celsius" -#endif // D_STR_CELSIUS -#ifndef D_STR_FAHRENHEIT -#define D_STR_FAHRENHEIT "Fahrenheit" -#endif // D_STR_FAHRENHEIT -#ifndef D_STR_CELSIUS_FAHRENHEIT -#define D_STR_CELSIUS_FAHRENHEIT D_STR_CELSIUS "/" D_STR_FAHRENHEIT -#endif // D_STR_CELSIUS_FAHRENHEIT -#ifndef D_STR_UP -#define D_STR_UP "Up" -#endif // D_STR_UP -#ifndef D_STR_TEMPUP -#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP // Set `D_STR_TEMP` first! -#endif // D_STR_TEMPUP -#ifndef D_STR_DOWN -#define D_STR_DOWN "Down" -#endif // D_STR_DOWN -#ifndef D_STR_TEMPDOWN -#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN // Set `D_STR_TEMP` first! -#endif // D_STR_TEMPDOWN -#ifndef D_STR_CHANGE -#define D_STR_CHANGE "Change" -#endif // D_STR_CHANGE -#ifndef D_STR_START -#define D_STR_START "Start" -#endif // D_STR_START -#ifndef D_STR_STOP -#define D_STR_STOP "Stop" -#endif // D_STR_STOP -#ifndef D_STR_MOVE -#define D_STR_MOVE "Move" -#endif // D_STR_MOVE -#ifndef D_STR_SET -#define D_STR_SET "Set" -#endif // D_STR_SET -#ifndef D_STR_CANCEL -#define D_STR_CANCEL "Cancel" -#endif // D_STR_CANCEL -#ifndef D_STR_COMFORT -#define D_STR_COMFORT "Comfort" -#endif // D_STR_COMFORT -#ifndef D_STR_SENSOR -#define D_STR_SENSOR "Sensor" -#endif // D_STR_SENSOR -#ifndef D_STR_ABSENSEDETECT -#define D_STR_ABSENSEDETECT "Absense detect" -#endif // D_STR_ABSENSEDETECT -#ifndef D_STR_DIRECT -#define D_STR_DIRECT "Direct" -#endif // D_STR_DIRECT -#ifndef D_STR_INDIRECT -#define D_STR_INDIRECT "Indirect" -#endif // D_STR_INDIRECT -#ifndef D_STR_DIRECTINDIRECTMODE -#define D_STR_DIRECTINDIRECTMODE D_STR_DIRECT " / " \ -D_STR_INDIRECT " " D_STR_MODE -#endif // D_STR_DIRECTINDIRECTMODE -#ifndef D_STR_DISPLAY -#define D_STR_DISPLAY "Display" -#endif // D_STR_DISPLAY -#ifndef D_STR_WEEKLY -#define D_STR_WEEKLY "Weekly" -#endif // D_STR_WEEKLY -#ifndef D_STR_WEEKLYTIMER -#define D_STR_WEEKLYTIMER D_STR_WEEKLY " " D_STR_TIMER // Needs `D_STR_WEEKLY`! -#endif // D_STR_WEEKLYTIMER -#ifndef D_STR_WIFI -#define D_STR_WIFI "WiFi" -#endif // D_STR_WIFI -#ifndef D_STR_LAST -#define D_STR_LAST "Last" -#endif // D_STR_LAST -#ifndef D_STR_FAST -#define D_STR_FAST "Fast" -#endif // D_STR_FAST -#ifndef D_STR_SLOW -#define D_STR_SLOW "Slow" -#endif // D_STR_SLOW -#ifndef D_STR_AIRFLOW -#define D_STR_AIRFLOW "Air Flow" -#endif // D_STR_AIRFLOW -#ifndef D_STR_STEP -#define D_STR_STEP "Step" -#endif // D_STR_STEP -#ifndef D_STR_NA -#define D_STR_NA "N/A" -#endif // D_STR_NA -#ifndef D_STR_INSIDE -#define D_STR_INSIDE "Inside" -#endif // D_STR_INSIDE -#ifndef D_STR_OUTSIDE -#define D_STR_OUTSIDE "Outside" -#endif // D_STR_OUTSIDE -#ifndef D_STR_LOUD -#define D_STR_LOUD "Loud" -#endif // D_STR_LOUD -#ifndef D_STR_UPPER -#define D_STR_UPPER "Upper" -#endif // D_STR_UPPER -#ifndef D_STR_LOWER -#define D_STR_LOWER "Lower" -#endif // D_STR_LOWER -#ifndef D_STR_BREEZE -#define D_STR_BREEZE "Breeze" -#endif // D_STR_BREEZE -#ifndef D_STR_CIRCULATE -#define D_STR_CIRCULATE "Circulate" -#endif // D_STR_CIRCULATE -#ifndef D_STR_CEILING -#define D_STR_CEILING "Ceiling" -#endif // D_STR_CEILING -#ifndef D_STR_WALL -#define D_STR_WALL "Wall" -#endif // D_STR_WALL -#ifndef D_STR_ROOM -#define D_STR_ROOM "Room" -#endif // D_STR_ROOM -#ifndef D_STR_6THSENSE -#define D_STR_6THSENSE "6th Sense" -#endif // D_STR_6THSENSE -#ifndef D_STR_ZONEFOLLOW -#define D_STR_ZONEFOLLOW "Zone Follow" -#endif // D_STR_ZONEFOLLOW -#ifndef D_STR_FIXED -#define D_STR_FIXED "Fixed" -#endif // D_STR_FIXED -#ifndef D_STR_TYPE -#define D_STR_TYPE "Type" -#endif // D_STR_TYPE -#ifndef D_STR_SPECIAL -#define D_STR_SPECIAL "Special" -#endif // D_STR_SPECIAL -#ifndef D_STR_RECYCLE -#define D_STR_RECYCLE "Recycle" -#endif // D_STR_RECYCLE -#ifndef D_STR_ID -#define D_STR_ID "Id" -#endif // D_STR_ID -#ifndef D_STR_VANE -#define D_STR_VANE "Vane" -#endif // D_STR_VANE -#ifndef D_STR_LOCK -#define D_STR_LOCK "Lock" -#endif // D_STR_LOCK -#ifndef D_STR_REPORT -#define D_STR_REPORT "Report" -#endif // D_STR_REPORT - -#ifndef D_STR_AUTO -#define D_STR_AUTO "Auto" -#endif // D_STR_AUTO -#ifndef D_STR_AUTOMATIC -#define D_STR_AUTOMATIC "Automatic" -#endif // D_STR_AUTOMATIC -#ifndef D_STR_MANUAL -#define D_STR_MANUAL "Manual" -#endif // D_STR_MANUAL -#ifndef D_STR_COOL -#define D_STR_COOL "Cool" -#endif // D_STR_COOL -#ifndef D_STR_COOLING -#define D_STR_COOLING "Cooling" -#endif // D_STR_COOLING -#ifndef D_STR_HEAT -#define D_STR_HEAT "Heat" -#endif // D_STR_HEAT -#ifndef D_STR_HEATING -#define D_STR_HEATING "Heating" -#endif // D_STR_HEATING -#ifndef D_STR_FAN -#define D_STR_FAN "Fan" -#endif // D_STR_FAN -#ifndef D_STR_FANONLY -#define D_STR_FANONLY "fan-only" -#endif // D_STR_FANONLY -#ifndef D_STR_FAN_ONLY -#define D_STR_FAN_ONLY "fan_only" -#endif // D_STR_FAN_ONLY -#ifndef D_STR_ONLY -#define D_STR_ONLY "Only" -#endif // D_STR_ONLY -#ifndef D_STR_FANSPACEONLY -#define D_STR_FANSPACEONLY D_STR_FAN " " D_STR_ONLY -#endif // D_STR_FANSPACEONLY -#ifndef D_STR_FANONLYNOSPACE -#define D_STR_FANONLYNOSPACE D_STR_FAN D_STR_ONLY -#endif // D_STR_FANONLYNOSPACE -#ifndef D_STR_DRY -#define D_STR_DRY "Dry" -#endif // D_STR_DRY -#ifndef D_STR_DRYING -#define D_STR_DRYING "Drying" -#endif // D_STR_DRYING -#ifndef D_STR_DEHUMIDIFY -#define D_STR_DEHUMIDIFY "Dehumidify" -#endif // D_STR_DEHUMIDIFY - -#ifndef D_STR_MAX -#define D_STR_MAX "Max" -#endif // D_STR_MAX -#ifndef D_STR_MAXIMUM -#define D_STR_MAXIMUM "Maximum" -#endif // D_STR_MAXIMUM -#ifndef D_STR_MIN -#define D_STR_MIN "Min" -#endif // D_STR_MIN -#ifndef D_STR_MINIMUM -#define D_STR_MINIMUM "Minimum" -#endif // D_STR_MINIMUM -#ifndef D_STR_MED -#define D_STR_MED "Med" -#endif // D_STR_MED -#ifndef D_STR_MEDIUM -#define D_STR_MEDIUM "Medium" -#endif // D_STR_MEDIUM -#ifndef D_STR_MED_HIGH -#define D_STR_MED_HIGH D_STR_MED "-" D_STR_HIGH -#endif // D_STR_MED_HIGH - -#ifndef D_STR_HIGHEST -#define D_STR_HIGHEST "Highest" -#endif // D_STR_HIGHEST -#ifndef D_STR_HIGH -#define D_STR_HIGH "High" -#endif // D_STR_HIGH -#ifndef D_STR_HI -#define D_STR_HI "Hi" -#endif // D_STR_HI -#ifndef D_STR_MID -#define D_STR_MID "Mid" -#endif // D_STR_MID -#ifndef D_STR_MIDDLE -#define D_STR_MIDDLE "Middle" -#endif // D_STR_MIDDLE -#ifndef D_STR_LOW -#define D_STR_LOW "Low" -#endif // D_STR_LOW -#ifndef D_STR_LO -#define D_STR_LO "Lo" -#endif // D_STR_LO -#ifndef D_STR_LOWEST -#define D_STR_LOWEST "Lowest" -#endif // D_STR_LOWEST -#ifndef D_STR_RIGHT -#define D_STR_RIGHT "Right" -#endif // D_STR_RIGHT -#ifndef D_STR_MAXRIGHT -#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` first! -#endif // D_STR_MAXRIGHT -#ifndef D_STR_MAXRIGHT_NOSPACE -#define D_STR_MAXRIGHT_NOSPACE D_STR_MAX D_STR_RIGHT // Set `D_STR_MAX` first! -#endif // D_STR_MAXRIGHT_NOSPACE -#ifndef D_STR_RIGHTMAX -#define D_STR_RIGHTMAX D_STR_RIGHT " " D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_RIGHTMAX -#ifndef D_STR_RIGHTMAX_NOSPACE -#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_RIGHTMAX_NOSPACE -#ifndef D_STR_LEFT -#define D_STR_LEFT "Left" -#endif // D_STR_LEFT -#ifndef D_STR_MAXLEFT -#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` first! -#endif // D_STR_MAXLEFT -#ifndef D_STR_MAXLEFT_NOSPACE -#define D_STR_MAXLEFT_NOSPACE D_STR_MAX D_STR_LEFT // Set `D_STR_MAX` first! -#endif // D_STR_MAXLEFT_NOSPACE -#ifndef D_STR_LEFTMAX -#define D_STR_LEFTMAX D_STR_LEFT " " D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_LEFTMAX -#ifndef D_STR_LEFTMAX_NOSPACE -#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_LEFTMAX_NOSPACE -#ifndef D_STR_WIDE -#define D_STR_WIDE "Wide" -#endif // D_STR_WIDE -#ifndef D_STR_CENTRE -#define D_STR_CENTRE "Centre" -#endif // D_STR_CENTRE -#ifndef D_STR_TOP -#define D_STR_TOP "Top" -#endif // D_STR_TOP -#ifndef D_STR_BOTTOM -#define D_STR_BOTTOM "Bottom" -#endif // D_STR_BOTTOM -#ifndef D_STR_UPPER_MIDDLE -#define D_STR_UPPER_MIDDLE D_STR_UPPER "-" D_STR_MIDDLE -#endif // D_STR_UPPER_MIDDLE -#ifndef D_STR_CONFIG -#define D_STR_CONFIG "Config" -#endif // D_STR_CONFIG -#ifndef D_STR_CONTROL -#define D_STR_CONTROL "Control" -#endif // D_STR_CONTROL -#ifndef D_STR_SET_TIMER -#define D_STR_SET_TIMER D_STR_SET " " D_STR_TIMER -#endif // D_STR_AC_TIMER -#ifndef D_STR_SCHEDULE -#define D_STR_SCHEDULE "Schedule" -#endif // D_STR_SCHEDULE -#ifndef D_STR_CH -#define D_STR_CH "CH#" -#endif // D_STR_CH -#ifndef D_STR_TIMER_ACTIVE_DAYS -#define D_STR_TIMER_ACTIVE_DAYS "TimerActiveDays" -#endif // D_STR_TIMER_ACTIVE_DAYS -#ifndef D_STR_KEY -#define D_STR_KEY "Key" -#endif // D_STR_KEY -#ifndef D_STR_VALUE -#define D_STR_VALUE "Value" -#endif // D_STR_VALUE - -// Compound words/phrases/descriptions from pre-defined words. -// Note: Obviously these need to be defined *after* their component words. -#ifndef D_STR_ECONOTOGGLE -#define D_STR_ECONOTOGGLE D_STR_ECONO " " D_STR_TOGGLE -#endif // D_STR_ECONOTOGGLE -#ifndef D_STR_EYEAUTO -#define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO -#endif // D_STR_EYEAUTO -#ifndef D_STR_LIGHTTOGGLE -#define D_STR_LIGHTTOGGLE D_STR_LIGHT " " D_STR_TOGGLE -#endif // D_STR_LIGHTTOGGLE -#ifndef D_STR_OUTSIDEQUIET -#define D_STR_OUTSIDEQUIET D_STR_OUTSIDE " " D_STR_QUIET -#endif // D_STR_OUTSIDEQUIET -#ifndef D_STR_POWERTOGGLE -#define D_STR_POWERTOGGLE D_STR_POWER " " D_STR_TOGGLE -#endif // D_STR_POWERTOGGLE -#ifndef D_STR_POWERBUTTON -#define D_STR_POWERBUTTON D_STR_POWER " " D_STR_BUTTON -#endif // D_STR_POWERBUTTON -#ifndef D_STR_PREVIOUSPOWER -#define D_STR_PREVIOUSPOWER D_STR_PREVIOUS " " D_STR_POWER -#endif // D_STR_PREVIOUSPOWER -#ifndef D_STR_DISPLAYTEMP -#define D_STR_DISPLAYTEMP D_STR_DISPLAY " " D_STR_TEMP -#endif // D_STR_DISPLAYTEMP -#ifndef D_STR_IFEELREPORT -#define D_STR_IFEELREPORT D_STR_IFEEL " " D_STR_REPORT -#endif // D_STR_IFEELREPORT -#ifndef D_STR_SENSORTEMP -#define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP -#endif // D_STR_SENSORTEMP -#ifndef D_STR_SLEEP_TIMER -#define D_STR_SLEEP_TIMER D_STR_SLEEP " " D_STR_TIMER -#endif // D_STR_SLEEP_TIMER -#ifndef D_STR_SWINGVMODE -#define D_STR_SWINGVMODE D_STR_SWINGV " " D_STR_MODE -#endif // D_STR_SWINGVMODE -#ifndef D_STR_SWINGVTOGGLE -#define D_STR_SWINGVTOGGLE D_STR_SWINGV " " D_STR_TOGGLE -#endif // D_STR_SWINGVTOGGLE -#ifndef D_STR_TURBOTOGGLE -#define D_STR_TURBOTOGGLE D_STR_TURBO " " D_STR_TOGGLE -#endif // D_STR_TURBOTOGGLE - -// Separators -#ifndef D_CHR_TIME_SEP -#define D_CHR_TIME_SEP ':' -#endif // D_CHR_TIME_SEP -#ifndef D_STR_SPACELBRACE -#define D_STR_SPACELBRACE " (" -#endif // D_STR_SPACELBRACE -#ifndef D_STR_COMMASPACE -#define D_STR_COMMASPACE ", " -#endif // D_STR_COMMASPACE -#ifndef D_STR_COLONSPACE -#define D_STR_COLONSPACE ": " -#endif // D_STR_COLONSPACE -#ifndef D_STR_DASH -#define D_STR_DASH "-" -#endif // D_STR_DASH - -#ifndef D_STR_DAY -#define D_STR_DAY "Day" -#endif // D_STR_DAY -#ifndef D_STR_DAYS -#define D_STR_DAYS D_STR_DAY "s" -#endif // D_STR_DAYS -#ifndef D_STR_HOUR -#define D_STR_HOUR "Hour" -#endif // D_STR_HOUR -#ifndef D_STR_HOURS -#define D_STR_HOURS D_STR_HOUR "s" -#endif // D_STR_HOURS -#ifndef D_STR_MINUTE -#define D_STR_MINUTE "Minute" -#endif // D_STR_MINUTE -#ifndef D_STR_MINUTES -#define D_STR_MINUTES D_STR_MINUTE "s" -#endif // D_STR_MINUTES -#ifndef D_STR_SECOND -#define D_STR_SECOND "Second" -#endif // D_STR_SECOND -#ifndef D_STR_SECONDS -#define D_STR_SECONDS D_STR_SECOND "s" -#endif // D_STR_SECONDS -#ifndef D_STR_NOW -#define D_STR_NOW "Now" -#endif // D_STR_NOW -#ifndef D_STR_THREELETTERDAYS -#define D_STR_THREELETTERDAYS "SunMonTueWedThuFriSat" -#endif // D_STR_THREELETTERDAYS - -#ifndef D_STR_YES -#define D_STR_YES "Yes" -#endif // D_STR_YES -#ifndef D_STR_NO -#define D_STR_NO "No" -#endif // D_STR_NO -#ifndef D_STR_TRUE -#define D_STR_TRUE "True" -#endif // D_STR_TRUE -#ifndef D_STR_FALSE -#define D_STR_FALSE "False" -#endif // D_STR_FALSE - -#ifndef D_STR_REPEAT -#define D_STR_REPEAT "Repeat" -#endif // D_STR_REPEAT -#ifndef D_STR_CODE -#define D_STR_CODE "Code" -#endif // D_STR_CODE -#ifndef D_STR_BITS -#define D_STR_BITS "Bits" -#endif // D_STR_BITS - -// Model Names -#ifndef D_STR_YAW1F -#define D_STR_YAW1F "YAW1F" -#endif // D_STR_YAW1F -#ifndef D_STR_YBOFB -#define D_STR_YBOFB "YBOFB" -#endif // D_STR_YBOFB -#ifndef D_STR_YX1FSF -#define D_STR_YX1FSF "YX1FSF" -#endif // D_STR_YX1FSF -#ifndef D_STR_V9014557_A -#define D_STR_V9014557_A "V9014557-A" -#endif // D_STR_V9014557_A -#ifndef D_STR_V9014557_B -#define D_STR_V9014557_B "V9014557-B" -#endif // D_STR_V9014557_B -#ifndef D_STR_RLT0541HTA_A -#define D_STR_RLT0541HTA_A "R-LT0541-HTA-A" -#endif // D_STR_RLT0541HTA_A -#ifndef D_STR_RLT0541HTA_B -#define D_STR_RLT0541HTA_B "R-LT0541-HTA-B" -#endif // D_STR_RLT0541HTA_B -#ifndef D_STR_ARRAH2E -#define D_STR_ARRAH2E "ARRAH2E" -#endif // D_STR_ARRAH2E -#ifndef D_STR_ARDB1 -#define D_STR_ARDB1 "ARDB1" -#endif // D_STR_ARDB1 -#ifndef D_STR_ARREB1E -#define D_STR_ARREB1E "ARREB1E" -#endif // D_STR_ARREB1E -#ifndef D_STR_ARJW2 -#define D_STR_ARJW2 "ARJW2" -#endif // D_STR_ARJW2 -#ifndef D_STR_ARRY4 -#define D_STR_ARRY4 "ARRY4" -#endif // D_STR_ARRY4 -#ifndef D_STR_ARREW4E -#define D_STR_ARREW4E "ARREW4E" -#endif // D_STR_ARREW4E -#ifndef D_STR_GE6711AR2853M -#define D_STR_GE6711AR2853M "GE6711AR2853M" -#endif // D_STR_GE6711AR2853M -#ifndef D_STR_AKB75215403 -#define D_STR_AKB75215403 "AKB75215403" -#endif // D_STR_AKB75215403 -#ifndef D_STR_AKB74955603 -#define D_STR_AKB74955603 "AKB74955603" -#endif // D_STR_AKB74955603 -#ifndef D_STR_AKB73757604 -#define D_STR_AKB73757604 "AKB73757604" -#endif // D_STR_AKB73757604 -#ifndef D_STR_LG6711A20083V -#define D_STR_LG6711A20083V "LG6711A20083V" -#endif // D_STR_LG6711A20083V -#ifndef D_STR_KKG9AC1 -#define D_STR_KKG9AC1 "KKG9AC1" -#endif // D_STR_KKG9AC1 -#ifndef D_STR_KKG29AC1 -#define D_STR_KKG29AC1 "KKG29AC1" -#endif // D_STR_KKG9AC1 -#ifndef D_STR_LKE -#define D_STR_LKE "LKE" -#endif // D_STR_LKE -#ifndef D_STR_NKE -#define D_STR_NKE "NKE" -#endif // D_STR_NKE -#ifndef D_STR_DKE -#define D_STR_DKE "DKE" -#endif // D_STR_DKE -#ifndef D_STR_PKR -#define D_STR_PKR "PKR" -#endif // D_STR_PKR -#ifndef D_STR_JKE -#define D_STR_JKE "JKE" -#endif // D_STR_JKE -#ifndef D_STR_CKP -#define D_STR_CKP "CKP" -#endif // D_STR_CKP -#ifndef D_STR_RKR -#define D_STR_RKR "RKR" -#endif // D_STR_RKR -#ifndef D_STR_PANASONICLKE -#define D_STR_PANASONICLKE "PANASONICLKE" -#endif // D_STR_PANASONICLKE -#ifndef D_STR_PANASONICNKE -#define D_STR_PANASONICNKE "PANASONICNKE" -#endif // D_STR_PANASONICNKE -#ifndef D_STR_PANASONICDKE -#define D_STR_PANASONICDKE "PANASONICDKE" -#endif // D_STR_PANASONICDKE -#ifndef D_STR_PANASONICPKR -#define D_STR_PANASONICPKR "PANASONICPKR" -#endif // D_STR_PANASONICPKR -#ifndef D_STR_PANASONICJKE -#define D_STR_PANASONICJKE "PANASONICJKE" -#endif // D_STR_PANASONICJKE -#ifndef D_STR_PANASONICCKP -#define D_STR_PANASONICCKP "PANASONICCKP" -#endif // D_STR_PANASONICCKP -#ifndef D_STR_PANASONICRKR -#define D_STR_PANASONICRKR "PANASONICRKR" -#endif // D_STR_PANASONICRKR -#ifndef D_STR_A907 -#define D_STR_A907 "A907" -#endif // D_STR_A907 -#ifndef D_STR_A705 -#define D_STR_A705 "A705" -#endif // D_STR_A705 -#ifndef D_STR_A903 -#define D_STR_A903 "A903" -#endif // D_STR_A903 -#ifndef D_STR_TAC09CHSD -#define D_STR_TAC09CHSD "TAC09CHSD" -#endif // D_STR_TAC09CHSD -#ifndef D_STR_GZ055BE1 -#define D_STR_GZ055BE1 "GZ055BE1" -#endif // D_STR_GZ055BE1 -#ifndef D_STR_122LZF -#define D_STR_122LZF "122LZF" -#endif // D_STR_122LZF -#ifndef D_STR_DG11J13A -#define D_STR_DG11J13A "DG11J13A" -#endif // D_STR_DG11J13A -#ifndef D_STR_DG11J104 -#define D_STR_DG11J104 "DG11J104" -#endif // D_STR_DG11J104 -#ifndef D_STR_DG11J191 -#define D_STR_DG11J191 "DG11J191" -#endif // D_STR_DG11J191 -#ifndef D_STR_ARGO_WREM2 -#define D_STR_ARGO_WREM2 "WREM2" -#endif // D_STR_ARGO_WREM2 -#ifndef D_STR_ARGO_WREM3 -#define D_STR_ARGO_WREM3 "WREM3" -#endif // D_STR_ARGO_WREM3 - -// Protocols Names -#ifndef D_STR_AIRTON -#define D_STR_AIRTON "AIRTON" -#endif // D_STR_AIRTON -#ifndef D_STR_AIRWELL -#define D_STR_AIRWELL "AIRWELL" -#endif // D_STR_AIRWELL -#ifndef D_STR_AIWA_RC_T501 -#define D_STR_AIWA_RC_T501 "AIWA_RC_T501" -#endif // D_STR_AIWA_RC_T501 -#ifndef D_STR_AMCOR -#define D_STR_AMCOR "AMCOR" -#endif // D_STR_AMCOR -#ifndef D_STR_ARGO -#define D_STR_ARGO "ARGO" -#endif // D_STR_ARGO -#ifndef D_STR_ARRIS -#define D_STR_ARRIS "ARRIS" -#endif // D_STR_ARRIS -#ifndef D_STR_BOSCH -#define D_STR_BOSCH "BOSCH" -#endif // D_STR_BOSCH -#ifndef D_STR_BOSCH144 -#define D_STR_BOSCH144 D_STR_BOSCH "144" -#endif // D_STR_BOSCH144 -#ifndef D_STR_BOSE -#define D_STR_BOSE "BOSE" -#endif // D_STR_BOSE -#ifndef D_STR_CARRIER_AC -#define D_STR_CARRIER_AC "CARRIER_AC" -#endif // D_STR_CARRIER_AC -#ifndef D_STR_CARRIER_AC40 -#define D_STR_CARRIER_AC40 D_STR_CARRIER_AC "40" -#endif // D_STR_CARRIER_AC40 -#ifndef D_STR_CARRIER_AC64 -#define D_STR_CARRIER_AC64 D_STR_CARRIER_AC "64" -#endif // D_STR_CARRIER_AC64 -#ifndef D_STR_CARRIER_AC84 -#define D_STR_CARRIER_AC84 D_STR_CARRIER_AC "84" -#endif // D_STR_CARRIER_AC84 -#ifndef D_STR_CARRIER_AC128 -#define D_STR_CARRIER_AC128 D_STR_CARRIER_AC "128" -#endif // D_STR_CARRIER_AC128 -#ifndef D_STR_CLIMABUTLER -#define D_STR_CLIMABUTLER "CLIMABUTLER" -#endif // D_STR_CLIMABUTLER -#ifndef D_STR_COOLIX -#define D_STR_COOLIX "COOLIX" -#endif // D_STR_COOLIX -#ifndef D_STR_COOLIX48 -#define D_STR_COOLIX48 D_STR_COOLIX "48" -#endif // D_STR_COOLIX48 -#ifndef D_STR_CORONA_AC -#define D_STR_CORONA_AC "CORONA_AC" -#endif // D_STR_CORONA_AC -#ifndef D_STR_DAIKIN -#define D_STR_DAIKIN "DAIKIN" -#endif // D_STR_DAIKIN -#ifndef D_STR_DAIKIN128 -#define D_STR_DAIKIN128 D_STR_DAIKIN "128" -#endif // D_STR_DAIKIN128 -#ifndef D_STR_DAIKIN152 -#define D_STR_DAIKIN152 D_STR_DAIKIN "152" -#endif // D_STR_DAIKIN152 -#ifndef D_STR_DAIKIN160 -#define D_STR_DAIKIN160 D_STR_DAIKIN "160" -#endif // D_STR_DAIKIN160 -#ifndef D_STR_DAIKIN176 -#define D_STR_DAIKIN176 D_STR_DAIKIN "176" -#endif // D_STR_DAIKIN176 -#ifndef D_STR_DAIKIN2 -#define D_STR_DAIKIN2 D_STR_DAIKIN "2" -#endif // D_STR_DAIKIN2 -#ifndef D_STR_DAIKIN200 -#define D_STR_DAIKIN200 D_STR_DAIKIN "200" -#endif // D_STR_DAIKIN200 -#ifndef D_STR_DAIKIN216 -#define D_STR_DAIKIN216 D_STR_DAIKIN "216" -#endif // D_STR_DAIKIN216 -#ifndef D_STR_DAIKIN312 -#define D_STR_DAIKIN312 D_STR_DAIKIN "312" -#endif // D_STR_DAIKIN312 -#ifndef D_STR_DAIKIN64 -#define D_STR_DAIKIN64 D_STR_DAIKIN "64" -#endif // D_STR_DAIKIN64 -#ifndef D_STR_DELONGHI_AC -#define D_STR_DELONGHI_AC "DELONGHI_AC" -#endif // D_STR_DELONGHI_AC -#ifndef D_STR_DENON -#define D_STR_DENON "DENON" -#endif // D_STR_DENON -#ifndef D_STR_DISH -#define D_STR_DISH "DISH" -#endif // D_STR_DISH -#ifndef D_STR_DOSHISHA -#define D_STR_DOSHISHA "DOSHISHA" -#endif // D_STR_DOSHISHA -#ifndef D_STR_ECOCLIM -#define D_STR_ECOCLIM "ECOCLIM" -#endif // D_STR_ECOCLIM -#ifndef D_STR_ELECTRA_AC -#define D_STR_ELECTRA_AC "ELECTRA_AC" -#endif // D_STR_ELECTRA_AC -#ifndef D_STR_ELITESCREENS -#define D_STR_ELITESCREENS "ELITESCREENS" -#endif // D_STR_ELITESCREENS -#ifndef D_STR_EPSON -#define D_STR_EPSON "EPSON" -#endif // D_STR_EPSON -#ifndef D_STR_FUJITSU_AC -#define D_STR_FUJITSU_AC "FUJITSU_AC" -#endif // D_STR_FUJITSU_AC -#ifndef D_STR_GICABLE -#define D_STR_GICABLE "GICABLE" -#endif // D_STR_GICABLE -#ifndef D_STR_GLOBALCACHE -#define D_STR_GLOBALCACHE "GLOBALCACHE" -#endif // D_STR_GLOBALCACHE -#ifndef D_STR_GOODWEATHER -#define D_STR_GOODWEATHER "GOODWEATHER" -#endif // D_STR_GOODWEATHER -#ifndef D_STR_GORENJE -#define D_STR_GORENJE "GORENJE" -#endif // D_STR_GORENJE -#ifndef D_STR_GREE -#define D_STR_GREE "GREE" -#endif // D_STR_GREE -#ifndef D_STR_HAIER_AC -#define D_STR_HAIER_AC "HAIER_AC" -#endif // D_STR_HAIER_AC -#ifndef D_STR_HAIER_AC_YRW02 -#define D_STR_HAIER_AC_YRW02 D_STR_HAIER_AC "_YRW02" -#endif // D_STR_HAIER_AC_YRW02 -#ifndef D_STR_HAIER_AC160 -#define D_STR_HAIER_AC160 D_STR_HAIER_AC "160" -#endif // D_STR_HAIER_AC160 -#ifndef D_STR_HAIER_AC176 -#define D_STR_HAIER_AC176 D_STR_HAIER_AC "176" -#endif // D_STR_HAIER_AC176 -#ifndef D_STR_HITACHI_AC -#define D_STR_HITACHI_AC "HITACHI_AC" -#endif // D_STR_HITACHI_AC -#ifndef D_STR_HITACHI_AC1 -#define D_STR_HITACHI_AC1 D_STR_HITACHI_AC "1" -#endif // D_STR_HITACHI_AC1 -#ifndef D_STR_HITACHI_AC2 -#define D_STR_HITACHI_AC2 D_STR_HITACHI_AC "2" -#endif // D_STR_HITACHI_AC2 -#ifndef D_STR_HITACHI_AC3 -#define D_STR_HITACHI_AC3 D_STR_HITACHI_AC "3" -#endif // D_STR_HITACHI_AC3 -#ifndef D_STR_HITACHI_AC264 -#define D_STR_HITACHI_AC264 D_STR_HITACHI_AC "264" -#endif // D_STR_HITACHI_AC264 -#ifndef D_STR_HITACHI_AC296 -#define D_STR_HITACHI_AC296 D_STR_HITACHI_AC "296" -#endif // D_STR_HITACHI_AC296 -#ifndef D_STR_HITACHI_AC344 -#define D_STR_HITACHI_AC344 D_STR_HITACHI_AC "344" -#endif // D_STR_HITACHI_AC344 -#ifndef D_STR_HITACHI_AC424 -#define D_STR_HITACHI_AC424 D_STR_HITACHI_AC "424" -#endif // D_STR_HITACHI_AC424 -#ifndef D_STR_INAX -#define D_STR_INAX "INAX" -#endif // D_STR_INAX -#ifndef D_STR_JVC -#define D_STR_JVC "JVC" -#endif // D_STR_JVC -#ifndef D_STR_KELON -#define D_STR_KELON "KELON" -#endif // D_STR_KELON -#ifndef D_STR_KELON168 -#define D_STR_KELON168 D_STR_KELON "168" -#endif // D_STR_KELON168 -#ifndef D_STR_KELVINATOR -#define D_STR_KELVINATOR "KELVINATOR" -#endif // D_STR_KELVINATOR -#ifndef D_STR_LASERTAG -#define D_STR_LASERTAG "LASERTAG" -#endif // D_STR_LASERTAG -#ifndef D_STR_LEGOPF -#define D_STR_LEGOPF "LEGOPF" -#endif // D_STR_LEGOPF -#ifndef D_STR_LG -#define D_STR_LG "LG" -#endif // D_STR_LG -#ifndef D_STR_LG2 -#define D_STR_LG2 "LG2" -#endif // D_STR_LG2 -#ifndef D_STR_LUTRON -#define D_STR_LUTRON "LUTRON" -#endif // D_STR_LUTRON -#ifndef D_STR_MAGIQUEST -#define D_STR_MAGIQUEST "MAGIQUEST" -#endif // D_STR_MAGIQUEST -#ifndef D_STR_METZ -#define D_STR_METZ "METZ" -#endif // D_STR_METZ -#ifndef D_STR_MIDEA -#define D_STR_MIDEA "MIDEA" -#endif // D_STR_MIDEA -#ifndef D_STR_MIDEA24 -#define D_STR_MIDEA24 "MIDEA24" -#endif // D_STR_MIDEA24 -#ifndef D_STR_MILESTAG2 -#define D_STR_MILESTAG2 "MILESTAG2" -#endif // D_STR_MILESTAG2 -#ifndef D_STR_MIRAGE -#define D_STR_MIRAGE "MIRAGE" -#endif // D_STR_MIRAGE -#ifndef D_STR_MITSUBISHI -#define D_STR_MITSUBISHI "MITSUBISHI" -#endif // D_STR_MITSUBISHI -#ifndef D_STR_MITSUBISHI112 -#define D_STR_MITSUBISHI112 "MITSUBISHI112" -#endif // D_STR_MITSUBISHI112 -#ifndef D_STR_MITSUBISHI136 -#define D_STR_MITSUBISHI136 "MITSUBISHI136" -#endif // D_STR_MITSUBISHI136 -#ifndef D_STR_MITSUBISHI2 -#define D_STR_MITSUBISHI2 "MITSUBISHI2" -#endif // D_STR_MITSUBISHI2 -#ifndef D_STR_MITSUBISHI_AC -#define D_STR_MITSUBISHI_AC "MITSUBISHI_AC" -#endif // D_STR_MITSUBISHI_AC -#ifndef D_STR_MITSUBISHI_HEAVY_152 -#define D_STR_MITSUBISHI_HEAVY_152 "MITSUBISHI_HEAVY_152" -#endif // D_STR_MITSUBISHI_HEAVY_152 -#ifndef D_STR_MITSUBISHI_HEAVY_88 -#define D_STR_MITSUBISHI_HEAVY_88 "MITSUBISHI_HEAVY_88" -#endif // D_STR_MITSUBISHI_HEAVY_88 -#ifndef D_STR_MULTIBRACKETS -#define D_STR_MULTIBRACKETS "MULTIBRACKETS" -#endif // D_STR_MULTIBRACKETS -#ifndef D_STR_MWM -#define D_STR_MWM "MWM" -#endif // D_STR_MWM -#ifndef D_STR_NEC -#define D_STR_NEC "NEC" -#endif // D_STR_NEC -#ifndef D_STR_NEC_LIKE -#define D_STR_NEC_LIKE D_STR_NEC "_LIKE" -#endif // D_STR_NEC_LIKE -#ifndef D_STR_NEC_NON_STRICT -#define D_STR_NEC_NON_STRICT D_STR_NEC " (NON-STRICT)" -#endif // D_STR_NEC_NON_STRICT -#ifndef D_STR_NEOCLIMA -#define D_STR_NEOCLIMA "NEOCLIMA" -#endif // D_STR_NEOCLIMA -#ifndef D_STR_NIKAI -#define D_STR_NIKAI "NIKAI" -#endif // D_STR_NIKAI -#ifndef D_STR_PANASONIC -#define D_STR_PANASONIC "PANASONIC" -#endif // D_STR_PANASONIC -#ifndef D_STR_PANASONIC_AC -#define D_STR_PANASONIC_AC "PANASONIC_AC" -#endif // D_STR_PANASONIC_AC -#ifndef D_STR_PANASONIC_AC32 -#define D_STR_PANASONIC_AC32 D_STR_PANASONIC_AC"32" -#endif // D_STR_PANASONIC_AC32 -#ifndef D_STR_PIONEER -#define D_STR_PIONEER "PIONEER" -#endif // D_STR_PIONEER -#ifndef D_STR_PRONTO -#define D_STR_PRONTO "PRONTO" -#endif // D_STR_PRONTO -#ifndef D_STR_RAW -#define D_STR_RAW "RAW" -#endif // D_STR_RAW -#ifndef D_STR_RC5 -#define D_STR_RC5 "RC5" -#endif // D_STR_RC5 -#ifndef D_STR_RC5X -#define D_STR_RC5X "RC5X" -#endif // D_STR_RC5X -#ifndef D_STR_RC6 -#define D_STR_RC6 "RC6" -#endif // D_STR_RC6 -#ifndef D_STR_RCMM -#define D_STR_RCMM "RCMM" -#endif // D_STR_RCMM -#ifndef D_STR_RHOSS -#define D_STR_RHOSS "RHOSS" -#endif // D_STR_RHOSS -#ifndef D_STR_SAMSUNG -#define D_STR_SAMSUNG "SAMSUNG" -#endif // D_STR_SAMSUNG -#ifndef D_STR_SAMSUNG36 -#define D_STR_SAMSUNG36 "SAMSUNG36" -#endif // D_STR_SAMSUNG36 -#ifndef D_STR_SAMSUNG_AC -#define D_STR_SAMSUNG_AC "SAMSUNG_AC" -#endif // D_STR_SAMSUNG_AC -#ifndef D_STR_SANYO -#define D_STR_SANYO "SANYO" -#endif // D_STR_SANYO -#ifndef D_STR_SANYO_AC -#define D_STR_SANYO_AC D_STR_SANYO "_AC" -#endif // D_STR_SANYO_AC -#ifndef D_STR_SANYO_AC88 -#define D_STR_SANYO_AC88 D_STR_SANYO_AC "88" -#endif // D_STR_SANYO_AC88 -#ifndef D_STR_SANYO_AC152 -#define D_STR_SANYO_AC152 D_STR_SANYO_AC "152" -#endif // D_STR_SANYO_AC152 -#ifndef D_STR_SANYO_LC7461 -#define D_STR_SANYO_LC7461 D_STR_SANYO "_LC7461" -#endif // D_STR_SANYO_LC7461 -#ifndef D_STR_SHARP -#define D_STR_SHARP "SHARP" -#endif // D_STR_SHARP -#ifndef D_STR_SHARP_AC -#define D_STR_SHARP_AC "SHARP_AC" -#endif // D_STR_SHARP_AC -#ifndef D_STR_SHERWOOD -#define D_STR_SHERWOOD "SHERWOOD" -#endif // D_STR_SHERWOOD -#ifndef D_STR_SONY -#define D_STR_SONY "SONY" -#endif // D_STR_SONY -#ifndef D_STR_SONY_38K -#define D_STR_SONY_38K "SONY_38K" -#endif // D_STR_SONY_38K -#ifndef D_STR_SYMPHONY -#define D_STR_SYMPHONY "SYMPHONY" -#endif // D_STR_SYMPHONY -#ifndef D_STR_TCL96AC -#define D_STR_TCL96AC "TCL96AC" -#endif // D_STR_TCL96AC -#ifndef D_STR_TCL112AC -#define D_STR_TCL112AC "TCL112AC" -#endif // D_STR_TCL112AC -#ifndef D_STR_TECHNIBEL_AC -#define D_STR_TECHNIBEL_AC "TECHNIBEL_AC" -#endif // D_STR_TECHNIBEL_AC -#ifndef D_STR_TECO -#define D_STR_TECO "TECO" -#endif // D_STR_TECO -#ifndef D_STR_TEKNOPOINT -#define D_STR_TEKNOPOINT "TEKNOPOINT" -#endif // D_STR_TEKNOPOINT -#ifndef D_STR_TOSHIBA_AC -#define D_STR_TOSHIBA_AC "TOSHIBA_AC" -#endif // D_STR_TOSHIBA_AC -#ifndef D_STR_TOTO -#define D_STR_TOTO "TOTO" -#endif // D_STR_TOTO -#ifndef D_STR_TRANSCOLD -#define D_STR_TRANSCOLD "TRANSCOLD" -#endif // D_STR_TRANSCOLD -#ifndef D_STR_TROTEC -#define D_STR_TROTEC "TROTEC" -#endif // D_STR_TROTEC -#ifndef D_STR_TROTEC_3550 -#define D_STR_TROTEC_3550 D_STR_TROTEC "_3550" -#endif // D_STR_TROTEC_3550 -#ifndef D_STR_TRUMA -#define D_STR_TRUMA "TRUMA" -#endif // D_STR_TRUMA -#ifndef D_STR_UNUSED -#define D_STR_UNUSED "UNUSED" -#endif // D_STR_UNUSED -#ifndef D_STR_VESTEL_AC -#define D_STR_VESTEL_AC "VESTEL_AC" -#endif // D_STR_VESTEL_AC -#ifndef D_STR_VOLTAS -#define D_STR_VOLTAS "VOLTAS" -#endif // D_STR_VOLTAS -#ifndef D_STR_WHIRLPOOL_AC -#define D_STR_WHIRLPOOL_AC "WHIRLPOOL_AC" -#endif // D_STR_WHIRLPOOL_AC -#ifndef D_STR_WHYNTER -#define D_STR_WHYNTER "WHYNTER" -#endif // D_STR_WHYNTER -#ifndef D_STR_WOWWEE -#define D_STR_WOWWEE "WOWWEE" -#endif // D_STR_WOWWEE -#ifndef D_STR_XMP -#define D_STR_XMP "XMP" -#endif // D_STR_XMP -#ifndef D_STR_YORK -#define D_STR_YORK "YORK" -#endif // D_STR_YORK -#ifndef D_STR_ZEPEAL -#define D_STR_ZEPEAL "ZEPEAL" -#endif // D_STR_ZEPEAL - -// IRrecvDumpV2+ -#ifndef D_STR_TIMESTAMP -#define D_STR_TIMESTAMP "Timestamp" -#endif // D_STR_TIMESTAMP -#ifndef D_STR_LIBRARY -#define D_STR_LIBRARY "Library" -#endif // D_STR_LIBRARY -#ifndef D_STR_MESGDESC -#define D_STR_MESGDESC "Mesg Desc." -#endif // D_STR_MESGDESC -#ifndef D_STR_TOLERANCE -#define D_STR_TOLERANCE "Tolerance" -#endif // D_STR_TOLERANCE -#ifndef D_STR_IRRECVDUMP_STARTUP -#define D_STR_IRRECVDUMP_STARTUP \ - "IRrecvDump is now running and waiting for IR input on Pin %d" -#endif // D_STR_IRRECVDUMP_STARTUP -#ifndef D_WARN_BUFFERFULL -#define D_WARN_BUFFERFULL \ - "WARNING: IR code is too big for buffer (>= %d). " \ - "This result shouldn't be trusted until this is resolved. " \ - "Edit & increase `kCaptureBufferSize`." -#endif // D_WARN_BUFFERFULL - -#endif // LOCALE_DEFAULTS_H_ +// Copyright 2019 - David Conran (@crankyoldgit) +// The default text to use throughout the library. +// The library will use this text if no locale (_IR_LOCALE_) is set or if +// the locale doesn't define particular values. +// If they are defined, this file should NOT override them. +// +// This file should contain a #define for every translateable/locale dependant +// string used by the library. Language specific files don't have to include +// everything. +// +// NOTE: ASCII/UTF-8 characters only. Unicode is NOT supported. +// +// The defaults are English (AU) / en-AU. Australia (AU) is pretty much the same +// as English (UK) for this libraries use case. +#ifndef LOCALE_DEFAULTS_H_ +#define LOCALE_DEFAULTS_H_ + +#ifndef D_STR_UNKNOWN +#define D_STR_UNKNOWN "UNKNOWN" +#endif // D_STR_UNKNOWN +#ifndef D_STR_PROTOCOL +#define D_STR_PROTOCOL "Protocol" +#endif // D_STR_PROTOCOL +#ifndef D_STR_POWER +#define D_STR_POWER "Power" +#endif // D_STR_POWER +#ifndef D_STR_PREVIOUS +#define D_STR_PREVIOUS "Previous" +#endif // D_STR_PREVIOUS +#ifndef D_STR_ON +#define D_STR_ON "On" +#endif // D_STR_ON +#ifndef D_STR_1 +#define D_STR_1 "1" +#endif // D_STR_1 +#ifndef D_STR_OFF +#define D_STR_OFF "Off" +#endif // D_STR_OFF +#ifndef D_STR_0 +#define D_STR_0 "0" +#endif // D_STR_0 +#ifndef D_STR_MODE +#define D_STR_MODE "Mode" +#endif // D_STR_MODE +#ifndef D_STR_TOGGLE +#define D_STR_TOGGLE "Toggle" +#endif // D_STR_TOGGLE +#ifndef D_STR_TURBO +#define D_STR_TURBO "Turbo" +#endif // D_STR_TURBO +#ifndef D_STR_SUPER +#define D_STR_SUPER "Super" +#endif // D_STR_SUPER +#ifndef D_STR_SLEEP +#define D_STR_SLEEP "Sleep" +#endif // D_STR_SLEEP +#ifndef D_STR_LIGHT +#define D_STR_LIGHT "Light" +#endif // D_STR_LIGHT +#ifndef D_STR_POWERFUL +#define D_STR_POWERFUL "Powerful" +#endif // D_STR_POWERFUL +#ifndef D_STR_QUIET +#define D_STR_QUIET "Quiet" +#endif // D_STR_QUIET +#ifndef D_STR_ECONO +#define D_STR_ECONO "Econo" +#endif // D_STR_ECONO +#ifndef D_STR_SWING +#define D_STR_SWING "Swing" +#endif // D_STR_SWING +#ifndef D_STR_SWINGH +#define D_STR_SWINGH D_STR_SWING"(H)" // Set `D_STR_SWING` first! +#endif // D_STR_SWINGH +#ifndef D_STR_SWINGV +#define D_STR_SWINGV D_STR_SWING"(V)" // Set `D_STR_SWING` first! +#endif // D_STR_SWINGV +#ifndef D_STR_BEEP +#define D_STR_BEEP "Beep" +#endif // D_STR_BEEP +#ifndef D_STR_MOULD +#define D_STR_MOULD "Mould" +#endif // D_STR_MOULD +#ifndef D_STR_CLEAN +#define D_STR_CLEAN "Clean" +#endif // D_STR_CLEAN +#ifndef D_STR_PURIFY +#define D_STR_PURIFY "Purify" +#endif // D_STR_PURIFY +#ifndef D_STR_TIMER +#define D_STR_TIMER "Timer" +#endif // D_STR_TIMER +#ifndef D_STR_ONTIMER +#define D_STR_ONTIMER D_STR_ON " " D_STR_TIMER // Set `D_STR_ON` first! +#endif // D_STR_ONTIMER +#ifndef D_STR_OFFTIMER +#define D_STR_OFFTIMER D_STR_OFF " " D_STR_TIMER // Set `D_STR_OFF` first! +#endif // D_STR_OFFTIMER +#ifndef D_STR_TIMERMODE +#define D_STR_TIMERMODE D_STR_TIMER " " D_STR_MODE // Set `D_STR_MODE` first! +#endif // D_STR_TIMERMODE +#ifndef D_STR_CLOCK +#define D_STR_CLOCK "Clock" +#endif // D_STR_CLOCK +#ifndef D_STR_COMMAND +#define D_STR_COMMAND "Command" +#endif // D_STR_COMMAND +#ifndef D_STR_XFAN +#define D_STR_XFAN "XFan" +#endif // D_STR_XFAN +#ifndef D_STR_HEALTH +#define D_STR_HEALTH "Health" +#endif // D_STR_HEALTH +#ifndef D_STR_MODEL +#define D_STR_MODEL "Model" +#endif // D_STR_MODEL +#ifndef D_STR_TEMP +#define D_STR_TEMP "Temp" +#endif // D_STR_TEMP +#ifndef D_STR_IFEEL +#define D_STR_IFEEL "IFeel" +#endif // D_STR_IFEEL +#ifndef D_STR_ISEE +#define D_STR_ISEE "ISee" +#endif // D_STR_ISEE +#ifndef D_STR_HUMID +#define D_STR_HUMID "Humid" +#endif // D_STR_HUMID +#ifndef D_STR_SAVE +#define D_STR_SAVE "Save" +#endif // D_STR_SAVE +#ifndef D_STR_EYE +#define D_STR_EYE "Eye" +#endif // D_STR_EYE +#ifndef D_STR_FOLLOW +#define D_STR_FOLLOW "Follow" +#endif // D_STR_FOLLOW +#ifndef D_STR_ION +#define D_STR_ION "Ion" +#endif // D_STR_ION +#ifndef D_STR_FRESH +#define D_STR_FRESH "Fresh" +#endif // D_STR_FRESH +#ifndef D_STR_HOLD +#define D_STR_HOLD "Hold" +#endif // D_STR_HOLD +#ifndef D_STR_8C_HEAT +#define D_STR_8C_HEAT "8C " D_STR_HEAT // Set `D_STR_HEAT` first! +#endif // D_STR_8C_HEAT +#ifndef D_STR_10C_HEAT +#define D_STR_10C_HEAT "10C " D_STR_HEAT // Set `D_STR_HEAT` first! +#endif // D_STR_10C_HEAT +#ifndef D_STR_BUTTON +#define D_STR_BUTTON "Button" +#endif // D_STR_BUTTON +#ifndef D_STR_NIGHT +#define D_STR_NIGHT "Night" +#endif // D_STR_NIGHT +#ifndef D_STR_SILENT +#define D_STR_SILENT "Silent" +#endif // D_STR_SILENT +#ifndef D_STR_FILTER +#define D_STR_FILTER "Filter" +#endif // D_STR_FILTER +#ifndef D_STR_3D +#define D_STR_3D "3D" +#endif // D_STR_3D +#ifndef D_STR_CELSIUS +#define D_STR_CELSIUS "Celsius" +#endif // D_STR_CELSIUS +#ifndef D_STR_FAHRENHEIT +#define D_STR_FAHRENHEIT "Fahrenheit" +#endif // D_STR_FAHRENHEIT +#ifndef D_STR_CELSIUS_FAHRENHEIT +#define D_STR_CELSIUS_FAHRENHEIT D_STR_CELSIUS "/" D_STR_FAHRENHEIT +#endif // D_STR_CELSIUS_FAHRENHEIT +#ifndef D_STR_UP +#define D_STR_UP "Up" +#endif // D_STR_UP +#ifndef D_STR_TEMPUP +#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP // Set `D_STR_TEMP` first! +#endif // D_STR_TEMPUP +#ifndef D_STR_DOWN +#define D_STR_DOWN "Down" +#endif // D_STR_DOWN +#ifndef D_STR_TEMPDOWN +#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN // Set `D_STR_TEMP` first! +#endif // D_STR_TEMPDOWN +#ifndef D_STR_CHANGE +#define D_STR_CHANGE "Change" +#endif // D_STR_CHANGE +#ifndef D_STR_START +#define D_STR_START "Start" +#endif // D_STR_START +#ifndef D_STR_STOP +#define D_STR_STOP "Stop" +#endif // D_STR_STOP +#ifndef D_STR_MOVE +#define D_STR_MOVE "Move" +#endif // D_STR_MOVE +#ifndef D_STR_SET +#define D_STR_SET "Set" +#endif // D_STR_SET +#ifndef D_STR_CANCEL +#define D_STR_CANCEL "Cancel" +#endif // D_STR_CANCEL +#ifndef D_STR_COMFORT +#define D_STR_COMFORT "Comfort" +#endif // D_STR_COMFORT +#ifndef D_STR_SENSOR +#define D_STR_SENSOR "Sensor" +#endif // D_STR_SENSOR +#ifndef D_STR_ABSENSEDETECT +#define D_STR_ABSENSEDETECT "Absense detect" +#endif // D_STR_ABSENSEDETECT +#ifndef D_STR_DIRECT +#define D_STR_DIRECT "Direct" +#endif // D_STR_DIRECT +#ifndef D_STR_INDIRECT +#define D_STR_INDIRECT "Indirect" +#endif // D_STR_INDIRECT +#ifndef D_STR_DIRECTINDIRECTMODE +#define D_STR_DIRECTINDIRECTMODE D_STR_DIRECT " / " \ +D_STR_INDIRECT " " D_STR_MODE +#endif // D_STR_DIRECTINDIRECTMODE +#ifndef D_STR_DISPLAY +#define D_STR_DISPLAY "Display" +#endif // D_STR_DISPLAY +#ifndef D_STR_WEEKLY +#define D_STR_WEEKLY "Weekly" +#endif // D_STR_WEEKLY +#ifndef D_STR_WEEKLYTIMER +#define D_STR_WEEKLYTIMER D_STR_WEEKLY " " D_STR_TIMER // Needs `D_STR_WEEKLY`! +#endif // D_STR_WEEKLYTIMER +#ifndef D_STR_WIFI +#define D_STR_WIFI "WiFi" +#endif // D_STR_WIFI +#ifndef D_STR_LAST +#define D_STR_LAST "Last" +#endif // D_STR_LAST +#ifndef D_STR_FAST +#define D_STR_FAST "Fast" +#endif // D_STR_FAST +#ifndef D_STR_SLOW +#define D_STR_SLOW "Slow" +#endif // D_STR_SLOW +#ifndef D_STR_AIRFLOW +#define D_STR_AIRFLOW "Air Flow" +#endif // D_STR_AIRFLOW +#ifndef D_STR_STEP +#define D_STR_STEP "Step" +#endif // D_STR_STEP +#ifndef D_STR_NA +#define D_STR_NA "N/A" +#endif // D_STR_NA +#ifndef D_STR_INSIDE +#define D_STR_INSIDE "Inside" +#endif // D_STR_INSIDE +#ifndef D_STR_OUTSIDE +#define D_STR_OUTSIDE "Outside" +#endif // D_STR_OUTSIDE +#ifndef D_STR_LOUD +#define D_STR_LOUD "Loud" +#endif // D_STR_LOUD +#ifndef D_STR_UPPER +#define D_STR_UPPER "Upper" +#endif // D_STR_UPPER +#ifndef D_STR_LOWER +#define D_STR_LOWER "Lower" +#endif // D_STR_LOWER +#ifndef D_STR_BREEZE +#define D_STR_BREEZE "Breeze" +#endif // D_STR_BREEZE +#ifndef D_STR_CIRCULATE +#define D_STR_CIRCULATE "Circulate" +#endif // D_STR_CIRCULATE +#ifndef D_STR_CEILING +#define D_STR_CEILING "Ceiling" +#endif // D_STR_CEILING +#ifndef D_STR_WALL +#define D_STR_WALL "Wall" +#endif // D_STR_WALL +#ifndef D_STR_ROOM +#define D_STR_ROOM "Room" +#endif // D_STR_ROOM +#ifndef D_STR_6THSENSE +#define D_STR_6THSENSE "6th Sense" +#endif // D_STR_6THSENSE +#ifndef D_STR_ZONEFOLLOW +#define D_STR_ZONEFOLLOW "Zone Follow" +#endif // D_STR_ZONEFOLLOW +#ifndef D_STR_FIXED +#define D_STR_FIXED "Fixed" +#endif // D_STR_FIXED +#ifndef D_STR_TYPE +#define D_STR_TYPE "Type" +#endif // D_STR_TYPE +#ifndef D_STR_SPECIAL +#define D_STR_SPECIAL "Special" +#endif // D_STR_SPECIAL +#ifndef D_STR_RECYCLE +#define D_STR_RECYCLE "Recycle" +#endif // D_STR_RECYCLE +#ifndef D_STR_ID +#define D_STR_ID "Id" +#endif // D_STR_ID +#ifndef D_STR_VANE +#define D_STR_VANE "Vane" +#endif // D_STR_VANE +#ifndef D_STR_LOCK +#define D_STR_LOCK "Lock" +#endif // D_STR_LOCK +#ifndef D_STR_REPORT +#define D_STR_REPORT "Report" +#endif // D_STR_REPORT + +#ifndef D_STR_AUTO +#define D_STR_AUTO "Auto" +#endif // D_STR_AUTO +#ifndef D_STR_AUTOMATIC +#define D_STR_AUTOMATIC "Automatic" +#endif // D_STR_AUTOMATIC +#ifndef D_STR_MANUAL +#define D_STR_MANUAL "Manual" +#endif // D_STR_MANUAL +#ifndef D_STR_COOL +#define D_STR_COOL "Cool" +#endif // D_STR_COOL +#ifndef D_STR_COOLING +#define D_STR_COOLING "Cooling" +#endif // D_STR_COOLING +#ifndef D_STR_HEAT +#define D_STR_HEAT "Heat" +#endif // D_STR_HEAT +#ifndef D_STR_HEATING +#define D_STR_HEATING "Heating" +#endif // D_STR_HEATING +#ifndef D_STR_FAN +#define D_STR_FAN "Fan" +#endif // D_STR_FAN +#ifndef D_STR_FANONLY +#define D_STR_FANONLY "fan-only" +#endif // D_STR_FANONLY +#ifndef D_STR_FAN_ONLY +#define D_STR_FAN_ONLY "fan_only" +#endif // D_STR_FAN_ONLY +#ifndef D_STR_ONLY +#define D_STR_ONLY "Only" +#endif // D_STR_ONLY +#ifndef D_STR_FANSPACEONLY +#define D_STR_FANSPACEONLY D_STR_FAN " " D_STR_ONLY +#endif // D_STR_FANSPACEONLY +#ifndef D_STR_FANONLYNOSPACE +#define D_STR_FANONLYNOSPACE D_STR_FAN D_STR_ONLY +#endif // D_STR_FANONLYNOSPACE +#ifndef D_STR_DRY +#define D_STR_DRY "Dry" +#endif // D_STR_DRY +#ifndef D_STR_DRYING +#define D_STR_DRYING "Drying" +#endif // D_STR_DRYING +#ifndef D_STR_DEHUMIDIFY +#define D_STR_DEHUMIDIFY "Dehumidify" +#endif // D_STR_DEHUMIDIFY + +#ifndef D_STR_MAX +#define D_STR_MAX "Max" +#endif // D_STR_MAX +#ifndef D_STR_MAXIMUM +#define D_STR_MAXIMUM "Maximum" +#endif // D_STR_MAXIMUM +#ifndef D_STR_MIN +#define D_STR_MIN "Min" +#endif // D_STR_MIN +#ifndef D_STR_MINIMUM +#define D_STR_MINIMUM "Minimum" +#endif // D_STR_MINIMUM +#ifndef D_STR_MED +#define D_STR_MED "Med" +#endif // D_STR_MED +#ifndef D_STR_MEDIUM +#define D_STR_MEDIUM "Medium" +#endif // D_STR_MEDIUM +#ifndef D_STR_MED_HIGH +#define D_STR_MED_HIGH D_STR_MED "-" D_STR_HIGH +#endif // D_STR_MED_HIGH + +#ifndef D_STR_HIGHEST +#define D_STR_HIGHEST "Highest" +#endif // D_STR_HIGHEST +#ifndef D_STR_HIGH +#define D_STR_HIGH "High" +#endif // D_STR_HIGH +#ifndef D_STR_HI +#define D_STR_HI "Hi" +#endif // D_STR_HI +#ifndef D_STR_MID +#define D_STR_MID "Mid" +#endif // D_STR_MID +#ifndef D_STR_MIDDLE +#define D_STR_MIDDLE "Middle" +#endif // D_STR_MIDDLE +#ifndef D_STR_LOW +#define D_STR_LOW "Low" +#endif // D_STR_LOW +#ifndef D_STR_LO +#define D_STR_LO "Lo" +#endif // D_STR_LO +#ifndef D_STR_LOWEST +#define D_STR_LOWEST "Lowest" +#endif // D_STR_LOWEST +#ifndef D_STR_RIGHT +#define D_STR_RIGHT "Right" +#endif // D_STR_RIGHT +#ifndef D_STR_MAXRIGHT +#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` first! +#endif // D_STR_MAXRIGHT +#ifndef D_STR_MAXRIGHT_NOSPACE +#define D_STR_MAXRIGHT_NOSPACE D_STR_MAX D_STR_RIGHT // Set `D_STR_MAX` first! +#endif // D_STR_MAXRIGHT_NOSPACE +#ifndef D_STR_RIGHTMAX +#define D_STR_RIGHTMAX D_STR_RIGHT " " D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_RIGHTMAX +#ifndef D_STR_RIGHTMAX_NOSPACE +#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_RIGHTMAX_NOSPACE +#ifndef D_STR_LEFT +#define D_STR_LEFT "Left" +#endif // D_STR_LEFT +#ifndef D_STR_MAXLEFT +#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` first! +#endif // D_STR_MAXLEFT +#ifndef D_STR_MAXLEFT_NOSPACE +#define D_STR_MAXLEFT_NOSPACE D_STR_MAX D_STR_LEFT // Set `D_STR_MAX` first! +#endif // D_STR_MAXLEFT_NOSPACE +#ifndef D_STR_LEFTMAX +#define D_STR_LEFTMAX D_STR_LEFT " " D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_LEFTMAX +#ifndef D_STR_LEFTMAX_NOSPACE +#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_LEFTMAX_NOSPACE +#ifndef D_STR_WIDE +#define D_STR_WIDE "Wide" +#endif // D_STR_WIDE +#ifndef D_STR_CENTRE +#define D_STR_CENTRE "Centre" +#endif // D_STR_CENTRE +#ifndef D_STR_TOP +#define D_STR_TOP "Top" +#endif // D_STR_TOP +#ifndef D_STR_BOTTOM +#define D_STR_BOTTOM "Bottom" +#endif // D_STR_BOTTOM +#ifndef D_STR_UPPER_MIDDLE +#define D_STR_UPPER_MIDDLE D_STR_UPPER "-" D_STR_MIDDLE +#endif // D_STR_UPPER_MIDDLE +#ifndef D_STR_CONFIG +#define D_STR_CONFIG "Config" +#endif // D_STR_CONFIG +#ifndef D_STR_CONTROL +#define D_STR_CONTROL "Control" +#endif // D_STR_CONTROL +#ifndef D_STR_SET_TIMER +#define D_STR_SET_TIMER D_STR_SET " " D_STR_TIMER +#endif // D_STR_AC_TIMER +#ifndef D_STR_SCHEDULE +#define D_STR_SCHEDULE "Schedule" +#endif // D_STR_SCHEDULE +#ifndef D_STR_CH +#define D_STR_CH "CH#" +#endif // D_STR_CH +#ifndef D_STR_TIMER_ACTIVE_DAYS +#define D_STR_TIMER_ACTIVE_DAYS "TimerActiveDays" +#endif // D_STR_TIMER_ACTIVE_DAYS +#ifndef D_STR_KEY +#define D_STR_KEY "Key" +#endif // D_STR_KEY +#ifndef D_STR_VALUE +#define D_STR_VALUE "Value" +#endif // D_STR_VALUE + +// Compound words/phrases/descriptions from pre-defined words. +// Note: Obviously these need to be defined *after* their component words. +#ifndef D_STR_ECONOTOGGLE +#define D_STR_ECONOTOGGLE D_STR_ECONO " " D_STR_TOGGLE +#endif // D_STR_ECONOTOGGLE +#ifndef D_STR_EYEAUTO +#define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO +#endif // D_STR_EYEAUTO +#ifndef D_STR_LIGHTTOGGLE +#define D_STR_LIGHTTOGGLE D_STR_LIGHT " " D_STR_TOGGLE +#endif // D_STR_LIGHTTOGGLE +#ifndef D_STR_OUTSIDEQUIET +#define D_STR_OUTSIDEQUIET D_STR_OUTSIDE " " D_STR_QUIET +#endif // D_STR_OUTSIDEQUIET +#ifndef D_STR_POWERTOGGLE +#define D_STR_POWERTOGGLE D_STR_POWER " " D_STR_TOGGLE +#endif // D_STR_POWERTOGGLE +#ifndef D_STR_POWERBUTTON +#define D_STR_POWERBUTTON D_STR_POWER " " D_STR_BUTTON +#endif // D_STR_POWERBUTTON +#ifndef D_STR_PREVIOUSPOWER +#define D_STR_PREVIOUSPOWER D_STR_PREVIOUS " " D_STR_POWER +#endif // D_STR_PREVIOUSPOWER +#ifndef D_STR_DISPLAYTEMP +#define D_STR_DISPLAYTEMP D_STR_DISPLAY " " D_STR_TEMP +#endif // D_STR_DISPLAYTEMP +#ifndef D_STR_IFEELREPORT +#define D_STR_IFEELREPORT D_STR_IFEEL " " D_STR_REPORT +#endif // D_STR_IFEELREPORT +#ifndef D_STR_SENSORTEMP +#define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP +#endif // D_STR_SENSORTEMP +#ifndef D_STR_SLEEP_TIMER +#define D_STR_SLEEP_TIMER D_STR_SLEEP " " D_STR_TIMER +#endif // D_STR_SLEEP_TIMER +#ifndef D_STR_SWINGVMODE +#define D_STR_SWINGVMODE D_STR_SWINGV " " D_STR_MODE +#endif // D_STR_SWINGVMODE +#ifndef D_STR_SWINGVTOGGLE +#define D_STR_SWINGVTOGGLE D_STR_SWINGV " " D_STR_TOGGLE +#endif // D_STR_SWINGVTOGGLE +#ifndef D_STR_TURBOTOGGLE +#define D_STR_TURBOTOGGLE D_STR_TURBO " " D_STR_TOGGLE +#endif // D_STR_TURBOTOGGLE + +// Separators +#ifndef D_CHR_TIME_SEP +#define D_CHR_TIME_SEP ':' +#endif // D_CHR_TIME_SEP +#ifndef D_STR_SPACELBRACE +#define D_STR_SPACELBRACE " (" +#endif // D_STR_SPACELBRACE +#ifndef D_STR_COMMASPACE +#define D_STR_COMMASPACE ", " +#endif // D_STR_COMMASPACE +#ifndef D_STR_COLONSPACE +#define D_STR_COLONSPACE ": " +#endif // D_STR_COLONSPACE +#ifndef D_STR_DASH +#define D_STR_DASH "-" +#endif // D_STR_DASH + +#ifndef D_STR_DAY +#define D_STR_DAY "Day" +#endif // D_STR_DAY +#ifndef D_STR_DAYS +#define D_STR_DAYS D_STR_DAY "s" +#endif // D_STR_DAYS +#ifndef D_STR_HOUR +#define D_STR_HOUR "Hour" +#endif // D_STR_HOUR +#ifndef D_STR_HOURS +#define D_STR_HOURS D_STR_HOUR "s" +#endif // D_STR_HOURS +#ifndef D_STR_MINUTE +#define D_STR_MINUTE "Minute" +#endif // D_STR_MINUTE +#ifndef D_STR_MINUTES +#define D_STR_MINUTES D_STR_MINUTE "s" +#endif // D_STR_MINUTES +#ifndef D_STR_SECOND +#define D_STR_SECOND "Second" +#endif // D_STR_SECOND +#ifndef D_STR_SECONDS +#define D_STR_SECONDS D_STR_SECOND "s" +#endif // D_STR_SECONDS +#ifndef D_STR_NOW +#define D_STR_NOW "Now" +#endif // D_STR_NOW +#ifndef D_STR_THREELETTERDAYS +#define D_STR_THREELETTERDAYS "SunMonTueWedThuFriSat" +#endif // D_STR_THREELETTERDAYS + +#ifndef D_STR_YES +#define D_STR_YES "Yes" +#endif // D_STR_YES +#ifndef D_STR_NO +#define D_STR_NO "No" +#endif // D_STR_NO +#ifndef D_STR_TRUE +#define D_STR_TRUE "True" +#endif // D_STR_TRUE +#ifndef D_STR_FALSE +#define D_STR_FALSE "False" +#endif // D_STR_FALSE + +#ifndef D_STR_REPEAT +#define D_STR_REPEAT "Repeat" +#endif // D_STR_REPEAT +#ifndef D_STR_CODE +#define D_STR_CODE "Code" +#endif // D_STR_CODE +#ifndef D_STR_BITS +#define D_STR_BITS "Bits" +#endif // D_STR_BITS + +// Model Names +#ifndef D_STR_YAW1F +#define D_STR_YAW1F "YAW1F" +#endif // D_STR_YAW1F +#ifndef D_STR_YBOFB +#define D_STR_YBOFB "YBOFB" +#endif // D_STR_YBOFB +#ifndef D_STR_YX1FSF +#define D_STR_YX1FSF "YX1FSF" +#endif // D_STR_YX1FSF +#ifndef D_STR_V9014557_A +#define D_STR_V9014557_A "V9014557-A" +#endif // D_STR_V9014557_A +#ifndef D_STR_V9014557_B +#define D_STR_V9014557_B "V9014557-B" +#endif // D_STR_V9014557_B +#ifndef D_STR_RLT0541HTA_A +#define D_STR_RLT0541HTA_A "R-LT0541-HTA-A" +#endif // D_STR_RLT0541HTA_A +#ifndef D_STR_RLT0541HTA_B +#define D_STR_RLT0541HTA_B "R-LT0541-HTA-B" +#endif // D_STR_RLT0541HTA_B +#ifndef D_STR_ARRAH2E +#define D_STR_ARRAH2E "ARRAH2E" +#endif // D_STR_ARRAH2E +#ifndef D_STR_ARDB1 +#define D_STR_ARDB1 "ARDB1" +#endif // D_STR_ARDB1 +#ifndef D_STR_ARREB1E +#define D_STR_ARREB1E "ARREB1E" +#endif // D_STR_ARREB1E +#ifndef D_STR_ARJW2 +#define D_STR_ARJW2 "ARJW2" +#endif // D_STR_ARJW2 +#ifndef D_STR_ARRY4 +#define D_STR_ARRY4 "ARRY4" +#endif // D_STR_ARRY4 +#ifndef D_STR_ARREW4E +#define D_STR_ARREW4E "ARREW4E" +#endif // D_STR_ARREW4E +#ifndef D_STR_GE6711AR2853M +#define D_STR_GE6711AR2853M "GE6711AR2853M" +#endif // D_STR_GE6711AR2853M +#ifndef D_STR_AKB75215403 +#define D_STR_AKB75215403 "AKB75215403" +#endif // D_STR_AKB75215403 +#ifndef D_STR_AKB74955603 +#define D_STR_AKB74955603 "AKB74955603" +#endif // D_STR_AKB74955603 +#ifndef D_STR_AKB73757604 +#define D_STR_AKB73757604 "AKB73757604" +#endif // D_STR_AKB73757604 +#ifndef D_STR_LG6711A20083V +#define D_STR_LG6711A20083V "LG6711A20083V" +#endif // D_STR_LG6711A20083V +#ifndef D_STR_KKG9AC1 +#define D_STR_KKG9AC1 "KKG9AC1" +#endif // D_STR_KKG9AC1 +#ifndef D_STR_KKG29AC1 +#define D_STR_KKG29AC1 "KKG29AC1" +#endif // D_STR_KKG9AC1 +#ifndef D_STR_LKE +#define D_STR_LKE "LKE" +#endif // D_STR_LKE +#ifndef D_STR_NKE +#define D_STR_NKE "NKE" +#endif // D_STR_NKE +#ifndef D_STR_DKE +#define D_STR_DKE "DKE" +#endif // D_STR_DKE +#ifndef D_STR_PKR +#define D_STR_PKR "PKR" +#endif // D_STR_PKR +#ifndef D_STR_JKE +#define D_STR_JKE "JKE" +#endif // D_STR_JKE +#ifndef D_STR_CKP +#define D_STR_CKP "CKP" +#endif // D_STR_CKP +#ifndef D_STR_RKR +#define D_STR_RKR "RKR" +#endif // D_STR_RKR +#ifndef D_STR_PANASONICLKE +#define D_STR_PANASONICLKE "PANASONICLKE" +#endif // D_STR_PANASONICLKE +#ifndef D_STR_PANASONICNKE +#define D_STR_PANASONICNKE "PANASONICNKE" +#endif // D_STR_PANASONICNKE +#ifndef D_STR_PANASONICDKE +#define D_STR_PANASONICDKE "PANASONICDKE" +#endif // D_STR_PANASONICDKE +#ifndef D_STR_PANASONICPKR +#define D_STR_PANASONICPKR "PANASONICPKR" +#endif // D_STR_PANASONICPKR +#ifndef D_STR_PANASONICJKE +#define D_STR_PANASONICJKE "PANASONICJKE" +#endif // D_STR_PANASONICJKE +#ifndef D_STR_PANASONICCKP +#define D_STR_PANASONICCKP "PANASONICCKP" +#endif // D_STR_PANASONICCKP +#ifndef D_STR_PANASONICRKR +#define D_STR_PANASONICRKR "PANASONICRKR" +#endif // D_STR_PANASONICRKR +#ifndef D_STR_A907 +#define D_STR_A907 "A907" +#endif // D_STR_A907 +#ifndef D_STR_A705 +#define D_STR_A705 "A705" +#endif // D_STR_A705 +#ifndef D_STR_A903 +#define D_STR_A903 "A903" +#endif // D_STR_A903 +#ifndef D_STR_TAC09CHSD +#define D_STR_TAC09CHSD "TAC09CHSD" +#endif // D_STR_TAC09CHSD +#ifndef D_STR_GZ055BE1 +#define D_STR_GZ055BE1 "GZ055BE1" +#endif // D_STR_GZ055BE1 +#ifndef D_STR_122LZF +#define D_STR_122LZF "122LZF" +#endif // D_STR_122LZF +#ifndef D_STR_DG11J13A +#define D_STR_DG11J13A "DG11J13A" +#endif // D_STR_DG11J13A +#ifndef D_STR_DG11J104 +#define D_STR_DG11J104 "DG11J104" +#endif // D_STR_DG11J104 +#ifndef D_STR_DG11J191 +#define D_STR_DG11J191 "DG11J191" +#endif // D_STR_DG11J191 +#ifndef D_STR_ARGO_WREM2 +#define D_STR_ARGO_WREM2 "WREM2" +#endif // D_STR_ARGO_WREM2 +#ifndef D_STR_ARGO_WREM3 +#define D_STR_ARGO_WREM3 "WREM3" +#endif // D_STR_ARGO_WREM3 + +// Protocols Names +#ifndef D_STR_AIRTON +#define D_STR_AIRTON "AIRTON" +#endif // D_STR_AIRTON +#ifndef D_STR_AIRWELL +#define D_STR_AIRWELL "AIRWELL" +#endif // D_STR_AIRWELL +#ifndef D_STR_AIWA_RC_T501 +#define D_STR_AIWA_RC_T501 "AIWA_RC_T501" +#endif // D_STR_AIWA_RC_T501 +#ifndef D_STR_AMCOR +#define D_STR_AMCOR "AMCOR" +#endif // D_STR_AMCOR +#ifndef D_STR_ARGO +#define D_STR_ARGO "ARGO" +#endif // D_STR_ARGO +#ifndef D_STR_ARRIS +#define D_STR_ARRIS "ARRIS" +#endif // D_STR_ARRIS +#ifndef D_STR_BOSCH +#define D_STR_BOSCH "BOSCH" +#endif // D_STR_BOSCH +#ifndef D_STR_BOSCH144 +#define D_STR_BOSCH144 D_STR_BOSCH "144" +#endif // D_STR_BOSCH144 +#ifndef D_STR_BOSE +#define D_STR_BOSE "BOSE" +#endif // D_STR_BOSE +#ifndef D_STR_CARRIER_AC +#define D_STR_CARRIER_AC "CARRIER_AC" +#endif // D_STR_CARRIER_AC +#ifndef D_STR_CARRIER_AC40 +#define D_STR_CARRIER_AC40 D_STR_CARRIER_AC "40" +#endif // D_STR_CARRIER_AC40 +#ifndef D_STR_CARRIER_AC64 +#define D_STR_CARRIER_AC64 D_STR_CARRIER_AC "64" +#endif // D_STR_CARRIER_AC64 +#ifndef D_STR_CARRIER_AC84 +#define D_STR_CARRIER_AC84 D_STR_CARRIER_AC "84" +#endif // D_STR_CARRIER_AC84 +#ifndef D_STR_CARRIER_AC128 +#define D_STR_CARRIER_AC128 D_STR_CARRIER_AC "128" +#endif // D_STR_CARRIER_AC128 +#ifndef D_STR_CLIMABUTLER +#define D_STR_CLIMABUTLER "CLIMABUTLER" +#endif // D_STR_CLIMABUTLER +#ifndef D_STR_COOLIX +#define D_STR_COOLIX "COOLIX" +#endif // D_STR_COOLIX +#ifndef D_STR_COOLIX48 +#define D_STR_COOLIX48 D_STR_COOLIX "48" +#endif // D_STR_COOLIX48 +#ifndef D_STR_CORONA_AC +#define D_STR_CORONA_AC "CORONA_AC" +#endif // D_STR_CORONA_AC +#ifndef D_STR_DAIKIN +#define D_STR_DAIKIN "DAIKIN" +#endif // D_STR_DAIKIN +#ifndef D_STR_DAIKIN128 +#define D_STR_DAIKIN128 D_STR_DAIKIN "128" +#endif // D_STR_DAIKIN128 +#ifndef D_STR_DAIKIN152 +#define D_STR_DAIKIN152 D_STR_DAIKIN "152" +#endif // D_STR_DAIKIN152 +#ifndef D_STR_DAIKIN160 +#define D_STR_DAIKIN160 D_STR_DAIKIN "160" +#endif // D_STR_DAIKIN160 +#ifndef D_STR_DAIKIN176 +#define D_STR_DAIKIN176 D_STR_DAIKIN "176" +#endif // D_STR_DAIKIN176 +#ifndef D_STR_DAIKIN2 +#define D_STR_DAIKIN2 D_STR_DAIKIN "2" +#endif // D_STR_DAIKIN2 +#ifndef D_STR_DAIKIN200 +#define D_STR_DAIKIN200 D_STR_DAIKIN "200" +#endif // D_STR_DAIKIN200 +#ifndef D_STR_DAIKIN216 +#define D_STR_DAIKIN216 D_STR_DAIKIN "216" +#endif // D_STR_DAIKIN216 +#ifndef D_STR_DAIKIN312 +#define D_STR_DAIKIN312 D_STR_DAIKIN "312" +#endif // D_STR_DAIKIN312 +#ifndef D_STR_DAIKIN64 +#define D_STR_DAIKIN64 D_STR_DAIKIN "64" +#endif // D_STR_DAIKIN64 +#ifndef D_STR_DELONGHI_AC +#define D_STR_DELONGHI_AC "DELONGHI_AC" +#endif // D_STR_DELONGHI_AC +#ifndef D_STR_DENON +#define D_STR_DENON "DENON" +#endif // D_STR_DENON +#ifndef D_STR_DISH +#define D_STR_DISH "DISH" +#endif // D_STR_DISH +#ifndef D_STR_DOSHISHA +#define D_STR_DOSHISHA "DOSHISHA" +#endif // D_STR_DOSHISHA +#ifndef D_STR_ECOCLIM +#define D_STR_ECOCLIM "ECOCLIM" +#endif // D_STR_ECOCLIM +#ifndef D_STR_ELECTRA_AC +#define D_STR_ELECTRA_AC "ELECTRA_AC" +#endif // D_STR_ELECTRA_AC +#ifndef D_STR_ELITESCREENS +#define D_STR_ELITESCREENS "ELITESCREENS" +#endif // D_STR_ELITESCREENS +#ifndef D_STR_EPSON +#define D_STR_EPSON "EPSON" +#endif // D_STR_EPSON +#ifndef D_STR_FUJITSU_AC +#define D_STR_FUJITSU_AC "FUJITSU_AC" +#endif // D_STR_FUJITSU_AC +#ifndef D_STR_FUJITSU_AC264 +#define D_STR_FUJITSU_AC264 "FUJITSU_AC264" +#endif // D_STR_FUJITSU_AC264 +#ifndef D_STR_GICABLE +#define D_STR_GICABLE "GICABLE" +#endif // D_STR_GICABLE +#ifndef D_STR_GLOBALCACHE +#define D_STR_GLOBALCACHE "GLOBALCACHE" +#endif // D_STR_GLOBALCACHE +#ifndef D_STR_GOODWEATHER +#define D_STR_GOODWEATHER "GOODWEATHER" +#endif // D_STR_GOODWEATHER +#ifndef D_STR_GORENJE +#define D_STR_GORENJE "GORENJE" +#endif // D_STR_GORENJE +#ifndef D_STR_GREE +#define D_STR_GREE "GREE" +#endif // D_STR_GREE +#ifndef D_STR_HAIER_AC +#define D_STR_HAIER_AC "HAIER_AC" +#endif // D_STR_HAIER_AC +#ifndef D_STR_HAIER_AC_YRW02 +#define D_STR_HAIER_AC_YRW02 D_STR_HAIER_AC "_YRW02" +#endif // D_STR_HAIER_AC_YRW02 +#ifndef D_STR_HAIER_AC160 +#define D_STR_HAIER_AC160 D_STR_HAIER_AC "160" +#endif // D_STR_HAIER_AC160 +#ifndef D_STR_HAIER_AC176 +#define D_STR_HAIER_AC176 D_STR_HAIER_AC "176" +#endif // D_STR_HAIER_AC176 +#ifndef D_STR_HITACHI_AC +#define D_STR_HITACHI_AC "HITACHI_AC" +#endif // D_STR_HITACHI_AC +#ifndef D_STR_HITACHI_AC1 +#define D_STR_HITACHI_AC1 D_STR_HITACHI_AC "1" +#endif // D_STR_HITACHI_AC1 +#ifndef D_STR_HITACHI_AC2 +#define D_STR_HITACHI_AC2 D_STR_HITACHI_AC "2" +#endif // D_STR_HITACHI_AC2 +#ifndef D_STR_HITACHI_AC3 +#define D_STR_HITACHI_AC3 D_STR_HITACHI_AC "3" +#endif // D_STR_HITACHI_AC3 +#ifndef D_STR_HITACHI_AC264 +#define D_STR_HITACHI_AC264 D_STR_HITACHI_AC "264" +#endif // D_STR_HITACHI_AC264 +#ifndef D_STR_HITACHI_AC296 +#define D_STR_HITACHI_AC296 D_STR_HITACHI_AC "296" +#endif // D_STR_HITACHI_AC296 +#ifndef D_STR_HITACHI_AC344 +#define D_STR_HITACHI_AC344 D_STR_HITACHI_AC "344" +#endif // D_STR_HITACHI_AC344 +#ifndef D_STR_HITACHI_AC424 +#define D_STR_HITACHI_AC424 D_STR_HITACHI_AC "424" +#endif // D_STR_HITACHI_AC424 +#ifndef D_STR_INAX +#define D_STR_INAX "INAX" +#endif // D_STR_INAX +#ifndef D_STR_JVC +#define D_STR_JVC "JVC" +#endif // D_STR_JVC +#ifndef D_STR_KELON +#define D_STR_KELON "KELON" +#endif // D_STR_KELON +#ifndef D_STR_KELON168 +#define D_STR_KELON168 D_STR_KELON "168" +#endif // D_STR_KELON168 +#ifndef D_STR_KELVINATOR +#define D_STR_KELVINATOR "KELVINATOR" +#endif // D_STR_KELVINATOR +#ifndef D_STR_LASERTAG +#define D_STR_LASERTAG "LASERTAG" +#endif // D_STR_LASERTAG +#ifndef D_STR_LEGOPF +#define D_STR_LEGOPF "LEGOPF" +#endif // D_STR_LEGOPF +#ifndef D_STR_LG +#define D_STR_LG "LG" +#endif // D_STR_LG +#ifndef D_STR_LG2 +#define D_STR_LG2 "LG2" +#endif // D_STR_LG2 +#ifndef D_STR_LUTRON +#define D_STR_LUTRON "LUTRON" +#endif // D_STR_LUTRON +#ifndef D_STR_MAGIQUEST +#define D_STR_MAGIQUEST "MAGIQUEST" +#endif // D_STR_MAGIQUEST +#ifndef D_STR_METZ +#define D_STR_METZ "METZ" +#endif // D_STR_METZ +#ifndef D_STR_MIDEA +#define D_STR_MIDEA "MIDEA" +#endif // D_STR_MIDEA +#ifndef D_STR_MIDEA24 +#define D_STR_MIDEA24 "MIDEA24" +#endif // D_STR_MIDEA24 +#ifndef D_STR_MILESTAG2 +#define D_STR_MILESTAG2 "MILESTAG2" +#endif // D_STR_MILESTAG2 +#ifndef D_STR_MIRAGE +#define D_STR_MIRAGE "MIRAGE" +#endif // D_STR_MIRAGE +#ifndef D_STR_MITSUBISHI +#define D_STR_MITSUBISHI "MITSUBISHI" +#endif // D_STR_MITSUBISHI +#ifndef D_STR_MITSUBISHI112 +#define D_STR_MITSUBISHI112 "MITSUBISHI112" +#endif // D_STR_MITSUBISHI112 +#ifndef D_STR_MITSUBISHI136 +#define D_STR_MITSUBISHI136 "MITSUBISHI136" +#endif // D_STR_MITSUBISHI136 +#ifndef D_STR_MITSUBISHI2 +#define D_STR_MITSUBISHI2 "MITSUBISHI2" +#endif // D_STR_MITSUBISHI2 +#ifndef D_STR_MITSUBISHI_AC +#define D_STR_MITSUBISHI_AC "MITSUBISHI_AC" +#endif // D_STR_MITSUBISHI_AC +#ifndef D_STR_MITSUBISHI_HEAVY_152 +#define D_STR_MITSUBISHI_HEAVY_152 "MITSUBISHI_HEAVY_152" +#endif // D_STR_MITSUBISHI_HEAVY_152 +#ifndef D_STR_MITSUBISHI_HEAVY_88 +#define D_STR_MITSUBISHI_HEAVY_88 "MITSUBISHI_HEAVY_88" +#endif // D_STR_MITSUBISHI_HEAVY_88 +#ifndef D_STR_MULTIBRACKETS +#define D_STR_MULTIBRACKETS "MULTIBRACKETS" +#endif // D_STR_MULTIBRACKETS +#ifndef D_STR_MWM +#define D_STR_MWM "MWM" +#endif // D_STR_MWM +#ifndef D_STR_NEC +#define D_STR_NEC "NEC" +#endif // D_STR_NEC +#ifndef D_STR_NEC_LIKE +#define D_STR_NEC_LIKE D_STR_NEC "_LIKE" +#endif // D_STR_NEC_LIKE +#ifndef D_STR_NEC_NON_STRICT +#define D_STR_NEC_NON_STRICT D_STR_NEC " (NON-STRICT)" +#endif // D_STR_NEC_NON_STRICT +#ifndef D_STR_NEOCLIMA +#define D_STR_NEOCLIMA "NEOCLIMA" +#endif // D_STR_NEOCLIMA +#ifndef D_STR_NIKAI +#define D_STR_NIKAI "NIKAI" +#endif // D_STR_NIKAI +#ifndef D_STR_PANASONIC +#define D_STR_PANASONIC "PANASONIC" +#endif // D_STR_PANASONIC +#ifndef D_STR_PANASONIC_AC +#define D_STR_PANASONIC_AC "PANASONIC_AC" +#endif // D_STR_PANASONIC_AC +#ifndef D_STR_PANASONIC_AC32 +#define D_STR_PANASONIC_AC32 D_STR_PANASONIC_AC"32" +#endif // D_STR_PANASONIC_AC32 +#ifndef D_STR_PIONEER +#define D_STR_PIONEER "PIONEER" +#endif // D_STR_PIONEER +#ifndef D_STR_PRONTO +#define D_STR_PRONTO "PRONTO" +#endif // D_STR_PRONTO +#ifndef D_STR_RAW +#define D_STR_RAW "RAW" +#endif // D_STR_RAW +#ifndef D_STR_RC5 +#define D_STR_RC5 "RC5" +#endif // D_STR_RC5 +#ifndef D_STR_RC5X +#define D_STR_RC5X "RC5X" +#endif // D_STR_RC5X +#ifndef D_STR_RC6 +#define D_STR_RC6 "RC6" +#endif // D_STR_RC6 +#ifndef D_STR_RCMM +#define D_STR_RCMM "RCMM" +#endif // D_STR_RCMM +#ifndef D_STR_RHOSS +#define D_STR_RHOSS "RHOSS" +#endif // D_STR_RHOSS +#ifndef D_STR_SAMSUNG +#define D_STR_SAMSUNG "SAMSUNG" +#endif // D_STR_SAMSUNG +#ifndef D_STR_SAMSUNG36 +#define D_STR_SAMSUNG36 "SAMSUNG36" +#endif // D_STR_SAMSUNG36 +#ifndef D_STR_SAMSUNG_AC +#define D_STR_SAMSUNG_AC "SAMSUNG_AC" +#endif // D_STR_SAMSUNG_AC +#ifndef D_STR_SANYO +#define D_STR_SANYO "SANYO" +#endif // D_STR_SANYO +#ifndef D_STR_SANYO_AC +#define D_STR_SANYO_AC D_STR_SANYO "_AC" +#endif // D_STR_SANYO_AC +#ifndef D_STR_SANYO_AC88 +#define D_STR_SANYO_AC88 D_STR_SANYO_AC "88" +#endif // D_STR_SANYO_AC88 +#ifndef D_STR_SANYO_AC152 +#define D_STR_SANYO_AC152 D_STR_SANYO_AC "152" +#endif // D_STR_SANYO_AC152 +#ifndef D_STR_SANYO_LC7461 +#define D_STR_SANYO_LC7461 D_STR_SANYO "_LC7461" +#endif // D_STR_SANYO_LC7461 +#ifndef D_STR_SHARP +#define D_STR_SHARP "SHARP" +#endif // D_STR_SHARP +#ifndef D_STR_SHARP_AC +#define D_STR_SHARP_AC "SHARP_AC" +#endif // D_STR_SHARP_AC +#ifndef D_STR_SHERWOOD +#define D_STR_SHERWOOD "SHERWOOD" +#endif // D_STR_SHERWOOD +#ifndef D_STR_SONY +#define D_STR_SONY "SONY" +#endif // D_STR_SONY +#ifndef D_STR_SONY_38K +#define D_STR_SONY_38K "SONY_38K" +#endif // D_STR_SONY_38K +#ifndef D_STR_SYMPHONY +#define D_STR_SYMPHONY "SYMPHONY" +#endif // D_STR_SYMPHONY +#ifndef D_STR_TCL96AC +#define D_STR_TCL96AC "TCL96AC" +#endif // D_STR_TCL96AC +#ifndef D_STR_TCL112AC +#define D_STR_TCL112AC "TCL112AC" +#endif // D_STR_TCL112AC +#ifndef D_STR_TECHNIBEL_AC +#define D_STR_TECHNIBEL_AC "TECHNIBEL_AC" +#endif // D_STR_TECHNIBEL_AC +#ifndef D_STR_TECO +#define D_STR_TECO "TECO" +#endif // D_STR_TECO +#ifndef D_STR_TEKNOPOINT +#define D_STR_TEKNOPOINT "TEKNOPOINT" +#endif // D_STR_TEKNOPOINT +#ifndef D_STR_TOSHIBA_AC +#define D_STR_TOSHIBA_AC "TOSHIBA_AC" +#endif // D_STR_TOSHIBA_AC +#ifndef D_STR_TOTO +#define D_STR_TOTO "TOTO" +#endif // D_STR_TOTO +#ifndef D_STR_TRANSCOLD +#define D_STR_TRANSCOLD "TRANSCOLD" +#endif // D_STR_TRANSCOLD +#ifndef D_STR_TROTEC +#define D_STR_TROTEC "TROTEC" +#endif // D_STR_TROTEC +#ifndef D_STR_TROTEC_3550 +#define D_STR_TROTEC_3550 D_STR_TROTEC "_3550" +#endif // D_STR_TROTEC_3550 +#ifndef D_STR_TRUMA +#define D_STR_TRUMA "TRUMA" +#endif // D_STR_TRUMA +#ifndef D_STR_UNUSED +#define D_STR_UNUSED "UNUSED" +#endif // D_STR_UNUSED +#ifndef D_STR_VESTEL_AC +#define D_STR_VESTEL_AC "VESTEL_AC" +#endif // D_STR_VESTEL_AC +#ifndef D_STR_VOLTAS +#define D_STR_VOLTAS "VOLTAS" +#endif // D_STR_VOLTAS +#ifndef D_STR_WHIRLPOOL_AC +#define D_STR_WHIRLPOOL_AC "WHIRLPOOL_AC" +#endif // D_STR_WHIRLPOOL_AC +#ifndef D_STR_WHYNTER +#define D_STR_WHYNTER "WHYNTER" +#endif // D_STR_WHYNTER +#ifndef D_STR_WOWWEE +#define D_STR_WOWWEE "WOWWEE" +#endif // D_STR_WOWWEE +#ifndef D_STR_XMP +#define D_STR_XMP "XMP" +#endif // D_STR_XMP +#ifndef D_STR_YORK +#define D_STR_YORK "YORK" +#endif // D_STR_YORK +#ifndef D_STR_ZEPEAL +#define D_STR_ZEPEAL "ZEPEAL" +#endif // D_STR_ZEPEAL + +// IRrecvDumpV2+ +#ifndef D_STR_TIMESTAMP +#define D_STR_TIMESTAMP "Timestamp" +#endif // D_STR_TIMESTAMP +#ifndef D_STR_LIBRARY +#define D_STR_LIBRARY "Library" +#endif // D_STR_LIBRARY +#ifndef D_STR_MESGDESC +#define D_STR_MESGDESC "Mesg Desc." +#endif // D_STR_MESGDESC +#ifndef D_STR_TOLERANCE +#define D_STR_TOLERANCE "Tolerance" +#endif // D_STR_TOLERANCE +#ifndef D_STR_IRRECVDUMP_STARTUP +#define D_STR_IRRECVDUMP_STARTUP \ + "IRrecvDump is now running and waiting for IR input on Pin %d" +#endif // D_STR_IRRECVDUMP_STARTUP +#ifndef D_WARN_BUFFERFULL +#define D_WARN_BUFFERFULL \ + "WARNING: IR code is too big for buffer (>= %d). " \ + "This result shouldn't be trusted until this is resolved. " \ + "Edit & increase `kCaptureBufferSize`." +#endif // D_WARN_BUFFERFULL + +#endif // LOCALE_DEFAULTS_H_ diff --git a/test/ir_Fujitsu_test.cpp b/test/ir_Fujitsu_test.cpp index ed5ee59f1..e573e4a52 100644 --- a/test/ir_Fujitsu_test.cpp +++ b/test/ir_Fujitsu_test.cpp @@ -1,1448 +1,2220 @@ -// Copyright 2017 Jonny Graham, David Conran -#include "IRac.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "ir_Fujitsu.h" -#include "gtest/gtest.h" - -// Tests for Fujitsu A/C methods. - -// Test sending typical data only. -TEST(TestIRFujitsuACClass, GetRawDefault) { - IRFujitsuAC ac(kGpioUnused); // AR-RAH2E - ac.setSwing(kFujitsuAcSwingBoth); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setTemp(24); - ac.setCmd(kFujitsuAcCmdTurnOn); - uint8_t expected_arrah2e[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", - ac.toString()); - - uint8_t expected_ardb1[15] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x4D}; - ac.setModel(ARDB1); - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 15 * 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawTurnOff) { - IRFujitsuAC ac(kGpioUnused); - ac.setModel(ARRAH2E); - ac.off(); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", - ac.toString()); - - ac.setModel(ARDB1); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawStepHoriz) { - IRFujitsuAC ac(kGpioUnused); - ac.stepHoriz(); - uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; - EXPECT_STATE_EQ(expected, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(H), Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawStepVert) { - IRFujitsuAC ac(kGpioUnused); - ac.setModel(ARRAH2E); - ac.stepVert(); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(V), Timer: Off", - ac.toString()); - - ac.setModel(ARDB1); - ac.stepVert(); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, - ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: Step Swing(V)", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { - IRFujitsuAC ac(kGpioUnused); - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingHoriz); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setTemp(25); - uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, - 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; - EXPECT_STATE_EQ(expected, ac.getRaw(), 16 * 8); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 25C, " - "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawWithFan) { - IRFujitsuAC ac(kGpioUnused); - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingHoriz); - ac.setMode(kFujitsuAcModeFan); - ac.setFanSpeed(kFujitsuAcFanMed); - ac.setTemp(20); // temp doesn't matter for fan - // but it is sent by the RC anyway - ac.setModel(ARRAH2E); - uint8_t expected_arrah2e[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x40, 0x03, 0x22, 0x00, 0x00, 0x00, 0x20, 0x4B}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " - "Fan: 2 (Medium), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", - ac.toString()); - - ac.setModel(ARDB1); - uint8_t expected_ardb1[15] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x40, 0x03, 0x02, 0x00, 0x00, 0x00, 0x8B}; - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), ac.getStateLength() * 8); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " - "Fan: 2 (Medium), Command: N/A", ac.toString()); -} - -TEST(TestIRFujitsuACClass, SetRaw) { - IRFujitsuAC ac(kGpioUnused); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - uint8_t expected_default_arrah2e[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; - EXPECT_STATE_EQ(expected_default_arrah2e, ac.getRaw(), - ac.getStateLength() * 8); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, " - "Timer: Off", - ac.toString()); - // Now set a new state via setRaw(); - // This state is a real state from an AR-DB1 remote. - uint8_t new_state1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - ac.setRaw(new_state1, kFujitsuAcStateLength - 1); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_STATE_EQ(new_state1, ac.getRaw(), ac.getStateLength() * 8); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " - "Fan: 0 (Auto), Command: N/A", ac.toString()); -} - -TEST(TestSendFujitsuAC, GenerateMessage) { - IRFujitsuAC ac(kGpioUnused); - IRsendTest irsend(kGpioUnused); - ac.begin(); - irsend.begin(); - - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingBoth); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setTemp(24); - - EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); - EXPECT_EQ(kFujitsuAcModeCool, ac.getMode()); - EXPECT_EQ(24, ac.getTemp()); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - EXPECT_EQ(kFujitsuAcCmdStayOn, ac.getCmd()); - - irsend.reset(); - irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLength); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s8100", - irsend.outputStr()); -} - -TEST(TestSendFujitsuAC, GenerateShortMessage) { - IRFujitsuAC ac(kGpioUnused); - IRsendTest irsend(kGpioUnused); - ac.begin(); - irsend.begin(); - - ac.off(); - - EXPECT_EQ(kFujitsuAcCmdTurnOff, ac.getCmd()); - - irsend.reset(); - irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" - "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" - "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", - irsend.outputStr()); -} - -// Issue #275 -TEST(TestSendFujitsuAC, Issue275) { - IRFujitsuAC ac(kGpioUnused); - IRsendTest irsend(kGpioUnused); - ac.begin(); - irsend.begin(); - irsend.reset(); - - ac.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - // Header - "m3324s1574" - // 0 0 1 0 1 0 0 0 (0x28) - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - // 1 1 0 0 0 1 1 0 (0xC6) - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - // 0 0 0 0 0 0 0 0 (0x00) - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 1 0 0 0 0 0 0 (0x40) - "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" - // 1 0 1 1 1 1 1 1 (0xBF) - "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - // Footer - "m448s8100", irsend.outputStr()); - - irsend.reset(); - // Per report in Issue #275 - uint16_t off[115] = { - 3350, 1650, - 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, - 450, 1250, 450}; - irsend.sendRaw(off, 115, 38); - EXPECT_EQ( - "f38000d50" - // Header - "m3350s1650" - // 0 0 1 0 1 0 0 0 (0x28) - "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" - // 1 1 0 0 0 1 1 0 (0xC6) - "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" - // 0 0 0 0 0 0 0 0 (0x00) - "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 1 0 0 0 0 0 0 (0x40) - "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" - // 1 0 1 1 1 1 1 1 (0xBF) - "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" - // Footer - "m450", - irsend.outputStr()); -} - -TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { - IRsendTest irsend(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - IRrecv irrecv(kGpioUnused); - - irsend.begin(); - irsend.reset(); - - ac.setModel(ARRAH2E); - ac.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", - IRAcUtils::resultAcToString(&irsend.capture)); - stdAc::state_t r, p; - ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); - - irsend.reset(); - - ac.setModel(ARDB1); - ac.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); -} - -TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { - IRsendTest irsend(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - IRrecv irrecv(kGpioUnused); - irsend.begin(); - - irsend.reset(); - - ac.setModel(ARRAH2E); - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingVert); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setTemp(18); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - ASSERT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - uint8_t expected_arrah2e[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; - EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " - "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 1 (Swing(V)), Command: N/A, " - "Timer: Off", - ac.toString()); - - irsend.reset(); - - ac.setModel(ARDB1); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected_ardb1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAB}; - EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " - "Fan: 4 (Quiet), Command: N/A", ac.toString()); -} - -TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - irsend.begin(); - - irsend.reset(); - // "Off" Message recorded from an AR-DB1 remote. - uint16_t rawData[99] = { - 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, - 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, - 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, - 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, - 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, - 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, - 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, - 420, 388, 436}; - irsend.sendRaw(rawData, 99, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); - uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Command: N/A", ac.toString()); -} - -TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - irsend.begin(); - irsend.reset(); - uint16_t rawData1[243] = { - 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, - 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, - 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, - 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, - 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, - 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, - 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, - 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, - 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, - 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, - 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, - 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, - 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, - 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, - 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, - 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, - 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, - 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, - 436, 1216, 436}; - irsend.sendRaw(rawData1, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; - EXPECT_STATE_EQ(expected1, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " - "Fan: 4 (Quiet), Command: N/A", ac.toString()); - - irsend.reset(); - uint16_t rawData2[243] = { - 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, - 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, - 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, - 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, - 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, - 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, - 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, - 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, - 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, - 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, - 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, - 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, - 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, - 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, - 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, - 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, - 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, - 436, 1214, 440}; - irsend.sendRaw(rawData2, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected2[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - EXPECT_STATE_EQ(expected2, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " - "Fan: 0 (Auto), Command: N/A", ac.toString()); -} - -TEST(TestDecodeFujitsuAC, Issue414) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - // Capture as supplied by arpmota - uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, - 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, - 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, - 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, - 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, - 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, - 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, - 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, - 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, - 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, - 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, - 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, - 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, - 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, - 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, - 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, - 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, - 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, - 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; - uint8_t state[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x2B}; - irsend.begin(); - irsend.reset(); - irsend.sendRaw(rawData, 259, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - - // Resend it using the state this time. - irsend.reset(); - irsend.sendFujitsuAC(state, 16); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" - "m448s8100", irsend.outputStr()); -} - -TEST(TestIRFujitsuACClass, toCommon) { - IRFujitsuAC ac(kGpioUnused); - ac.setMode(kFujitsuAcModeCool); - ac.setTemp(20); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setSwing(kFujitsuAcSwingBoth); - - // Now test it. - ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - ASSERT_TRUE(ac.toCommon().power); - ASSERT_TRUE(ac.toCommon().celsius); - ASSERT_EQ(20, ac.toCommon().degrees); - ASSERT_TRUE(ac.toCommon().quiet); - - ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); - ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); - ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); - ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); - // Unsupported. - ASSERT_FALSE(ac.toCommon().filter); - ASSERT_FALSE(ac.toCommon().clean); - ASSERT_FALSE(ac.toCommon().turbo); - ASSERT_FALSE(ac.toCommon().light); - ASSERT_FALSE(ac.toCommon().econo); - ASSERT_FALSE(ac.toCommon().beep); - ASSERT_EQ(-1, ac.toCommon().sleep); - ASSERT_EQ(-1, ac.toCommon().clock); - - // Check off mode which is special. - ac.off(); - ASSERT_FALSE(ac.toCommon().power); - ac.send(); - ac.stateReset(); - IRrecv irrecv(kGpioUnused); - ac._irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); - - // Now test it. - EXPECT_EQ( // Off mode technically has no temp, mode, fan, etc. - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", - ac.toString()); - ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - ASSERT_FALSE(ac.toCommon().power); - ASSERT_TRUE(ac.toCommon().celsius); - ASSERT_EQ(16, ac.toCommon().degrees); - ASSERT_FALSE(ac.toCommon().quiet); - - ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); - ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); - ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); - ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); - // Unsupported. - ASSERT_FALSE(ac.toCommon().filter); - ASSERT_FALSE(ac.toCommon().clean); - ASSERT_FALSE(ac.toCommon().turbo); - ASSERT_FALSE(ac.toCommon().light); - ASSERT_FALSE(ac.toCommon().econo); - ASSERT_FALSE(ac.toCommon().beep); - ASSERT_EQ(-1, ac.toCommon().sleep); - ASSERT_EQ(-1, ac.toCommon().clock); -} - -TEST(TestDecodeFujitsuAC, Issue716) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - // Powerful command from a raw data capture. - // Capture as supplied by u4mzu4 - uint16_t rawData[115] = { - 3320, 1610, 432, 406, 432, 406, 432, 1220, 432, 406, 432, 1192, 458, 406, - 432, 406, 432, 406, 432, 1218, 432, 1220, 432, 406, 432, 406, 432, 406, - 432, 1192, 458, 1192, 460, 406, 432, 406, 432, 406, 432, 406, 432, 406, - 432, 406, 432, 406, 432, 408, 432, 406, 432, 406, 430, 406, 432, 406, 432, - 406, 432, 1190, 460, 406, 432, 408, 430, 406, 432, 406, 432, 406, 432, - 406, 432, 406, 434, 1192, 458, 406, 432, 406, 432, 406, 432, 1194, 458, - 406, 432, 406, 432, 1194, 456, 1196, 454, 1220, 432, 406, 432, 406, 432, - 408, 430, 1194, 458, 1194, 456, 406, 432, 406, 430, 406, 432, 1194, 458, - 1194, 458}; // FUJITSU_AC - uint8_t powerful[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; - irsend.begin(); - irsend.reset(); - irsend.sendRaw(rawData, 115, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLengthShort * 8, irsend.capture.bits); - EXPECT_STATE_EQ(powerful, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: Powerful, Outside Quiet: Off, " - "Timer: Off", - ac.toString()); - - // Economy (just from the state) - uint8_t econo[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x09, 0xF6}; - // Make sure we can't accidentally inherit the correct model. - ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, - fujitsu_ac_remote_model_t::ARREB1E); - ac.setModel(fujitsu_ac_remote_model_t::ARDB1); - ac.setRaw(econo, kFujitsuAcStateLengthShort); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: Econo, Outside Quiet: Off, " - "Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, OutsideQuiet) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, - fujitsu_ac_remote_model_t::ARREB1E); - ASSERT_NE(fujitsu_ac_remote_model_t::ARRAH2E, - fujitsu_ac_remote_model_t::ARREB1E); - // States as supplied by u4mzu4 - // https://github.com/crankyoldgit/IRremoteESP8266/issues/716#issuecomment-495852309 - uint8_t off[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; - uint8_t on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xAF}; - // Make sure we can't accidentally inherit the correct model. - ac.setModel(fujitsu_ac_remote_model_t::ARDB1); - ac.setRaw(off, kFujitsuAcStateLength); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_FALSE(ac.getOutsideQuiet()); - // We can really only tell the difference between ARRAH2E & ARREB1E if - // the option is set. Otheriwse they appear the same. - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", ac.toString()); - ac.setModel(fujitsu_ac_remote_model_t::ARREB1E); - EXPECT_EQ( - "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: N/A, Outside Quiet: Off, Timer: Off", - ac.toString()); - - // Make sure we can't accidentally inherit the correct model. - ac.setModel(fujitsu_ac_remote_model_t::ARDB1); - ac.setRaw(on, kFujitsuAcStateLength); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_TRUE(ac.getOutsideQuiet()); - EXPECT_EQ( - "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: N/A, Outside Quiet: On, Timer: Off", - ac.toString()); - - ac.setOutsideQuiet(false); - EXPECT_FALSE(ac.getOutsideQuiet()); - ac.setOutsideQuiet(true); - EXPECT_TRUE(ac.getOutsideQuiet()); - ac.setOutsideQuiet(false); - EXPECT_FALSE(ac.getOutsideQuiet()); -} - -TEST(TestIRFujitsuACClass, toggleSwing) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - ac.begin(); - ac.setModel(ARJW2); - ac.setSwing(kFujitsuAcSwingOff); - ac.setCmd(kFujitsuAcCmdStayOn); - ASSERT_EQ(kFujitsuAcSwingOff, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingHoriz, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); - ac.toggleSwingVert(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); - ac.toggleSwingVert(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); - - // Both - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - ac.toggleSwingVert(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - - EXPECT_EQ( - "Model: 4 (ARJW2), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: Toggle Swing(H)", - ac.toString()); - - // Test without the update set. - ac.toggleSwingHoriz(false); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - ac.toggleSwingVert(false); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); -} - -TEST(TestDecodeFujitsuAC, Issue726) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - // fan:auto mode:auto temp:24 power:on - // Capture as supplied by huexpub - // Rawdata was very messy. Had to use `./auto_analyse_raw_data.py -r 250` to - // get it to parse due to timings being above tolerances. - uint8_t auto_auto_on_24[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; - irsend.begin(); - irsend.reset(); - irsend.sendFujitsuAC(auto_auto_on_24, 16); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); - EXPECT_STATE_EQ(auto_auto_on_24, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, Clean) { - IRFujitsuAC ac(kGpioUnused); - // Data from: - // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 - uint8_t clean_off[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; - uint8_t clean_on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08}; - ac.setRaw(clean_on, kFujitsuAcStateLength); - EXPECT_TRUE(ac.getClean()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", - ac.toString()); - ac.setClean(false); - EXPECT_FALSE(ac.getClean()); - EXPECT_STATE_EQ(clean_off, ac.getRaw(), ac.getStateLength() * 8) - ac.setClean(true); - EXPECT_TRUE(ac.getClean()); - EXPECT_STATE_EQ(clean_on, ac.getRaw(), ac.getStateLength() * 8) - ac.setRaw(clean_off, kFujitsuAcStateLength); - EXPECT_FALSE(ac.getClean()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. - ac.setClean(true); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - // But ARRY4 does. - ac.setModel(fujitsu_ac_remote_model_t::ARRY4); - EXPECT_TRUE(ac.getClean()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, Filter) { - IRFujitsuAC ac(kGpioUnused); - // Data from: - // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 - uint8_t filter_on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x07}; - uint8_t filter_off[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; - ac.setRaw(filter_on, kFujitsuAcStateLength); - EXPECT_TRUE(ac.getFilter()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", - ac.toString()); - ac.setFilter(false); - EXPECT_FALSE(ac.getFilter()); - ac.setFilter(true); - EXPECT_TRUE(ac.getFilter()); - ac.setRaw(filter_off, kFujitsuAcStateLength); - EXPECT_FALSE(ac.getFilter()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. - ac.setFilter(true); - EXPECT_FALSE(ac.getFilter()); - // But ARRY4 does. - ac.setModel(fujitsu_ac_remote_model_t::ARRY4); - EXPECT_TRUE(ac.getFilter()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, Timers) { - IRFujitsuAC ac(kGpioUnused); - // Data from: - // https://github.com/crankyoldgit/IRremoteESP8266/issues/1255#issuecomment-686445720 - const uint8_t timer_on_12h[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x30, 0x01, 0x00, 0x00, 0xAD, 0x20, 0x32}; - ac.setRaw(timer_on_12h, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); - EXPECT_EQ(12 * 60, ac.getOnTimer()); - EXPECT_EQ(0, ac.getOffSleepTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, On Timer: 12:00", - ac.toString()); - - const uint8_t timer_on_8h30m[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x30, 0x01, 0x00, 0xE0, 0x9F, 0x20, 0x60}; - ac.setRaw(timer_on_8h30m, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); - EXPECT_EQ(8 * 60 + 30, ac.getOnTimer()); - EXPECT_EQ(0, ac.getOffSleepTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, On Timer: 08:30", - ac.toString()); - - // TIMER OFF 11H - const uint8_t timer_off_11h[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x20, 0x01, 0x94, 0x0A, 0x00, 0x20, 0x51}; - ac.setRaw(timer_off_11h, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); - EXPECT_EQ(11 * 60, ac.getOffSleepTimer()); - EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Off Timer: 11:00", - ac.toString()); - - // TIMER OFF 0.5H - const uint8_t timer_off_30m[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x20, 0x01, 0x1E, 0x08, 0x00, 0x20, 0xC9}; - ac.setRaw(timer_off_30m, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); - EXPECT_EQ(30, ac.getOffSleepTimer()); - EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", - ac.toString()); - - // TIMER SLEEP 3H - const uint8_t timer_sleep_3h[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x10, 0x01, 0xB4, 0x08, 0x00, 0x20, 0x43}; - ac.setRaw(timer_sleep_3h, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcSleepTimer, ac.getTimerType()); - EXPECT_EQ(3 * 60, ac.getOffSleepTimer()); - EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", - ac.toString()); - - // Re-construct a known timer state from scratch. - ac.stateReset(); - ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); - ac.setPower(true); - ac.setMode(kFujitsuAcModeAuto); - ac.setTemp(26); - ac.setFanSpeed(1); - ac.setClean(false); - ac.setFilter(false); - ac.setSwing(0); - - ac.setOffTimer(30); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_STATE_EQ(timer_off_30m, ac.getRaw(), ac.getStateLength() * 8); - - ac.setOnTimer(12 * 60); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, On Timer: 12:00", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(12 * 60, ac.getOnTimer()); - EXPECT_TRUE(ac.getOnTimer()); - EXPECT_STATE_EQ(timer_on_12h, ac.getRaw(), ac.getStateLength() * 8); - EXPECT_EQ(12 * 60, ac.getOnTimer()); - EXPECT_TRUE(ac.getOnTimer()); - - ac.setSleepTimer(3 * 60); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_STATE_EQ(timer_sleep_3h, ac.getRaw(), ac.getStateLength() * 8); -} - -TEST(TestIRFujitsuACClass, ARREW4E) { - IRFujitsuAC ac(kGpioUnused); - - uint8_t on_18_cool_auto[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x50, 0x01, 0x00, 0x21, 0x03, 0x20, 0x20, 0x1A}; - - EXPECT_TRUE(ac.validChecksum(on_18_cool_auto, kFujitsuAcStateLength)); - ac.setRaw(on_18_cool_auto, kFujitsuAcStateLength); - EXPECT_EQ(0, ac.getId()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); - EXPECT_EQ(18, ac.getTemp()); - - uint8_t mode_C_power_on_18[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x51, 0x01, 0x00, 0x17, 0x07, 0x54, 0x20, 0xEB}; - EXPECT_TRUE(ac.validChecksum(mode_C_power_on_18, kFujitsuAcStateLength)); - ac.setRaw(mode_C_power_on_18, kFujitsuAcStateLength); - EXPECT_EQ(2, ac.getId()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); - EXPECT_EQ(18, ac.getTemp()); - - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - - irsend.begin(); - irsend.reset(); - irsend.sendFujitsuAC(mode_C_power_on_18, kFujitsuAcStateLength); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); - EXPECT_STATE_EQ(mode_C_power_on_18, irsend.capture.state, - irsend.capture.bits); -} - -TEST(TestDecodeFujitsuAC, Issue1455) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - irsend.begin(); - irsend.reset(); - uint16_t rawData[259] = { - 3220, 1700, 354, 446, 380, 448, 380, 1296, 352, 446, 382, 1296, 354, 448, - 380, 448, 378, 446, 382, 1296, 352, 1296, 354, 448, 378, 446, 380, 446, - 382, 1294, 354, 1270, 380, 448, 380, 446, 380, 448, 380, 446, 380, 450, - 378, 448, 380, 446, 380, 448, 380, 448, 380, 448, 380, 450, 376, 450, 380, - 448, 378, 1298, 352, 448, 378, 448, 380, 448, 380, 448, 378, 450, 378, - 450, 378, 448, 378, 1296, 354, 446, 382, 446, 380, 448, 378, 448, 380, - 1296, 352, 1296, 354, 1296, 354, 1272, 376, 1272, 378, 1296, 354, 1294, - 354, 1296, 354, 446, 380, 448, 378, 1296, 354, 448, 378, 448, 378, 448, - 380, 446, 380, 1272, 378, 446, 380, 450, 378, 448, 378, 1296, 354, 1296, - 354, 446, 380, 448, 378, 448, 378, 446, 382, 446, 380, 1296, 354, 1296, - 354, 446, 380, 1296, 354, 446, 380, 446, 380, 446, 380, 1294, 354, 448, - 380, 448, 380, 448, 380, 448, 380, 446, 380, 448, 380, 446, 380, 448, - 380, 446, 380, 446, 380, 448, 380, 446, 380, 448, 380, 448, 380, 446, 380, - 1296, 352, 446, 380, 1296, 354, 446, 380, 448, 380, 448, 380, 1296, 354, - 448, 378, 448, 380, 446, 380, 446, 382, 446, 380, 446, 382, 446, 380, - 1272, 378, 446, 380, 446, 382, 1294, 354, 446, 382, 1294, 354, 446, 382, - 446, 382, 446, 380, 448, 380, 448, 380, 448, 380, 448, 378, 1296, 354, - 446, 382, 446, 380, 1296, 354, 446, 382, 1296, 354, 446, 382, 1294, 354, - 446, 382, 446, 380, 446, 382}; // UNKNOWN 8383D7DE - irsend.sendRaw(rawData, 259, 38); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 19C, " - "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - IRAcUtils::resultAcToString(&irsend.capture)); - stdAc::state_t r, p; - ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); -} - -TEST(TestIRFujitsuACClass, Heat10Deg) { - IRFujitsuAC ac(kGpioUnused); - const uint8_t heat_on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x10, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x69, 0x0B, 0x00, 0x23, 0x06, 0x23, 0x20, 0xEF}; - ac.setRaw(heat_on, kFujitsuAcStateLength); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - ac.toString()); - ac.stateReset(); - ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); - ac.setId(1); - ac.setMode(kFujitsuAcModeFan); - ac.setTemp(21); - ac.setFanSpeed(kFujitsuAcFanAuto); - ac.setSwing(0); - ac.setOutsideQuiet(false); - ac.setPower(true); - ac.set10CHeat(true); - EXPECT_TRUE(ac.get10CHeat()); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - - ac.set10CHeat(false); - EXPECT_FALSE(ac.get10CHeat()); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 21C, " - "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - ac.toString()); - - // For https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 - ac.set10CHeat(true); - EXPECT_TRUE(ac.get10CHeat()); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setSwing(kFujitsuAcSwingVert); - EXPECT_FALSE(ac.get10CHeat()); - ac.set10CHeat(false); - EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); - EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); - EXPECT_FALSE(ac.get10CHeat()); - ac.set10CHeat(true); - EXPECT_TRUE(ac.get10CHeat()); - EXPECT_EQ(kFujitsuAcFanAuto, ac.getFanSpeed()); - EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); -} - -TEST(TestUtils, Housekeeping) { - ASSERT_EQ("FUJITSU_AC", typeToString(decode_type_t::FUJITSU_AC)); - ASSERT_EQ(decode_type_t::FUJITSU_AC, strToDecodeType("FUJITSU_AC")); - ASSERT_TRUE(hasACState(decode_type_t::FUJITSU_AC)); - ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::FUJITSU_AC)); - ASSERT_EQ(0, IRsend::defaultBits(decode_type_t::FUJITSU_AC)); // No default - ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::FUJITSU_AC)); -} - -TEST(TestIRFujitsuACClass, Temperature) { - IRFujitsuAC ac(kGpioUnused); - // Most models - // Celsius - ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); - ac.setTemp(kFujitsuAcMinTemp); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMinTemp - 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp + 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - // Fahrenheit (can't be used by most model, check it converts correctly) - ac.setTemp(77, false); // 77F is 25C - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(25, ac.getTemp()); - - // ARREW4E is different. - ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); - ac.setTemp(kFujitsuAcMinTemp); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMinTemp - 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp + 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - ac.setTemp(22.5); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(22.5, ac.getTemp()); - // Fahrenheit - ac.setTemp(77, false); - EXPECT_FALSE(ac.getCelsius()); - EXPECT_EQ(77, ac.getTemp()); - - // Real example - const uint8_t arrew4e_22c[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x70, 0x01, 0x00, 0x20, 0x03, 0x58, 0x20, 0xC3}; - ac.setRaw(arrew4e_22c, 16); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(22, ac.getTemp()); - const uint8_t arrew4e_25_5c[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x8C, 0x01, 0x00, 0x21, 0x03, 0x12, 0x20, 0xEC}; - ac.setRaw(arrew4e_25_5c, 16); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(25.5, ac.getTemp()); - const uint8_t arrew4e_69f[16] = { - 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x66, 0x04, 0x00, 0x16, 0x01, 0x32, 0x20, 0xFC}; - ac.setRaw(arrew4e_69f, 16); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); - EXPECT_FALSE(ac.getCelsius()); - EXPECT_EQ(69, ac.getTemp()); -} - -TEST(TestIRFujitsuACClass, ARREW4EShortCodes) { - // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 - IRFujitsuAC ac(kGpioUnused); - ac.setId(3); - ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); - - const uint8_t off[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x02, 0xFD}; - ac.off(); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(off, ac.getRaw(), kFujitsuAcStateLengthShort * 8); - - const uint8_t econo[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x09, 0xF6}; - ac.setCmd(kFujitsuAcCmdEcono); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(econo, ac.getRaw(), kFujitsuAcStateLengthShort * 8); - - const uint8_t powerful[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x39, 0xC6}; - ac.setCmd(kFujitsuAcCmdPowerful); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(powerful, ac.getRaw(), kFujitsuAcStateLengthShort * 8); - - const uint8_t stepvert[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x6C, 0x93}; - ac.setCmd(kFujitsuAcCmdStepVert); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(stepvert, ac.getRaw(), kFujitsuAcStateLengthShort * 8); -} - -// https://github.com/crankyoldgit/IRremoteESP8266/discussions/1701#discussioncomment-1910164 -TEST(TestIRFujitsuACClass, Discussion1701) { - IRFujitsuAC ac(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRac irac(kGpioUnused); - - const String expected_raw_output = - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s1182m448s390m448s390m448s1182m448s390" - "m448s8100"; - const String expected_arrew4e_str = - "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off"; - const uint8_t expected_arrew4e_state[kFujitsuAcStateLength] = - {0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4C}; - - // Method used in `TurnOnFujitsuAC` - ac.begin(); - ac.setModel(ARREW4E); - ac.setSwing(kFujitsuAcSwingOff); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setTemp(24); // 24C - ac.setCmd(kFujitsuAcCmdTurnOn); - ASSERT_EQ(expected_arrew4e_str, ac.toString()); - ac.send(); - ac._irsend.makeDecodeResult(); - // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) - EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); - EXPECT_EQ(expected_arrew4e_str, - IRAcUtils::resultAcToString(&ac._irsend.capture)); - EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, - ac._irsend.capture.bits); - EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); - - // Now try to reproduce it via the IRac class. - ac._irsend.reset(); - ac.stateReset(); - ASSERT_NE(expected_arrew4e_str, ac.toString()); - - irac.fujitsu(&ac, - ARREW4E, // Model - true, // Power - stdAc::opmode_t::kCool, // Mode - true, // Celsius - 24, // Degrees - stdAc::fanspeed_t::kHigh, // Fan speed - stdAc::swingv_t::kOff, // Vertical swing - stdAc::swingh_t::kOff, // Horizontal swing - false, // Quiet - false, // Turbo (Powerful) - false, // Econo - false, // Filter - false); // Clean - ASSERT_EQ(expected_arrew4e_str, ac.toString()); - ac._irsend.makeDecodeResult(); - // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) - EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); - EXPECT_EQ(expected_arrew4e_str, - IRAcUtils::resultAcToString(&ac._irsend.capture)); - EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, - ac._irsend.capture.bits); - EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); - // Success. -} - -TEST(TestIRFujitsuACClass, toCommon_Issue1780HandlePrev) { - IRFujitsuAC ac(kGpioUnused); - ac.setMode(kFujitsuAcModeCool); - ac.setTemp(20); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setSwing(kFujitsuAcSwingBoth); - ac.on(); - ASSERT_TRUE(ac.toCommon().power); - stdAc::state_t prev = ac.toCommon(); // Copy in the state. - ac.off(); - ASSERT_FALSE(ac.toCommon().power); - ac.send(); // This should send a short code. - prev.degrees = 27; - ac.stateReset(); - IRrecv irrecv(kGpioUnused); - ac._irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); - ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - ASSERT_FALSE(ac.toCommon().power); - ASSERT_TRUE(ac.toCommon().celsius); - ASSERT_EQ(16, ac.toCommon().degrees); - ASSERT_EQ(27, ac.toCommon(&prev).degrees); - ASSERT_FALSE(ac.toCommon().quiet); - - ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); - ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon(&prev).mode); - ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); - ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon(&prev).fanspeed); - ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); - ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); - // Unsupported. - ASSERT_FALSE(ac.toCommon().filter); - ASSERT_FALSE(ac.toCommon().clean); - ASSERT_FALSE(ac.toCommon().turbo); - ASSERT_FALSE(ac.toCommon().light); - ASSERT_FALSE(ac.toCommon().econo); - ASSERT_FALSE(ac.toCommon().beep); - ASSERT_EQ(-1, ac.toCommon().sleep); - ASSERT_EQ(-1, ac.toCommon().clock); - - stdAc::state_t result_inc_prev; - ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result_inc_prev, - &prev)); - ASSERT_EQ(27, result_inc_prev.degrees); - ASSERT_EQ(stdAc::opmode_t::kCool, result_inc_prev.mode); - ASSERT_EQ(stdAc::fanspeed_t::kMin, result_inc_prev.fanspeed); -} - -TEST(TestIRFujitsuACClass, Improve10CHeat) { - IRFujitsuAC ac(kGpioUnused); - // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G2 - const uint8_t Arrah2u_10CHeatOn[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x41, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x64}; - ASSERT_FALSE(ac.get10CHeat()); - ac.setRaw(Arrah2u_10CHeatOn, 16); - ASSERT_TRUE(ac.get10CHeat()); - ASSERT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_TRUE(ac.get10CHeat()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); - - ac.stateReset(); - // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G8 - const uint8_t Arreg1u_10CHeatOn[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x61, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x44}; - ASSERT_FALSE(ac.get10CHeat()); - ac.setRaw(Arreg1u_10CHeatOn, 16); - ASSERT_TRUE(ac.get10CHeat()); - ASSERT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_TRUE(ac.get10CHeat()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); -} +// Copyright 2017 Jonny Graham, David Conran +// Copyright 2023 Takeshi Shimizu +#include "IRac.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Fujitsu.h" +#include "gtest/gtest.h" + +// Tests for Fujitsu A/C methods. + +// Test sending typical data only. +TEST(TestIRFujitsuACClass, GetRawDefault) { + IRFujitsuAC ac(kGpioUnused); // AR-RAH2E + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); + ac.setCmd(kFujitsuAcCmdTurnOn); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", + ac.toString()); + + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x4D}; + ac.setModel(ARDB1); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 15 * 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawTurnOff) { + IRFujitsuAC ac(kGpioUnused); + ac.setModel(ARRAH2E); + ac.off(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", + ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepHoriz) { + IRFujitsuAC ac(kGpioUnused); + ac.stepHoriz(); + uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; + EXPECT_STATE_EQ(expected, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(H), Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepVert) { + IRFujitsuAC ac(kGpioUnused); + ac.setModel(ARRAH2E); + ac.stepVert(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(V), Timer: Off", + ac.toString()); + + ac.setModel(ARDB1); + ac.stepVert(); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, + ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: Step Swing(V)", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { + IRFujitsuAC ac(kGpioUnused); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingHoriz); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setTemp(25); + uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, + 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; + EXPECT_STATE_EQ(expected, ac.getRaw(), 16 * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 25C, " + "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawWithFan) { + IRFujitsuAC ac(kGpioUnused); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingHoriz); + ac.setMode(kFujitsuAcModeFan); + ac.setFanSpeed(kFujitsuAcFanMed); + ac.setTemp(20); // temp doesn't matter for fan + // but it is sent by the RC anyway + ac.setModel(ARRAH2E); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x40, 0x03, 0x22, 0x00, 0x00, 0x00, 0x20, 0x4B}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " + "Fan: 2 (Medium), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", + ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x40, 0x03, 0x02, 0x00, 0x00, 0x00, 0x8B}; + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " + "Fan: 2 (Medium), Command: N/A", ac.toString()); +} + +TEST(TestIRFujitsuACClass, SetRaw) { + IRFujitsuAC ac(kGpioUnused); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + uint8_t expected_default_arrah2e[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_STATE_EQ(expected_default_arrah2e, ac.getRaw(), + ac.getStateLength() * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, " + "Timer: Off", + ac.toString()); + // Now set a new state via setRaw(); + // This state is a real state from an AR-DB1 remote. + uint8_t new_state1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + ac.setRaw(new_state1, kFujitsuAcStateLength - 1); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_STATE_EQ(new_state1, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestSendFujitsuAC, GenerateMessage) { + IRFujitsuAC ac(kGpioUnused); + IRsendTest irsend(kGpioUnused); + ac.begin(); + irsend.begin(); + + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); + + EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcModeCool, ac.getMode()); + EXPECT_EQ(24, ac.getTemp()); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdStayOn, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLength); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s8100", + irsend.outputStr()); +} + +TEST(TestSendFujitsuAC, GenerateShortMessage) { + IRFujitsuAC ac(kGpioUnused); + IRsendTest irsend(kGpioUnused); + ac.begin(); + irsend.begin(); + + ac.off(); + + EXPECT_EQ(kFujitsuAcCmdTurnOff, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" + "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" + "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", + irsend.outputStr()); +} + +// Issue #275 +TEST(TestSendFujitsuAC, Issue275) { + IRFujitsuAC ac(kGpioUnused); + IRsendTest irsend(kGpioUnused); + ac.begin(); + irsend.begin(); + irsend.reset(); + + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + // Header + "m3324s1574" + // 0 0 1 0 1 0 0 0 (0x28) + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + // 1 1 0 0 0 1 1 0 (0xC6) + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + // 0 0 0 0 0 0 0 0 (0x00) + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 1 0 0 0 0 0 0 (0x40) + "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" + // 1 0 1 1 1 1 1 1 (0xBF) + "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + // Footer + "m448s8100", irsend.outputStr()); + + irsend.reset(); + // Per report in Issue #275 + uint16_t off[115] = { + 3350, 1650, + 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, + 450, 1250, 450}; + irsend.sendRaw(off, 115, 38); + EXPECT_EQ( + "f38000d50" + // Header + "m3350s1650" + // 0 0 1 0 1 0 0 0 (0x28) + "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" + // 1 1 0 0 0 1 1 0 (0xC6) + "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" + // 0 0 0 0 0 0 0 0 (0x00) + "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 1 0 0 0 0 0 0 (0x40) + "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" + // 1 0 1 1 1 1 1 1 (0xBF) + "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" + // Footer + "m450", + irsend.outputStr()); +} + +TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { + IRsendTest irsend(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + irsend.begin(); + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); + + irsend.reset(); + + ac.setModel(ARDB1); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { + IRsendTest irsend(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingVert); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setTemp(18); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + ASSERT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + uint8_t expected_arrah2e[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " + "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 1 (Swing(V)), Command: N/A, " + "Timer: Off", + ac.toString()); + + irsend.reset(); + + ac.setModel(ARDB1); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected_ardb1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAB}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " + "Fan: 4 (Quiet), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + irsend.begin(); + + irsend.reset(); + // "Off" Message recorded from an AR-DB1 remote. + uint16_t rawData[99] = { + 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, + 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, + 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, + 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, + 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, + 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, + 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, + 420, 388, 436}; + irsend.sendRaw(rawData, 99, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + irsend.begin(); + irsend.reset(); + uint16_t rawData1[243] = { + 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, + 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, + 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, + 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, + 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, + 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, + 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, + 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, + 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, + 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, + 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, + 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, + 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, + 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, + 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, + 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, + 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, + 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, + 436, 1216, 436}; + irsend.sendRaw(rawData1, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; + EXPECT_STATE_EQ(expected1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " + "Fan: 4 (Quiet), Command: N/A", ac.toString()); + + irsend.reset(); + uint16_t rawData2[243] = { + 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, + 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, + 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, + 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, + 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, + 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, + 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, + 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, + 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, + 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, + 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, + 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, + 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, + 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, + 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, + 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, + 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, + 436, 1214, 440}; + irsend.sendRaw(rawData2, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected2[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + EXPECT_STATE_EQ(expected2, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, Issue414) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + // Capture as supplied by arpmota + uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, + 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, + 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, + 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, + 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, + 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, + 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, + 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, + 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, + 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, + 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, + 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, + 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, + 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, + 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, + 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, + 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, + 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, + 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; + uint8_t state[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x2B}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 259, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + + // Resend it using the state this time. + irsend.reset(); + irsend.sendFujitsuAC(state, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" + "m448s8100", irsend.outputStr()); +} + +TEST(TestIRFujitsuACClass, toCommon) { + IRFujitsuAC ac(kGpioUnused); + ac.setMode(kFujitsuAcModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setSwing(kFujitsuAcSwingBoth); + + // Now test it. + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); + + // Check off mode which is special. + ac.off(); + ASSERT_FALSE(ac.toCommon().power); + ac.send(); + ac.stateReset(); + IRrecv irrecv(kGpioUnused); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + + // Now test it. + EXPECT_EQ( // Off mode technically has no temp, mode, fan, etc. + "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", + ac.toString()); + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_FALSE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(16, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeFujitsuAC, Issue716) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + // Powerful command from a raw data capture. + // Capture as supplied by u4mzu4 + uint16_t rawData[115] = { + 3320, 1610, 432, 406, 432, 406, 432, 1220, 432, 406, 432, 1192, 458, 406, + 432, 406, 432, 406, 432, 1218, 432, 1220, 432, 406, 432, 406, 432, 406, + 432, 1192, 458, 1192, 460, 406, 432, 406, 432, 406, 432, 406, 432, 406, + 432, 406, 432, 406, 432, 408, 432, 406, 432, 406, 430, 406, 432, 406, 432, + 406, 432, 1190, 460, 406, 432, 408, 430, 406, 432, 406, 432, 406, 432, + 406, 432, 406, 434, 1192, 458, 406, 432, 406, 432, 406, 432, 1194, 458, + 406, 432, 406, 432, 1194, 456, 1196, 454, 1220, 432, 406, 432, 406, 432, + 408, 430, 1194, 458, 1194, 456, 406, 432, 406, 430, 406, 432, 1194, 458, + 1194, 458}; // FUJITSU_AC + uint8_t powerful[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 115, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLengthShort * 8, irsend.capture.bits); + EXPECT_STATE_EQ(powerful, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: Powerful, Outside Quiet: Off, " + "Timer: Off", + ac.toString()); + + // Economy (just from the state) + uint8_t econo[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x09, 0xF6}; + // Make sure we can't accidentally inherit the correct model. + ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, + fujitsu_ac_remote_model_t::ARREB1E); + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(econo, kFujitsuAcStateLengthShort); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: Econo, Outside Quiet: Off, " + "Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, OutsideQuiet) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, + fujitsu_ac_remote_model_t::ARREB1E); + ASSERT_NE(fujitsu_ac_remote_model_t::ARRAH2E, + fujitsu_ac_remote_model_t::ARREB1E); + // States as supplied by u4mzu4 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/716#issuecomment-495852309 + uint8_t off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; + uint8_t on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xAF}; + // Make sure we can't accidentally inherit the correct model. + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(off, kFujitsuAcStateLength); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_FALSE(ac.getOutsideQuiet()); + // We can really only tell the difference between ARRAH2E & ARREB1E if + // the option is set. Otheriwse they appear the same. + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", ac.toString()); + ac.setModel(fujitsu_ac_remote_model_t::ARREB1E); + EXPECT_EQ( + "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: N/A, Outside Quiet: Off, Timer: Off", + ac.toString()); + + // Make sure we can't accidentally inherit the correct model. + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(on, kFujitsuAcStateLength); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_TRUE(ac.getOutsideQuiet()); + EXPECT_EQ( + "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: N/A, Outside Quiet: On, Timer: Off", + ac.toString()); + + ac.setOutsideQuiet(false); + EXPECT_FALSE(ac.getOutsideQuiet()); + ac.setOutsideQuiet(true); + EXPECT_TRUE(ac.getOutsideQuiet()); + ac.setOutsideQuiet(false); + EXPECT_FALSE(ac.getOutsideQuiet()); +} + +TEST(TestIRFujitsuACClass, toggleSwing) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + ac.begin(); + ac.setModel(ARJW2); + ac.setSwing(kFujitsuAcSwingOff); + ac.setCmd(kFujitsuAcCmdStayOn); + ASSERT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingHoriz, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + + // Both + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + + EXPECT_EQ( + "Model: 4 (ARJW2), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: Toggle Swing(H)", + ac.toString()); + + // Test without the update set. + ac.toggleSwingHoriz(false); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + ac.toggleSwingVert(false); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); +} + +TEST(TestDecodeFujitsuAC, Issue726) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + // fan:auto mode:auto temp:24 power:on + // Capture as supplied by huexpub + // Rawdata was very messy. Had to use `./auto_analyse_raw_data.py -r 250` to + // get it to parse due to timings being above tolerances. + uint8_t auto_auto_on_24[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; + irsend.begin(); + irsend.reset(); + irsend.sendFujitsuAC(auto_auto_on_24, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(auto_auto_on_24, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, Clean) { + IRFujitsuAC ac(kGpioUnused); + // Data from: + // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 + uint8_t clean_off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; + uint8_t clean_on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08}; + ac.setRaw(clean_on, kFujitsuAcStateLength); + EXPECT_TRUE(ac.getClean()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", + ac.toString()); + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + EXPECT_STATE_EQ(clean_off, ac.getRaw(), ac.getStateLength() * 8) + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + EXPECT_STATE_EQ(clean_on, ac.getRaw(), ac.getStateLength() * 8) + ac.setRaw(clean_off, kFujitsuAcStateLength); + EXPECT_FALSE(ac.getClean()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. + ac.setClean(true); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + // But ARRY4 does. + ac.setModel(fujitsu_ac_remote_model_t::ARRY4); + EXPECT_TRUE(ac.getClean()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, Filter) { + IRFujitsuAC ac(kGpioUnused); + // Data from: + // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 + uint8_t filter_on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x07}; + uint8_t filter_off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; + ac.setRaw(filter_on, kFujitsuAcStateLength); + EXPECT_TRUE(ac.getFilter()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", + ac.toString()); + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + ac.setRaw(filter_off, kFujitsuAcStateLength); + EXPECT_FALSE(ac.getFilter()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. + ac.setFilter(true); + EXPECT_FALSE(ac.getFilter()); + // But ARRY4 does. + ac.setModel(fujitsu_ac_remote_model_t::ARRY4); + EXPECT_TRUE(ac.getFilter()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, Timers) { + IRFujitsuAC ac(kGpioUnused); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1255#issuecomment-686445720 + const uint8_t timer_on_12h[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x30, 0x01, 0x00, 0x00, 0xAD, 0x20, 0x32}; + ac.setRaw(timer_on_12h, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); + EXPECT_EQ(12 * 60, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffSleepTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, On Timer: 12:00", + ac.toString()); + + const uint8_t timer_on_8h30m[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x30, 0x01, 0x00, 0xE0, 0x9F, 0x20, 0x60}; + ac.setRaw(timer_on_8h30m, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); + EXPECT_EQ(8 * 60 + 30, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffSleepTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, On Timer: 08:30", + ac.toString()); + + // TIMER OFF 11H + const uint8_t timer_off_11h[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x20, 0x01, 0x94, 0x0A, 0x00, 0x20, 0x51}; + ac.setRaw(timer_off_11h, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); + EXPECT_EQ(11 * 60, ac.getOffSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 11:00", + ac.toString()); + + // TIMER OFF 0.5H + const uint8_t timer_off_30m[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x20, 0x01, 0x1E, 0x08, 0x00, 0x20, 0xC9}; + ac.setRaw(timer_off_30m, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); + EXPECT_EQ(30, ac.getOffSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", + ac.toString()); + + // TIMER SLEEP 3H + const uint8_t timer_sleep_3h[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x10, 0x01, 0xB4, 0x08, 0x00, 0x20, 0x43}; + ac.setRaw(timer_sleep_3h, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcSleepTimer, ac.getTimerType()); + EXPECT_EQ(3 * 60, ac.getOffSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", + ac.toString()); + + // Re-construct a known timer state from scratch. + ac.stateReset(); + ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); + ac.setPower(true); + ac.setMode(kFujitsuAcModeAuto); + ac.setTemp(26); + ac.setFanSpeed(1); + ac.setClean(false); + ac.setFilter(false); + ac.setSwing(0); + + ac.setOffTimer(30); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_STATE_EQ(timer_off_30m, ac.getRaw(), ac.getStateLength() * 8); + + ac.setOnTimer(12 * 60); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, On Timer: 12:00", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(12 * 60, ac.getOnTimer()); + EXPECT_TRUE(ac.getOnTimer()); + EXPECT_STATE_EQ(timer_on_12h, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ(12 * 60, ac.getOnTimer()); + EXPECT_TRUE(ac.getOnTimer()); + + ac.setSleepTimer(3 * 60); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_STATE_EQ(timer_sleep_3h, ac.getRaw(), ac.getStateLength() * 8); +} + +TEST(TestIRFujitsuACClass, ARREW4E) { + IRFujitsuAC ac(kGpioUnused); + + uint8_t on_18_cool_auto[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x50, 0x01, 0x00, 0x21, 0x03, 0x20, 0x20, 0x1A}; + + EXPECT_TRUE(ac.validChecksum(on_18_cool_auto, kFujitsuAcStateLength)); + ac.setRaw(on_18_cool_auto, kFujitsuAcStateLength); + EXPECT_EQ(0, ac.getId()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); + EXPECT_EQ(18, ac.getTemp()); + + uint8_t mode_C_power_on_18[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x51, 0x01, 0x00, 0x17, 0x07, 0x54, 0x20, 0xEB}; + EXPECT_TRUE(ac.validChecksum(mode_C_power_on_18, kFujitsuAcStateLength)); + ac.setRaw(mode_C_power_on_18, kFujitsuAcStateLength); + EXPECT_EQ(2, ac.getId()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); + EXPECT_EQ(18, ac.getTemp()); + + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + irsend.begin(); + irsend.reset(); + irsend.sendFujitsuAC(mode_C_power_on_18, kFujitsuAcStateLength); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(mode_C_power_on_18, irsend.capture.state, + irsend.capture.bits); +} + +TEST(TestDecodeFujitsuAC, Issue1455) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + uint16_t rawData[259] = { + 3220, 1700, 354, 446, 380, 448, 380, 1296, 352, 446, 382, 1296, 354, 448, + 380, 448, 378, 446, 382, 1296, 352, 1296, 354, 448, 378, 446, 380, 446, + 382, 1294, 354, 1270, 380, 448, 380, 446, 380, 448, 380, 446, 380, 450, + 378, 448, 380, 446, 380, 448, 380, 448, 380, 448, 380, 450, 376, 450, 380, + 448, 378, 1298, 352, 448, 378, 448, 380, 448, 380, 448, 378, 450, 378, + 450, 378, 448, 378, 1296, 354, 446, 382, 446, 380, 448, 378, 448, 380, + 1296, 352, 1296, 354, 1296, 354, 1272, 376, 1272, 378, 1296, 354, 1294, + 354, 1296, 354, 446, 380, 448, 378, 1296, 354, 448, 378, 448, 378, 448, + 380, 446, 380, 1272, 378, 446, 380, 450, 378, 448, 378, 1296, 354, 1296, + 354, 446, 380, 448, 378, 448, 378, 446, 382, 446, 380, 1296, 354, 1296, + 354, 446, 380, 1296, 354, 446, 380, 446, 380, 446, 380, 1294, 354, 448, + 380, 448, 380, 448, 380, 448, 380, 446, 380, 448, 380, 446, 380, 448, + 380, 446, 380, 446, 380, 448, 380, 446, 380, 448, 380, 448, 380, 446, 380, + 1296, 352, 446, 380, 1296, 354, 446, 380, 448, 380, 448, 380, 1296, 354, + 448, 378, 448, 380, 446, 380, 446, 382, 446, 380, 446, 382, 446, 380, + 1272, 378, 446, 380, 446, 382, 1294, 354, 446, 382, 1294, 354, 446, 382, + 446, 382, 446, 380, 448, 380, 448, 380, 448, 380, 448, 378, 1296, 354, + 446, 382, 446, 380, 1296, 354, 446, 382, 1296, 354, 446, 382, 1294, 354, + 446, 382, 446, 380, 446, 382}; // UNKNOWN 8383D7DE + irsend.sendRaw(rawData, 259, 38); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 19C, " + "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestIRFujitsuACClass, Heat10Deg) { + IRFujitsuAC ac(kGpioUnused); + const uint8_t heat_on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x10, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x69, 0x0B, 0x00, 0x23, 0x06, 0x23, 0x20, 0xEF}; + ac.setRaw(heat_on, kFujitsuAcStateLength); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + ac.toString()); + ac.stateReset(); + ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); + ac.setId(1); + ac.setMode(kFujitsuAcModeFan); + ac.setTemp(21); + ac.setFanSpeed(kFujitsuAcFanAuto); + ac.setSwing(0); + ac.setOutsideQuiet(false); + ac.setPower(true); + ac.set10CHeat(true); + EXPECT_TRUE(ac.get10CHeat()); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + + ac.set10CHeat(false); + EXPECT_FALSE(ac.get10CHeat()); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 21C, " + "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + ac.toString()); + + // For https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 + ac.set10CHeat(true); + EXPECT_TRUE(ac.get10CHeat()); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setSwing(kFujitsuAcSwingVert); + EXPECT_FALSE(ac.get10CHeat()); + ac.set10CHeat(false); + EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + EXPECT_FALSE(ac.get10CHeat()); + ac.set10CHeat(true); + EXPECT_TRUE(ac.get10CHeat()); + EXPECT_EQ(kFujitsuAcFanAuto, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("FUJITSU_AC", typeToString(decode_type_t::FUJITSU_AC)); + ASSERT_EQ(decode_type_t::FUJITSU_AC, strToDecodeType("FUJITSU_AC")); + ASSERT_TRUE(hasACState(decode_type_t::FUJITSU_AC)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::FUJITSU_AC)); + ASSERT_EQ(0, IRsend::defaultBits(decode_type_t::FUJITSU_AC)); // No default + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::FUJITSU_AC)); + + ASSERT_EQ("FUJITSU_AC264", typeToString(decode_type_t::FUJITSU_AC264)); + ASSERT_EQ(decode_type_t::FUJITSU_AC264, strToDecodeType("FUJITSU_AC264")); + ASSERT_TRUE(hasACState(decode_type_t::FUJITSU_AC264)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::FUJITSU_AC264)); + ASSERT_EQ(264, IRsend::defaultBits(decode_type_t::FUJITSU_AC264)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::FUJITSU_AC264)); +} + +TEST(TestIRFujitsuACClass, Temperature) { + IRFujitsuAC ac(kGpioUnused); + // Most models + // Celsius + ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); + ac.setTemp(kFujitsuAcMinTemp); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMinTemp - 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp + 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + // Fahrenheit (can't be used by most model, check it converts correctly) + ac.setTemp(77, false); // 77F is 25C + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(25, ac.getTemp()); + + // ARREW4E is different. + ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); + ac.setTemp(kFujitsuAcMinTemp); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMinTemp - 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp + 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + ac.setTemp(22.5); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(22.5, ac.getTemp()); + // Fahrenheit + ac.setTemp(77, false); + EXPECT_FALSE(ac.getCelsius()); + EXPECT_EQ(77, ac.getTemp()); + + // Real example + const uint8_t arrew4e_22c[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x70, 0x01, 0x00, 0x20, 0x03, 0x58, 0x20, 0xC3}; + ac.setRaw(arrew4e_22c, 16); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(22, ac.getTemp()); + const uint8_t arrew4e_25_5c[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x8C, 0x01, 0x00, 0x21, 0x03, 0x12, 0x20, 0xEC}; + ac.setRaw(arrew4e_25_5c, 16); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(25.5, ac.getTemp()); + const uint8_t arrew4e_69f[16] = { + 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x66, 0x04, 0x00, 0x16, 0x01, 0x32, 0x20, 0xFC}; + ac.setRaw(arrew4e_69f, 16); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); + EXPECT_FALSE(ac.getCelsius()); + EXPECT_EQ(69, ac.getTemp()); +} + +TEST(TestIRFujitsuACClass, ARREW4EShortCodes) { + // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 + IRFujitsuAC ac(kGpioUnused); + ac.setId(3); + ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); + + const uint8_t off[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x02, 0xFD}; + ac.off(); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(off, ac.getRaw(), kFujitsuAcStateLengthShort * 8); + + const uint8_t econo[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x09, 0xF6}; + ac.setCmd(kFujitsuAcCmdEcono); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(econo, ac.getRaw(), kFujitsuAcStateLengthShort * 8); + + const uint8_t powerful[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x39, 0xC6}; + ac.setCmd(kFujitsuAcCmdPowerful); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(powerful, ac.getRaw(), kFujitsuAcStateLengthShort * 8); + + const uint8_t stepvert[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x6C, 0x93}; + ac.setCmd(kFujitsuAcCmdStepVert); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(stepvert, ac.getRaw(), kFujitsuAcStateLengthShort * 8); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/discussions/1701#discussioncomment-1910164 +TEST(TestIRFujitsuACClass, Discussion1701) { + IRFujitsuAC ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRac irac(kGpioUnused); + + const String expected_raw_output = + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s1182m448s390m448s390m448s1182m448s390" + "m448s8100"; + const String expected_arrew4e_str = + "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off"; + const uint8_t expected_arrew4e_state[kFujitsuAcStateLength] = + {0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4C}; + + // Method used in `TurnOnFujitsuAC` + ac.begin(); + ac.setModel(ARREW4E); + ac.setSwing(kFujitsuAcSwingOff); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); // 24C + ac.setCmd(kFujitsuAcCmdTurnOn); + ASSERT_EQ(expected_arrew4e_str, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) + EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); + EXPECT_EQ(expected_arrew4e_str, + IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); + + // Now try to reproduce it via the IRac class. + ac._irsend.reset(); + ac.stateReset(); + ASSERT_NE(expected_arrew4e_str, ac.toString()); + + irac.fujitsu(&ac, + ARREW4E, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + true, // Celsius + 24, // Degrees + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Vertical swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo (Powerful) + false, // Econo + false, // Filter + false); // Clean + ASSERT_EQ(expected_arrew4e_str, ac.toString()); + ac._irsend.makeDecodeResult(); + // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) + EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); + EXPECT_EQ(expected_arrew4e_str, + IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); + // Success. +} + +TEST(TestIRFujitsuACClass, toCommon_Issue1780HandlePrev) { + IRFujitsuAC ac(kGpioUnused); + ac.setMode(kFujitsuAcModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setSwing(kFujitsuAcSwingBoth); + ac.on(); + ASSERT_TRUE(ac.toCommon().power); + stdAc::state_t prev = ac.toCommon(); // Copy in the state. + ac.off(); + ASSERT_FALSE(ac.toCommon().power); + ac.send(); // This should send a short code. + prev.degrees = 27; + ac.stateReset(); + IRrecv irrecv(kGpioUnused); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_FALSE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(16, ac.toCommon().degrees); + ASSERT_EQ(27, ac.toCommon(&prev).degrees); + ASSERT_FALSE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon(&prev).mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon(&prev).fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); + + stdAc::state_t result_inc_prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result_inc_prev, + &prev)); + ASSERT_EQ(27, result_inc_prev.degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, result_inc_prev.mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, result_inc_prev.fanspeed); +} + +TEST(TestIRFujitsuACClass, Improve10CHeat) { + IRFujitsuAC ac(kGpioUnused); + // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G2 + const uint8_t Arrah2u_10CHeatOn[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x41, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x64}; + ASSERT_FALSE(ac.get10CHeat()); + ac.setRaw(Arrah2u_10CHeatOn, 16); + ASSERT_TRUE(ac.get10CHeat()); + ASSERT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.get10CHeat()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); + + ac.stateReset(); + // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G8 + const uint8_t Arreg1u_10CHeatOn[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x61, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x44}; + ASSERT_FALSE(ac.get10CHeat()); + ac.setRaw(Arreg1u_10CHeatOn, 16); + ASSERT_TRUE(ac.get10CHeat()); + ASSERT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.get10CHeat()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); +} + +// Tests for Fujitsu A/C 264 methods. +TEST(TestDecodeFujitsuAc264, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + uint16_t rawData[531] = {3378, 1534, 498, 322, 526, 294, 524, 1116, 522, 302, + 492, 1166, 472, 350, 470, 352, 466, 352, 468, 1172, 468, 1176, 466, 352, + 466, 354, 466, 356, 466, 1176, 464, 1174, 468, 354, 466, 356, 464, 352, + 468, 356, 466, 352, 466, 354, 466, 356, 464, 354, 468, 352, 466, 356, 466, + 352, 466, 356, 464, 356, 464, 1178, 464, 356, 466, 356, 464, 356, 462, 356, + 466, 356, 464, 356, 462, 356, 464, 1178, 464, 356, 466, 356, 462, 358, 464, + 354, 464, 1176, 464, 1178, 464, 1180, 462, 1176, 454, 1188, 442, 1202, 440, + 1198, 442, 380, 464, 1176, 440, 382, 442, 1200, 438, 1202, 440, 380, 440, + 382, 438, 382, 440, 404, 416, 382, 440, 380, 440, 382, 438, 380, 440, 380, + 440, 1202, 440, 404, 414, 1202, 440, 382, 440, 380, 438, 1204, 440, 380, + 440, 382, 436, 400, 422, 1226, 416, 1226, 416, 404, 416, 406, 414, 406, + 414, 406, 414, 406, 416, 404, 416, 404, 418, 404, 414, 406, 416, 404, 418, + 404, 416, 404, 416, 404, 416, 404, 414, 404, 416, 406, 416, 404, 414, 404, + 416, 406, 414, 406, 414, 406, 416, 404, 414, 406, 414, 408, 414, 404, 414, + 408, 412, 406, 416, 404, 414, 406, 414, 408, 414, 406, 414, 408, 412, 406, + 414, 408, 412, 408, 414, 404, 414, 408, 412, 406, 414, 404, 416, 406, 412, + 408, 414, 404, 414, 410, 412, 408, 412, 406, 412, 408, 414, 408, 414, 406, + 412, 1230, 412, 408, 414, 408, 412, 1228, 414, 408, 412, 408, 412, 406, + 414, 408, 412, 1228, 414, 1228, 412, 408, 416, 408, 410, 408, 412, 408, + 414, 408, 412, 408, 412, 410, 410, 408, 410, 408, 412, 408, 414, 408, 412, + 408, 414, 408, 412, 1228, 414, 408, 412, 408, 410, 410, 410, 410, 412, 406, + 412, 408, 412, 410, 410, 1232, 410, 408, 410, 410, 410, 412, 386, 1254, + 412, 410, 408, 412, 412, 408, 410, 1230, 412, 1232, 410, 1230, 408, 1232, + 410, 1232, 386, 432, 386, 436, 408, 410, 386, 434, 404, 418, 386, 434, 386, + 432, 390, 432, 386, 1254, 388, 1254, 386, 434, 388, 432, 388, 434, 386, + 436, 384, 456, 362, 1256, 386, 434, 386, 434, 386, 434, 384, 436, 386, 434, + 386, 1256, 386, 458, 360, 434, 388, 1254, 386, 434, 388, 434, 386, 434, + 388, 434, 384, 460, 360, 436, 384, 458, 360, 436, 386, 460, 362, 434, 386, + 458, 360, 460, 360, 460, 362, 460, 360, 458, 362, 460, 360, 458, 360, 460, + 362, 460, 360, 460, 360, 460, 362, 456, 362, 458, 360, 462, 360, 458, 362, + 458, 362, 456, 364, 458, 362, 460, 360, 460, 362, 458, 362, 458, 360, 460, + 360, 458, 360, 1282, 360, 1280, 362, 1280, 360, 1282, 360, 1280, 360, 1280, + 360, 1282, 360, 1278, 362, 1280, 360, 1282, 360, 1280, 362, 1280, 360, + 1282, 360, 1280, 362, 1280, 360, 1282, 360, 1282, 362, 1280, 360, 1280, + 360, 1282, 358, 1282, 360, 1282, 360, 1282, 358, 1282, 360, 462, 358, 462, + 358, 462, 360, 460, 360, 462, 360, 484, 334, 462, 360, 462, 358, 458, 360, + 462, 356, 1282, 360, 1282, 360, 1308, 332, 486, 334, 1284, 358, 462, 360 + }; // FUJITSU_AC264 + + uint8_t expectedState[kFujitsuAc264StateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x1A, 0x40, + 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x06, 0x00, 0x01, 0x11, 0x1F, 0x60, 0x10, 0x24, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, + 0x5C}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 531, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC264, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAc264Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRFujitsuAC264 ac(kGpioUnused); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ("Power: On, Mode: 1 (Cool), Temp: 25C, Temp (Auto): 0C, " + "Fan: 0 (Auto), Fan Angle: 15 (Stay), Swing: Off, Economy: Off, " + "Clean: Off, Command: Cool, Current Time: 17:31, Sleep Timer: Off, " + "On Timer: Off, Off Timer: Off", + ac.toString()); +} + +TEST(TestDecodeFujitsuAc264, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + uint8_t sendCode[kFujitsuAc264StateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x1A, 0x40, + 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x06, 0x00, 0x01, 0x11, 0x1F, 0x60, 0x10, 0x24, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, + 0x5C}; + + irsend.begin(); + irsend.reset(); + irsend.sendFujitsuAC264(sendCode); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC264, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAc264Bits, irsend.capture.bits); + EXPECT_STATE_EQ(sendCode, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s390m448s1182m448s390m448s1182m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s1182m448s390" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s390m448s390m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s1182m448s1182m448s390m448s1182m448s390" + "m448s8100", + irsend.outputStr()); +} + +TEST(TestFujitsuAc264Class, toCommon) { + IRFujitsuAC264 ac(kGpioUnused); + ac.setPower(true); + ac.setMode(kFujitsuAc264ModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAc264FanSpeedHigh); + ac.setSwing(true); + ac.setEcoFan(false); + ac.setClock(1 * 60 + 23); // "1:23" + ac.setSleepTimer(2 * 60); + // Now test it. + ASSERT_EQ(decode_type_t::FUJITSU_AC264, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(83, ac.toCommon().clock); + ASSERT_EQ(120, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); +} + +TEST(TestFujitsuAc264Class, Power) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Value from getPower is not updated untill ac.send() + ac.on(); + EXPECT_FALSE(ac.getPower()); + + ac.on(); + ac.send(); + EXPECT_TRUE(ac.getPower()); + + // Value from getPower is not updated untill ac.send() + ac.off(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + ac.send(); + EXPECT_FALSE(ac.getPower()); + + // Value from getPower is not updated untill ac.send() + ac.setPower(true); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + ac.send(); + EXPECT_TRUE(ac.getPower()); + + // Value from getPower is not updated untill ac.send() + ac.setPower(false); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + ac.send(); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestFujitsuAc264Class, Temperature) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setMode(kFujitsuAc264ModeHeat); + // Value over maximum is fixed to maximum (30C) + ac.setTemp(40); + EXPECT_EQ(30, ac.getTemp()); + + // Value under minimum is fixed to minimum (16C in Heat) + ac.setTemp(10); + EXPECT_EQ(16, ac.getTemp()); + + ac.setMode(kFujitsuAc264ModeCool); + // Value over maximum is fixed to maximum (30C) + ac.setTemp(40); + EXPECT_EQ(30, ac.getTemp()); + + // Value under minimum is fixed to minimum (18C) + ac.setTemp(10); + EXPECT_EQ(18, ac.getTemp()); + + // Valid values in suppoerted range + ac.setMode(kFujitsuAc264ModeHeat); + for (float i = 16; i <= 30; i += 0.5) { + ac.setTemp(i); + EXPECT_EQ(i, ac.getTemp()); + } + + // Valid values in suppoerted range + ac.setMode(kFujitsuAc264ModeCool); + for (float i = 18; i <= 30; i += 0.5) { + ac.setTemp(i); + EXPECT_EQ(i, ac.getTemp()); + } + + // Value in supported range, but not multiple of 0.5 + // Fractional part of the value which is not multiple of 0.5 is truncated. + ac.setTemp(22.9); + EXPECT_EQ(22.5, ac.getTemp()); + + ac.setTemp(20.1); + EXPECT_EQ(20, ac.getTemp()); +} + +TEST(TestFujitsuAc264Class, TemperatureAuto) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Value over maximum is fixed to maximum (+2C) + ac.setTempAuto(10); + EXPECT_EQ(2, ac.getTempAuto()); + + // Value under minimum is fixed to minimum (-2C) + ac.setTempAuto(-10); + EXPECT_EQ(-2, ac.getTempAuto()); + + // Valid values in suppoerted range + for (float i = -2; i <= 2; i += 0.5) { + ac.setTempAuto(i); + EXPECT_EQ(i, ac.getTempAuto()); + } + + // Value in supported range, but not multiple of 0.5 + // Fractional part of the value which is not multiple of 0.5 is truncated. + ac.setTempAuto(0.8); + EXPECT_EQ(0.5, ac.getTempAuto()); + + ac.setTempAuto(1.2); + EXPECT_EQ(1, ac.getTempAuto()); + + ac.setTempAuto(-1.4); + EXPECT_EQ(-1, ac.getTempAuto()); +} + +TEST(TestFujitsuAc264Class, Mode) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setMode(2); + EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + // Unexpected value should default to Auto. + ac.setMode(255); + EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeAuto); + EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeCool); + EXPECT_EQ(kFujitsuAc264ModeCool, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeFan); + EXPECT_EQ(kFujitsuAc264ModeFan, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeHeat); + EXPECT_EQ(kFujitsuAc264ModeHeat, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeDry); + EXPECT_EQ(kFujitsuAc264ModeDry, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeDry, true); + EXPECT_EQ(kFujitsuAc264ModeDry, ac.getMode()); + EXPECT_TRUE(ac.isWeakDry()); +} + +TEST(TestFujitsuAc264Class, FanSpeed) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFanSpeed(0); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(255); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(2); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(4); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(5); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(7); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Beyond Max should default to Auto. + ac.setFanSpeed(kFujitsuAc264FanSpeedHigh + 1); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedAuto); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedQuiet); + EXPECT_EQ(kFujitsuAc264FanSpeedQuiet, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedLow); + EXPECT_EQ(kFujitsuAc264FanSpeedLow, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedMed); + EXPECT_EQ(kFujitsuAc264FanSpeedMed, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedHigh); + EXPECT_EQ(kFujitsuAc264FanSpeedHigh, ac.getFanSpeed()); +} + +TEST(TestFujitsuAc264Class, FanAngle) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to Stay. + ac.setFanAngle(0); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(255); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(8); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(13); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(32); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle1); + EXPECT_EQ(kFujitsuAc264FanAngle1, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle2); + EXPECT_EQ(kFujitsuAc264FanAngle2, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle3); + EXPECT_EQ(kFujitsuAc264FanAngle3, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle4); + EXPECT_EQ(kFujitsuAc264FanAngle4, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle5); + EXPECT_EQ(kFujitsuAc264FanAngle5, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle6); + EXPECT_EQ(kFujitsuAc264FanAngle6, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle7); + EXPECT_EQ(kFujitsuAc264FanAngle7, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngleStay); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); +} + +TEST(TestFujitsuAc264Class, Swing) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + + ac.setSwing(false); + EXPECT_FALSE(ac.getSwing()); +} + +TEST(TestFujitsuAc264Class, Economy) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setEconomy(true); + EXPECT_TRUE(ac.getEconomy()); + + ac.setEconomy(false); + EXPECT_FALSE(ac.getEconomy()); +} + +TEST(TestFujitsuAc264Class, Clean) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); +} + +TEST(TestFujitsuAc264Class, Clock) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to 0. + ac.setClock(2000); + EXPECT_EQ(0, ac.getClock()); + + // Valid values in suppoerted range + for (uint16_t i = 0; i < 1440; ++i) { + ac.setClock(i); + EXPECT_EQ(i, ac.getClock()); + } +} + +TEST(TestFujitsuAc264Class, SleepTimer) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to 0 (invalid). + ac.setSleepTimer(12 * 60 + 1); + EXPECT_EQ(0, ac.getSleepTimer()); + + // Valid values in suppoerted range + for (uint16_t i = 0; i <= 12; ++i) { + ac.setSleepTimer(i * 60); + EXPECT_EQ(i * 60, ac.getSleepTimer()); + } +} + +TEST(TestFujitsuAc264Class, OnTimer) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value is ignored. + ac.setOnTimer(144); + EXPECT_EQ(0, ac.getOnTimer()); + + // Valid values in suppoerted range + for (uint8_t i = 0; i < 144; ++i) { + ac.setOnTimer(i); + EXPECT_EQ(i, ac.getOnTimer()); + } + + // Unexpected value is ignored. + ac.setOnTimer(255); + EXPECT_EQ(143, ac.getOnTimer()); + + // On timer enabled + ac.setTimerEnable(kFujitsuAc264OnTimerEnable); + EXPECT_EQ(kFujitsuAc264OnTimerEnable, ac.getTimerEnable()); + + // On timer disabled + ac.setTimerEnable(kFujitsuAc264OnOffTimerDisable); + EXPECT_EQ(kFujitsuAc264OnOffTimerDisable, ac.getTimerEnable()); +} + +TEST(TestFujitsuAc264Class, OffTimer) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value is ignored. + ac.setOffTimer(144); + EXPECT_EQ(0, ac.getOffTimer()); + + // Valid values in suppoerted range + for (uint8_t i = 0; i < 144; ++i) { + ac.setOffTimer(i); + EXPECT_EQ(i, ac.getOffTimer()); + } + + // Unexpected value is ignored. + ac.setOffTimer(255); + EXPECT_EQ(143, ac.getOffTimer()); + + // Off timer enabled + ac.setTimerEnable(kFujitsuAc264OffTimerEnable); + EXPECT_EQ(kFujitsuAc264OffTimerEnable, ac.getTimerEnable()); + + // On & off timer enabled + ac.setTimerEnable(kFujitsuAc264OnOffTimerEnable); + EXPECT_EQ(kFujitsuAc264OnOffTimerEnable, ac.getTimerEnable()); + + // On & Off timer disabled + ac.setTimerEnable(kFujitsuAc264OnOffTimerDisable); + EXPECT_EQ(kFujitsuAc264OnOffTimerDisable, ac.getTimerEnable()); +} + +TEST(TestFujitsuAc264Class, Sterilization) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // When AC is powered on, sterilization is ignored. + ac.toggleSterilization(); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.off(); + ac.send(); + + // When AC is powered off, sterilization is accepted. + ac.toggleSterilization(); + EXPECT_EQ(kFujitsuAc264SpCmdToggleSterilization, ac.getCmd()); +} + +TEST(TestFujitsuAc264Class, OutsideQuiet) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // When AC is powered on, outside quiet is ignored. + ac.setOutsideQuiet(true); + ac.send(); + EXPECT_FALSE(ac.getOutsideQuiet()); + + ac.off(); + ac.send(); + + // When AC is powered off, outside quiet is accepted. + ac.setOutsideQuiet(true); + ac.send(); + EXPECT_TRUE(ac.getOutsideQuiet()); + + ac.setOutsideQuiet(false); + ac.send(); + EXPECT_FALSE(ac.getOutsideQuiet()); +} + +TEST(TestFujitsuAc264Class, EcoFan) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // When AC is powered on, eco fan is ignored. + ac.setEcoFan(true); + ac.send(); + EXPECT_FALSE(ac.getEcoFan()); + + ac.off(); + ac.send(); + + // When AC is powered off, eco fan is accepted. + ac.setEcoFan(true); + ac.send(); + EXPECT_TRUE(ac.getEcoFan()); + + ac.setEcoFan(false); + ac.send(); + EXPECT_FALSE(ac.getEcoFan()); +} + +TEST(TestFujitsuAc264Class, Powerful) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.off(); + ac.send(); + + // When AC is powered off, powerful is ignored. + ac.togglePowerful(); + ac.send(); + EXPECT_EQ(kFujitsuAc264SpCmdTurnOff, ac.getCmd()); + + ac.on(); + ac.send(); + + // When AC is powered on, powerful is accepted. + ac.togglePowerful(); + ac.send(); + EXPECT_EQ(kFujitsuAc264SpCmdTogglePowerful, ac.getCmd()); + + ac.togglePowerful(); + ac.send(); + EXPECT_EQ(kFujitsuAc264SpCmdTogglePowerful, ac.getCmd()); +} + +TEST(TestFujitsuAc264Class, NormalCommands) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // Special commands are ignored. + ac.setCmd(kFujitsuAc264SpCmdTogglePowerful); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdTurnOff); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdEcoFanOff); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdEcoFanOn); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdOutsideQuietOff); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdOutsideQuietOn); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdToggleSterilization); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + // Normal commands can be set. + ac.setCmd(kFujitsuAc264CmdCool); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdHeat); + EXPECT_EQ(kFujitsuAc264CmdHeat, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdDry); + EXPECT_EQ(kFujitsuAc264CmdDry, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdAuto); + EXPECT_EQ(kFujitsuAc264CmdAuto, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdFan); + EXPECT_EQ(kFujitsuAc264CmdFan, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdTemp); + EXPECT_EQ(kFujitsuAc264CmdTemp, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdSwing); + EXPECT_EQ(kFujitsuAc264CmdSwing, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdSleepTime); + EXPECT_EQ(kFujitsuAc264CmdSleepTime, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdEconomy); + EXPECT_EQ(kFujitsuAc264CmdEconomy, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdClean); + EXPECT_EQ(kFujitsuAc264CmdClean, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdFanSpeed); + EXPECT_EQ(kFujitsuAc264CmdFanSpeed, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdFanAngle); + EXPECT_EQ(kFujitsuAc264CmdFanAngle, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdCancelSleepTimer); + EXPECT_EQ(kFujitsuAc264CmdCancelSleepTimer, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdOnTimer); + EXPECT_EQ(kFujitsuAc264CmdOnTimer, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdOffTimer); + EXPECT_EQ(kFujitsuAc264CmdOffTimer, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdCancelOnOffTimer); + EXPECT_EQ(kFujitsuAc264CmdCancelOnOffTimer, ac.getCmd()); +} + +TEST(TestFujitsuAc264Class, SpecialCommands) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + uint8_t expected_turnoff[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + uint8_t expected_powerful[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; + uint8_t expected_ecofanoff[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x51, 0xAE}; + uint8_t expected_ecofanon[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x50, 0xAF}; + uint8_t expected_outsidequietoff[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x01, 0x00, 0x00, 0xFE, 0xBF, 0x00, 0x41}; + uint8_t expected_outsidequieton[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x00, 0x00, 0x00, 0xFF, 0xBF, 0x00, 0x41}; + uint8_t expected_sterilization[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x60, 0x03, 0x00, 0x00, 0xFC, 0x9F, 0x00, 0x41}; + + ac.on(); + ac.send(); + + ac.togglePowerful(); + ac.send(); + EXPECT_STATE_EQ(expected_powerful, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Powerful", ac.toString()); + + ac.off(); + ac.send(); + EXPECT_STATE_EQ(expected_turnoff, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Power Off", ac.toString()); + + ac.setEcoFan(true); + ac.send(); + EXPECT_STATE_EQ(expected_ecofanon, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Eco Fan On", ac.toString()); + + ac.setEcoFan(false); + ac.send(); + EXPECT_STATE_EQ(expected_ecofanoff, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Eco Fan Off", ac.toString()); + + ac.setOutsideQuiet(true); + ac.send(); + EXPECT_STATE_EQ(expected_outsidequieton, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); + EXPECT_EQ("Command: Outside Quiet On", ac.toString()); + + ac.setOutsideQuiet(false); + ac.send(); + EXPECT_STATE_EQ(expected_outsidequietoff, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); + EXPECT_EQ("Command: Outside Quiet Off", ac.toString()); + + ac.toggleSterilization(); + ac.send(); + EXPECT_STATE_EQ(expected_sterilization, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); + EXPECT_EQ("Command: Sterilization", ac.toString()); +} From cfa7fcd18a73a5342a926a490e66d1992f708476 Mon Sep 17 00:00:00 2001 From: Takeshi Shimizu Date: Fri, 1 Sep 2023 21:09:12 +0900 Subject: [PATCH 2/2] Normalize all the line endings --- src/IRac.cpp | 10288 ++++++++++++++++++------------------- src/IRac.h | 1182 ++--- src/IRrecv.cpp | 4178 +++++++-------- src/IRrecv.h | 1788 +++---- src/IRremoteESP8266.h | 3078 +++++------ src/IRsend.cpp | 2896 +++++------ src/IRsend.h | 1852 +++---- src/IRtext.cpp | 1126 ++-- src/IRutils.cpp | 2878 +++++------ src/ir_Fujitsu.cpp | 4184 +++++++-------- src/ir_Fujitsu.h | 1088 ++-- src/locale/defaults.h | 2302 ++++----- test/ir_Fujitsu_test.cpp | 4440 ++++++++-------- 13 files changed, 20640 insertions(+), 20640 deletions(-) diff --git a/src/IRac.cpp b/src/IRac.cpp index 3ce3cb5e1..518e9d943 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -1,5144 +1,5144 @@ -// Copyright 2019 David Conran - -// Provide a universal/standard interface for sending A/C nessages. -// It does not provide complete and maximum granular control but tries -// to offer most common functionality across all supported devices. - -#include "IRac.h" -#ifndef UNIT_TEST -#include -#endif -#include -#ifndef ARDUINO -#include -#endif -#include -#if __cplusplus >= 201103L && defined(_GLIBCXX_USE_C99_MATH_TR1) - using std::roundf; -#else - using ::roundf; -#endif -#include "IRsend.h" -#include "IRremoteESP8266.h" -#include "IRtext.h" -#include "IRutils.h" -#include "ir_Airton.h" -#include "ir_Airwell.h" -#include "ir_Amcor.h" -#include "ir_Argo.h" -#include "ir_Bosch.h" -#include "ir_Carrier.h" -#include "ir_Coolix.h" -#include "ir_Corona.h" -#include "ir_Daikin.h" -#include "ir_Ecoclim.h" -#include "ir_Electra.h" -#include "ir_Fujitsu.h" -#include "ir_Haier.h" -#include "ir_Hitachi.h" -#include "ir_Kelon.h" -#include "ir_Kelvinator.h" -#include "ir_LG.h" -#include "ir_Midea.h" -#include "ir_Mitsubishi.h" -#include "ir_MitsubishiHeavy.h" -#include "ir_Neoclima.h" -#include "ir_Panasonic.h" -#include "ir_Rhoss.h" -#include "ir_Samsung.h" -#include "ir_Sanyo.h" -#include "ir_Sharp.h" -#include "ir_Tcl.h" -#include "ir_Technibel.h" -#include "ir_Teco.h" -#include "ir_Toshiba.h" -#include "ir_Transcold.h" -#include "ir_Trotec.h" -#include "ir_Truma.h" -#include "ir_Vestel.h" -#include "ir_Voltas.h" -#include "ir_Whirlpool.h" - -// On the ESP8266 platform we need to use a special version of string handling -// functions to handle the strings stored in the flash address space. -#ifndef STRCASECMP -#if defined(ESP8266) -#define STRCASECMP(LHS, RHS) \ - strcasecmp_P(LHS, reinterpret_cast(RHS)) -#else // ESP8266 -#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS) -#endif // ESP8266 -#endif // STRCASECMP - -#ifndef UNIT_TEST -#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) -#else -/* NOTE: THIS IS NOT A DOXYGEN COMMENT (would require ENABLE_PREPROCESSING-YES) -/// If compiling for UT *and* a test receiver @c IRrecv is provided via the -/// @c _utReceived param, this injects an "output" gadget @c _lastDecodeResults -/// into the @c IRAc::sendAc method, so that the UT code may parse the "sent" -/// value and drive further assertions -/// -/// @note The @c decode_results "returned" is a shallow copy (empty rawbuf), -/// mostly b/c the class does not have a custom/deep copy c-tor -/// and defining it would be an overkill for this purpose -/// @note For future maintainers: If @c IRAc class is ever refactored to use -/// polymorphism (static or dynamic)... this macro should be removed -/// and replaced with proper GMock injection. -*/ -#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) \ - { \ - if (_utReceiver) { \ - _lastDecodeResults = nullptr; \ - (ac)._irsend.makeDecodeResult(); \ - if (_utReceiver->decode(&(ac)._irsend.capture)) { \ - _lastDecodeResults = std::unique_ptr( \ - new decode_results((ac)._irsend.capture)); \ - _lastDecodeResults->rawbuf = nullptr; \ - } \ - } \ - } -#endif // UNIT_TEST - -/// Class constructor -/// @param[in] pin Gpio pin to use when transmitting IR messages. -/// @param[in] inverted true, gpio output defaults to high. false, to low. -/// @param[in] use_modulation true means use frequency modulation. false, don't. -IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { - _pin = pin; - _inverted = inverted; - _modulation = use_modulation; - this->markAsSent(); -} - -/// Initialise the given state with the supplied settings. -/// @param[out] state A Ptr to where the settings will be stored. -/// @param[in] vendor The vendor/protocol type. -/// @param[in] model The A/C model if applicable. -/// @param[in] power The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. -/// Others it may be the time to enter/exit sleep mode. -/// i.e. Time in Nr. of mins since midnight. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::initState(stdAc::state_t *state, - const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, - const float degrees, const bool celsius, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep, - const int16_t clock) { - state->protocol = vendor; - state->model = model; - state->power = power; - state->mode = mode; - state->degrees = degrees; - state->celsius = celsius; - state->fanspeed = fan; - state->swingv = swingv; - state->swingh = swingh; - state->quiet = quiet; - state->turbo = turbo; - state->econo = econo; - state->light = light; - state->filter = filter; - state->clean = clean; - state->beep = beep; - state->sleep = sleep; - state->clock = clock; -} - -/// Initialise the given state with the supplied settings. -/// @param[out] state A Ptr to where the settings will be stored. -/// @note Sets all the parameters to reasonable base/automatic defaults. -void IRac::initState(stdAc::state_t *state) { - stdAc::state_t def; - *state = def; -} - -/// Get the current internal A/C climate state. -/// @return A Ptr to a state containing the current (to be sent) settings. -stdAc::state_t IRac::getState(void) { return next; } - -/// Get the previous internal A/C climate state that should have already been -/// sent to the device. i.e. What the A/C unit should already be set to. -/// @return A Ptr to a state containing the previously sent settings. -stdAc::state_t IRac::getStatePrev(void) { return _prev; } - -/// Is the given protocol supported by the IRac class? -/// @param[in] protocol The vendor/protocol type. -/// @return true if the protocol is supported by this class, otherwise false. -bool IRac::isProtocolSupported(const decode_type_t protocol) { - switch (protocol) { -#if SEND_AIRTON - case decode_type_t::AIRTON: -#endif // SEND_AIRTON -#if SEND_AIRWELL - case decode_type_t::AIRWELL: -#endif // SEND_AIRWELL -#if SEND_AMCOR - case decode_type_t::AMCOR: -#endif -#if SEND_ARGO - case decode_type_t::ARGO: -#endif -#if SEND_BOSCH144 - case decode_type_t::BOSCH144: -#endif -#if SEND_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - case decode_type_t::COOLIX: -#endif -#if SEND_CORONA_AC - case decode_type_t::CORONA_AC: -#endif -#if SEND_DAIKIN - case decode_type_t::DAIKIN: -#endif -#if SEND_DAIKIN128 - case decode_type_t::DAIKIN128: -#endif -#if SEND_DAIKIN152 - case decode_type_t::DAIKIN152: -#endif -#if SEND_DAIKIN160 - case decode_type_t::DAIKIN160: -#endif -#if SEND_DAIKIN176 - case decode_type_t::DAIKIN176: -#endif -#if SEND_DAIKIN2 - case decode_type_t::DAIKIN2: -#endif -#if SEND_DAIKIN216 - case decode_type_t::DAIKIN216: -#endif -#if SEND_DAIKIN64 - case decode_type_t::DAIKIN64: -#endif -#if SEND_DELONGHI_AC - case decode_type_t::DELONGHI_AC: -#endif -#if SEND_ECOCLIM - case decode_type_t::ECOCLIM: -#endif -#if SEND_ELECTRA_AC - case decode_type_t::ELECTRA_AC: -#endif -#if SEND_FUJITSU_AC - case decode_type_t::FUJITSU_AC: -#endif -#if SEND_FUJITSU_AC264 - case decode_type_t::FUJITSU_AC264: -#endif -#if SEND_GOODWEATHER - case decode_type_t::GOODWEATHER: -#endif -#if SEND_GREE - case decode_type_t::GREE: -#endif -#if SEND_HAIER_AC - case decode_type_t::HAIER_AC: -#endif -#if SEND_HAIER_AC160 - case decode_type_t::HAIER_AC160: -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - case decode_type_t::HAIER_AC176: -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: -#endif -#if SEND_HITACHI_AC - case decode_type_t::HITACHI_AC: -#endif -#if SEND_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: -#endif -#if SEND_HITACHI_AC264 - case decode_type_t::HITACHI_AC264: -#endif -#if SEND_HITACHI_AC296 - case decode_type_t::HITACHI_AC296: -#endif -#if SEND_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: -#endif -#if SEND_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: -#endif -#if SEND_KELON - case decode_type_t::KELON: -#endif -#if SEND_KELVINATOR - case decode_type_t::KELVINATOR: -#endif -#if SEND_LG - case decode_type_t::LG: - case decode_type_t::LG2: -#endif -#if SEND_MIDEA - case decode_type_t::MIDEA: -#endif // SEND_MIDEA -#if SEND_MIRAGE - case decode_type_t::MIRAGE: -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: -#endif -#if SEND_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: -#endif -#if SEND_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: -#endif -#if SEND_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: - case decode_type_t::MITSUBISHI_HEAVY_152: -#endif -#if SEND_NEOCLIMA - case decode_type_t::NEOCLIMA: -#endif -#if SEND_PANASONIC_AC - case decode_type_t::PANASONIC_AC: -#endif -#if SEND_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: -#endif -#if SEND_RHOSS - case decode_type_t::RHOSS: -#endif -#if SEND_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: -#endif -#if SEND_SANYO_AC - case decode_type_t::SANYO_AC: -#endif -#if SEND_SANYO_AC88 - case decode_type_t::SANYO_AC88: -#endif -#if SEND_SHARP_AC - case decode_type_t::SHARP_AC: -#endif -#if SEND_TCL112AC - case decode_type_t::TCL112AC: -#endif -#if SEND_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: -#endif -#if SEND_TECO - case decode_type_t::TECO: -#endif -#if SEND_TEKNOPOINT - case decode_type_t::TEKNOPOINT: -#endif // SEND_TEKNOPOINT -#if SEND_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: -#endif -#if SEND_TRANSCOLD - case decode_type_t::TRANSCOLD: -#endif -#if SEND_TROTEC - case decode_type_t::TROTEC: -#endif -#if SEND_TROTEC_3550 - case decode_type_t::TROTEC_3550: -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - case decode_type_t::TRUMA: -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case decode_type_t::VESTEL_AC: -#endif -#if SEND_VOLTAS - case decode_type_t::VOLTAS: -#endif -#if SEND_YORK - case decode_type_t::YORK: -#endif -#if SEND_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: -#endif - return true; - default: - return false; - } -} - -#if SEND_AIRTON -/// Send an Airton 56-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRAirtonAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/health/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::airton(IRAirtonAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool light, const bool econo, const bool filter, - const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Quiet setting available. - ac->setLight(light); - ac->setHealth(filter); - ac->setTurbo(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Convert to a boolean. - ac->send(); -} -#endif // SEND_AIRTON - -#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. -/// @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::amcor(IRAmcorAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setPower(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_AMCOR - -#if SEND_ARGO -/// Send an Argo A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRArgoAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::argo(IRArgoAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, - const bool turbo, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(static_cast(roundf(degrees))); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } - ac->setiFeel(iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setFlap(ac->convertSwingV(swingv)); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - ac->setMax(turbo); - // No Economy setting available. - // No Clean setting available. - // No Beep setting available. - ac->setNight(sleep >= 0); // Convert to a boolean. - ac->send(); -} - -/// Send an Argo A/C WREM-3 AC **control** message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The set temperature setting in degrees Celsius. -/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) -/// The overflow is *not* checked, though. -/// @note The value is rounded to nearest integer, rounding halfway cases -/// away from zero. E.g. 1.5 [C] becomes 2 [C]. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] night Enable night mode (raises temp by +1*C after 1h). -/// @param[in] econo Enable eco mode (limits power consumed). -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Enable filter mode -/// @param[in] light Enable device display/LEDs -void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on, - const stdAc::opmode_t mode, const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool iFeel, - const bool night, const bool econo, const bool turbo, const bool filter, - const bool light) { - ac->begin(); - ac->setMessageType(argoIrMessageType_t::AC_CONTROL); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } - ac->setiFeel(iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setFlap(ac->convertSwingV(swingv)); - ac->setNight(night); - ac->setEco(econo); - ac->setMax(turbo); - ac->setFilter(filter); - ac->setLight(light); - // No Clean setting available. - // No Beep setting available - always beeps in this mode :) - ac->send(); -} - -/// Send an Argo A/C WREM-3 iFeel (room temp) silent (no beep) report. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] sensorTemp The room (iFeel) temperature setting -/// in degrees Celsius. -/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) -/// The overflow is *not* checked, though. -/// @note The value is rounded to nearest integer, rounding halfway cases -/// away from zero. E.g. 1.5 [C] becomes 2 [C]. -void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) { - ac->begin(); - ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT); - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - ac->send(); -} - -/// Send an Argo A/C WREM-3 Config command. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] param The parameter ID. -/// @param[in] value The parameter value. -/// @param[in] safe If true, will only allow setting the below parameters -/// in order to avoid accidentally setting a restricted -/// vendor-specific param and breaking the A/C device -/// @note Known parameters (P, where xx is the @c param) -/// P05 - Temperature Scale (0-Celsius, 1-Fahrenheit) -/// P06 - Transmission channel (0..3) -/// P12 - ECO mode power input limit (30..99, default: 75) -void IRac::argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, - const uint8_t value, bool safe /*= true*/) { - if (safe) { - switch (param) { - case 5: // temp. scale (note this is likely excess as not transmitted) - if (value > 1) { return; /* invalid */ } - break; - case 6: // channel (note this is likely excess as not transmitted) - if (value > 3) { return; /* invalid */ } - break; - case 12: // eco power limit - if (value < 30 || value > 99) { return; /* invalid */ } - break; - default: - return; /* invalid */ - } - } - ac->begin(); - ac->setMessageType(argoIrMessageType_t::CONFIG_PARAM_SET); - ac->setConfigEntry(param, value); - ac->send(); -} - -/// Send an Argo A/C WREM-3 Delay timer command. -/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. -/// @param[in] on Whether the unit is currently on. The timer, upon elapse -/// will toggle this state -/// @param[in] currentTime currentTime in minutes, starting from 00:00 -/// @note For timer mode, this value is not really used much so can be zero. -/// @param[in] delayMinutes Number of minutes after which the @c on state should -/// be toggled -/// @note Schedule timers are not exposed via this interface -void IRac::argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, - const uint16_t currentTime, const uint16_t delayMinutes) { - ac->begin(); - ac->setMessageType(argoIrMessageType_t::TIMER_COMMAND); - ac->setPower(on); - ac->setTimerType(argoTimerType_t::DELAY_TIMER); - ac->setCurrentTimeMinutes(currentTime); - // Note: Day of week is not set (no need) - ac->setDelayTimerMinutes(delayMinutes); - ac->send(); -} -#endif // SEND_ARGO - -#if SEND_BOSCH144 -/// Send a Bosch144 A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRBosch144AC 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. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @note -1 is Off, >= 0 is on. -void IRac::bosch144(IRBosch144AC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const bool quiet) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setMode(ac->convertMode(mode)); - ac->setQuiet(quiet); - ac->send(); // Send the state, which will also power on the unit. - // The following are all options/settings that create their own special - // messages. Often they only make sense to be sent after the unit is turned - // on. For instance, assuming a person wants to have the a/c on and in turbo - // mode. If we send the turbo message, it is ignored if the unit is off. - // Hence we send the special mode/setting messages after a normal message - // which will turn on the device. - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Sleep setting available. -} -#endif // SEND_BOSCH144 - -#if SEND_CARRIER_AC64 -/// Send a Carrier 64-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRCarrierAc64 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::carrier64(IRCarrierAc64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV((int8_t)swingv >= 0); - // 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. - ac->setSleep(sleep >= 0); // Convert to a boolean. - ac->send(); -} -#endif // SEND_CARRIER_AC64 - -#if SEND_COOLIX -/// Send a Coolix A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRCoolixAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -/// @note -1 is Off, >= 0 is on. -void IRac::coolix(IRCoolixAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool light, - const bool clean, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Quiet setting available. - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } else { - ac->clearSensorTemp(); - } - ac->setZoneFollow(iFeel); - ac->send(); // Send the state, which will also power on the unit. - // The following are all options/settings that create their own special - // messages. Often they only make sense to be sent after the unit is turned - // on. For instance, assuming a person wants to have the a/c on and in turbo - // mode. If we send the turbo message, it is ignored if the unit is off. - // Hence we send the special mode/setting messages after a normal message - // which will turn on the device. - if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { - // Swing has a special command that needs to be sent independently. - ac->setSwing(); - ac->send(); - } - if (turbo) { - // Turbo has a special command that needs to be sent independently. - ac->setTurbo(); - ac->send(); - } - if (sleep >= 0) { - // Sleep has a special command that needs to be sent independently. - ac->setSleep(); - ac->send(); - } - if (light) { - // Light has a special command that needs to be sent independently. - ac->setLed(); - ac->send(); - } - if (clean) { - // Clean has a special command that needs to be sent independently. - ac->setClean(); - ac->send(); - } -} -#endif // SEND_COOLIX - -#if SEND_CORONA_AC -/// Send a Corona A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRCoronaAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] econo Run the device in economical mode. -void IRac::corona(IRCoronaAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool econo) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Quiet setting available. - // No Light setting available. - // No Filter setting available. - // No Turbo setting available. - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - ac->send(); -} -#endif // SEND_CARRIER_AC64 - -#if SEND_DAIKIN -/// Send a Daikin A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikinESP 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::daikin(IRDaikinESP *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - // No Light setting available. - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setMold(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_DAIKIN - -#if SEND_DAIKIN128 -/// Send a Daikin 128-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin128 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::daikin128(IRDaikin128 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool light, - const bool econo, const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - // No Horizontal Swing setting avaliable. - ac->setQuiet(quiet); - ac->setLightToggle(light ? kDaikin128BitWall : 0); - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep > 0); - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_DAIKIN128 - -#if SEND_DAIKIN152 -/// Send a Daikin 152-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin152 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -void IRac::daikin152(IRDaikin152 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool econo) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV((int8_t)swingv >= 0); - // No Horizontal Swing setting avaliable. - ac->setQuiet(quiet); - // No Light setting available. - // No Filter setting available. - ac->setPowerful(turbo); - ac->setEcono(econo); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_DAIKIN152 - -#if SEND_DAIKIN160 -/// Send a Daikin 160-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin160 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. -/// @param[in] swingv The vertical swing setting. -void IRac::daikin160(IRDaikin160 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->send(); -} -#endif // SEND_DAIKIN160 - -#if SEND_DAIKIN176 -/// Send a Daikin 176-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin176 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. -/// @param[in] swingh The horizontal swing setting. -void IRac::daikin176(IRDaikin176 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->send(); -} -#endif // SEND_DAIKIN176 - -#if SEND_DAIKIN2 -/// Send a Daikin2 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin2 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::daikin2(IRDaikin2 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter, const bool clean, - const bool beep, const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setLight(light ? 1 : 3); // On/High is 1, Off is 3. - ac->setPowerful(turbo); - ac->setEcono(econo); - ac->setPurify(filter); - ac->setMold(clean); - ac->setClean(true); // Hardwire auto clean to be on per request (@sheppy99) - ac->setBeep(beep ? 2 : 3); // On/Loud is 2, Off is 3. - if (sleep > 0) ac->enableSleepTimer(sleep); - if (clock >= 0) ac->setCurrentTime(clock); - ac->send(); -} -#endif // SEND_DAIKIN2 - -#if SEND_DAIKIN216 -/// Send a Daikin 216-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin216 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -void IRac::daikin216(IRDaikin216 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setPowerful(turbo); - ac->send(); -} -#endif // SEND_DAIKIN216 - -#if SEND_DAIKIN64 -/// Send a Daikin 64-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDaikin64 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::daikin64(IRDaikin64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, - const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical((int8_t)swingv >= 0); - ac->setTurbo(turbo); - ac->setQuiet(quiet); - ac->setSleep(sleep >= 0); - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_DAIKIN64 - -#if SEND_DELONGHI_AC -/// Send a Delonghi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRDelonghiAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::delonghiac(IRDelonghiAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const bool turbo, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setBoost(turbo); - ac->setSleep(sleep >= 0); - ac->send(); -} -#endif // SEND_DELONGHI_AC - -#if SEND_ECOCLIM -/// Send an EcoClim A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IREcoclimAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::ecoclim(IREcoclimAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const int16_t sleep, - const int16_t clock) { - ac->begin(); - ac->setPower(on); - uint8_t new_mode; - if (sleep >= 0) // EcoClim has a descrete Sleep operation mode, not a setting - new_mode = kEcoclimSleep; // Override the requested operating mode. - else - new_mode = ac->convertMode(mode); // Not Sleep, so use the supplied mode. - ac->setMode(new_mode); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } else { - ac->setSensorTemp(degrees); //< Set to the desired temp - // until we can disable. - } - // No SwingV setting available - // No SwingH setting available - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Econo setting available. - // No Filter setting available. - // No Clean setting available - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_ECOCLIM - -#if SEND_ELECTRA_AC -/// Send an Electra A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRElectraAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] lighttoggle Should we toggle the LED/Display? -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::electra(IRElectraAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, const bool iFeel, - const bool turbo, const bool lighttoggle, const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setLightToggle(lighttoggle); - // No Econo setting available. - // No Filter setting available. - ac->setClean(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->setIFeel(iFeel); - ac->send(); -} -#endif // SEND_ELECTRA_AC - -#if SEND_FUJITSU_AC -/// Send a Fujitsu A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRFujitsuAC object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. -void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool filter, const bool clean, const int16_t sleep) { - ac->begin(); - ac->setModel(model); - if (on) { - // Do all special messages (except "Off") first, - // These need to be sent separately. - switch (ac->getModel()) { - // Some functions are only available on some models. - case fujitsu_ac_remote_model_t::ARREB1E: - if (turbo) { - ac->setCmd(kFujitsuAcCmdPowerful); - // Powerful is a separate command. - ac->send(); - } - if (econo) { - ac->setCmd(kFujitsuAcCmdEcono); - // Econo is a separate command. - ac->send(); - } - break; - default: - {}; - } - // Normal operation. - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFanSpeed(ac->convertFan(fan)); - uint8_t swing = kFujitsuAcSwingOff; - if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; - if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; - ac->setSwing(swing); - if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); - // No Light setting available. - ac->setFilter(filter); - ac->setClean(clean); - // No Beep setting available. - ac->setSleepTimer(sleep > 0 ? sleep : 0); - // No Sleep setting available. - // No Clock setting available. - ac->on(); // Ref: Issue #860 - } else { - // Off is special case/message. We don't need to send other messages. - ac->off(); - } - ac->send(); -} -#endif // SEND_FUJITSU_AC - -#if SEND_FUJITSU_AC264 -/// Send a Fujitsu A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRFujitsuAC264 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Toggle the device's turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignored. -void IRac::fujitsu264(IRFujitsuAC264 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool econo, - const bool clean, const int16_t sleep, - const int16_t clock) { - ac->begin(); - if (on) { - // Do all special messages (except "Off") first, - // These need to be sent separately. - // Some functions are only available on some models. - if (turbo) { - ac->togglePowerful(); - // Powerful is a separate command. - ac->send(); - } - // Normal operation. - ac->setMode(ac->convertMode(mode)); - if (mode == stdAc::opmode_t::kAuto) - ac->setTempAuto(degrees); - else - ac->setTemp(degrees); - ac->setFanSpeed(ac->convertFanSpeed(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - if (quiet) ac->setFanSpeed(kFujitsuAc264FanSpeedQuiet); - ac->setEconomy(econo); - ac->setClean(clean); - ac->setSleepTimer(sleep > 0 ? sleep : 0); - if (clock >= 0) ac->setClock(clock); - ac->on(); - } else { - // Off is special case/message. We don't need to send other messages. - ac->off(); - } - ac->send(); -} -#endif // SEND_FUJITSU_AC264 - -#if SEND_GOODWEATHER -/// Send a Goodweather A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGoodweatherAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::goodweather(IRGoodweatherAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff - : kGoodweatherSwingSlow); - ac->setTurbo(turbo); - ac->setLight(light); - // No Clean setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Horizontal Swing setting available. - // No Econo setting available. - // No Filter setting available. - // No Beep setting available. - // No Quiet setting available. - // No Clock setting available. - ac->setPower(on); - ac->send(); -} -#endif // SEND_GOODWEATHER - -#if SEND_GREE -/// Send a Gree A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGreeAC object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Toggle the device's economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool econo, - const bool light, const bool clean, const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. - ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setIFeel(iFeel); - ac->setLight(light); - ac->setTurbo(turbo); - ac->setEcono(econo); - ac->setXFan(clean); - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Econo setting available. - // No Filter setting available. - // No Beep setting available. - // No Quiet setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_GREE - -#if SEND_HAIER_AC -/// Send a Haier A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRGreeAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::haier(IRHaierAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool filter, const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - if (clock >= 0) ac->setCurrTime(clock); - if (on) - ac->setCommand(kHaierAcCmdOn); - else - ac->setCommand(kHaierAcCmdOff); - ac->send(); -} -#endif // SEND_HAIER_AC - -#if SEND_HAIER_AC160 -/// Send a Haier 160 bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierAC160 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the clean mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] prevlight Previous LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haier160(IRHaierAC160 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool quiet, const bool filter, - const bool clean, const bool light, const bool prevlight, - const int16_t sleep) { - ac->begin(); - // No Model setting available. - ac->setMode(ac->convertMode(mode)); - ac->setUseFahrenheit(!celsius); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setHealth(filter); - ac->setClean(clean); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - // Light needs to be sent last as the "button" value seems to control it. - ac->setLightToggle(light ^ prevlight); - ac->send(); -} -#endif // SEND_HAIER_AC160 - -#if SEND_HAIER_AC176 -/// Send a Haier 176 bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haier176(IRHaierAC176 *ac, const haier_ac176_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool quiet, const bool filter, - const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setUseFahrenheit(!celsius); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - ac->send(); -} -#endif // SEND_HAIER_AC176 - -#if SEND_HAIER_AC_YRW02 -/// Send a Haier YRWO2 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] quiet Run the device in quiet mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haierYrwo2(IRHaierACYRW02 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool quiet, const bool filter, - const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setUseFahrenheit(!celsius); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - ac->setPower(on); - ac->send(); -} -#endif // SEND_HAIER_AC_YRW02 - -#if SEND_HITACHI_AC -/// Send a Hitachi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::hitachi(IRHitachiAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC - -#if SEND_HITACHI_AC1 -/// Send a Hitachi1 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc1 object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] power_toggle The power toggle 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] swing_toggle The swing_toggle setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @note The sleep mode used is the "Sleep 2" setting. -void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, - const bool on, const bool power_toggle, - const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool swing_toggle, const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setPowerToggle(power_toggle); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - ac->setSwingToggle(swing_toggle); - ac->setSleep((sleep >= 0) ? kHitachiAc1Sleep2 : kHitachiAc1SleepOff); - // No Sleep setting available. - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC1 - -#if SEND_HITACHI_AC264 -/// Send a Hitachi 264-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc264 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::hitachi264(IRHitachiAc264 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setPower(on); - // No Swing(V) setting available. - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC264 - -#if SEND_HITACHI_AC296 -/// Send a Hitachi 296-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc296 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::hitachi296(IRHitachiAc296 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setPower(on); - // No Swing(V) setting available. - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC296 - -#if SEND_HITACHI_AC344 -/// Send a Hitachi 344-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc344 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::hitachi344(IRHitachiAc344 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setPower(on); - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - - // SwingVToggle is special. Needs to be last method called. - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - ac->send(); -} -#endif // SEND_HITACHI_AC344 - -#if SEND_HITACHI_AC424 -/// Send a Hitachi 424-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRHitachiAc424 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. -/// @param[in] swingv The vertical swing setting. -void IRac::hitachi424(IRHitachiAc424 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setPower(on); - // SwingVToggle is special. Needs to be last method called. - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Swing(H) setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_HITACHI_AC424 - -#if SEND_KELON -/// Send a Kelon A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRKelonAc object to use. -/// @param[in] togglePower Whether to toggle the unit's power -/// @param[in] mode The operation mode setting. -/// @param[in] dryGrade The dehumidification intensity grade -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] toggleSwing Whether to toggle the swing setting -/// @param[in] superCool Run the device in Super cooling mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on -void IRac::kelon(IRKelonAc *ac, const bool togglePower, - const stdAc::opmode_t mode, const int8_t dryGrade, - const float degrees, const stdAc::fanspeed_t fan, - const bool toggleSwing, const bool superCool, - const int16_t sleep) { - ac->begin(); - ac->setMode(IRKelonAc::convertMode(mode)); - ac->setFan(IRKelonAc::convertFan(fan)); - ac->setTemp(static_cast(degrees)); - ac->setSleep(sleep >= 0); - ac->setSupercool(superCool); - ac->setDryGrade(dryGrade); - - ac->setTogglePower(togglePower); - ac->setToggleSwingVertical(toggleSwing); - - ac->send(); -} -#endif // SEND_KELON - -#if SEND_KELVINATOR -/// Send a Kelvinator A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRKelvinatorAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc -void IRac::kelvinator(IRKelvinatorAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool filter, const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan((uint8_t)fan); // No conversion needed. - ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. - ac->convertSwingV(swingv)); - ac->setSwingHorizontal((int8_t)swingh >= 0); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setLight(light); - ac->setIonFilter(filter); - ac->setXFan(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_KELVINATOR - -#if SEND_LG -/// Send a LG A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRLgAc object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingv_prev The previous vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] light Turn on the LED/Display mode. -void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, - const stdAc::swingh_t swingh, const bool light) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv_prev)); - ac->updateSwingPrev(); - ac->setSwingV(ac->convertSwingV(swingv)); - const uint8_t pos = ac->convertVaneSwingV(swingv); - for (uint8_t vane = 0; vane < kLgAcSwingVMaxVanes; vane++) - ac->setVaneSwingV(vane, pos); - // Toggle the swingv for LG6711A20083V models if we need to. - // i.e. Off to Not-Off, send a toggle. Not-Off to Off, send a toggle. - if ((model == lg_ac_remote_model_t::LG6711A20083V) && - ((swingv == stdAc::swingv_t::kOff) != - (swingv_prev == stdAc::swingv_t::kOff))) - ac->setSwingV(kLgAcSwingVToggle); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_LG - -#if SEND_MIDEA -/// Send a Midea A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMideaAC object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] sensorTemp The room (iFeel) temperature sensor reading -/// in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] quiet_prev The device's previous quiet/silent mode. -/// @param[in] turbo Toggle the device's turbo/powerful mode. -/// @param[in] econo Toggle the device's economical mode. -/// @param[in] light Toggle the LED/Display mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @note On Danby A/C units, swingv controls the Ion Filter instead. -void IRac::midea(IRMideaAC *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool iFeel, const bool quiet, const bool quiet_prev, - const bool turbo, const bool econo, const bool light, - const bool clean, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setUseCelsius(celsius); - ac->setTemp(degrees, celsius); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(sensorTemp, celsius); - } - ac->setEnableSensorTemp(iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - ac->setQuiet(quiet, quiet_prev); - ac->setTurboToggle(turbo); - ac->setEconoToggle(econo); - ac->setLightToggle(light); - // No Filter setting available. - ac->setCleanToggle(clean); - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MIDEA - -#if SEND_MIRAGE -/// Send a Mirage 120-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. -/// @param[in] state The desired state to send. -void IRac::mirage(IRMirageAc *ac, const stdAc::state_t state) { - ac->begin(); - ac->fromCommon(state); - ac->send(); -} -#endif // SEND_MIRAGE - -#if SEND_MITSUBISHI_AC -/// Send a Mitsubishi A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @note Clock can only be set in 10 minute increments. i.e. % 10. -void IRac::mitsubishi(IRMitsubishiAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const int16_t clock) { - ac->begin(); - // Uncomment next line if you *really* need the weekly timer enabled via IRac. - // ac->setWeeklyTimerEnabled(true); // Weekly Timer is disabled by default. - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setVane(ac->convertSwingV(swingv)); - ac->setVaneLeft(ac->convertSwingV(swingv)); - ac->setWideVane(ac->convertSwingH(swingh)); - if (quiet) ac->setFan(kMitsubishiAcFanSilent); - ac->setISave10C(false); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. - ac->send(); -} -#endif // SEND_MITSUBISHI_AC - -#if SEND_MITSUBISHI112 -/// Send a Mitsubishi 112-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishi112 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -void IRac::mitsubishi112(IRMitsubishi112 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - ac->setSwingH(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - // FIXME - Econo - // ac->setEcono(econo); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHI112 - -#if SEND_MITSUBISHI136 -/// Send a Mitsubishi 136-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishi136 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -void IRac::mitsubishi136(IRMitsubishi136 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool quiet) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. - ac->setQuiet(quiet); - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHI136 - -#if SEND_MITSUBISHIHEAVY -/// Send a Mitsubishi Heavy 88-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy88Ac 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool econo, - const bool clean) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - // No Filter setting available. - ac->setClean(clean); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} - -/// Send a Mitsubishi Heavy 152-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy152Ac 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, - const bool econo, const bool filter, - const bool clean, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setSilent(quiet); - ac->setTurbo(turbo); - // No Light setting available. - ac->setEcono(econo); - ac->setClean(clean); - ac->setFilter(filter); - // No Beep setting available. - ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_MITSUBISHIHEAVY - -#if SEND_NEOCLIMA -/// Send a Neoclima A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRNeoclimaAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::neoclima(IRNeoclimaAc *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const bool filter, const int16_t sleep) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setLight(light); - ac->setEcono(econo); - ac->setIon(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->setPower(on); - ac->send(); -} -#endif // SEND_NEOCLIMA - -#if SEND_PANASONIC_AC -/// Send a Panasonic A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRPanasonicAc object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool filter, - const int16_t clock) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(ac->convertSwingH(swingh)); - ac->setQuiet(quiet); - ac->setPowerful(turbo); - ac->setIon(filter); - // No Light setting available. - // No Econo setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_PANASONIC_AC - -#if SEND_PANASONIC_AC32 -/// Send a Panasonic A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRPanasonicAc32 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -void IRac::panasonic32(IRPanasonicAc32 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPowerToggle(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - // No Turbo setting available. - // No Filter setting available. - // No Light setting available. - // No Econo setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_PANASONIC_AC32 - -#if SEND_SAMSUNG_AC -/// Send a Samsung A/C message with the supplied settings. -/// @note Multiple IR messages may be generated & sent. -/// @param[in, out] ac A Ptr to an IRSamsungAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Toggle the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Toggle beep setting for receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. -/// @param[in] prevpower The power setting from the previous A/C state. -/// @param[in] prevsleep Nr. of minutes for sleep from the previous A/C state. -/// @param[in] forceextended Do we force sending the special extended message? -void IRac::samsung(IRSamsungAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, - const bool filter, const bool clean, - const bool beep, const int16_t sleep, - const bool prevpower, const int16_t prevsleep, - const bool forceextended) { - ac->begin(); - ac->stateReset(forceextended || (sleep != prevsleep), prevpower); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - ac->setQuiet(quiet); - ac->setPowerful(turbo); // FYI, `setEcono(true)` will override this. - ac->setDisplay(light); - ac->setEcono(econo); - ac->setIon(filter); - ac->setClean(clean); // Toggle - ac->setBeep(beep); // Toggle - ac->setSleepTimer((sleep <= 0) ? 0 : sleep); - // No Clock setting available. - // Do setMode() again as it can affect fan speed. - ac->setMode(ac->convertMode(mode)); - ac->send(); -} -#endif // SEND_SAMSUNG_AC - -#if SEND_SANYO_AC -/// Send a Sanyo A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRSanyoAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees -/// Celsius. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::sanyo(IRSanyoAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool iFeel, const bool beep, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - if (sensorTemp != kNoTempValue) { - ac->setSensorTemp(static_cast(roundf(sensorTemp))); - } else { - ac->setSensorTemp(degrees); // Set the sensor temp to the desired - // (normal) temp. - } - ac->setSensor(!iFeel); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(ac->convertSwingV(swingv)); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Econo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - ac->setBeep(beep); - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_SANYO_AC - -#if SEND_SANYO_AC88 -/// Send a Sanyo 88-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRSanyoAc88 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::sanyo88(IRSanyoAc88 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool filter, const int16_t sleep, - const int16_t clock) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Econo setting available. - // No Light setting available. - ac->setFilter(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (clock >= 0) ac->setClock(clock); - ac->send(); -} -#endif // SEND_SANYO_AC88 - -#if SEND_SHARP_AC -/// Send a Sharp A/C message with the supplied settings. -/// @note Multiple IR messages may be generated & sent. -/// @param[in, out] ac A Ptr to an IRSharpAc object to use. -/// @param[in] model The A/C model to use. -/// @param[in] on The power setting. -/// @param[in] prev_power The power setting from the previous A/C state. -/// @param[in] mode The operation mode setting. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingv_prev The previous vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, - const bool on, const bool prev_power, - const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingv_t swingv_prev, const bool turbo, - const bool light, const bool filter, const bool clean) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan, model)); - if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv)); - // Econo deliberately not used as it cycles through 3 modes uncontrollably. - // ac->setEconoToggle(econo); - ac->setIon(filter); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setLightToggle(light); - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - // Do setMode() again as it can affect fan speed and temp. - ac->setMode(ac->convertMode(mode)); - // Clean after mode, as it can affect the mode, temp & fan speed. - if (clean) { - // A/C needs to be off before we can enter clean mode. - ac->setPower(false, prev_power); - ac->send(); - } - ac->setClean(clean); - ac->setPower(on, prev_power); - if (turbo) { - ac->send(); // Send the current state. - // Set up turbo mode as it needs to be sent after everything else. - ac->setTurbo(true); - } - ac->send(); -} -#endif // SEND_SHARP_AC - -#if SEND_TCL112AC -/// Send a TCL 112-bit A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -void IRac::tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingVertical(ac->convertSwingV(swingv)); - ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); - ac->setQuiet(quiet); - ac->setTurbo(turbo); - ac->setLight(light); - ac->setEcono(econo); - ac->setHealth(filter); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TCL112AC - -#if SEND_TECHNIBEL_AC -/// Send a Technibel A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTechnibelAc object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::technibel(IRTechnibelAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, !celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TECHNIBEL_AC - -#if SEND_TECO -/// Send a Teco A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTecoAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::teco(IRTecoAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool light, const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TECO - -#if SEND_TOSHIBA_AC -/// Send a Toshiba A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRToshibaAC 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] filter Turn on the (Pure/ion/pollen/etc) filter mode. -void IRac::toshiba(IRToshibaAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool econo, const bool filter) { - ac->begin(); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // The API has no "step" option, so off is off, anything else is on. - ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff - : kToshibaAcSwingOn); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setEcono(econo); - // No Light setting available. - ac->setFilter(filter); - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - // Do this last because Toshiba A/C has an odd quirk with how power off works. - ac->setPower(on); - ac->send(); -} -#endif // SEND_TOSHIBA_AC - -#if SEND_TROTEC -/// Send a Trotec A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrotecESP 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. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::trotec(IRTrotecESP *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const int16_t sleep) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setSpeed(ac->convertFan(fan)); - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TROTEC - -#if SEND_TROTEC_3550 -/// Send a Trotec 3550 A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. -/// @param[in] on The power setting. -/// @param[in] mode The operation mode setting. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] fan The speed setting for the fan. -/// @param[in] swingv The vertical swing setting. -void IRac::trotec3550(IRTrotec3550 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees, celsius); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TROTEC_3550 - -#if SEND_TRUMA -/// Send a Truma A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRTrumaAc 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. -/// @param[in] quiet Run the device quietly if we can. -void IRac::truma(IRTrumaAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const bool quiet) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setQuiet(quiet); // Only available in Cool mode. - // No Vertical swing setting available. - // No Horizontal swing setting available. - // No Turbo setting available. - // No Light setting available. - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - // No Sleep setting available. - // No Clock setting available. - ac->send(); -} -#endif // SEND_TRUMA - -#if SEND_VESTEL_AC -/// Send a Vestel A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRVestelAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @param[in] sendNormal Do we send a Normal settings message at all? -/// i.e In addition to the clock/time/timer message -void IRac::vestel(IRVestelAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool filter, const int16_t sleep, - const int16_t clock, const bool sendNormal) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setTurbo(turbo); - // No Light setting available. - ac->setIon(filter); - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (sendNormal) ac->send(); // Send the normal message. - if (clock >= 0) { - ac->setTime(clock); - ac->send(); // Setting the clock requires a different "timer" message. - } -} -#endif // SEND_VESTEL_AC - -#if SEND_VOLTAS -/// Send a Voltas A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRVoltas object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::voltas(IRVoltas *ac, - const voltas_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const int16_t sleep) { - ac->begin(); - ac->setModel(model); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwingV(swingv != stdAc::swingv_t::kOff); - ac->setSwingH(swingh != stdAc::swingh_t::kOff); - // No Quiet setting available. - ac->setTurbo(turbo); - ac->setEcono(econo); - ac->setLight(light); - // No Filter setting available. - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - // No Clock setting available. - ac->send(); -} -#endif // SEND_VOLTAS - -#if SEND_WHIRLPOOL_AC -/// Send a Whirlpool A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRWhirlpoolAc object to use. -/// @param[in] model The A/C model 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep, const int16_t clock) { - ac->begin(); - ac->setModel(model); - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. - // No Quiet setting available. - ac->setSuper(turbo); - ac->setLight(light); - // No Filter setting available - // No Clean setting available. - // No Beep setting available. - ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. - if (clock >= 0) ac->setClock(clock); - ac->setPowerToggle(on); - ac->send(); -} -#endif // SEND_WHIRLPOOL_AC - -#if SEND_TRANSCOLD -/// Send a Transcold A/C message with the supplied settings. -/// @note May result in multiple messages being sent. -/// @param[in, out] ac A Ptr to an IRTranscoldAc 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. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @note -1 is Off, >= 0 is on. -void IRac::transcold(IRTranscoldAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh) { - ac->begin(); - ac->setPower(on); - if (!on) { - // after turn off AC no more commands should - // be accepted - ac->send(); - return; - } - ac->setMode(ac->convertMode(mode)); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // No Filter setting available. - // No Beep setting available. - // No Clock setting available. - // No Econo setting available. - // No Quiet setting available. - if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { - // Swing has a special command that needs to be sent independently. - ac->setSwing(); - ac->send(); - } - - ac->send(); -} -#endif // SEND_TRANSCOLD - -#if SEND_RHOSS -/// Send an Rhoss A/C message with the supplied settings. -/// @param[in, out] ac A Ptr to an IRRhossAc 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. -/// @param[in] swing The swing setting. -void IRac::rhoss(IRRhossAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swing) { - ac->begin(); - ac->setPower(on); - ac->setMode(ac->convertMode(mode)); - ac->setSwing(swing != stdAc::swingv_t::kOff); - ac->setTemp(degrees); - ac->setFan(ac->convertFan(fan)); - // 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_RHOSS - -/// Create a new state base on the provided state that has been suitably fixed. -/// @note This is for use with Home Assistant, which requires mode to be off if -/// the power is off. -/// @param[in] state The state_t structure describing the desired a/c state. -/// @return A stdAc::state_t with the needed settings. -stdAc::state_t IRac::cleanState(const stdAc::state_t state) { - stdAc::state_t result = state; - // A hack for Home Assistant, it appears to need/want an Off opmode. - // So enforce the power is off if the mode is also off. - if (state.mode == stdAc::opmode_t::kOff) result.power = false; - return result; -} - -/// Create a new state base on desired & previous states but handle -/// any state changes for options that need to be toggled. -/// @param[in] desired The state_t structure describing the desired a/c state. -/// @param[in] prev A Ptr to the previous state_t structure. -/// @return A stdAc::state_t with the needed settings. -stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, - const stdAc::state_t *prev) { - stdAc::state_t result = desired; - // If we've been given a previous state AND the it's the same A/C basically. - if (prev != NULL && desired.protocol == prev->protocol && - desired.model == prev->model) { - // Check if we have to handle toggle settings for specific A/C protocols. - switch (desired.protocol) { - case decode_type_t::COOLIX: - case decode_type_t::TRANSCOLD: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - result.turbo = desired.turbo ^ prev->turbo; - result.light = desired.light ^ prev->light; - result.clean = desired.clean ^ prev->clean; - result.sleep = ((desired.sleep >= 0) ^ (prev->sleep >= 0)) ? 0 : -1; - break; - case decode_type_t::DAIKIN128: - result.power = desired.power ^ prev->power; - result.light = desired.light ^ prev->light; - break; - case decode_type_t::ELECTRA_AC: - result.light = desired.light ^ prev->light; - break; - case decode_type_t::FUJITSU_AC: - result.turbo = desired.turbo ^ prev->turbo; - result.econo = desired.econo ^ prev->econo; - break; - case decode_type_t::MIDEA: - result.turbo = desired.turbo ^ prev->turbo; - result.econo = desired.econo ^ prev->econo; - result.light = desired.light ^ prev->light; - result.clean = desired.clean ^ prev->clean; - // FALL THRU - case decode_type_t::CORONA_AC: - case decode_type_t::HITACHI_AC344: - case decode_type_t::HITACHI_AC424: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - break; - case decode_type_t::SHARP_AC: - result.light = desired.light ^ prev->light; - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - break; - case decode_type_t::KELON: - if ((desired.swingv == stdAc::swingv_t::kOff) ^ - (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. - result.swingv = stdAc::swingv_t::kAuto; - else - result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. - // FALL-THRU - case decode_type_t::AIRWELL: - case decode_type_t::DAIKIN64: - case decode_type_t::PANASONIC_AC32: - case decode_type_t::WHIRLPOOL_AC: - result.power = desired.power ^ prev->power; - break; - case decode_type_t::MIRAGE: - if (desired.model == mirage_ac_remote_model_t::KKG29AC1) - result.light = desired.light ^ prev->light; - result.clean = desired.clean ^ prev->clean; - break; - case decode_type_t::PANASONIC_AC: - // CKP models use a power mode toggle. - if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) - result.power = desired.power ^ prev->power; - break; - case decode_type_t::SAMSUNG_AC: - result.beep = desired.beep ^ prev->beep; - result.clean = desired.clean ^ prev->clean; - break; - default: - {}; - } - } - return result; -} - -/// Send A/C message for a given device using common A/C settings. -/// @param[in] vendor The vendor/protocol type. -/// @param[in] model The A/C model if applicable. -/// @param[in] power The power setting. -/// @param[in] mode The operation mode setting. -/// @note Changing mode from "Off" to something else does NOT turn on a device. -/// You need to use `power` for that. -/// @param[in] degrees The temperature setting in degrees. -/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. -/// @param[in] fan The speed setting for the fan. -/// @note The following are all "if supported" by the underlying A/C classes. -/// @param[in] swingv The vertical swing setting. -/// @param[in] swingh The horizontal swing setting. -/// @param[in] quiet Run the device in quiet/silent mode. -/// @param[in] turbo Run the device in turbo/powerful mode. -/// @param[in] econo Run the device in economical mode. -/// @param[in] light Turn on the LED/Display mode. -/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. -/// @param[in] sleep Nr. of minutes for sleep mode. -/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. -/// Others it may be the time to enter/exit sleep mode. -/// i.e. Time in Nr. of mins since midnight. -/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. -/// @return True, if accepted/converted/attempted etc. False, if unsupported. -bool IRac::sendAc(const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, - const float degrees, const bool celsius, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep, const int16_t clock) { - stdAc::state_t to_send; - initState(&to_send, vendor, model, power, mode, degrees, celsius, fan, swingv, - swingh, quiet, turbo, econo, light, filter, clean, beep, sleep, - clock); - return this->sendAc(to_send, &to_send); -} - -/// Send A/C message for a given device using state_t structures. -/// @param[in] desired The state_t structure describing the desired new ac state -/// @param[in] prev A Ptr to the state_t structure containing the previous state -/// @note Changing mode from "Off" to something else does NOT turn on a device. -/// You need to use `power` for that. -/// @return True, if accepted/converted/attempted etc. False, if unsupported. -bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { - // Convert the temp from Fahrenheit to Celsius if we are not in Celsius mode. - float degC __attribute__((unused)) = - desired.celsius ? desired.degrees : fahrenheitToCelsius(desired.degrees); - // Convert the sensorTemp from Fahrenheit to Celsius if we are not in Celsius - // mode. - float sensorTempC __attribute__((unused)) = - desired.sensorTemperature ? desired.sensorTemperature - : fahrenheitToCelsius(desired.sensorTemperature); - // special `state_t` that is required to be sent based on that. - stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev); - // Some protocols expect a previous state for power. - // Construct a pointer-safe previous power state incase prev is NULL/NULLPTR. -#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) - const bool prev_power = (prev != NULL) ? prev->power : !send.power; - const int16_t prev_sleep = (prev != NULL) ? prev->sleep : -1; -#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) -#if (SEND_LG || SEND_SHARP_AC) - const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv - : stdAc::swingv_t::kOff; -#endif // (SEND_LG || SEND_SHARP_AC) -#if (SEND_HAIER_AC160) - const bool prev_light = (prev != NULL) ? prev->light : !send.light; -#endif // (SEND_HAIER_AC160) -#if SEND_MIDEA - const bool prev_quiet = (prev != NULL) ? prev->quiet : !send.quiet; -#endif // SEND_MIDEA - // Per vendor settings & setup. - switch (send.protocol) { -#if SEND_AIRTON - case AIRTON: - { - IRAirtonAc ac(_pin, _inverted, _modulation); - airton(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.turbo, send.light, send.econo, send.filter, - send.sleep); - break; - } -#endif // SEND_AIRTON -#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: - { - IRAmcorAc ac(_pin, _inverted, _modulation); - amcor(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_AMCOR -#if SEND_ARGO - case ARGO: - { - if (send.model == argo_ac_remote_model_t::SAC_WREM3) { - IRArgoAC_WREM3 ac(_pin, _inverted, _modulation); - switch (send.command) { - case stdAc::ac_command_t::kSensorTempReport: - argoWrem3_iFeelReport(&ac, sensorTempC); - break; - case stdAc::ac_command_t::kConfigCommand: - /// @warning: this is ABUSING current **common** parameters: - /// @c clock and @c sleep as config key and value - /// Hence, value pre-validation is performed (safe-mode) - /// to avoid accidental device misconfiguration - argoWrem3_ConfigSet(&ac, send.clock, send.sleep, true); - break; - case stdAc::ac_command_t::kTimerCommand: - argoWrem3_SetTimer(&ac, send.power, send.clock, send.sleep); - break; - case stdAc::ac_command_t::kControlCommand: - default: - argoWrem3_ACCommand(&ac, send.power, send.mode, degC, sensorTempC, - send.fanspeed, send.swingv, send.iFeel, send.quiet, send.econo, - send.turbo, send.filter, send.light); - break; - } - OUTPUT_DECODE_RESULTS_FOR_UT(ac); - } else { - IRArgoAC ac(_pin, _inverted, _modulation); - argo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.iFeel, send.turbo, send.sleep); - OUTPUT_DECODE_RESULTS_FOR_UT(ac); - } - break; - } -#endif // SEND_ARGO -#if SEND_BOSCH144 - case BOSCH144: - { - IRBosch144AC ac(_pin, _inverted, _modulation); - bosch144(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); - break; - } -#endif // SEND_BOSCH144 -#if SEND_CARRIER_AC64 - case CARRIER_AC64: - { - IRCarrierAc64 ac(_pin, _inverted, _modulation); - carrier64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.sleep); - break; - } -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - case COOLIX: - { - IRCoolixAC ac(_pin, _inverted, _modulation); - coolix(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.swingh, send.iFeel, send.turbo, send.light, - send.clean, send.sleep); - break; - } -#endif // SEND_COOLIX -#if SEND_CORONA_AC - case CORONA_AC: - { - IRCoronaAc ac(_pin, _inverted, _modulation); - corona(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.econo); - break; - } -#endif // SEND_CORONA_AC -#if SEND_DAIKIN - case DAIKIN: - { - IRDaikinESP ac(_pin, _inverted, _modulation); - daikin(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.econo, send.clean); - break; - } -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - case DAIKIN128: - { - IRDaikin128 ac(_pin, _inverted, _modulation); - daikin128(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.light, send.econo, send.sleep, - send.clock); - break; - } -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN152 - case DAIKIN152: - { - IRDaikin152 ac(_pin, _inverted, _modulation); - daikin152(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.econo); - break; - } -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - case DAIKIN160: - { - IRDaikin160 ac(_pin, _inverted, _modulation); - daikin160(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - case DAIKIN176: - { - IRDaikin176 ac(_pin, _inverted, _modulation); - daikin176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingh); - break; - } -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - case DAIKIN2: - { - IRDaikin2 ac(_pin, _inverted, _modulation); - daikin2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.light, send.econo, - send.filter, send.clean, send.beep, send.sleep, send.clock); - break; - } -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN216 - case DAIKIN216: - { - IRDaikin216 ac(_pin, _inverted, _modulation); - daikin216(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo); - break; - } -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN64 - case DAIKIN64: - { - IRDaikin64 ac(_pin, _inverted, _modulation); - daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.sleep, send.clock); - break; - } -#endif // SEND_DAIKIN64 -#if SEND_DELONGHI_AC - case DELONGHI_AC: - { - IRDelonghiAc ac(_pin, _inverted, _modulation); - delonghiac(&ac, send.power, send.mode, send.celsius, degC, send.fanspeed, - send.turbo, send.sleep); - break; - } -#endif // SEND_DELONGHI_AC -#if SEND_ECOCLIM - case ECOCLIM: - { - IREcoclimAc ac(_pin, _inverted, _modulation); - ecoclim(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.iFeel, send.clock); - break; - } -#endif // SEND_ECOCLIM -#if SEND_ELECTRA_AC - case ELECTRA_AC: - { - IRElectraAc ac(_pin, _inverted, _modulation); - electra(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.swingh, send.iFeel, send.turbo, send.light, - send.clean); - break; - } -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - case FUJITSU_AC: - { - IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)send.model, _inverted, - _modulation); - fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, - send.swingv, send.swingh, send.quiet, - send.turbo, send.econo, send.filter, send.clean, send.sleep); - break; - } -#endif // SEND_FUJITSU_AC -#if SEND_FUJITSU_AC264 - case FUJITSU_AC264: - { - IRFujitsuAC264 ac(_pin, _inverted, _modulation); - fujitsu264(&ac, send.power, send.mode, send.degrees, send.fanspeed, - send.swingv, send.quiet, send.turbo, send.econo, - send.clean, send.sleep); - break; - } -#endif // SEND_FUJITSU_AC264 -#if SEND_GOODWEATHER - case GOODWEATHER: - { - IRGoodweatherAc ac(_pin, _inverted, _modulation); - goodweather(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.light, send.sleep); - break; - } -#endif // SEND_GOODWEATHER -#if SEND_GREE - case GREE: - { - IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, - _modulation); - gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, - send.iFeel, send.turbo, send.econo, send.light, send.clean, - send.sleep); - break; - } -#endif // SEND_GREE -#if SEND_HAIER_AC - case HAIER_AC: - { - IRHaierAC ac(_pin, _inverted, _modulation); - haier(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC160 - case HAIER_AC160: - { - IRHaierAC160 ac(_pin, _inverted, _modulation); - haier160(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.turbo, send.filter, send.clean, - send.light, prev_light, send.sleep); - break; - } -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - case HAIER_AC176: - { - IRHaierAC176 ac(_pin, _inverted, _modulation); - haier176(&ac, (haier_ac176_remote_model_t)send.model, send.power, - send.mode, send.celsius, send.degrees, send.fanspeed, - send.swingv, send.swingh, send.turbo, send.filter, send.sleep); - break; - } -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - { - IRHaierACYRW02 ac(_pin, _inverted, _modulation); - haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.swingh, send.turbo, - send.filter, send.sleep); - break; - } -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HITACHI_AC - case HITACHI_AC: - { - IRHitachiAc ac(_pin, _inverted, _modulation); - hitachi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh); - break; - } -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - { - IRHitachiAc1 ac(_pin, _inverted, _modulation); - bool power_toggle = false; - bool swing_toggle = false; - if (prev != NULL) { - power_toggle = (send.power != prev->power); - swing_toggle = (send.swingv != prev->swingv) || - (send.swingh != prev->swingh); - } - hitachi1(&ac, (hitachi_ac1_remote_model_t)send.model, send.power, - power_toggle, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, swing_toggle, send.sleep); - break; - } -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC264 - case HITACHI_AC264: - { - IRHitachiAc264 ac(_pin, _inverted, _modulation); - hitachi264(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - case HITACHI_AC296: - { - IRHitachiAc296 ac(_pin, _inverted, _modulation); - hitachi296(&ac, send.power, send.mode, degC, send.fanspeed); - break; - } -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - case HITACHI_AC344: - { - IRHitachiAc344 ac(_pin, _inverted, _modulation); - hitachi344(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh); - break; - } -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - case HITACHI_AC424: - { - IRHitachiAc424 ac(_pin, _inverted, _modulation); - hitachi424(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_HITACHI_AC424 -#if SEND_KELON - case KELON: { - IRKelonAc ac(_pin, _inverted, _modulation); - kelon(&ac, send.power, send.mode, 0, send.degrees, send.fanspeed, - send.swingv != stdAc::swingv_t::kOff, send.turbo, send.sleep); - break; - } -#endif -#if SEND_KELVINATOR - case KELVINATOR: - { - IRKelvinatorAC ac(_pin, _inverted, _modulation); - kelvinator(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.light, send.filter, - send.clean); - break; - } -#endif // SEND_KELVINATOR -#if SEND_LG - case LG: - case LG2: - { - IRLgAc ac(_pin, _inverted, _modulation); - lg(&ac, (lg_ac_remote_model_t)send.model, send.power, send.mode, - send.degrees, send.fanspeed, send.swingv, prev_swingv, send.swingh, - send.light); - break; - } -#endif // SEND_LG -#if SEND_MIDEA - case MIDEA: - { - IRMideaAC ac(_pin, _inverted, _modulation); - midea(&ac, send.power, send.mode, send.celsius, send.degrees, - send.sensorTemperature, send.fanspeed, send.swingv, send.iFeel, - send.quiet, prev_quiet, send.turbo, send.econo, send.light, - send.clean, send.sleep); - break; - } -#endif // SEND_MIDEA -#if SEND_MIRAGE - case MIRAGE: - { - IRMirageAc ac(_pin, _inverted, _modulation); - mirage(&ac, send); - break; - } -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - { - IRMitsubishiAC ac(_pin, _inverted, _modulation); - mitsubishi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.clock); - break; - } -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI112 - case MITSUBISHI112: - { - IRMitsubishi112 ac(_pin, _inverted, _modulation); - mitsubishi112(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.quiet); - break; - } -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHI136 - case MITSUBISHI136: - { - IRMitsubishi136 ac(_pin, _inverted, _modulation); - mitsubishi136(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.quiet); - break; - } -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: - { - IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); - mitsubishiHeavy88(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.turbo, send.econo, - send.clean); - break; - } - case MITSUBISHI_HEAVY_152: - { - IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); - mitsubishiHeavy152(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh, send.quiet, send.turbo, - send.econo, send.filter, send.clean, send.sleep); - break; - } -#endif // SEND_MITSUBISHIHEAVY -#if SEND_NEOCLIMA - case NEOCLIMA: - { - IRNeoclimaAc ac(_pin, _inverted, _modulation); - neoclima(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.swingh, send.turbo, - send.econo, send.light, send.filter, send.sleep); - break; - } -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - case PANASONIC_AC: - { - IRPanasonicAc ac(_pin, _inverted, _modulation); - panasonic(&ac, (panasonic_ac_remote_model_t)send.model, send.power, - send.mode, degC, send.fanspeed, send.swingv, send.swingh, - send.quiet, send.turbo, send.clock); - break; - } -#endif // SEND_PANASONIC_AC -#if SEND_PANASONIC_AC32 - case PANASONIC_AC32: - { - IRPanasonicAc32 ac(_pin, _inverted, _modulation); - panasonic32(&ac, send.power, send.mode, degC, send.fanspeed, - send.swingv, send.swingh); - break; - } -#endif // SEND_PANASONIC_AC32 -#if SEND_RHOSS - case RHOSS: - { - IRRhossAc ac(_pin, _inverted, _modulation); - rhoss(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); - break; - } -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - { - IRSamsungAc ac(_pin, _inverted, _modulation); - samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh, send.quiet, send.turbo, send.econo, send.light, - send.filter, send.clean, send.beep, send.sleep, - prev_power, prev_sleep); - break; - } -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - case SANYO_AC: - { - IRSanyoAc ac(_pin, _inverted, _modulation); - sanyo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, - send.swingv, send.iFeel, send.beep, send.sleep); - break; - } -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - case SANYO_AC88: - { - IRSanyoAc88 ac(_pin, _inverted, _modulation); - sanyo88(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_SANYO_AC88 -#if SEND_SHARP_AC - case SHARP_AC: - { - IRSharpAc ac(_pin, _inverted, _modulation); - sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power, - send.mode, degC, send.fanspeed, send.swingv, prev_swingv, - send.turbo, send.light, send.filter, send.clean); - break; - } -#endif // SEND_SHARP_AC -#if (SEND_TCL112AC || SEND_TEKNOPOINT) - case TCL112AC: - case TEKNOPOINT: - { - IRTcl112Ac ac(_pin, _inverted, _modulation); - tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model; - if (send.protocol == decode_type_t::TEKNOPOINT) - model = tcl_ac_remote_model_t::GZ055BE1; - tcl112(&ac, model, send.power, send.mode, - degC, send.fanspeed, send.swingv, send.swingh, send.quiet, - send.turbo, send.light, send.econo, send.filter); - break; - } -#endif // (SEND_TCL112AC || SEND_TEKNOPOINT) -#if SEND_TECHNIBEL_AC - case TECHNIBEL_AC: - { - IRTechnibelAc ac(_pin, _inverted, _modulation); - technibel(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.sleep); - break; - } -#endif // SEND_TECHNIBEL_AC -#if SEND_TECO - case TECO: - { - IRTecoAc ac(_pin, _inverted, _modulation); - teco(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.light, send.sleep); - break; - } -#endif // SEND_TECO -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - { - IRToshibaAC ac(_pin, _inverted, _modulation); - toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.econo, send.filter); - break; - } -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - case TROTEC: - { - IRTrotecESP ac(_pin, _inverted, _modulation); - trotec(&ac, send.power, send.mode, degC, send.fanspeed, send.sleep); - break; - } -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - case TROTEC_3550: - { - IRTrotec3550 ac(_pin, _inverted, _modulation); - trotec3550(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv); - break; - } -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - case TRUMA: - { - IRTrumaAc ac(_pin, _inverted, _modulation); - truma(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); - break; - } -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case VESTEL_AC: - { - IRVestelAc ac(_pin, _inverted, _modulation); - vestel(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep, send.clock); - break; - } -#endif // SEND_VESTEL_AC -#if SEND_VOLTAS - case VOLTAS: - { - IRVoltas ac(_pin, _inverted, _modulation); - voltas(&ac, (voltas_ac_remote_model_t)send.model, send.power, send.mode, - degC, send.fanspeed, send.swingv, send.swingh, send.turbo, - send.econo, send.light, send.sleep); - break; - } -#endif // SEND_VOLTAS -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - { - IRWhirlpoolAc ac(_pin, _inverted, _modulation); - whirlpool(&ac, (whirlpool_ac_remote_model_t)send.model, send.power, - send.mode, degC, send.fanspeed, send.swingv, send.turbo, - send.light, send.sleep, send.clock); - break; - } -#endif // SEND_WHIRLPOOL_AC -#if SEND_TRANSCOLD - case TRANSCOLD: - { - IRTranscoldAc ac(_pin, _inverted, _modulation); - transcold(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.swingh); - break; - } -#endif // SEND_TRANSCOLD_AC - default: - return false; // Fail, didn't match anything. - } - return true; // Success. -} // NOLINT(readability/fn_size) - -/// Update the previous state to the current one. -void IRac::markAsSent(void) { - _prev = next; -} - -/// Send an A/C message based soley on our internal state. -/// @return True, if accepted/converted/attempted. False, if unsupported. -bool IRac::sendAc(void) { - bool success = this->sendAc(next, &_prev); - if (success) this->markAsSent(); - return success; -} - -/// Compare two AirCon states. -/// @note The comparison excludes the clock. -/// @param a A state_t to be compared. -/// @param b A state_t to be compared. -/// @return True if they differ, False if they don't. -bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { - return a.protocol != b.protocol || a.model != b.model || a.power != b.power || - a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || - a.fanspeed != b.fanspeed || a.swingv != b.swingv || - a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || - a.econo != b.econo || a.light != b.light || a.filter != b.filter || - a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep || - a.command != b.command || a.sensorTemperature != b.sensorTemperature || - a.iFeel != b.iFeel; -} - -/// Check if the internal state has changed from what was previously sent. -/// @note The comparison excludes the clock. -/// @return True if it has changed, False if not. -bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); } - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::ac_command_t IRac::strToCommandType(const char *str, - const stdAc::ac_command_t def) { - if (!STRCASECMP(str, kControlCommandStr)) - return stdAc::ac_command_t::kControlCommand; - else if (!STRCASECMP(str, kIFeelReportStr) || - !STRCASECMP(str, kIFeelStr)) - return stdAc::ac_command_t::kSensorTempReport; - else if (!STRCASECMP(str, kSetTimerCommandStr) || - !STRCASECMP(str, kTimerStr)) - return stdAc::ac_command_t::kTimerCommand; - else if (!STRCASECMP(str, kConfigCommandStr)) - return stdAc::ac_command_t::kConfigCommand; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::opmode_t IRac::strToOpmode(const char *str, - const stdAc::opmode_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr)) - return stdAc::opmode_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::opmode_t::kOff; - else if (!STRCASECMP(str, kCoolStr) || - !STRCASECMP(str, kCoolingStr)) - return stdAc::opmode_t::kCool; - else if (!STRCASECMP(str, kHeatStr) || - !STRCASECMP(str, kHeatingStr)) - return stdAc::opmode_t::kHeat; - else if (!STRCASECMP(str, kDryStr) || - !STRCASECMP(str, kDryingStr) || - !STRCASECMP(str, kDehumidifyStr)) - return stdAc::opmode_t::kDry; - else if (!STRCASECMP(str, kFanStr) || - // The following Fans strings with "only" are required to help with - // HomeAssistant & Google Home Climate integration. - // For compatibility only. - // Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes - !STRCASECMP(str, kFanOnlyStr) || - !STRCASECMP(str, kFan_OnlyStr) || - !STRCASECMP(str, kFanOnlyWithSpaceStr) || - !STRCASECMP(str, kFanOnlyNoSpaceStr)) - return stdAc::opmode_t::kFan; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::fanspeed_t IRac::strToFanspeed(const char *str, - const stdAc::fanspeed_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr)) - return stdAc::fanspeed_t::kAuto; - else if (!STRCASECMP(str, kMinStr) || - !STRCASECMP(str, kMinimumStr) || - !STRCASECMP(str, kLowestStr)) - return stdAc::fanspeed_t::kMin; - else if (!STRCASECMP(str, kLowStr) || - !STRCASECMP(str, kLoStr)) - return stdAc::fanspeed_t::kLow; - else if (!STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kMidStr)) - return stdAc::fanspeed_t::kMedium; - else if (!STRCASECMP(str, kHighStr) || - !STRCASECMP(str, kHiStr)) - return stdAc::fanspeed_t::kHigh; - else if (!STRCASECMP(str, kMaxStr) || - !STRCASECMP(str, kMaximumStr) || - !STRCASECMP(str, kHighestStr)) - return stdAc::fanspeed_t::kMax; - else if (!STRCASECMP(str, kMedHighStr)) - return stdAc::fanspeed_t::kMediumHigh; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::swingv_t IRac::strToSwingV(const char *str, - const stdAc::swingv_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr) || - !STRCASECMP(str, kOnStr) || - !STRCASECMP(str, kSwingStr)) - return stdAc::swingv_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::swingv_t::kOff; - else if (!STRCASECMP(str, kMinStr) || - !STRCASECMP(str, kMinimumStr) || - !STRCASECMP(str, kLowestStr) || - !STRCASECMP(str, kBottomStr) || - !STRCASECMP(str, kDownStr)) - return stdAc::swingv_t::kLowest; - else if (!STRCASECMP(str, kLowStr)) - return stdAc::swingv_t::kLow; - else if (!STRCASECMP(str, kMidStr) || - !STRCASECMP(str, kMiddleStr) || - !STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kCentreStr)) - return stdAc::swingv_t::kMiddle; - else if (!STRCASECMP(str, kUpperMiddleStr)) - return stdAc::swingv_t::kUpperMiddle; - else if (!STRCASECMP(str, kHighStr) || - !STRCASECMP(str, kHiStr)) - return stdAc::swingv_t::kHigh; - else if (!STRCASECMP(str, kHighestStr) || - !STRCASECMP(str, kMaxStr) || - !STRCASECMP(str, kMaximumStr) || - !STRCASECMP(str, kTopStr) || - !STRCASECMP(str, kUpStr)) - return stdAc::swingv_t::kHighest; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -stdAc::swingh_t IRac::strToSwingH(const char *str, - const stdAc::swingh_t def) { - if (!STRCASECMP(str, kAutoStr) || - !STRCASECMP(str, kAutomaticStr) || - !STRCASECMP(str, kOnStr) || !STRCASECMP(str, kSwingStr)) - return stdAc::swingh_t::kAuto; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, kStopStr)) - return stdAc::swingh_t::kOff; - else if (!STRCASECMP(str, kLeftMaxNoSpaceStr) || // "LeftMax" - !STRCASECMP(str, kLeftMaxStr) || // "Left Max" - !STRCASECMP(str, kMaxLeftNoSpaceStr) || // "MaxLeft" - !STRCASECMP(str, kMaxLeftStr)) // "Max Left" - return stdAc::swingh_t::kLeftMax; - else if (!STRCASECMP(str, kLeftStr)) - return stdAc::swingh_t::kLeft; - else if (!STRCASECMP(str, kMidStr) || - !STRCASECMP(str, kMiddleStr) || - !STRCASECMP(str, kMedStr) || - !STRCASECMP(str, kMediumStr) || - !STRCASECMP(str, kCentreStr)) - return stdAc::swingh_t::kMiddle; - else if (!STRCASECMP(str, kRightStr)) - return stdAc::swingh_t::kRight; - else if (!STRCASECMP(str, kRightMaxNoSpaceStr) || // "RightMax" - !STRCASECMP(str, kRightMaxStr) || // "Right Max" - !STRCASECMP(str, kMaxRightNoSpaceStr) || // "MaxRight" - !STRCASECMP(str, kMaxRightStr)) // "Max Right" - return stdAc::swingh_t::kRightMax; - else if (!STRCASECMP(str, kWideStr)) - return stdAc::swingh_t::kWide; - else - return def; -} - -/// Convert the supplied str into the appropriate enum. -/// @note Assumes str is the model code or an integer >= 1. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The enum to return if no conversion was possible. -/// @return The equivalent enum. -/// @note After adding a new model you should update modelToStr() too. -int16_t IRac::strToModel(const char *str, const int16_t def) { - // Gree - if (!STRCASECMP(str, kYaw1fStr)) { - return gree_ac_remote_model_t::YAW1F; - } else if (!STRCASECMP(str, kYbofbStr)) { - return gree_ac_remote_model_t::YBOFB; - } else if (!STRCASECMP(str, kYx1fsfStr)) { - return gree_ac_remote_model_t::YX1FSF; - // Haier models - } else if (!STRCASECMP(str, kV9014557AStr)) { - return haier_ac176_remote_model_t::V9014557_A; - } else if (!STRCASECMP(str, kV9014557BStr)) { - return haier_ac176_remote_model_t::V9014557_B; - // HitachiAc1 models - } else if (!STRCASECMP(str, kRlt0541htaaStr)) { - return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; - } else if (!STRCASECMP(str, kRlt0541htabStr)) { - return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; - // Fujitsu A/C models - } else if (!STRCASECMP(str, kArrah2eStr)) { - return fujitsu_ac_remote_model_t::ARRAH2E; - } else if (!STRCASECMP(str, kArdb1Str)) { - return fujitsu_ac_remote_model_t::ARDB1; - } else if (!STRCASECMP(str, kArreb1eStr)) { - return fujitsu_ac_remote_model_t::ARREB1E; - } else if (!STRCASECMP(str, kArjw2Str)) { - return fujitsu_ac_remote_model_t::ARJW2; - } else if (!STRCASECMP(str, kArry4Str)) { - return fujitsu_ac_remote_model_t::ARRY4; - } else if (!STRCASECMP(str, kArrew4eStr)) { - return fujitsu_ac_remote_model_t::ARREW4E; - // LG A/C models - } else if (!STRCASECMP(str, kGe6711ar2853mStr)) { - return lg_ac_remote_model_t::GE6711AR2853M; - } else if (!STRCASECMP(str, kAkb75215403Str)) { - return lg_ac_remote_model_t::AKB75215403; - } else if (!STRCASECMP(str, kAkb74955603Str)) { - return lg_ac_remote_model_t::AKB74955603; - } else if (!STRCASECMP(str, kAkb73757604Str)) { - return lg_ac_remote_model_t::AKB73757604; - } else if (!STRCASECMP(str, kLg6711a20083vStr)) { - return lg_ac_remote_model_t::LG6711A20083V; - // Panasonic A/C families - } else if (!STRCASECMP(str, kLkeStr) || - !STRCASECMP(str, kPanasonicLkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicLke; - } else if (!STRCASECMP(str, kNkeStr) || - !STRCASECMP(str, kPanasonicNkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicNke; - } else if (!STRCASECMP(str, kDkeStr) || - !STRCASECMP(str, kPanasonicDkeStr) || - !STRCASECMP(str, kPkrStr) || - !STRCASECMP(str, kPanasonicPkrStr)) { - return panasonic_ac_remote_model_t::kPanasonicDke; - } else if (!STRCASECMP(str, kJkeStr) || - !STRCASECMP(str, kPanasonicJkeStr)) { - return panasonic_ac_remote_model_t::kPanasonicJke; - } else if (!STRCASECMP(str, kCkpStr) || - !STRCASECMP(str, kPanasonicCkpStr)) { - return panasonic_ac_remote_model_t::kPanasonicCkp; - } else if (!STRCASECMP(str, kRkrStr) || - !STRCASECMP(str, kPanasonicRkrStr)) { - return panasonic_ac_remote_model_t::kPanasonicRkr; - // Sharp A/C Models - } else if (!STRCASECMP(str, kA907Str)) { - return sharp_ac_remote_model_t::A907; - } else if (!STRCASECMP(str, kA705Str)) { - return sharp_ac_remote_model_t::A705; - } else if (!STRCASECMP(str, kA903Str)) { - return sharp_ac_remote_model_t::A903; - // TCL A/C Models - } else if (!STRCASECMP(str, kTac09chsdStr)) { - return tcl_ac_remote_model_t::TAC09CHSD; - } else if (!STRCASECMP(str, kGz055be1Str)) { - return tcl_ac_remote_model_t::GZ055BE1; - // Voltas A/C models - } else if (!STRCASECMP(str, k122lzfStr)) { - return voltas_ac_remote_model_t::kVoltas122LZF; - // Whirlpool A/C models - } else if (!STRCASECMP(str, kDg11j13aStr) || - !STRCASECMP(str, kDg11j104Str)) { - return whirlpool_ac_remote_model_t::DG11J13A; - } else if (!STRCASECMP(str, kDg11j191Str)) { - return whirlpool_ac_remote_model_t::DG11J191; - // Argo A/C models - } else if (!STRCASECMP(str, kArgoWrem2Str)) { - return argo_ac_remote_model_t::SAC_WREM2; - } else if (!STRCASECMP(str, kArgoWrem3Str)) { - return argo_ac_remote_model_t::SAC_WREM3; - } else { - int16_t number = atoi(str); - if (number > 0) - return number; - else - return def; - } -} - -/// Convert the supplied str into the appropriate boolean value. -/// @param[in] str A Ptr to a C-style string to be converted. -/// @param[in] def The boolean value to return if no conversion was possible. -/// @return The equivalent boolean value. -bool IRac::strToBool(const char *str, const bool def) { - if (!STRCASECMP(str, kOnStr) || - !STRCASECMP(str, k1Str) || - !STRCASECMP(str, kYesStr) || - !STRCASECMP(str, kTrueStr)) - return true; - else if (!STRCASECMP(str, kOffStr) || - !STRCASECMP(str, k0Str) || - !STRCASECMP(str, kNoStr) || - !STRCASECMP(str, kFalseStr)) - return false; - else - return def; -} - -/// Convert the supplied boolean into the appropriate String. -/// @param[in] value The boolean value to be converted. -/// @return The equivalent String for the locale. -String IRac::boolToString(const bool value) { - return value ? kOnStr : kOffStr; -} - -/// Convert the supplied operation mode into the appropriate String. -/// @param[in] cmdType The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::commandTypeToString(const stdAc::ac_command_t cmdType) { - switch (cmdType) { - case stdAc::ac_command_t::kControlCommand: return kControlCommandStr; - case stdAc::ac_command_t::kSensorTempReport: return kIFeelReportStr; - case stdAc::ac_command_t::kTimerCommand: return kSetTimerCommandStr; - case stdAc::ac_command_t::kConfigCommand: return kConfigCommandStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied operation mode into the appropriate String. -/// @param[in] mode The enum to be converted. -/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output. -/// @return The equivalent String for the locale. -String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) { - switch (mode) { - case stdAc::opmode_t::kOff: return kOffStr; - case stdAc::opmode_t::kAuto: return kAutoStr; - case stdAc::opmode_t::kCool: return kCoolStr; - case stdAc::opmode_t::kHeat: return kHeatStr; - case stdAc::opmode_t::kDry: return kDryStr; - case stdAc::opmode_t::kFan: return ha ? kFan_OnlyStr : kFanStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied fan speed enum into the appropriate String. -/// @param[in] speed The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kAuto: return kAutoStr; - case stdAc::fanspeed_t::kMax: return kMaxStr; - case stdAc::fanspeed_t::kHigh: return kHighStr; - case stdAc::fanspeed_t::kMedium: return kMediumStr; - case stdAc::fanspeed_t::kMediumHigh: return kMedHighStr; - case stdAc::fanspeed_t::kLow: return kLowStr; - case stdAc::fanspeed_t::kMin: return kMinStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied enum into the appropriate String. -/// @param[in] swingv The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::swingvToString(const stdAc::swingv_t swingv) { - switch (swingv) { - case stdAc::swingv_t::kOff: return kOffStr; - case stdAc::swingv_t::kAuto: return kAutoStr; - case stdAc::swingv_t::kHighest: return kHighestStr; - case stdAc::swingv_t::kHigh: return kHighStr; - case stdAc::swingv_t::kMiddle: return kMiddleStr; - case stdAc::swingv_t::kUpperMiddle: return kUpperMiddleStr; - case stdAc::swingv_t::kLow: return kLowStr; - case stdAc::swingv_t::kLowest: return kLowestStr; - default: return kUnknownStr; - } -} - -/// Convert the supplied enum into the appropriate String. -/// @param[in] swingh The enum to be converted. -/// @return The equivalent String for the locale. -String IRac::swinghToString(const stdAc::swingh_t swingh) { - switch (swingh) { - case stdAc::swingh_t::kOff: return kOffStr; - case stdAc::swingh_t::kAuto: return kAutoStr; - case stdAc::swingh_t::kLeftMax: return kLeftMaxStr; - case stdAc::swingh_t::kLeft: return kLeftStr; - case stdAc::swingh_t::kMiddle: return kMiddleStr; - case stdAc::swingh_t::kRight: return kRightStr; - case stdAc::swingh_t::kRightMax: return kRightMaxStr; - case stdAc::swingh_t::kWide: return kWideStr; - default: return kUnknownStr; - } -} - -namespace IRAcUtils { - /// Display the human readable state of an A/C message if we can. - /// @param[in] result A Ptr to the captured `decode_results` that contains an - /// A/C mesg. - /// @return A string with the human description of the A/C message. - /// An empty string if we can't. - String resultAcToString(const decode_results * const result) { - switch (result->decode_type) { -#if DECODE_AIRTON - case decode_type_t::AIRTON: { - IRAirtonAc ac(kGpioUnused); - ac.setRaw(result->value); // AIRTON uses value instead of state. - return ac.toString(); - } -#endif // DECODE_AIRTON -#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(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_AMCOR -#if DECODE_ARGO - case decode_type_t::ARGO: { - if (IRArgoAC_WREM3::isValidWrem3Message(result->state, result->bits, - true)) { - IRArgoAC_WREM3 ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } - IRArgoAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_ARGO -#if DECODE_BOSCH144 - case decode_type_t::BOSCH144: { - IRBosch144AC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_BOSCH144 -#if DECODE_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: { - IRCarrierAc64 ac(kGpioUnused); - ac.setRaw(result->value); // CARRIER_AC64 uses value instead of state. - return ac.toString(); - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_COOLIX - case decode_type_t::COOLIX: { - IRCoolixAC ac(kGpioUnused); - ac.on(); - ac.setRaw(result->value); // Coolix uses value instead of state. - return ac.toString(); - } -#endif // DECODE_COOLIX -#if DECODE_CORONA_AC - case decode_type_t::CORONA_AC: { - IRCoronaAc ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_CORONA_AC -#if DECODE_DAIKIN - case decode_type_t::DAIKIN: { - IRDaikinESP ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN128 - case decode_type_t::DAIKIN128: { - IRDaikin128 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - case decode_type_t::DAIKIN152: { - IRDaikin152 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - case decode_type_t::DAIKIN160: { - IRDaikin160 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - case decode_type_t::DAIKIN176: { - IRDaikin176 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN2 - case decode_type_t::DAIKIN2: { - IRDaikin2 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN2 -#if DECODE_DAIKIN216 - case decode_type_t::DAIKIN216: { - IRDaikin216 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN64 - case decode_type_t::DAIKIN64: { - IRDaikin64 ac(kGpioUnused); - ac.setRaw(result->value); // Daikin64 uses value instead of state. - return ac.toString(); - } -#endif // DECODE_DAIKIN64 -#if DECODE_DELONGHI_AC - case decode_type_t::DELONGHI_AC: { - IRDelonghiAc ac(kGpioUnused); - ac.setRaw(result->value); // DelonghiAc uses value instead of state. - return ac.toString(); - } -#endif // DECODE_DELONGHI_AC -#if DECODE_ECOCLIM - case decode_type_t::ECOCLIM: { - if (result->bits == kEcoclimBits) { - IREcoclimAc ac(kGpioUnused); - ac.setRaw(result->value); // EcoClim uses value instead of state. - return ac.toString(); - } - return ""; - } -#endif // DECODE_ECOCLIM -#if DECODE_ELECTRA_AC - case decode_type_t::ELECTRA_AC: { - IRElectraAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_ELECTRA_AC -#if DECODE_FUJITSU_AC - case decode_type_t::FUJITSU_AC: { - IRFujitsuAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_FUJITSU_AC -#if DECODE_FUJITSU_AC264 - case decode_type_t::FUJITSU_AC264: { - IRFujitsuAC264 ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_FUJITSU_AC264 -#if DECODE_GOODWEATHER - case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(kGpioUnused); - ac.setRaw(result->value); // Goodweather uses value instead of state. - return ac.toString(); - } -#endif // DECODE_GOODWEATHER -#if DECODE_GREE - case decode_type_t::GREE: { - IRGreeAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_GREE -#if DECODE_HAIER_AC - case decode_type_t::HAIER_AC: { - IRHaierAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC160 - case decode_type_t::HAIER_AC160: { - IRHaierAC160 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC160 -#if DECODE_HAIER_AC176 - case decode_type_t::HAIER_AC176: { - IRHaierAC176 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC176 -#if DECODE_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC_YRW02 -#if DECODE_HITACHI_AC - case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC -#if DECODE_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: { - IRHitachiAc1 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC1 -#if DECODE_HITACHI_AC264 - case decode_type_t::HITACHI_AC264: { - IRHitachiAc264 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC264 -#if DECODE_HITACHI_AC296 - case decode_type_t::HITACHI_AC296: { - IRHitachiAc296 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: { - IRHitachiAc344 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC424 -#if DECODE_KELON - case decode_type_t::KELON: { - IRKelonAc ac(kGpioUnused); - ac.setRaw(result->value); - return ac.toString(); - } -#endif // DECODE_KELON -#if DECODE_KELVINATOR - case decode_type_t::KELVINATOR: { - IRKelvinatorAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_KELVINATOR -#if DECODE_LG - case decode_type_t::LG: - case decode_type_t::LG2: { - IRLgAc ac(kGpioUnused); - ac.setRaw(result->value, result->decode_type); // Use value, not state. - return ac.isValidLgAc() ? ac.toString() : ""; - } -#endif // DECODE_LG -#if DECODE_MIDEA - case decode_type_t::MIDEA: { - IRMideaAC ac(kGpioUnused); - ac.setRaw(result->value); // Midea uses value instead of state. - return ac.toString(); - } -#endif // DECODE_MIDEA -#if DECODE_MIRAGE - case decode_type_t::MIRAGE: { - IRMirageAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MIRAGE -#if DECODE_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: { - IRMitsubishiAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI_AC -#if DECODE_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: { - IRMitsubishi112 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI112 -#if DECODE_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: { - IRMitsubishi136 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHI136 -#if DECODE_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } - case decode_type_t::MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_MITSUBISHIHEAVY -#if DECODE_NEOCLIMA - case decode_type_t::NEOCLIMA: { - IRNeoclimaAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_NEOCLIMA -#if DECODE_PANASONIC_AC - case decode_type_t::PANASONIC_AC: { - if (result->bits > kPanasonicAcShortBits) { - IRPanasonicAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: { - if (result->bits >= kPanasonicAc32Bits) { - IRPanasonicAc32 ac(kGpioUnused); - ac.setRaw(result->value); // Uses value instead of state. - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_RHOSS - case decode_type_t::RHOSS: { - IRRhossAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_RHOSS -#if DECODE_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: { - IRSamsungAc ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_SANYO_AC - case decode_type_t::SANYO_AC: { - IRSanyoAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - case decode_type_t::SANYO_AC88: { - IRSanyoAc88 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SANYO_AC88 -#if DECODE_SHARP_AC - case decode_type_t::SHARP_AC: { - IRSharpAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_SHARP_AC -#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) - case decode_type_t::TCL112AC: - case decode_type_t::TEKNOPOINT: { - IRTcl112Ac ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) -#if DECODE_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: { - IRTechnibelAc ac(kGpioUnused); - ac.setRaw(result->value); // TechnibelAc uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TECHNIBEL_AC -#if DECODE_TECO - case decode_type_t::TECO: { - IRTecoAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_TECO -#if DECODE_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_TRANSCOLD - case decode_type_t::TRANSCOLD: { - IRTranscoldAc ac(kGpioUnused); - ac.on(); - ac.setRaw(result->value); // TRANSCOLD uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TRANSCOLD -#if DECODE_TROTEC - case decode_type_t::TROTEC: { - IRTrotecESP ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - case decode_type_t::TROTEC_3550: { - IRTrotec3550 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_TROTEC_3550 -#if DECODE_TRUMA - case decode_type_t::TRUMA: { - IRTrumaAc ac(kGpioUnused); - ac.setRaw(result->value); // Truma uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TRUMA -#if DECODE_VESTEL_AC - case decode_type_t::VESTEL_AC: { - IRVestelAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_VESTEL_AC -#if DECODE_VOLTAS - case decode_type_t::VOLTAS: { - IRVoltas ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_VOLTAS -#if DECODE_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_YORK - case decode_type_t::YORK: { - IRYorkAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_YORK - default: - return ""; - } - } - - /// Convert a valid IR A/C remote message that we understand enough into a - /// Common A/C state. - /// @param[in] decode A PTR to a successful raw IR decode object. - /// @param[in] result A PTR to a state structure to store the result in. - /// @param[in] prev A PTR to a state structure which has the prev. state. - /// @return A boolean indicating success or failure. - bool decodeToState(const decode_results *decode, stdAc::state_t *result, - const stdAc::state_t *prev -/// @cond IGNORE -// *prev flagged as "unused" due to potential compiler warning when some -// protocols that use it are disabled. It really is used. - __attribute__((unused)) -/// @endcond - ) { - if (decode == NULL || result == NULL) return false; // Safety check. - switch (decode->decode_type) { -#if DECODE_AIRTON - case decode_type_t::AIRTON: { - IRAirtonAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_AIRTON -#if DECODE_AIRWELL - case decode_type_t::AIRWELL: { - IRAirwellAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_AIRWELL -#if DECODE_AMCOR - case decode_type_t::AMCOR: { - IRAmcorAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_AMCOR -#if DECODE_ARGO - case decode_type_t::ARGO: { - const uint16_t length = decode->bits / 8; - if (IRArgoAC_WREM3::isValidWrem3Message(decode->state, - decode->bits, true)) { - IRArgoAC_WREM3 ac(kGpioUnused); - ac.setRaw(decode->state, length); - *result = ac.toCommon(); - } else { - IRArgoAC ac(kGpioUnused); - switch (length) { - case kArgoStateLength: - case kArgoShortStateLength: - ac.setRaw(decode->state, length); - *result = ac.toCommon(); - break; - default: - return false; - } - } - break; - } -#endif // DECODE_ARGO -#if DECODE_BOSCH144 - case decode_type_t::BOSCH144: { - IRBosch144AC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_BOSCH144 -#if DECODE_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: { - IRCarrierAc64 ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_COOLIX - case decode_type_t::COOLIX: { - IRCoolixAC ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_COOLIX -#if DECODE_CORONA_AC - case decode_type_t::CORONA_AC: { - IRCoronaAc ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); - break; - } -#endif // DECODE_CARRIER_AC64 -#if DECODE_DAIKIN - case decode_type_t::DAIKIN: { - IRDaikinESP ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN128 - case decode_type_t::DAIKIN128: { - IRDaikin128 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - case decode_type_t::DAIKIN152: { - IRDaikin152 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - case decode_type_t::DAIKIN160: { - IRDaikin160 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - case decode_type_t::DAIKIN176: { - IRDaikin176 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN2 - case decode_type_t::DAIKIN2: { - IRDaikin2 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN2 -#if DECODE_DAIKIN216 - case decode_type_t::DAIKIN216: { - IRDaikin216 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN64 - case decode_type_t::DAIKIN64: { - IRDaikin64 ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_DAIKIN64 -#if DECODE_DELONGHI_AC - case decode_type_t::DELONGHI_AC: { - IRDelonghiAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_DELONGHI_AC -#if DECODE_ECOCLIM - case decode_type_t::ECOCLIM: { - if (decode->bits == kEcoclimBits) { - IREcoclimAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - } else { - return false; - } - break; - } -#endif // DECODE_ECOCLIM -#if DECODE_ELECTRA_AC - case decode_type_t::ELECTRA_AC: { - IRElectraAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_ELECTRA_AC -#if DECODE_FUJITSU_AC - case decode_type_t::FUJITSU_AC: { - IRFujitsuAC ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_FUJITSU_AC -#if DECODE_FUJITSU_AC264 - case decode_type_t::FUJITSU_AC264: { - IRFujitsuAC264 ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_FUJITSU_AC264 -#if DECODE_GOODWEATHER - case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_GOODWEATHER -#if DECODE_GREE - case decode_type_t::GREE: { - IRGreeAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_GREE -#if DECODE_HAIER_AC - case decode_type_t::HAIER_AC: { - IRHaierAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC160 - case decode_type_t::HAIER_AC160: { - IRHaierAC160 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_HAIER_AC160 -#if DECODE_HAIER_AC176 - case decode_type_t::HAIER_AC176: { - IRHaierAC176 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC176 -#if DECODE_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HAIER_AC_YRW02 -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) - case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) -#if DECODE_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: { - IRHitachiAc1 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC1 -#if DECODE_HITACHI_AC264 - case decode_type_t::HITACHI_AC264: { - IRHitachiAc264 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC264 -#if DECODE_HITACHI_AC296 - case decode_type_t::HITACHI_AC296: { - IRHitachiAc296 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: { - IRHitachiAc344 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_HITACHI_AC424 -#if DECODE_KELON - case decode_type_t::KELON: { - IRKelonAc ac(kGpioUnused); - ac.setRaw(decode->value); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_KELON -#if DECODE_KELVINATOR - case decode_type_t::KELVINATOR: { - IRKelvinatorAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_KELVINATOR -#if DECODE_LG - case decode_type_t::LG: - case decode_type_t::LG2: { - IRLgAc ac(kGpioUnused); - ac.setRaw(decode->value, decode->decode_type); // Use value, not state. - if (!ac.isValidLgAc()) return false; - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_LG -#if DECODE_MIDEA - case decode_type_t::MIDEA: { - IRMideaAC ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_MIDEA -#if DECODE_MIRAGE - case decode_type_t::MIRAGE: { - IRMirageAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MIRAGE -#if DECODE_MITSUBISHI_AC - case decode_type_t::MITSUBISHI_AC: { - IRMitsubishiAC ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI_AC -#if DECODE_MITSUBISHI112 - case decode_type_t::MITSUBISHI112: { - IRMitsubishi112 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI112 -#if DECODE_MITSUBISHI136 - case decode_type_t::MITSUBISHI136: { - IRMitsubishi136 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHI136 -#if DECODE_MITSUBISHIHEAVY - case decode_type_t::MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } - case decode_type_t::MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_MITSUBISHIHEAVY -#if DECODE_NEOCLIMA - case decode_type_t::NEOCLIMA: { - IRNeoclimaAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_NEOCLIMA -#if DECODE_PANASONIC_AC - case decode_type_t::PANASONIC_AC: { - IRPanasonicAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: { - IRPanasonicAc32 ac(kGpioUnused); - if (decode->bits >= kPanasonicAc32Bits) { - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(prev); - } else { - return false; - } - break; - } -#endif // DECODE_PANASONIC_AC32 -#if DECODE_RHOSS - case decode_type_t::RHOSS: { - IRRhossAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_RHOSS -#if DECODE_SAMSUNG_AC - case decode_type_t::SAMSUNG_AC: { - IRSamsungAc ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_SANYO_AC - case decode_type_t::SANYO_AC: { - IRSanyoAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - case decode_type_t::SANYO_AC88: { - IRSanyoAc88 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_SANYO_AC88 -#if DECODE_SHARP_AC - case decode_type_t::SHARP_AC: { - IRSharpAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_SHARP_AC -#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) - case decode_type_t::TCL112AC: - case decode_type_t::TEKNOPOINT: { - IRTcl112Ac ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - // Teknopoint uses the TCL protocol, but with a different model number. - // Just keep the original protocol type ... for now. - result->protocol = decode->decode_type; - break; - } -#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) -#if DECODE_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: { - IRTechnibelAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TECHNIBEL_AC -#if DECODE_TECO - case decode_type_t::TECO: { - IRTecoAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TECO -#if DECODE_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(kGpioUnused); - ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_TRANSCOLD - case decode_type_t::TRANSCOLD: { - IRTranscoldAc ac(kGpioUnused); - ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_TRANSCOLD -#if DECODE_TROTEC - case decode_type_t::TROTEC: { - IRTrotecESP ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - case decode_type_t::TROTEC_3550: { - IRTrotec3550 ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(); - break; - } -#endif // DECODE_TROTEC_3550 -#if DECODE_TRUMA - case decode_type_t::TRUMA: { - IRTrumaAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_TRUMA -#if DECODE_VESTEL_AC - case decode_type_t::VESTEL_AC: { - IRVestelAc ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_VESTEL_AC -#if DECODE_VOLTAS - case decode_type_t::VOLTAS: { - IRVoltas ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_VOLTAS -#if DECODE_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_YORK - case decode_type_t::YORK: { - IRYorkAc ac(kGpioUnused); - ac.setRaw(decode->state); - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_YORK - default: - return false; - } - return true; - } -} // namespace IRAcUtils +// Copyright 2019 David Conran + +// Provide a universal/standard interface for sending A/C nessages. +// It does not provide complete and maximum granular control but tries +// to offer most common functionality across all supported devices. + +#include "IRac.h" +#ifndef UNIT_TEST +#include +#endif +#include +#ifndef ARDUINO +#include +#endif +#include +#if __cplusplus >= 201103L && defined(_GLIBCXX_USE_C99_MATH_TR1) + using std::roundf; +#else + using ::roundf; +#endif +#include "IRsend.h" +#include "IRremoteESP8266.h" +#include "IRtext.h" +#include "IRutils.h" +#include "ir_Airton.h" +#include "ir_Airwell.h" +#include "ir_Amcor.h" +#include "ir_Argo.h" +#include "ir_Bosch.h" +#include "ir_Carrier.h" +#include "ir_Coolix.h" +#include "ir_Corona.h" +#include "ir_Daikin.h" +#include "ir_Ecoclim.h" +#include "ir_Electra.h" +#include "ir_Fujitsu.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelon.h" +#include "ir_Kelvinator.h" +#include "ir_LG.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" +#include "ir_Panasonic.h" +#include "ir_Rhoss.h" +#include "ir_Samsung.h" +#include "ir_Sanyo.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Technibel.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Transcold.h" +#include "ir_Trotec.h" +#include "ir_Truma.h" +#include "ir_Vestel.h" +#include "ir_Voltas.h" +#include "ir_Whirlpool.h" + +// On the ESP8266 platform we need to use a special version of string handling +// functions to handle the strings stored in the flash address space. +#ifndef STRCASECMP +#if defined(ESP8266) +#define STRCASECMP(LHS, RHS) \ + strcasecmp_P(LHS, reinterpret_cast(RHS)) +#else // ESP8266 +#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS) +#endif // ESP8266 +#endif // STRCASECMP + +#ifndef UNIT_TEST +#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) +#else +/* NOTE: THIS IS NOT A DOXYGEN COMMENT (would require ENABLE_PREPROCESSING-YES) +/// If compiling for UT *and* a test receiver @c IRrecv is provided via the +/// @c _utReceived param, this injects an "output" gadget @c _lastDecodeResults +/// into the @c IRAc::sendAc method, so that the UT code may parse the "sent" +/// value and drive further assertions +/// +/// @note The @c decode_results "returned" is a shallow copy (empty rawbuf), +/// mostly b/c the class does not have a custom/deep copy c-tor +/// and defining it would be an overkill for this purpose +/// @note For future maintainers: If @c IRAc class is ever refactored to use +/// polymorphism (static or dynamic)... this macro should be removed +/// and replaced with proper GMock injection. +*/ +#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) \ + { \ + if (_utReceiver) { \ + _lastDecodeResults = nullptr; \ + (ac)._irsend.makeDecodeResult(); \ + if (_utReceiver->decode(&(ac)._irsend.capture)) { \ + _lastDecodeResults = std::unique_ptr( \ + new decode_results((ac)._irsend.capture)); \ + _lastDecodeResults->rawbuf = nullptr; \ + } \ + } \ + } +#endif // UNIT_TEST + +/// Class constructor +/// @param[in] pin Gpio pin to use when transmitting IR messages. +/// @param[in] inverted true, gpio output defaults to high. false, to low. +/// @param[in] use_modulation true means use frequency modulation. false, don't. +IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { + _pin = pin; + _inverted = inverted; + _modulation = use_modulation; + this->markAsSent(); +} + +/// Initialise the given state with the supplied settings. +/// @param[out] state A Ptr to where the settings will be stored. +/// @param[in] vendor The vendor/protocol type. +/// @param[in] model The A/C model if applicable. +/// @param[in] power The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. +/// Others it may be the time to enter/exit sleep mode. +/// i.e. Time in Nr. of mins since midnight. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::initState(stdAc::state_t *state, + const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, + const int16_t clock) { + state->protocol = vendor; + state->model = model; + state->power = power; + state->mode = mode; + state->degrees = degrees; + state->celsius = celsius; + state->fanspeed = fan; + state->swingv = swingv; + state->swingh = swingh; + state->quiet = quiet; + state->turbo = turbo; + state->econo = econo; + state->light = light; + state->filter = filter; + state->clean = clean; + state->beep = beep; + state->sleep = sleep; + state->clock = clock; +} + +/// Initialise the given state with the supplied settings. +/// @param[out] state A Ptr to where the settings will be stored. +/// @note Sets all the parameters to reasonable base/automatic defaults. +void IRac::initState(stdAc::state_t *state) { + stdAc::state_t def; + *state = def; +} + +/// Get the current internal A/C climate state. +/// @return A Ptr to a state containing the current (to be sent) settings. +stdAc::state_t IRac::getState(void) { return next; } + +/// Get the previous internal A/C climate state that should have already been +/// sent to the device. i.e. What the A/C unit should already be set to. +/// @return A Ptr to a state containing the previously sent settings. +stdAc::state_t IRac::getStatePrev(void) { return _prev; } + +/// Is the given protocol supported by the IRac class? +/// @param[in] protocol The vendor/protocol type. +/// @return true if the protocol is supported by this class, otherwise false. +bool IRac::isProtocolSupported(const decode_type_t protocol) { + switch (protocol) { +#if SEND_AIRTON + case decode_type_t::AIRTON: +#endif // SEND_AIRTON +#if SEND_AIRWELL + case decode_type_t::AIRWELL: +#endif // SEND_AIRWELL +#if SEND_AMCOR + case decode_type_t::AMCOR: +#endif +#if SEND_ARGO + case decode_type_t::ARGO: +#endif +#if SEND_BOSCH144 + case decode_type_t::BOSCH144: +#endif +#if SEND_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + case decode_type_t::COOLIX: +#endif +#if SEND_CORONA_AC + case decode_type_t::CORONA_AC: +#endif +#if SEND_DAIKIN + case decode_type_t::DAIKIN: +#endif +#if SEND_DAIKIN128 + case decode_type_t::DAIKIN128: +#endif +#if SEND_DAIKIN152 + case decode_type_t::DAIKIN152: +#endif +#if SEND_DAIKIN160 + case decode_type_t::DAIKIN160: +#endif +#if SEND_DAIKIN176 + case decode_type_t::DAIKIN176: +#endif +#if SEND_DAIKIN2 + case decode_type_t::DAIKIN2: +#endif +#if SEND_DAIKIN216 + case decode_type_t::DAIKIN216: +#endif +#if SEND_DAIKIN64 + case decode_type_t::DAIKIN64: +#endif +#if SEND_DELONGHI_AC + case decode_type_t::DELONGHI_AC: +#endif +#if SEND_ECOCLIM + case decode_type_t::ECOCLIM: +#endif +#if SEND_ELECTRA_AC + case decode_type_t::ELECTRA_AC: +#endif +#if SEND_FUJITSU_AC + case decode_type_t::FUJITSU_AC: +#endif +#if SEND_FUJITSU_AC264 + case decode_type_t::FUJITSU_AC264: +#endif +#if SEND_GOODWEATHER + case decode_type_t::GOODWEATHER: +#endif +#if SEND_GREE + case decode_type_t::GREE: +#endif +#if SEND_HAIER_AC + case decode_type_t::HAIER_AC: +#endif +#if SEND_HAIER_AC160 + case decode_type_t::HAIER_AC160: +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + case decode_type_t::HAIER_AC176: +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: +#endif +#if SEND_HITACHI_AC + case decode_type_t::HITACHI_AC: +#endif +#if SEND_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: +#endif +#if SEND_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: +#endif +#if SEND_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: +#endif +#if SEND_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: +#endif +#if SEND_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: +#endif +#if SEND_KELON + case decode_type_t::KELON: +#endif +#if SEND_KELVINATOR + case decode_type_t::KELVINATOR: +#endif +#if SEND_LG + case decode_type_t::LG: + case decode_type_t::LG2: +#endif +#if SEND_MIDEA + case decode_type_t::MIDEA: +#endif // SEND_MIDEA +#if SEND_MIRAGE + case decode_type_t::MIRAGE: +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: +#endif +#if SEND_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: +#endif +#if SEND_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: +#endif +#if SEND_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: + case decode_type_t::MITSUBISHI_HEAVY_152: +#endif +#if SEND_NEOCLIMA + case decode_type_t::NEOCLIMA: +#endif +#if SEND_PANASONIC_AC + case decode_type_t::PANASONIC_AC: +#endif +#if SEND_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: +#endif +#if SEND_RHOSS + case decode_type_t::RHOSS: +#endif +#if SEND_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: +#endif +#if SEND_SANYO_AC + case decode_type_t::SANYO_AC: +#endif +#if SEND_SANYO_AC88 + case decode_type_t::SANYO_AC88: +#endif +#if SEND_SHARP_AC + case decode_type_t::SHARP_AC: +#endif +#if SEND_TCL112AC + case decode_type_t::TCL112AC: +#endif +#if SEND_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: +#endif +#if SEND_TECO + case decode_type_t::TECO: +#endif +#if SEND_TEKNOPOINT + case decode_type_t::TEKNOPOINT: +#endif // SEND_TEKNOPOINT +#if SEND_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: +#endif +#if SEND_TRANSCOLD + case decode_type_t::TRANSCOLD: +#endif +#if SEND_TROTEC + case decode_type_t::TROTEC: +#endif +#if SEND_TROTEC_3550 + case decode_type_t::TROTEC_3550: +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + case decode_type_t::TRUMA: +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case decode_type_t::VESTEL_AC: +#endif +#if SEND_VOLTAS + case decode_type_t::VOLTAS: +#endif +#if SEND_YORK + case decode_type_t::YORK: +#endif +#if SEND_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: +#endif + return true; + default: + return false; + } +} + +#if SEND_AIRTON +/// Send an Airton 56-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRAirtonAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/health/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::airton(IRAirtonAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool light, const bool econo, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Quiet setting available. + ac->setLight(light); + ac->setHealth(filter); + ac->setTurbo(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_AIRTON + +#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. +/// @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::amcor(IRAmcorAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setPower(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_AMCOR + +#if SEND_ARGO +/// Send an Argo A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRArgoAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::argo(IRArgoAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, + const bool turbo, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(static_cast(roundf(degrees))); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } + ac->setiFeel(iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setFlap(ac->convertSwingV(swingv)); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + ac->setMax(turbo); + // No Economy setting available. + // No Clean setting available. + // No Beep setting available. + ac->setNight(sleep >= 0); // Convert to a boolean. + ac->send(); +} + +/// Send an Argo A/C WREM-3 AC **control** message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The set temperature setting in degrees Celsius. +/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) +/// The overflow is *not* checked, though. +/// @note The value is rounded to nearest integer, rounding halfway cases +/// away from zero. E.g. 1.5 [C] becomes 2 [C]. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] night Enable night mode (raises temp by +1*C after 1h). +/// @param[in] econo Enable eco mode (limits power consumed). +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Enable filter mode +/// @param[in] light Enable device display/LEDs +void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on, + const stdAc::opmode_t mode, const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool iFeel, + const bool night, const bool econo, const bool turbo, const bool filter, + const bool light) { + ac->begin(); + ac->setMessageType(argoIrMessageType_t::AC_CONTROL); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } + ac->setiFeel(iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setFlap(ac->convertSwingV(swingv)); + ac->setNight(night); + ac->setEco(econo); + ac->setMax(turbo); + ac->setFilter(filter); + ac->setLight(light); + // No Clean setting available. + // No Beep setting available - always beeps in this mode :) + ac->send(); +} + +/// Send an Argo A/C WREM-3 iFeel (room temp) silent (no beep) report. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] sensorTemp The room (iFeel) temperature setting +/// in degrees Celsius. +/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t) +/// The overflow is *not* checked, though. +/// @note The value is rounded to nearest integer, rounding halfway cases +/// away from zero. E.g. 1.5 [C] becomes 2 [C]. +void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) { + ac->begin(); + ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT); + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + ac->send(); +} + +/// Send an Argo A/C WREM-3 Config command. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] param The parameter ID. +/// @param[in] value The parameter value. +/// @param[in] safe If true, will only allow setting the below parameters +/// in order to avoid accidentally setting a restricted +/// vendor-specific param and breaking the A/C device +/// @note Known parameters (P, where xx is the @c param) +/// P05 - Temperature Scale (0-Celsius, 1-Fahrenheit) +/// P06 - Transmission channel (0..3) +/// P12 - ECO mode power input limit (30..99, default: 75) +void IRac::argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, + const uint8_t value, bool safe /*= true*/) { + if (safe) { + switch (param) { + case 5: // temp. scale (note this is likely excess as not transmitted) + if (value > 1) { return; /* invalid */ } + break; + case 6: // channel (note this is likely excess as not transmitted) + if (value > 3) { return; /* invalid */ } + break; + case 12: // eco power limit + if (value < 30 || value > 99) { return; /* invalid */ } + break; + default: + return; /* invalid */ + } + } + ac->begin(); + ac->setMessageType(argoIrMessageType_t::CONFIG_PARAM_SET); + ac->setConfigEntry(param, value); + ac->send(); +} + +/// Send an Argo A/C WREM-3 Delay timer command. +/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use. +/// @param[in] on Whether the unit is currently on. The timer, upon elapse +/// will toggle this state +/// @param[in] currentTime currentTime in minutes, starting from 00:00 +/// @note For timer mode, this value is not really used much so can be zero. +/// @param[in] delayMinutes Number of minutes after which the @c on state should +/// be toggled +/// @note Schedule timers are not exposed via this interface +void IRac::argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, + const uint16_t currentTime, const uint16_t delayMinutes) { + ac->begin(); + ac->setMessageType(argoIrMessageType_t::TIMER_COMMAND); + ac->setPower(on); + ac->setTimerType(argoTimerType_t::DELAY_TIMER); + ac->setCurrentTimeMinutes(currentTime); + // Note: Day of week is not set (no need) + ac->setDelayTimerMinutes(delayMinutes); + ac->send(); +} +#endif // SEND_ARGO + +#if SEND_BOSCH144 +/// Send a Bosch144 A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRBosch144AC 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. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @note -1 is Off, >= 0 is on. +void IRac::bosch144(IRBosch144AC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const bool quiet) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setMode(ac->convertMode(mode)); + ac->setQuiet(quiet); + ac->send(); // Send the state, which will also power on the unit. + // The following are all options/settings that create their own special + // messages. Often they only make sense to be sent after the unit is turned + // on. For instance, assuming a person wants to have the a/c on and in turbo + // mode. If we send the turbo message, it is ignored if the unit is off. + // Hence we send the special mode/setting messages after a normal message + // which will turn on the device. + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Sleep setting available. +} +#endif // SEND_BOSCH144 + +#if SEND_CARRIER_AC64 +/// Send a Carrier 64-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRCarrierAc64 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::carrier64(IRCarrierAc64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV((int8_t)swingv >= 0); + // 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. + ac->setSleep(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_CARRIER_AC64 + +#if SEND_COOLIX +/// Send a Coolix A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRCoolixAC 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::coolix(IRCoolixAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool light, + const bool clean, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } else { + ac->clearSensorTemp(); + } + ac->setZoneFollow(iFeel); + ac->send(); // Send the state, which will also power on the unit. + // The following are all options/settings that create their own special + // messages. Often they only make sense to be sent after the unit is turned + // on. For instance, assuming a person wants to have the a/c on and in turbo + // mode. If we send the turbo message, it is ignored if the unit is off. + // Hence we send the special mode/setting messages after a normal message + // which will turn on the device. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + if (turbo) { + // Turbo has a special command that needs to be sent independently. + ac->setTurbo(); + ac->send(); + } + if (sleep >= 0) { + // Sleep has a special command that needs to be sent independently. + ac->setSleep(); + ac->send(); + } + if (light) { + // Light has a special command that needs to be sent independently. + ac->setLed(); + ac->send(); + } + if (clean) { + // Clean has a special command that needs to be sent independently. + ac->setClean(); + ac->send(); + } +} +#endif // SEND_COOLIX + +#if SEND_CORONA_AC +/// Send a Corona A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRCoronaAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] econo Run the device in economical mode. +void IRac::corona(IRCoronaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool econo) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Quiet setting available. + // No Light setting available. + // No Filter setting available. + // No Turbo setting available. + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + ac->send(); +} +#endif // SEND_CARRIER_AC64 + +#if SEND_DAIKIN +/// Send a Daikin A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikinESP 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setMold(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN + +#if SEND_DAIKIN128 +/// Send a Daikin 128-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin128 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::daikin128(IRDaikin128 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool light, + const bool econo, const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + // No Horizontal Swing setting avaliable. + ac->setQuiet(quiet); + ac->setLightToggle(light ? kDaikin128BitWall : 0); + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep > 0); + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN128 + +#if SEND_DAIKIN152 +/// Send a Daikin 152-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin152 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +void IRac::daikin152(IRDaikin152 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV((int8_t)swingv >= 0); + // No Horizontal Swing setting avaliable. + ac->setQuiet(quiet); + // No Light setting available. + // No Filter setting available. + ac->setPowerful(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_DAIKIN152 + +#if SEND_DAIKIN160 +/// Send a Daikin 160-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin160 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. +/// @param[in] swingv The vertical swing setting. +void IRac::daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->send(); +} +#endif // SEND_DAIKIN160 + +#if SEND_DAIKIN176 +/// Send a Daikin 176-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin176 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. +/// @param[in] swingh The horizontal swing setting. +void IRac::daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->send(); +} +#endif // SEND_DAIKIN176 + +#if SEND_DAIKIN2 +/// Send a Daikin2 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin2 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setLight(light ? 1 : 3); // On/High is 1, Off is 3. + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setPurify(filter); + ac->setMold(clean); + ac->setClean(true); // Hardwire auto clean to be on per request (@sheppy99) + ac->setBeep(beep ? 2 : 3); // On/Loud is 2, Off is 3. + if (sleep > 0) ac->enableSleepTimer(sleep); + if (clock >= 0) ac->setCurrentTime(clock); + ac->send(); +} +#endif // SEND_DAIKIN2 + +#if SEND_DAIKIN216 +/// Send a Daikin 216-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin216 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +void IRac::daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->send(); +} +#endif // SEND_DAIKIN216 + +#if SEND_DAIKIN64 +/// Send a Daikin 64-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDaikin64 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::daikin64(IRDaikin64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, + const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical((int8_t)swingv >= 0); + ac->setTurbo(turbo); + ac->setQuiet(quiet); + ac->setSleep(sleep >= 0); + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_DAIKIN64 + +#if SEND_DELONGHI_AC +/// Send a Delonghi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRDelonghiAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::delonghiac(IRDelonghiAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const bool turbo, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setBoost(turbo); + ac->setSleep(sleep >= 0); + ac->send(); +} +#endif // SEND_DELONGHI_AC + +#if SEND_ECOCLIM +/// Send an EcoClim A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IREcoclimAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::ecoclim(IREcoclimAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const int16_t sleep, + const int16_t clock) { + ac->begin(); + ac->setPower(on); + uint8_t new_mode; + if (sleep >= 0) // EcoClim has a descrete Sleep operation mode, not a setting + new_mode = kEcoclimSleep; // Override the requested operating mode. + else + new_mode = ac->convertMode(mode); // Not Sleep, so use the supplied mode. + ac->setMode(new_mode); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } else { + ac->setSensorTemp(degrees); //< Set to the desired temp + // until we can disable. + } + // No SwingV setting available + // No SwingH setting available + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + // No Clean setting available + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_ECOCLIM + +#if SEND_ELECTRA_AC +/// Send an Electra A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRElectraAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] lighttoggle Should we toggle the LED/Display? +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::electra(IRElectraAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool iFeel, + const bool turbo, const bool lighttoggle, const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLightToggle(lighttoggle); + // No Econo setting available. + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->setIFeel(iFeel); + ac->send(); +} +#endif // SEND_ELECTRA_AC + +#if SEND_FUJITSU_AC +/// Send a Fujitsu A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRFujitsuAC object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. +void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + if (on) { + // Do all special messages (except "Off") first, + // These need to be sent separately. + switch (ac->getModel()) { + // Some functions are only available on some models. + case fujitsu_ac_remote_model_t::ARREB1E: + if (turbo) { + ac->setCmd(kFujitsuAcCmdPowerful); + // Powerful is a separate command. + ac->send(); + } + if (econo) { + ac->setCmd(kFujitsuAcCmdEcono); + // Econo is a separate command. + ac->send(); + } + break; + default: + {}; + } + // Normal operation. + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFanSpeed(ac->convertFan(fan)); + uint8_t swing = kFujitsuAcSwingOff; + if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; + if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; + ac->setSwing(swing); + if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); + // No Light setting available. + ac->setFilter(filter); + ac->setClean(clean); + // No Beep setting available. + ac->setSleepTimer(sleep > 0 ? sleep : 0); + // No Sleep setting available. + // No Clock setting available. + ac->on(); // Ref: Issue #860 + } else { + // Off is special case/message. We don't need to send other messages. + ac->off(); + } + ac->send(); +} +#endif // SEND_FUJITSU_AC + +#if SEND_FUJITSU_AC264 +/// Send a Fujitsu A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRFujitsuAC264 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Toggle the device's turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignored. +void IRac::fujitsu264(IRFujitsuAC264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo, + const bool clean, const int16_t sleep, + const int16_t clock) { + ac->begin(); + if (on) { + // Do all special messages (except "Off") first, + // These need to be sent separately. + // Some functions are only available on some models. + if (turbo) { + ac->togglePowerful(); + // Powerful is a separate command. + ac->send(); + } + // Normal operation. + ac->setMode(ac->convertMode(mode)); + if (mode == stdAc::opmode_t::kAuto) + ac->setTempAuto(degrees); + else + ac->setTemp(degrees); + ac->setFanSpeed(ac->convertFanSpeed(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + if (quiet) ac->setFanSpeed(kFujitsuAc264FanSpeedQuiet); + ac->setEconomy(econo); + ac->setClean(clean); + ac->setSleepTimer(sleep > 0 ? sleep : 0); + if (clock >= 0) ac->setClock(clock); + ac->on(); + } else { + // Off is special case/message. We don't need to send other messages. + ac->off(); + } + ac->send(); +} +#endif // SEND_FUJITSU_AC264 + +#if SEND_GOODWEATHER +/// Send a Goodweather A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGoodweatherAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::goodweather(IRGoodweatherAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff + : kGoodweatherSwingSlow); + ac->setTurbo(turbo); + ac->setLight(light); + // No Clean setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_GOODWEATHER + +#if SEND_GREE +/// Send a Gree A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGreeAC object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Toggle the device's economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool econo, + const bool light, const bool clean, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setIFeel(iFeel); + ac->setLight(light); + ac->setTurbo(turbo); + ac->setEcono(econo); + ac->setXFan(clean); + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_GREE + +#if SEND_HAIER_AC +/// Send a Haier A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRGreeAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + if (clock >= 0) ac->setCurrTime(clock); + if (on) + ac->setCommand(kHaierAcCmdOn); + else + ac->setCommand(kHaierAcCmdOff); + ac->send(); +} +#endif // SEND_HAIER_AC + +#if SEND_HAIER_AC160 +/// Send a Haier 160 bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierAC160 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the clean mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] prevlight Previous LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haier160(IRHaierAC160 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool quiet, const bool filter, + const bool clean, const bool light, const bool prevlight, + const int16_t sleep) { + ac->begin(); + // No Model setting available. + ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setHealth(filter); + ac->setClean(clean); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + // Light needs to be sent last as the "button" value seems to control it. + ac->setLightToggle(light ^ prevlight); + ac->send(); +} +#endif // SEND_HAIER_AC160 + +#if SEND_HAIER_AC176 +/// Send a Haier 176 bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haier176(IRHaierAC176 *ac, const haier_ac176_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC176 + +#if SEND_HAIER_AC_YRW02 +/// Send a Haier YRWO2 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] quiet Run the device in quiet mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::haierYrwo2(IRHaierACYRW02 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + ac->setPower(on); + ac->send(); +} +#endif // SEND_HAIER_AC_YRW02 + +#if SEND_HITACHI_AC +/// Send a Hitachi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv != stdAc::swingv_t::kOff); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC + +#if SEND_HITACHI_AC1 +/// Send a Hitachi1 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc1 object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] power_toggle The power toggle 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] swing_toggle The swing_toggle setting. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @note The sleep mode used is the "Sleep 2" setting. +void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, + const bool on, const bool power_toggle, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool swing_toggle, const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setPowerToggle(power_toggle); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + ac->setSwingToggle(swing_toggle); + ac->setSleep((sleep >= 0) ? kHitachiAc1Sleep2 : kHitachiAc1SleepOff); + // No Sleep setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC1 + +#if SEND_HITACHI_AC264 +/// Send a Hitachi 264-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc264 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::hitachi264(IRHitachiAc264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // No Swing(V) setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC264 + +#if SEND_HITACHI_AC296 +/// Send a Hitachi 296-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc296 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::hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // No Swing(V) setting available. + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC296 + +#if SEND_HITACHI_AC344 +/// Send a Hitachi 344-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc344 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::hitachi344(IRHitachiAc344 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setPower(on); + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + + // SwingVToggle is special. Needs to be last method called. + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + ac->send(); +} +#endif // SEND_HITACHI_AC344 + +#if SEND_HITACHI_AC424 +/// Send a Hitachi 424-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc424 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. +/// @param[in] swingv The vertical swing setting. +void IRac::hitachi424(IRHitachiAc424 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // SwingVToggle is special. Needs to be last method called. + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Swing(H) setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_HITACHI_AC424 + +#if SEND_KELON +/// Send a Kelon A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRKelonAc object to use. +/// @param[in] togglePower Whether to toggle the unit's power +/// @param[in] mode The operation mode setting. +/// @param[in] dryGrade The dehumidification intensity grade +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] toggleSwing Whether to toggle the swing setting +/// @param[in] superCool Run the device in Super cooling mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on +void IRac::kelon(IRKelonAc *ac, const bool togglePower, + const stdAc::opmode_t mode, const int8_t dryGrade, + const float degrees, const stdAc::fanspeed_t fan, + const bool toggleSwing, const bool superCool, + const int16_t sleep) { + ac->begin(); + ac->setMode(IRKelonAc::convertMode(mode)); + ac->setFan(IRKelonAc::convertFan(fan)); + ac->setTemp(static_cast(degrees)); + ac->setSleep(sleep >= 0); + ac->setSupercool(superCool); + ac->setDryGrade(dryGrade); + + ac->setTogglePower(togglePower); + ac->setToggleSwingVertical(toggleSwing); + + ac->send(); +} +#endif // SEND_KELON + +#if SEND_KELVINATOR +/// Send a Kelvinator A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRKelvinatorAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc +void IRac::kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan((uint8_t)fan); // No conversion needed. + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setIonFilter(filter); + ac->setXFan(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_KELVINATOR + +#if SEND_LG +/// Send a LG A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRLgAc object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingv_prev The previous vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] light Turn on the LED/Display mode. +void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, + const stdAc::swingh_t swingh, const bool light) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv_prev)); + ac->updateSwingPrev(); + ac->setSwingV(ac->convertSwingV(swingv)); + const uint8_t pos = ac->convertVaneSwingV(swingv); + for (uint8_t vane = 0; vane < kLgAcSwingVMaxVanes; vane++) + ac->setVaneSwingV(vane, pos); + // Toggle the swingv for LG6711A20083V models if we need to. + // i.e. Off to Not-Off, send a toggle. Not-Off to Off, send a toggle. + if ((model == lg_ac_remote_model_t::LG6711A20083V) && + ((swingv == stdAc::swingv_t::kOff) != + (swingv_prev == stdAc::swingv_t::kOff))) + ac->setSwingV(kLgAcSwingVToggle); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_LG + +#if SEND_MIDEA +/// Send a Midea A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMideaAC object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] sensorTemp The room (iFeel) temperature sensor reading +/// in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] quiet_prev The device's previous quiet/silent mode. +/// @param[in] turbo Toggle the device's turbo/powerful mode. +/// @param[in] econo Toggle the device's economical mode. +/// @param[in] light Toggle the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @note On Danby A/C units, swingv controls the Ion Filter instead. +void IRac::midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool iFeel, const bool quiet, const bool quiet_prev, + const bool turbo, const bool econo, const bool light, + const bool clean, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setUseCelsius(celsius); + ac->setTemp(degrees, celsius); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(sensorTemp, celsius); + } + ac->setEnableSensorTemp(iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + ac->setQuiet(quiet, quiet_prev); + ac->setTurboToggle(turbo); + ac->setEconoToggle(econo); + ac->setLightToggle(light); + // No Filter setting available. + ac->setCleanToggle(clean); + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MIDEA + +#if SEND_MIRAGE +/// Send a Mirage 120-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. +/// @param[in] state The desired state to send. +void IRac::mirage(IRMirageAc *ac, const stdAc::state_t state) { + ac->begin(); + ac->fromCommon(state); + ac->send(); +} +#endif // SEND_MIRAGE + +#if SEND_MITSUBISHI_AC +/// Send a Mitsubishi A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @note Clock can only be set in 10 minute increments. i.e. % 10. +void IRac::mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const int16_t clock) { + ac->begin(); + // Uncomment next line if you *really* need the weekly timer enabled via IRac. + // ac->setWeeklyTimerEnabled(true); // Weekly Timer is disabled by default. + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setVane(ac->convertSwingV(swingv)); + ac->setVaneLeft(ac->convertSwingV(swingv)); + ac->setWideVane(ac->convertSwingH(swingh)); + if (quiet) ac->setFan(kMitsubishiAcFanSilent); + ac->setISave10C(false); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock / 10); // Clock is in 10 min increments. + ac->send(); +} +#endif // SEND_MITSUBISHI_AC + +#if SEND_MITSUBISHI112 +/// Send a Mitsubishi 112-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishi112 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +void IRac::mitsubishi112(IRMitsubishi112 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + // FIXME - Econo + // ac->setEcono(econo); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHI112 + +#if SEND_MITSUBISHI136 +/// Send a Mitsubishi 136-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishi136 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +void IRac::mitsubishi136(IRMitsubishi136 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + ac->setQuiet(quiet); + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHI136 + +#if SEND_MITSUBISHIHEAVY +/// Send a Mitsubishi Heavy 88-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy88Ac 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, + const bool clean) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + // No Filter setting available. + ac->setClean(clean); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} + +/// Send a Mitsubishi Heavy 152-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiHeavy152Ac 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, + const bool econo, const bool filter, + const bool clean, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setSilent(quiet); + ac->setTurbo(turbo); + // No Light setting available. + ac->setEcono(econo); + ac->setClean(clean); + ac->setFilter(filter); + // No Beep setting available. + ac->setNight(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHIHEAVY + +#if SEND_NEOCLIMA +/// Send a Neoclima A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRNeoclimaAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::neoclima(IRNeoclimaAc *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const bool filter, const int16_t sleep) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_NEOCLIMA + +#if SEND_PANASONIC_AC +/// Send a Panasonic A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRPanasonicAc object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool filter, + const int16_t clock) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + ac->setPowerful(turbo); + ac->setIon(filter); + // No Light setting available. + // No Econo setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_PANASONIC_AC + +#if SEND_PANASONIC_AC32 +/// Send a Panasonic A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRPanasonicAc32 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +void IRac::panasonic32(IRPanasonicAc32 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPowerToggle(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + // No Turbo setting available. + // No Filter setting available. + // No Light setting available. + // No Econo setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_PANASONIC_AC32 + +#if SEND_SAMSUNG_AC +/// Send a Samsung A/C message with the supplied settings. +/// @note Multiple IR messages may be generated & sent. +/// @param[in, out] ac A Ptr to an IRSamsungAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Toggle the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Toggle beep setting for receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. +/// @param[in] prevpower The power setting from the previous A/C state. +/// @param[in] prevsleep Nr. of minutes for sleep from the previous A/C state. +/// @param[in] forceextended Do we force sending the special extended message? +void IRac::samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, + const bool filter, const bool clean, + const bool beep, const int16_t sleep, + const bool prevpower, const int16_t prevsleep, + const bool forceextended) { + ac->begin(); + ac->stateReset(forceextended || (sleep != prevsleep), prevpower); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + ac->setQuiet(quiet); + ac->setPowerful(turbo); // FYI, `setEcono(true)` will override this. + ac->setDisplay(light); + ac->setEcono(econo); + ac->setIon(filter); + ac->setClean(clean); // Toggle + ac->setBeep(beep); // Toggle + ac->setSleepTimer((sleep <= 0) ? 0 : sleep); + // No Clock setting available. + // Do setMode() again as it can affect fan speed. + ac->setMode(ac->convertMode(mode)); + ac->send(); +} +#endif // SEND_SAMSUNG_AC + +#if SEND_SANYO_AC +/// Send a Sanyo A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRSanyoAc 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] sensorTemp The room (iFeel) temperature sensor reading in degrees +/// Celsius. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit. +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::sanyo(IRSanyoAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool iFeel, const bool beep, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + if (sensorTemp != kNoTempValue) { + ac->setSensorTemp(static_cast(roundf(sensorTemp))); + } else { + ac->setSensorTemp(degrees); // Set the sensor temp to the desired + // (normal) temp. + } + ac->setSensor(!iFeel); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Econo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + ac->setBeep(beep); + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_SANYO_AC + +#if SEND_SANYO_AC88 +/// Send a Sanyo 88-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRSanyoAc88 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::sanyo88(IRSanyoAc88 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool filter, const int16_t sleep, + const int16_t clock) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Econo setting available. + // No Light setting available. + ac->setFilter(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->send(); +} +#endif // SEND_SANYO_AC88 + +#if SEND_SHARP_AC +/// Send a Sharp A/C message with the supplied settings. +/// @note Multiple IR messages may be generated & sent. +/// @param[in, out] ac A Ptr to an IRSharpAc object to use. +/// @param[in] model The A/C model to use. +/// @param[in] on The power setting. +/// @param[in] prev_power The power setting from the previous A/C state. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingv_prev The previous vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, + const bool on, const bool prev_power, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingv_t swingv_prev, const bool turbo, + const bool light, const bool filter, const bool clean) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan, model)); + if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv)); + // Econo deliberately not used as it cycles through 3 modes uncontrollably. + // ac->setEconoToggle(econo); + ac->setIon(filter); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setLightToggle(light); + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + // Do setMode() again as it can affect fan speed and temp. + ac->setMode(ac->convertMode(mode)); + // Clean after mode, as it can affect the mode, temp & fan speed. + if (clean) { + // A/C needs to be off before we can enter clean mode. + ac->setPower(false, prev_power); + ac->send(); + } + ac->setClean(clean); + ac->setPower(on, prev_power); + if (turbo) { + ac->send(); // Send the current state. + // Set up turbo mode as it needs to be sent after everything else. + ac->setTurbo(true); + } + ac->send(); +} +#endif // SEND_SHARP_AC + +#if SEND_TCL112AC +/// Send a TCL 112-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +void IRac::tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff); + ac->setQuiet(quiet); + ac->setTurbo(turbo); + ac->setLight(light); + ac->setEcono(econo); + ac->setHealth(filter); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TCL112AC + +#if SEND_TECHNIBEL_AC +/// Send a Technibel A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTechnibelAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::technibel(IRTechnibelAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, !celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECHNIBEL_AC + +#if SEND_TECO +/// Send a Teco A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTecoAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool light, const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TECO + +#if SEND_TOSHIBA_AC +/// Send a Toshiba A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRToshibaAC 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (Pure/ion/pollen/etc) filter mode. +void IRac::toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool econo, const bool filter) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // The API has no "step" option, so off is off, anything else is on. + ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff + : kToshibaAcSwingOn); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setEcono(econo); + // No Light setting available. + ac->setFilter(filter); + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + // Do this last because Toshiba A/C has an odd quirk with how power off works. + ac->setPower(on); + ac->send(); +} +#endif // SEND_TOSHIBA_AC + +#if SEND_TROTEC +/// Send a Trotec A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrotecESP 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. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setSpeed(ac->convertFan(fan)); + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC + +#if SEND_TROTEC_3550 +/// Send a Trotec 3550 A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrotecESP object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +void IRac::trotec3550(IRTrotec3550 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, celsius); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TROTEC_3550 + +#if SEND_TRUMA +/// Send a Truma A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRTrumaAc 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. +/// @param[in] quiet Run the device quietly if we can. +void IRac::truma(IRTrumaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setQuiet(quiet); // Only available in Cool mode. + // No Vertical swing setting available. + // No Horizontal swing setting available. + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TRUMA + +#if SEND_VESTEL_AC +/// Send a Vestel A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRVestelAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @param[in] sendNormal Do we send a Normal settings message at all? +/// i.e In addition to the clock/time/timer message +void IRac::vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, const int16_t sleep, + const int16_t clock, const bool sendNormal) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setTurbo(turbo); + // No Light setting available. + ac->setIon(filter); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (sendNormal) ac->send(); // Send the normal message. + if (clock >= 0) { + ac->setTime(clock); + ac->send(); // Setting the clock requires a different "timer" message. + } +} +#endif // SEND_VESTEL_AC + +#if SEND_VOLTAS +/// Send a Voltas A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRVoltas object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +void IRac::voltas(IRVoltas *ac, + const voltas_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const int16_t sleep) { + ac->begin(); + ac->setModel(model); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setEcono(econo); + ac->setLight(light); + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + // No Clock setting available. + ac->send(); +} +#endif // SEND_VOLTAS + +#if SEND_WHIRLPOOL_AC +/// Send a Whirlpool A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRWhirlpoolAc object to use. +/// @param[in] model The A/C model 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep, const int16_t clock) { + ac->begin(); + ac->setModel(model); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv != stdAc::swingv_t::kOff); + // No Horizontal swing setting available. + // No Quiet setting available. + ac->setSuper(turbo); + ac->setLight(light); + // No Filter setting available + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean. + if (clock >= 0) ac->setClock(clock); + ac->setPowerToggle(on); + ac->send(); +} +#endif // SEND_WHIRLPOOL_AC + +#if SEND_TRANSCOLD +/// Send a Transcold A/C message with the supplied settings. +/// @note May result in multiple messages being sent. +/// @param[in, out] ac A Ptr to an IRTranscoldAc 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. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @note -1 is Off, >= 0 is on. +void IRac::transcold(IRTranscoldAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->begin(); + ac->setPower(on); + if (!on) { + // after turn off AC no more commands should + // be accepted + ac->send(); + return; + } + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // No Filter setting available. + // No Beep setting available. + // No Clock setting available. + // No Econo setting available. + // No Quiet setting available. + if (swingv != stdAc::swingv_t::kOff || swingh != stdAc::swingh_t::kOff) { + // Swing has a special command that needs to be sent independently. + ac->setSwing(); + ac->send(); + } + + ac->send(); +} +#endif // SEND_TRANSCOLD + +#if SEND_RHOSS +/// Send an Rhoss A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRRhossAc 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. +/// @param[in] swing The swing setting. +void IRac::rhoss(IRRhossAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swing) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setSwing(swing != stdAc::swingv_t::kOff); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + // 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_RHOSS + +/// Create a new state base on the provided state that has been suitably fixed. +/// @note This is for use with Home Assistant, which requires mode to be off if +/// the power is off. +/// @param[in] state The state_t structure describing the desired a/c state. +/// @return A stdAc::state_t with the needed settings. +stdAc::state_t IRac::cleanState(const stdAc::state_t state) { + stdAc::state_t result = state; + // A hack for Home Assistant, it appears to need/want an Off opmode. + // So enforce the power is off if the mode is also off. + if (state.mode == stdAc::opmode_t::kOff) result.power = false; + return result; +} + +/// Create a new state base on desired & previous states but handle +/// any state changes for options that need to be toggled. +/// @param[in] desired The state_t structure describing the desired a/c state. +/// @param[in] prev A Ptr to the previous state_t structure. +/// @return A stdAc::state_t with the needed settings. +stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev) { + stdAc::state_t result = desired; + // If we've been given a previous state AND the it's the same A/C basically. + if (prev != NULL && desired.protocol == prev->protocol && + desired.model == prev->model) { + // Check if we have to handle toggle settings for specific A/C protocols. + switch (desired.protocol) { + case decode_type_t::COOLIX: + case decode_type_t::TRANSCOLD: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + result.turbo = desired.turbo ^ prev->turbo; + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + result.sleep = ((desired.sleep >= 0) ^ (prev->sleep >= 0)) ? 0 : -1; + break; + case decode_type_t::DAIKIN128: + result.power = desired.power ^ prev->power; + result.light = desired.light ^ prev->light; + break; + case decode_type_t::ELECTRA_AC: + result.light = desired.light ^ prev->light; + break; + case decode_type_t::FUJITSU_AC: + result.turbo = desired.turbo ^ prev->turbo; + result.econo = desired.econo ^ prev->econo; + break; + case decode_type_t::MIDEA: + result.turbo = desired.turbo ^ prev->turbo; + result.econo = desired.econo ^ prev->econo; + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + // FALL THRU + case decode_type_t::CORONA_AC: + case decode_type_t::HITACHI_AC344: + case decode_type_t::HITACHI_AC424: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + break; + case decode_type_t::SHARP_AC: + result.light = desired.light ^ prev->light; + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + break; + case decode_type_t::KELON: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + // FALL-THRU + case decode_type_t::AIRWELL: + case decode_type_t::DAIKIN64: + case decode_type_t::PANASONIC_AC32: + case decode_type_t::WHIRLPOOL_AC: + result.power = desired.power ^ prev->power; + break; + case decode_type_t::MIRAGE: + if (desired.model == mirage_ac_remote_model_t::KKG29AC1) + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + break; + case decode_type_t::PANASONIC_AC: + // CKP models use a power mode toggle. + if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) + result.power = desired.power ^ prev->power; + break; + case decode_type_t::SAMSUNG_AC: + result.beep = desired.beep ^ prev->beep; + result.clean = desired.clean ^ prev->clean; + break; + default: + {}; + } + } + return result; +} + +/// Send A/C message for a given device using common A/C settings. +/// @param[in] vendor The vendor/protocol type. +/// @param[in] model The A/C model if applicable. +/// @param[in] power The power setting. +/// @param[in] mode The operation mode setting. +/// @note Changing mode from "Off" to something else does NOT turn on a device. +/// You need to use `power` for that. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. +/// @param[in] fan The speed setting for the fan. +/// @note The following are all "if supported" by the underlying A/C classes. +/// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] filter Turn on the (ion/pollen/etc) filter mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// -1 is Off, >= 0 is on. Some devices it is the nr. of mins to run for. +/// Others it may be the time to enter/exit sleep mode. +/// i.e. Time in Nr. of mins since midnight. +/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore. +/// @return True, if accepted/converted/attempted etc. False, if unsupported. +bool IRac::sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, const int16_t clock) { + stdAc::state_t to_send; + initState(&to_send, vendor, model, power, mode, degrees, celsius, fan, swingv, + swingh, quiet, turbo, econo, light, filter, clean, beep, sleep, + clock); + return this->sendAc(to_send, &to_send); +} + +/// Send A/C message for a given device using state_t structures. +/// @param[in] desired The state_t structure describing the desired new ac state +/// @param[in] prev A Ptr to the state_t structure containing the previous state +/// @note Changing mode from "Off" to something else does NOT turn on a device. +/// You need to use `power` for that. +/// @return True, if accepted/converted/attempted etc. False, if unsupported. +bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { + // Convert the temp from Fahrenheit to Celsius if we are not in Celsius mode. + float degC __attribute__((unused)) = + desired.celsius ? desired.degrees : fahrenheitToCelsius(desired.degrees); + // Convert the sensorTemp from Fahrenheit to Celsius if we are not in Celsius + // mode. + float sensorTempC __attribute__((unused)) = + desired.sensorTemperature ? desired.sensorTemperature + : fahrenheitToCelsius(desired.sensorTemperature); + // special `state_t` that is required to be sent based on that. + stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev); + // Some protocols expect a previous state for power. + // Construct a pointer-safe previous power state incase prev is NULL/NULLPTR. +#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) + const bool prev_power = (prev != NULL) ? prev->power : !send.power; + const int16_t prev_sleep = (prev != NULL) ? prev->sleep : -1; +#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) +#if (SEND_LG || SEND_SHARP_AC) + const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv + : stdAc::swingv_t::kOff; +#endif // (SEND_LG || SEND_SHARP_AC) +#if (SEND_HAIER_AC160) + const bool prev_light = (prev != NULL) ? prev->light : !send.light; +#endif // (SEND_HAIER_AC160) +#if SEND_MIDEA + const bool prev_quiet = (prev != NULL) ? prev->quiet : !send.quiet; +#endif // SEND_MIDEA + // Per vendor settings & setup. + switch (send.protocol) { +#if SEND_AIRTON + case AIRTON: + { + IRAirtonAc ac(_pin, _inverted, _modulation); + airton(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.turbo, send.light, send.econo, send.filter, + send.sleep); + break; + } +#endif // SEND_AIRTON +#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: + { + IRAmcorAc ac(_pin, _inverted, _modulation); + amcor(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_AMCOR +#if SEND_ARGO + case ARGO: + { + if (send.model == argo_ac_remote_model_t::SAC_WREM3) { + IRArgoAC_WREM3 ac(_pin, _inverted, _modulation); + switch (send.command) { + case stdAc::ac_command_t::kSensorTempReport: + argoWrem3_iFeelReport(&ac, sensorTempC); + break; + case stdAc::ac_command_t::kConfigCommand: + /// @warning: this is ABUSING current **common** parameters: + /// @c clock and @c sleep as config key and value + /// Hence, value pre-validation is performed (safe-mode) + /// to avoid accidental device misconfiguration + argoWrem3_ConfigSet(&ac, send.clock, send.sleep, true); + break; + case stdAc::ac_command_t::kTimerCommand: + argoWrem3_SetTimer(&ac, send.power, send.clock, send.sleep); + break; + case stdAc::ac_command_t::kControlCommand: + default: + argoWrem3_ACCommand(&ac, send.power, send.mode, degC, sensorTempC, + send.fanspeed, send.swingv, send.iFeel, send.quiet, send.econo, + send.turbo, send.filter, send.light); + break; + } + OUTPUT_DECODE_RESULTS_FOR_UT(ac); + } else { + IRArgoAC ac(_pin, _inverted, _modulation); + argo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.iFeel, send.turbo, send.sleep); + OUTPUT_DECODE_RESULTS_FOR_UT(ac); + } + break; + } +#endif // SEND_ARGO +#if SEND_BOSCH144 + case BOSCH144: + { + IRBosch144AC ac(_pin, _inverted, _modulation); + bosch144(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); + break; + } +#endif // SEND_BOSCH144 +#if SEND_CARRIER_AC64 + case CARRIER_AC64: + { + IRCarrierAc64 ac(_pin, _inverted, _modulation); + carrier64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.sleep); + break; + } +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + case COOLIX: + { + IRCoolixAC ac(_pin, _inverted, _modulation); + coolix(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.swingh, send.iFeel, send.turbo, send.light, + send.clean, send.sleep); + break; + } +#endif // SEND_COOLIX +#if SEND_CORONA_AC + case CORONA_AC: + { + IRCoronaAc ac(_pin, _inverted, _modulation); + corona(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.econo); + break; + } +#endif // SEND_CORONA_AC +#if SEND_DAIKIN + case DAIKIN: + { + IRDaikinESP ac(_pin, _inverted, _modulation); + daikin(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.econo, send.clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + { + IRDaikin128 ac(_pin, _inverted, _modulation); + daikin128(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.light, send.econo, send.sleep, + send.clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN152 + case DAIKIN152: + { + IRDaikin152 ac(_pin, _inverted, _modulation); + daikin152(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.econo); + break; + } +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + case DAIKIN160: + { + IRDaikin160 ac(_pin, _inverted, _modulation); + daikin160(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + { + IRDaikin176 ac(_pin, _inverted, _modulation); + daikin176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingh); + break; + } +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + { + IRDaikin2 ac(_pin, _inverted, _modulation); + daikin2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.light, send.econo, + send.filter, send.clean, send.beep, send.sleep, send.clock); + break; + } +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + { + IRDaikin216 ac(_pin, _inverted, _modulation); + daikin216(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 + case DAIKIN64: + { + IRDaikin64 ac(_pin, _inverted, _modulation); + daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.quiet, send.turbo, send.sleep, send.clock); + break; + } +#endif // SEND_DAIKIN64 +#if SEND_DELONGHI_AC + case DELONGHI_AC: + { + IRDelonghiAc ac(_pin, _inverted, _modulation); + delonghiac(&ac, send.power, send.mode, send.celsius, degC, send.fanspeed, + send.turbo, send.sleep); + break; + } +#endif // SEND_DELONGHI_AC +#if SEND_ECOCLIM + case ECOCLIM: + { + IREcoclimAc ac(_pin, _inverted, _modulation); + ecoclim(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.iFeel, send.clock); + break; + } +#endif // SEND_ECOCLIM +#if SEND_ELECTRA_AC + case ELECTRA_AC: + { + IRElectraAc ac(_pin, _inverted, _modulation); + electra(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.swingh, send.iFeel, send.turbo, send.light, + send.clean); + break; + } +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + { + IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)send.model, _inverted, + _modulation); + fujitsu(&ac, (fujitsu_ac_remote_model_t)send.model, send.power, send.mode, + send.celsius, send.degrees, send.fanspeed, + send.swingv, send.swingh, send.quiet, + send.turbo, send.econo, send.filter, send.clean, send.sleep); + break; + } +#endif // SEND_FUJITSU_AC +#if SEND_FUJITSU_AC264 + case FUJITSU_AC264: + { + IRFujitsuAC264 ac(_pin, _inverted, _modulation); + fujitsu264(&ac, send.power, send.mode, send.degrees, send.fanspeed, + send.swingv, send.quiet, send.turbo, send.econo, + send.clean, send.sleep); + break; + } +#endif // SEND_FUJITSU_AC264 +#if SEND_GOODWEATHER + case GOODWEATHER: + { + IRGoodweatherAc ac(_pin, _inverted, _modulation); + goodweather(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.light, send.sleep); + break; + } +#endif // SEND_GOODWEATHER +#if SEND_GREE + case GREE: + { + IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, + _modulation); + gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, + send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, + send.iFeel, send.turbo, send.econo, send.light, send.clean, + send.sleep); + break; + } +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + { + IRHaierAC ac(_pin, _inverted, _modulation); + haier(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC160 + case HAIER_AC160: + { + IRHaierAC160 ac(_pin, _inverted, _modulation); + haier160(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.turbo, send.filter, send.clean, + send.light, prev_light, send.sleep); + break; + } +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + case HAIER_AC176: + { + IRHaierAC176 ac(_pin, _inverted, _modulation); + haier176(&ac, (haier_ac176_remote_model_t)send.model, send.power, + send.mode, send.celsius, send.degrees, send.fanspeed, + send.swingv, send.swingh, send.turbo, send.filter, send.sleep); + break; + } +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + { + IRHaierACYRW02 ac(_pin, _inverted, _modulation); + haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.swingh, send.turbo, + send.filter, send.sleep); + break; + } +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + { + IRHitachiAc ac(_pin, _inverted, _modulation); + hitachi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh); + break; + } +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + { + IRHitachiAc1 ac(_pin, _inverted, _modulation); + bool power_toggle = false; + bool swing_toggle = false; + if (prev != NULL) { + power_toggle = (send.power != prev->power); + swing_toggle = (send.swingv != prev->swingv) || + (send.swingh != prev->swingh); + } + hitachi1(&ac, (hitachi_ac1_remote_model_t)send.model, send.power, + power_toggle, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, swing_toggle, send.sleep); + break; + } +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + case HITACHI_AC264: + { + IRHitachiAc264 ac(_pin, _inverted, _modulation); + hitachi264(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + { + IRHitachiAc296 ac(_pin, _inverted, _modulation); + hitachi296(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + case HITACHI_AC344: + { + IRHitachiAc344 ac(_pin, _inverted, _modulation); + hitachi344(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh); + break; + } +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + case HITACHI_AC424: + { + IRHitachiAc424 ac(_pin, _inverted, _modulation); + hitachi424(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_HITACHI_AC424 +#if SEND_KELON + case KELON: { + IRKelonAc ac(_pin, _inverted, _modulation); + kelon(&ac, send.power, send.mode, 0, send.degrees, send.fanspeed, + send.swingv != stdAc::swingv_t::kOff, send.turbo, send.sleep); + break; + } +#endif +#if SEND_KELVINATOR + case KELVINATOR: + { + IRKelvinatorAC ac(_pin, _inverted, _modulation); + kelvinator(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.light, send.filter, + send.clean); + break; + } +#endif // SEND_KELVINATOR +#if SEND_LG + case LG: + case LG2: + { + IRLgAc ac(_pin, _inverted, _modulation); + lg(&ac, (lg_ac_remote_model_t)send.model, send.power, send.mode, + send.degrees, send.fanspeed, send.swingv, prev_swingv, send.swingh, + send.light); + break; + } +#endif // SEND_LG +#if SEND_MIDEA + case MIDEA: + { + IRMideaAC ac(_pin, _inverted, _modulation); + midea(&ac, send.power, send.mode, send.celsius, send.degrees, + send.sensorTemperature, send.fanspeed, send.swingv, send.iFeel, + send.quiet, prev_quiet, send.turbo, send.econo, send.light, + send.clean, send.sleep); + break; + } +#endif // SEND_MIDEA +#if SEND_MIRAGE + case MIRAGE: + { + IRMirageAc ac(_pin, _inverted, _modulation); + mirage(&ac, send); + break; + } +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + { + IRMitsubishiAC ac(_pin, _inverted, _modulation); + mitsubishi(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.clock); + break; + } +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + { + IRMitsubishi112 ac(_pin, _inverted, _modulation); + mitsubishi112(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.quiet); + break; + } +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHI136 + case MITSUBISHI136: + { + IRMitsubishi136 ac(_pin, _inverted, _modulation); + mitsubishi136(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.quiet); + break; + } +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + { + IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); + mitsubishiHeavy88(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.turbo, send.econo, + send.clean); + break; + } + case MITSUBISHI_HEAVY_152: + { + IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); + mitsubishiHeavy152(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh, send.quiet, send.turbo, + send.econo, send.filter, send.clean, send.sleep); + break; + } +#endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + case NEOCLIMA: + { + IRNeoclimaAc ac(_pin, _inverted, _modulation); + neoclima(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.swingh, send.turbo, + send.econo, send.light, send.filter, send.sleep); + break; + } +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + { + IRPanasonicAc ac(_pin, _inverted, _modulation); + panasonic(&ac, (panasonic_ac_remote_model_t)send.model, send.power, + send.mode, degC, send.fanspeed, send.swingv, send.swingh, + send.quiet, send.turbo, send.clock); + break; + } +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC32 + case PANASONIC_AC32: + { + IRPanasonicAc32 ac(_pin, _inverted, _modulation); + panasonic32(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.swingh); + break; + } +#endif // SEND_PANASONIC_AC32 +#if SEND_RHOSS + case RHOSS: + { + IRRhossAc ac(_pin, _inverted, _modulation); + rhoss(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv); + break; + } +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + { + IRSamsungAc ac(_pin, _inverted, _modulation); + samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet, send.turbo, send.econo, send.light, + send.filter, send.clean, send.beep, send.sleep, + prev_power, prev_sleep); + break; + } +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + { + IRSanyoAc ac(_pin, _inverted, _modulation); + sanyo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed, + send.swingv, send.iFeel, send.beep, send.sleep); + break; + } +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + case SANYO_AC88: + { + IRSanyoAc88 ac(_pin, _inverted, _modulation); + sanyo88(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_SANYO_AC88 +#if SEND_SHARP_AC + case SHARP_AC: + { + IRSharpAc ac(_pin, _inverted, _modulation); + sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power, + send.mode, degC, send.fanspeed, send.swingv, prev_swingv, + send.turbo, send.light, send.filter, send.clean); + break; + } +#endif // SEND_SHARP_AC +#if (SEND_TCL112AC || SEND_TEKNOPOINT) + case TCL112AC: + case TEKNOPOINT: + { + IRTcl112Ac ac(_pin, _inverted, _modulation); + tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model; + if (send.protocol == decode_type_t::TEKNOPOINT) + model = tcl_ac_remote_model_t::GZ055BE1; + tcl112(&ac, model, send.power, send.mode, + degC, send.fanspeed, send.swingv, send.swingh, send.quiet, + send.turbo, send.light, send.econo, send.filter); + break; + } +#endif // (SEND_TCL112AC || SEND_TEKNOPOINT) +#if SEND_TECHNIBEL_AC + case TECHNIBEL_AC: + { + IRTechnibelAc ac(_pin, _inverted, _modulation); + technibel(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.sleep); + break; + } +#endif // SEND_TECHNIBEL_AC +#if SEND_TECO + case TECO: + { + IRTecoAc ac(_pin, _inverted, _modulation); + teco(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.light, send.sleep); + break; + } +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + { + IRToshibaAC ac(_pin, _inverted, _modulation); + toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.econo, send.filter); + break; + } +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + { + IRTrotecESP ac(_pin, _inverted, _modulation); + trotec(&ac, send.power, send.mode, degC, send.fanspeed, send.sleep); + break; + } +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + case TROTEC_3550: + { + IRTrotec3550 ac(_pin, _inverted, _modulation); + trotec3550(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv); + break; + } +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + case TRUMA: + { + IRTrumaAc ac(_pin, _inverted, _modulation); + truma(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet); + break; + } +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case VESTEL_AC: + { + IRVestelAc ac(_pin, _inverted, _modulation); + vestel(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.turbo, send.filter, send.sleep, send.clock); + break; + } +#endif // SEND_VESTEL_AC +#if SEND_VOLTAS + case VOLTAS: + { + IRVoltas ac(_pin, _inverted, _modulation); + voltas(&ac, (voltas_ac_remote_model_t)send.model, send.power, send.mode, + degC, send.fanspeed, send.swingv, send.swingh, send.turbo, + send.econo, send.light, send.sleep); + break; + } +#endif // SEND_VOLTAS +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + { + IRWhirlpoolAc ac(_pin, _inverted, _modulation); + whirlpool(&ac, (whirlpool_ac_remote_model_t)send.model, send.power, + send.mode, degC, send.fanspeed, send.swingv, send.turbo, + send.light, send.sleep, send.clock); + break; + } +#endif // SEND_WHIRLPOOL_AC +#if SEND_TRANSCOLD + case TRANSCOLD: + { + IRTranscoldAc ac(_pin, _inverted, _modulation); + transcold(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, + send.swingh); + break; + } +#endif // SEND_TRANSCOLD_AC + default: + return false; // Fail, didn't match anything. + } + return true; // Success. +} // NOLINT(readability/fn_size) + +/// Update the previous state to the current one. +void IRac::markAsSent(void) { + _prev = next; +} + +/// Send an A/C message based soley on our internal state. +/// @return True, if accepted/converted/attempted. False, if unsupported. +bool IRac::sendAc(void) { + bool success = this->sendAc(next, &_prev); + if (success) this->markAsSent(); + return success; +} + +/// Compare two AirCon states. +/// @note The comparison excludes the clock. +/// @param a A state_t to be compared. +/// @param b A state_t to be compared. +/// @return True if they differ, False if they don't. +bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { + return a.protocol != b.protocol || a.model != b.model || a.power != b.power || + a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || + a.fanspeed != b.fanspeed || a.swingv != b.swingv || + a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || + a.econo != b.econo || a.light != b.light || a.filter != b.filter || + a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep || + a.command != b.command || a.sensorTemperature != b.sensorTemperature || + a.iFeel != b.iFeel; +} + +/// Check if the internal state has changed from what was previously sent. +/// @note The comparison excludes the clock. +/// @return True if it has changed, False if not. +bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); } + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::ac_command_t IRac::strToCommandType(const char *str, + const stdAc::ac_command_t def) { + if (!STRCASECMP(str, kControlCommandStr)) + return stdAc::ac_command_t::kControlCommand; + else if (!STRCASECMP(str, kIFeelReportStr) || + !STRCASECMP(str, kIFeelStr)) + return stdAc::ac_command_t::kSensorTempReport; + else if (!STRCASECMP(str, kSetTimerCommandStr) || + !STRCASECMP(str, kTimerStr)) + return stdAc::ac_command_t::kTimerCommand; + else if (!STRCASECMP(str, kConfigCommandStr)) + return stdAc::ac_command_t::kConfigCommand; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::opmode_t IRac::strToOpmode(const char *str, + const stdAc::opmode_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr)) + return stdAc::opmode_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::opmode_t::kOff; + else if (!STRCASECMP(str, kCoolStr) || + !STRCASECMP(str, kCoolingStr)) + return stdAc::opmode_t::kCool; + else if (!STRCASECMP(str, kHeatStr) || + !STRCASECMP(str, kHeatingStr)) + return stdAc::opmode_t::kHeat; + else if (!STRCASECMP(str, kDryStr) || + !STRCASECMP(str, kDryingStr) || + !STRCASECMP(str, kDehumidifyStr)) + return stdAc::opmode_t::kDry; + else if (!STRCASECMP(str, kFanStr) || + // The following Fans strings with "only" are required to help with + // HomeAssistant & Google Home Climate integration. + // For compatibility only. + // Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes + !STRCASECMP(str, kFanOnlyStr) || + !STRCASECMP(str, kFan_OnlyStr) || + !STRCASECMP(str, kFanOnlyWithSpaceStr) || + !STRCASECMP(str, kFanOnlyNoSpaceStr)) + return stdAc::opmode_t::kFan; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::fanspeed_t IRac::strToFanspeed(const char *str, + const stdAc::fanspeed_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr)) + return stdAc::fanspeed_t::kAuto; + else if (!STRCASECMP(str, kMinStr) || + !STRCASECMP(str, kMinimumStr) || + !STRCASECMP(str, kLowestStr)) + return stdAc::fanspeed_t::kMin; + else if (!STRCASECMP(str, kLowStr) || + !STRCASECMP(str, kLoStr)) + return stdAc::fanspeed_t::kLow; + else if (!STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kMidStr)) + return stdAc::fanspeed_t::kMedium; + else if (!STRCASECMP(str, kHighStr) || + !STRCASECMP(str, kHiStr)) + return stdAc::fanspeed_t::kHigh; + else if (!STRCASECMP(str, kMaxStr) || + !STRCASECMP(str, kMaximumStr) || + !STRCASECMP(str, kHighestStr)) + return stdAc::fanspeed_t::kMax; + else if (!STRCASECMP(str, kMedHighStr)) + return stdAc::fanspeed_t::kMediumHigh; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::swingv_t IRac::strToSwingV(const char *str, + const stdAc::swingv_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr) || + !STRCASECMP(str, kOnStr) || + !STRCASECMP(str, kSwingStr)) + return stdAc::swingv_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::swingv_t::kOff; + else if (!STRCASECMP(str, kMinStr) || + !STRCASECMP(str, kMinimumStr) || + !STRCASECMP(str, kLowestStr) || + !STRCASECMP(str, kBottomStr) || + !STRCASECMP(str, kDownStr)) + return stdAc::swingv_t::kLowest; + else if (!STRCASECMP(str, kLowStr)) + return stdAc::swingv_t::kLow; + else if (!STRCASECMP(str, kMidStr) || + !STRCASECMP(str, kMiddleStr) || + !STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kCentreStr)) + return stdAc::swingv_t::kMiddle; + else if (!STRCASECMP(str, kUpperMiddleStr)) + return stdAc::swingv_t::kUpperMiddle; + else if (!STRCASECMP(str, kHighStr) || + !STRCASECMP(str, kHiStr)) + return stdAc::swingv_t::kHigh; + else if (!STRCASECMP(str, kHighestStr) || + !STRCASECMP(str, kMaxStr) || + !STRCASECMP(str, kMaximumStr) || + !STRCASECMP(str, kTopStr) || + !STRCASECMP(str, kUpStr)) + return stdAc::swingv_t::kHighest; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +stdAc::swingh_t IRac::strToSwingH(const char *str, + const stdAc::swingh_t def) { + if (!STRCASECMP(str, kAutoStr) || + !STRCASECMP(str, kAutomaticStr) || + !STRCASECMP(str, kOnStr) || !STRCASECMP(str, kSwingStr)) + return stdAc::swingh_t::kAuto; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, kStopStr)) + return stdAc::swingh_t::kOff; + else if (!STRCASECMP(str, kLeftMaxNoSpaceStr) || // "LeftMax" + !STRCASECMP(str, kLeftMaxStr) || // "Left Max" + !STRCASECMP(str, kMaxLeftNoSpaceStr) || // "MaxLeft" + !STRCASECMP(str, kMaxLeftStr)) // "Max Left" + return stdAc::swingh_t::kLeftMax; + else if (!STRCASECMP(str, kLeftStr)) + return stdAc::swingh_t::kLeft; + else if (!STRCASECMP(str, kMidStr) || + !STRCASECMP(str, kMiddleStr) || + !STRCASECMP(str, kMedStr) || + !STRCASECMP(str, kMediumStr) || + !STRCASECMP(str, kCentreStr)) + return stdAc::swingh_t::kMiddle; + else if (!STRCASECMP(str, kRightStr)) + return stdAc::swingh_t::kRight; + else if (!STRCASECMP(str, kRightMaxNoSpaceStr) || // "RightMax" + !STRCASECMP(str, kRightMaxStr) || // "Right Max" + !STRCASECMP(str, kMaxRightNoSpaceStr) || // "MaxRight" + !STRCASECMP(str, kMaxRightStr)) // "Max Right" + return stdAc::swingh_t::kRightMax; + else if (!STRCASECMP(str, kWideStr)) + return stdAc::swingh_t::kWide; + else + return def; +} + +/// Convert the supplied str into the appropriate enum. +/// @note Assumes str is the model code or an integer >= 1. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The enum to return if no conversion was possible. +/// @return The equivalent enum. +/// @note After adding a new model you should update modelToStr() too. +int16_t IRac::strToModel(const char *str, const int16_t def) { + // Gree + if (!STRCASECMP(str, kYaw1fStr)) { + return gree_ac_remote_model_t::YAW1F; + } else if (!STRCASECMP(str, kYbofbStr)) { + return gree_ac_remote_model_t::YBOFB; + } else if (!STRCASECMP(str, kYx1fsfStr)) { + return gree_ac_remote_model_t::YX1FSF; + // Haier models + } else if (!STRCASECMP(str, kV9014557AStr)) { + return haier_ac176_remote_model_t::V9014557_A; + } else if (!STRCASECMP(str, kV9014557BStr)) { + return haier_ac176_remote_model_t::V9014557_B; + // HitachiAc1 models + } else if (!STRCASECMP(str, kRlt0541htaaStr)) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; + } else if (!STRCASECMP(str, kRlt0541htabStr)) { + return hitachi_ac1_remote_model_t::R_LT0541_HTA_B; + // Fujitsu A/C models + } else if (!STRCASECMP(str, kArrah2eStr)) { + return fujitsu_ac_remote_model_t::ARRAH2E; + } else if (!STRCASECMP(str, kArdb1Str)) { + return fujitsu_ac_remote_model_t::ARDB1; + } else if (!STRCASECMP(str, kArreb1eStr)) { + return fujitsu_ac_remote_model_t::ARREB1E; + } else if (!STRCASECMP(str, kArjw2Str)) { + return fujitsu_ac_remote_model_t::ARJW2; + } else if (!STRCASECMP(str, kArry4Str)) { + return fujitsu_ac_remote_model_t::ARRY4; + } else if (!STRCASECMP(str, kArrew4eStr)) { + return fujitsu_ac_remote_model_t::ARREW4E; + // LG A/C models + } else if (!STRCASECMP(str, kGe6711ar2853mStr)) { + return lg_ac_remote_model_t::GE6711AR2853M; + } else if (!STRCASECMP(str, kAkb75215403Str)) { + return lg_ac_remote_model_t::AKB75215403; + } else if (!STRCASECMP(str, kAkb74955603Str)) { + return lg_ac_remote_model_t::AKB74955603; + } else if (!STRCASECMP(str, kAkb73757604Str)) { + return lg_ac_remote_model_t::AKB73757604; + } else if (!STRCASECMP(str, kLg6711a20083vStr)) { + return lg_ac_remote_model_t::LG6711A20083V; + // Panasonic A/C families + } else if (!STRCASECMP(str, kLkeStr) || + !STRCASECMP(str, kPanasonicLkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicLke; + } else if (!STRCASECMP(str, kNkeStr) || + !STRCASECMP(str, kPanasonicNkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicNke; + } else if (!STRCASECMP(str, kDkeStr) || + !STRCASECMP(str, kPanasonicDkeStr) || + !STRCASECMP(str, kPkrStr) || + !STRCASECMP(str, kPanasonicPkrStr)) { + return panasonic_ac_remote_model_t::kPanasonicDke; + } else if (!STRCASECMP(str, kJkeStr) || + !STRCASECMP(str, kPanasonicJkeStr)) { + return panasonic_ac_remote_model_t::kPanasonicJke; + } else if (!STRCASECMP(str, kCkpStr) || + !STRCASECMP(str, kPanasonicCkpStr)) { + return panasonic_ac_remote_model_t::kPanasonicCkp; + } else if (!STRCASECMP(str, kRkrStr) || + !STRCASECMP(str, kPanasonicRkrStr)) { + return panasonic_ac_remote_model_t::kPanasonicRkr; + // Sharp A/C Models + } else if (!STRCASECMP(str, kA907Str)) { + return sharp_ac_remote_model_t::A907; + } else if (!STRCASECMP(str, kA705Str)) { + return sharp_ac_remote_model_t::A705; + } else if (!STRCASECMP(str, kA903Str)) { + return sharp_ac_remote_model_t::A903; + // TCL A/C Models + } else if (!STRCASECMP(str, kTac09chsdStr)) { + return tcl_ac_remote_model_t::TAC09CHSD; + } else if (!STRCASECMP(str, kGz055be1Str)) { + return tcl_ac_remote_model_t::GZ055BE1; + // Voltas A/C models + } else if (!STRCASECMP(str, k122lzfStr)) { + return voltas_ac_remote_model_t::kVoltas122LZF; + // Whirlpool A/C models + } else if (!STRCASECMP(str, kDg11j13aStr) || + !STRCASECMP(str, kDg11j104Str)) { + return whirlpool_ac_remote_model_t::DG11J13A; + } else if (!STRCASECMP(str, kDg11j191Str)) { + return whirlpool_ac_remote_model_t::DG11J191; + // Argo A/C models + } else if (!STRCASECMP(str, kArgoWrem2Str)) { + return argo_ac_remote_model_t::SAC_WREM2; + } else if (!STRCASECMP(str, kArgoWrem3Str)) { + return argo_ac_remote_model_t::SAC_WREM3; + } else { + int16_t number = atoi(str); + if (number > 0) + return number; + else + return def; + } +} + +/// Convert the supplied str into the appropriate boolean value. +/// @param[in] str A Ptr to a C-style string to be converted. +/// @param[in] def The boolean value to return if no conversion was possible. +/// @return The equivalent boolean value. +bool IRac::strToBool(const char *str, const bool def) { + if (!STRCASECMP(str, kOnStr) || + !STRCASECMP(str, k1Str) || + !STRCASECMP(str, kYesStr) || + !STRCASECMP(str, kTrueStr)) + return true; + else if (!STRCASECMP(str, kOffStr) || + !STRCASECMP(str, k0Str) || + !STRCASECMP(str, kNoStr) || + !STRCASECMP(str, kFalseStr)) + return false; + else + return def; +} + +/// Convert the supplied boolean into the appropriate String. +/// @param[in] value The boolean value to be converted. +/// @return The equivalent String for the locale. +String IRac::boolToString(const bool value) { + return value ? kOnStr : kOffStr; +} + +/// Convert the supplied operation mode into the appropriate String. +/// @param[in] cmdType The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::commandTypeToString(const stdAc::ac_command_t cmdType) { + switch (cmdType) { + case stdAc::ac_command_t::kControlCommand: return kControlCommandStr; + case stdAc::ac_command_t::kSensorTempReport: return kIFeelReportStr; + case stdAc::ac_command_t::kTimerCommand: return kSetTimerCommandStr; + case stdAc::ac_command_t::kConfigCommand: return kConfigCommandStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied operation mode into the appropriate String. +/// @param[in] mode The enum to be converted. +/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output. +/// @return The equivalent String for the locale. +String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) { + switch (mode) { + case stdAc::opmode_t::kOff: return kOffStr; + case stdAc::opmode_t::kAuto: return kAutoStr; + case stdAc::opmode_t::kCool: return kCoolStr; + case stdAc::opmode_t::kHeat: return kHeatStr; + case stdAc::opmode_t::kDry: return kDryStr; + case stdAc::opmode_t::kFan: return ha ? kFan_OnlyStr : kFanStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied fan speed enum into the appropriate String. +/// @param[in] speed The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: return kAutoStr; + case stdAc::fanspeed_t::kMax: return kMaxStr; + case stdAc::fanspeed_t::kHigh: return kHighStr; + case stdAc::fanspeed_t::kMedium: return kMediumStr; + case stdAc::fanspeed_t::kMediumHigh: return kMedHighStr; + case stdAc::fanspeed_t::kLow: return kLowStr; + case stdAc::fanspeed_t::kMin: return kMinStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied enum into the appropriate String. +/// @param[in] swingv The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: return kOffStr; + case stdAc::swingv_t::kAuto: return kAutoStr; + case stdAc::swingv_t::kHighest: return kHighestStr; + case stdAc::swingv_t::kHigh: return kHighStr; + case stdAc::swingv_t::kMiddle: return kMiddleStr; + case stdAc::swingv_t::kUpperMiddle: return kUpperMiddleStr; + case stdAc::swingv_t::kLow: return kLowStr; + case stdAc::swingv_t::kLowest: return kLowestStr; + default: return kUnknownStr; + } +} + +/// Convert the supplied enum into the appropriate String. +/// @param[in] swingh The enum to be converted. +/// @return The equivalent String for the locale. +String IRac::swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: return kOffStr; + case stdAc::swingh_t::kAuto: return kAutoStr; + case stdAc::swingh_t::kLeftMax: return kLeftMaxStr; + case stdAc::swingh_t::kLeft: return kLeftStr; + case stdAc::swingh_t::kMiddle: return kMiddleStr; + case stdAc::swingh_t::kRight: return kRightStr; + case stdAc::swingh_t::kRightMax: return kRightMaxStr; + case stdAc::swingh_t::kWide: return kWideStr; + default: return kUnknownStr; + } +} + +namespace IRAcUtils { + /// Display the human readable state of an A/C message if we can. + /// @param[in] result A Ptr to the captured `decode_results` that contains an + /// A/C mesg. + /// @return A string with the human description of the A/C message. + /// An empty string if we can't. + String resultAcToString(const decode_results * const result) { + switch (result->decode_type) { +#if DECODE_AIRTON + case decode_type_t::AIRTON: { + IRAirtonAc ac(kGpioUnused); + ac.setRaw(result->value); // AIRTON uses value instead of state. + return ac.toString(); + } +#endif // DECODE_AIRTON +#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(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + if (IRArgoAC_WREM3::isValidWrem3Message(result->state, result->bits, + true)) { + IRArgoAC_WREM3 ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } + IRArgoAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_ARGO +#if DECODE_BOSCH144 + case decode_type_t::BOSCH144: { + IRBosch144AC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_BOSCH144 +#if DECODE_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: { + IRCarrierAc64 ac(kGpioUnused); + ac.setRaw(result->value); // CARRIER_AC64 uses value instead of state. + return ac.toString(); + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.on(); + ac.setRaw(result->value); // Coolix uses value instead of state. + return ac.toString(); + } +#endif // DECODE_COOLIX +#if DECODE_CORONA_AC + case decode_type_t::CORONA_AC: { + IRCoronaAc ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_CORONA_AC +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN128 + case decode_type_t::DAIKIN128: { + IRDaikin128 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + case decode_type_t::DAIKIN152: { + IRDaikin152 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(result->value); // Daikin64 uses value instead of state. + return ac.toString(); + } +#endif // DECODE_DAIKIN64 +#if DECODE_DELONGHI_AC + case decode_type_t::DELONGHI_AC: { + IRDelonghiAc ac(kGpioUnused); + ac.setRaw(result->value); // DelonghiAc uses value instead of state. + return ac.toString(); + } +#endif // DECODE_DELONGHI_AC +#if DECODE_ECOCLIM + case decode_type_t::ECOCLIM: { + if (result->bits == kEcoclimBits) { + IREcoclimAc ac(kGpioUnused); + ac.setRaw(result->value); // EcoClim uses value instead of state. + return ac.toString(); + } + return ""; + } +#endif // DECODE_ECOCLIM +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_FUJITSU_AC +#if DECODE_FUJITSU_AC264 + case decode_type_t::FUJITSU_AC264: { + IRFujitsuAC264 ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_FUJITSU_AC264 +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(result->value); // Goodweather uses value instead of state. + return ac.toString(); + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC160 + case decode_type_t::HAIER_AC160: { + IRHaierAC160 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC160 +#if DECODE_HAIER_AC176 + case decode_type_t::HAIER_AC176: { + IRHaierAC176 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC176 +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC_YRW02 +#if DECODE_HITACHI_AC + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: { + IRHitachiAc344 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: { + IRHitachiAc424 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC424 +#if DECODE_KELON + case decode_type_t::KELON: { + IRKelonAc ac(kGpioUnused); + ac.setRaw(result->value); + return ac.toString(); + } +#endif // DECODE_KELON +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_KELVINATOR +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(result->value, result->decode_type); // Use value, not state. + return ac.isValidLgAc() ? ac.toString() : ""; + } +#endif // DECODE_LG +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(result->value); // Midea uses value instead of state. + return ac.toString(); + } +#endif // DECODE_MIDEA +#if DECODE_MIRAGE + case decode_type_t::MIRAGE: { + IRMirageAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MIRAGE +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI112 +#if DECODE_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: { + IRMitsubishi136 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI136 +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_NEOCLIMA +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + if (result->bits > kPanasonicAcShortBits) { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: { + if (result->bits >= kPanasonicAc32Bits) { + IRPanasonicAc32 ac(kGpioUnused); + ac.setRaw(result->value); // Uses value instead of state. + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_RHOSS + case decode_type_t::RHOSS: { + IRRhossAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_RHOSS +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + case decode_type_t::SANYO_AC88: { + IRSanyoAc88 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SANYO_AC88 +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SHARP_AC +#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) + case decode_type_t::TCL112AC: + case decode_type_t::TEKNOPOINT: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) +#if DECODE_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: { + IRTechnibelAc ac(kGpioUnused); + ac.setRaw(result->value); // TechnibelAc uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TECHNIBEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TRANSCOLD + case decode_type_t::TRANSCOLD: { + IRTranscoldAc ac(kGpioUnused); + ac.on(); + ac.setRaw(result->value); // TRANSCOLD uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TRANSCOLD +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + case decode_type_t::TROTEC_3550: { + IRTrotec3550 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC_3550 +#if DECODE_TRUMA + case decode_type_t::TRUMA: { + IRTrumaAc ac(kGpioUnused); + ac.setRaw(result->value); // Truma uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TRUMA +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_VOLTAS + case decode_type_t::VOLTAS: { + IRVoltas ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_VOLTAS +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_YORK + case decode_type_t::YORK: { + IRYorkAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_YORK + default: + return ""; + } + } + + /// Convert a valid IR A/C remote message that we understand enough into a + /// Common A/C state. + /// @param[in] decode A PTR to a successful raw IR decode object. + /// @param[in] result A PTR to a state structure to store the result in. + /// @param[in] prev A PTR to a state structure which has the prev. state. + /// @return A boolean indicating success or failure. + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev +/// @cond IGNORE +// *prev flagged as "unused" due to potential compiler warning when some +// protocols that use it are disabled. It really is used. + __attribute__((unused)) +/// @endcond + ) { + if (decode == NULL || result == NULL) return false; // Safety check. + switch (decode->decode_type) { +#if DECODE_AIRTON + case decode_type_t::AIRTON: { + IRAirtonAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_AIRTON +#if DECODE_AIRWELL + case decode_type_t::AIRWELL: { + IRAirwellAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_AIRWELL +#if DECODE_AMCOR + case decode_type_t::AMCOR: { + IRAmcorAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_AMCOR +#if DECODE_ARGO + case decode_type_t::ARGO: { + const uint16_t length = decode->bits / 8; + if (IRArgoAC_WREM3::isValidWrem3Message(decode->state, + decode->bits, true)) { + IRArgoAC_WREM3 ac(kGpioUnused); + ac.setRaw(decode->state, length); + *result = ac.toCommon(); + } else { + IRArgoAC ac(kGpioUnused); + switch (length) { + case kArgoStateLength: + case kArgoShortStateLength: + ac.setRaw(decode->state, length); + *result = ac.toCommon(); + break; + default: + return false; + } + } + break; + } +#endif // DECODE_ARGO +#if DECODE_BOSCH144 + case decode_type_t::BOSCH144: { + IRBosch144AC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_BOSCH144 +#if DECODE_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: { + IRCarrierAc64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_COOLIX +#if DECODE_CORONA_AC + case decode_type_t::CORONA_AC: { + IRCoronaAc ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_CARRIER_AC64 +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN128 + case decode_type_t::DAIKIN128: { + IRDaikin128 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + case decode_type_t::DAIKIN152: { + IRDaikin152 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN64 + case decode_type_t::DAIKIN64: { + IRDaikin64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_DAIKIN64 +#if DECODE_DELONGHI_AC + case decode_type_t::DELONGHI_AC: { + IRDelonghiAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_DELONGHI_AC +#if DECODE_ECOCLIM + case decode_type_t::ECOCLIM: { + if (decode->bits == kEcoclimBits) { + IREcoclimAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + } else { + return false; + } + break; + } +#endif // DECODE_ECOCLIM +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_FUJITSU_AC +#if DECODE_FUJITSU_AC264 + case decode_type_t::FUJITSU_AC264: { + IRFujitsuAC264 ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_FUJITSU_AC264 +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC160 + case decode_type_t::HAIER_AC160: { + IRHaierAC160 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_HAIER_AC160 +#if DECODE_HAIER_AC176 + case decode_type_t::HAIER_AC176: { + IRHaierAC176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC176 +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC_YRW02 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: { + IRHitachiAc344 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: { + IRHitachiAc424 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC424 +#if DECODE_KELON + case decode_type_t::KELON: { + IRKelonAc ac(kGpioUnused); + ac.setRaw(decode->value); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_KELON +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_KELVINATOR +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(decode->value, decode->decode_type); // Use value, not state. + if (!ac.isValidLgAc()) return false; + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_LG +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_MIDEA +#if DECODE_MIRAGE + case decode_type_t::MIRAGE: { + IRMirageAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MIRAGE +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI112 +#if DECODE_MITSUBISHI136 + case decode_type_t::MITSUBISHI136: { + IRMitsubishi136 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI136 +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_NEOCLIMA +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: { + IRPanasonicAc32 ac(kGpioUnused); + if (decode->bits >= kPanasonicAc32Bits) { + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + } else { + return false; + } + break; + } +#endif // DECODE_PANASONIC_AC32 +#if DECODE_RHOSS + case decode_type_t::RHOSS: { + IRRhossAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_RHOSS +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SANYO_AC + case decode_type_t::SANYO_AC: { + IRSanyoAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + case decode_type_t::SANYO_AC88: { + IRSanyoAc88 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SANYO_AC88 +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_SHARP_AC +#if (DECODE_TCL112AC || DECODE_TEKNOPOINT) + case decode_type_t::TCL112AC: + case decode_type_t::TEKNOPOINT: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + // Teknopoint uses the TCL protocol, but with a different model number. + // Just keep the original protocol type ... for now. + result->protocol = decode->decode_type; + break; + } +#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) +#if DECODE_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: { + IRTechnibelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECHNIBEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TRANSCOLD + case decode_type_t::TRANSCOLD: { + IRTranscoldAc ac(kGpioUnused); + ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_TRANSCOLD +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + case decode_type_t::TROTEC_3550: { + IRTrotec3550 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC_3550 +#if DECODE_TRUMA + case decode_type_t::TRUMA: { + IRTrumaAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TRUMA +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_VESTEL_AC +#if DECODE_VOLTAS + case decode_type_t::VOLTAS: { + IRVoltas ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_VOLTAS +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_YORK + case decode_type_t::YORK: { + IRYorkAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_YORK + default: + return false; + } + return true; + } +} // namespace IRAcUtils diff --git a/src/IRac.h b/src/IRac.h index 1a6ab7f32..a9ad3033d 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -1,591 +1,591 @@ -#ifndef IRAC_H_ -#define IRAC_H_ - -// Copyright 2019 David Conran - -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "ir_Airton.h" -#include "ir_Airwell.h" -#include "ir_Amcor.h" -#include "ir_Argo.h" -#include "ir_Bosch.h" -#include "ir_Carrier.h" -#include "ir_Coolix.h" -#include "ir_Corona.h" -#include "ir_Daikin.h" -#include "ir_Delonghi.h" -#include "ir_Fujitsu.h" -#include "ir_Ecoclim.h" -#include "ir_Electra.h" -#include "ir_Goodweather.h" -#include "ir_Gree.h" -#include "ir_Haier.h" -#include "ir_Hitachi.h" -#include "ir_Kelon.h" -#include "ir_Kelvinator.h" -#include "ir_LG.h" -#include "ir_Midea.h" -#include "ir_Mirage.h" -#include "ir_Mitsubishi.h" -#include "ir_MitsubishiHeavy.h" -#include "ir_Neoclima.h" -#include "ir_Panasonic.h" -#include "ir_Rhoss.h" -#include "ir_Samsung.h" -#include "ir_Sanyo.h" -#include "ir_Sharp.h" -#include "ir_Tcl.h" -#include "ir_Technibel.h" -#include "ir_Teco.h" -#include "ir_Toshiba.h" -#include "ir_Transcold.h" -#include "ir_Trotec.h" -#include "ir_Truma.h" -#include "ir_Vestel.h" -#include "ir_Voltas.h" -#include "ir_Whirlpool.h" -#include "ir_York.h" - -// Constants -const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO. - -// Class -/// A universal/common/generic interface for controling supported A/Cs. -class IRac { - public: - explicit IRac(const uint16_t pin, const bool inverted = false, - const bool use_modulation = true); - static bool isProtocolSupported(const decode_type_t protocol); - static void initState(stdAc::state_t *state, - const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, - const float degrees, const bool celsius, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep, - const int16_t clock); - static void initState(stdAc::state_t *state); - void markAsSent(void); - bool sendAc(void); - bool sendAc(const stdAc::state_t desired, const stdAc::state_t *prev = NULL); - bool sendAc(const decode_type_t vendor, const int16_t model, - const bool power, const stdAc::opmode_t mode, const float degrees, - const bool celsius, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep = -1, - const int16_t clock = -1); - static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b); - static bool strToBool(const char *str, const bool def = false); - static int16_t strToModel(const char *str, const int16_t def = -1); - static stdAc::ac_command_t strToCommandType(const char *str, - const stdAc::ac_command_t def = stdAc::ac_command_t::kControlCommand); - static stdAc::opmode_t strToOpmode( - const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); - static stdAc::fanspeed_t strToFanspeed( - const char *str, - const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); - static stdAc::swingv_t strToSwingV( - const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); - static stdAc::swingh_t strToSwingH( - const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); - static String boolToString(const bool value); - static String commandTypeToString(const stdAc::ac_command_t cmdType); - static String opmodeToString(const stdAc::opmode_t mode, - const bool ha = false); - static String fanspeedToString(const stdAc::fanspeed_t speed); - static String swingvToString(const stdAc::swingv_t swingv); - static String swinghToString(const stdAc::swingh_t swingh); - stdAc::state_t getState(void); - stdAc::state_t getStatePrev(void); - bool hasStateChanged(void); - stdAc::state_t next; ///< The state we want the device to be in after we send -#ifdef UNIT_TEST - /// @cond IGNORE - /// UT-specific - /// See @c OUTPUT_DECODE_RESULTS_FOR_UT macro description in IRac.cpp - std::shared_ptr _utReceiver = nullptr; - std::unique_ptr _lastDecodeResults = nullptr; - /// @endcond -#else - - private: -#endif // UNIT_TEST - uint16_t _pin; ///< The GPIO to use to transmit messages from. - 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_AIRTON - void airton(IRAirtonAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool light, const bool econo, const bool filter, - const int16_t sleep = -1); -#endif // SEND_AIRTON -#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, - const stdAc::fanspeed_t fan); -#endif // SEND_AMCOR -#if SEND_ARGO - void argo(IRArgoAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, const bool turbo, - const int16_t sleep = -1); - void argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, const bool night, - const bool econo, const bool turbo, const bool filter, const bool light); - void argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp); - void argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, - const uint8_t value, bool safe = true); - void argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, - const uint16_t currentTime, const uint16_t delayMinutes); -#endif // SEND_ARGO -#if SEND_BOSCH144 - void bosch144(IRBosch144AC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const bool quiet); -#endif // SEND_BOSCH144 -#if SEND_CARRIER_AC64 -void carrier64(IRCarrierAc64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep = -1); -#endif // SEND_CARRIER_AC64 -#if SEND_COOLIX - void coolix(IRCoolixAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool light, - const bool clean, const int16_t sleep = -1); -#endif // SEND_COOLIX -#if SEND_CORONA_AC - void corona(IRCoronaAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool econo); -#endif // SEND_CORONA_AC -#if SEND_DAIKIN - void daikin(IRDaikinESP *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool clean); -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - void daikin128(IRDaikin128 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool light, - const bool econo, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_DAIKIN128 -#if SEND_DAIKIN152 - void daikin152(IRDaikin152 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool econo); -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - void daikin160(IRDaikin160 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv); -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - void daikin176(IRDaikin176 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingh_t swingh); -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - void daikin2(IRDaikin2 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter, const bool clean, - const bool beep, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN216 -void daikin216(IRDaikin216 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo); -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN64 - void daikin64(IRDaikin64 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, - const int16_t sleep = -1, const int16_t clock = -1); -#endif // SEND_DAIKIN64 -#if SEND_DELONGHI_AC - void delonghiac(IRDelonghiAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const bool turbo, const int16_t sleep = -1); -#endif // SEND_DELONGHI_AC -#if SEND_ECOCLIM -void ecoclim(IREcoclimAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_ECOCLIM -#if SEND_ELECTRA_AC -void electra(IRElectraAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, const bool iFeel, const bool turbo, - const bool lighttoggle, const bool clean); -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool filter, const bool clean, const int16_t sleep = -1); -#endif // SEND_FUJITSU_AC -#if SEND_FUJITSU_AC264 - void fujitsu264(IRFujitsuAC264 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool econo, - const bool clean, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_FUJITSU_AC264 -#if SEND_GOODWEATHER - void goodweather(IRGoodweatherAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep = -1); -#endif // SEND_GOODWEATHER -#if SEND_GREE - void gree(IRGreeAC *ac, const gree_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool iFeel, const bool turbo, const bool econo, - const bool light, const bool clean, const int16_t sleep = -1); -#endif // SEND_GREE -#if SEND_HAIER_AC - void haier(IRHaierAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool filter, const int16_t sleep = -1, - const int16_t clock = -1); -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC160 - void haier160(IRHaierAC160 *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool quiet, const bool filter, - const bool clean, const bool light, const bool prevlight, - const int16_t sleep = -1); -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - void haier176(IRHaierAC176 *ac, - const haier_ac176_remote_model_t model, const bool on, - const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool quiet, const bool filter, - const int16_t sleep = -1); -#endif // SEND_HAIER_AC176 -#if SEND_HAIER_AC_YRW02 - void haierYrwo2(IRHaierACYRW02 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, const bool turbo, - const bool quiet, const bool filter, - const int16_t sleep = -1); -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HITACHI_AC - void hitachi(IRHitachiAc *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - void hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, - const bool on, const bool power_toggle, - const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool swing_toggle, const int16_t sleep = -1); -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC264 - void hitachi264(IRHitachiAc264 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan); -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - void hitachi296(IRHitachiAc296 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan); -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - void hitachi344(IRHitachiAc344 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh); -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - void hitachi424(IRHitachiAc424 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv); -#endif // SEND_HITACHI_AC424 -#if SEND_KELON - void kelon(IRKelonAc *ac, const bool togglePower, const stdAc::opmode_t mode, - const int8_t dryGrade, const float degrees, - const stdAc::fanspeed_t fan, const bool toggleSwing, - const bool superCool, const int16_t sleep); -#endif // SEND_KELON -#if SEND_KELVINATOR - void kelvinator(IRKelvinatorAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool filter, const bool clean); -#endif // SEND_KELVINATOR -#if SEND_LG - void lg(IRLgAc *ac, const lg_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, - const stdAc::swingh_t swingh, const bool light); -#endif // SEND_LG -#if SEND_MIDEA - void midea(IRMideaAC *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const float sensorTemp, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool iFeel, const bool quiet, const bool quiet_prev, - const bool turbo, const bool econo, const bool light, - const bool clean, const int16_t sleep = -1); -#endif // SEND_MIDEA -#if SEND_MIRAGE - void mirage(IRMirageAc *ac, const stdAc::state_t state); -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - void mitsubishi(IRMitsubishiAC *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const int16_t clock = -1); -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI112 - void mitsubishi112(IRMitsubishi112 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet); -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHI136 - void mitsubishi136(IRMitsubishi136 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool quiet); -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHIHEAVY - void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool clean); - void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool filter, const bool clean, - const int16_t sleep = -1); -#endif // SEND_MITSUBISHIHEAVY -#if SEND_NEOCLIMA - void neoclima(IRNeoclimaAc *ac, const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const bool filter, const int16_t sleep = -1); -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool filter, - const int16_t clock = -1); -#endif // SEND_PANASONIC_AC -#if SEND_PANASONIC_AC32 - void panasonic32(IRPanasonicAc32 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); -#endif // SEND_PANASONIC_AC32 -#if SEND_RHOSS - void rhoss(IRRhossAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swing); -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - void samsung(IRSamsungAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool econo, - const bool light, const bool filter, const bool clean, - const bool beep, const int16_t sleep = -1, - const bool prevpower = true, const int16_t prevsleep = -1, - const bool forceextended = true); -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - void sanyo(IRSanyoAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const float sensorTemp, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool iFeel, const bool beep, - const int16_t sleep = -1); -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - void sanyo88(IRSanyoAc88 *ac, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool filter, - const int16_t sleep = -1, const int16_t clock = -1); -#endif // SEND_SANYO_AC88 -#if SEND_SHARP_AC - void sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, - const bool on, const bool prev_power, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, - const bool turbo, const bool light, - const bool filter, const bool clean); -#endif // SEND_SHARP_AC -#if SEND_TCL112AC - void tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet, const bool turbo, const bool light, - const bool econo, const bool filter); -#endif // SEND_TCL112AC -#if SEND_TECHNIBEL_AC - void technibel(IRTechnibelAc *ac, - const bool on, const stdAc::opmode_t mode, const bool celsius, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const int16_t sleep = -1); -#endif // SEND_TECHNIBEL_AC -#if SEND_TECO - void teco(IRTecoAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool light, const int16_t sleep = -1); -#endif // SEND_TECO -#if SEND_TOSHIBA_AC - void toshiba(IRToshibaAC *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool econo, const bool filter); -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - void trotec(IRTrotecESP *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const int16_t sleep = -1); -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - void trotec3550(IRTrotec3550 *ac, - const bool on, const stdAc::opmode_t mode, - const bool celsius, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv); -#endif // SEND_TROTEC_3550 -#if SEND_TRUMA - void truma(IRTrumaAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const bool quiet); -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - void vestel(IRVestelAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool filter, - const int16_t sleep = -1, const int16_t clock = -1, - const bool sendNormal = true); -#endif // SEND_VESTEL_AC -#if SEND_VOLTAS - void voltas(IRVoltas *ac, const voltas_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool turbo, const bool econo, const bool light, - const int16_t sleep = -1); -#endif // SEND_VOLTAS -#if SEND_WHIRLPOOL_AC - void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool light, - const int16_t sleep = -1, const int16_t clock = -1); -#endif // SEND_WHIRLPOOL_AC -#if SEND_TRANSCOLD - void transcold(IRTranscoldAc *ac, - const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); -#endif // SEND_TRANSCOLD -static stdAc::state_t cleanState(const stdAc::state_t state); -static stdAc::state_t handleToggles(const stdAc::state_t desired, - const stdAc::state_t *prev = NULL); -}; // IRac class - -/// Common functions for use with all A/Cs supported by the IRac class. -namespace IRAcUtils { - String resultAcToString(const decode_results * const results); - bool decodeToState(const decode_results *decode, stdAc::state_t *result, - const stdAc::state_t *prev = NULL); -} // namespace IRAcUtils -#endif // IRAC_H_ +#ifndef IRAC_H_ +#define IRAC_H_ + +// Copyright 2019 David Conran + +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "ir_Airton.h" +#include "ir_Airwell.h" +#include "ir_Amcor.h" +#include "ir_Argo.h" +#include "ir_Bosch.h" +#include "ir_Carrier.h" +#include "ir_Coolix.h" +#include "ir_Corona.h" +#include "ir_Daikin.h" +#include "ir_Delonghi.h" +#include "ir_Fujitsu.h" +#include "ir_Ecoclim.h" +#include "ir_Electra.h" +#include "ir_Goodweather.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelon.h" +#include "ir_Kelvinator.h" +#include "ir_LG.h" +#include "ir_Midea.h" +#include "ir_Mirage.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.h" +#include "ir_Panasonic.h" +#include "ir_Rhoss.h" +#include "ir_Samsung.h" +#include "ir_Sanyo.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Technibel.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Transcold.h" +#include "ir_Trotec.h" +#include "ir_Truma.h" +#include "ir_Vestel.h" +#include "ir_Voltas.h" +#include "ir_Whirlpool.h" +#include "ir_York.h" + +// Constants +const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO. + +// Class +/// A universal/common/generic interface for controling supported A/Cs. +class IRac { + public: + explicit IRac(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + static bool isProtocolSupported(const decode_type_t protocol); + static void initState(stdAc::state_t *state, + const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, + const float degrees, const bool celsius, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep, + const int16_t clock); + static void initState(stdAc::state_t *state); + void markAsSent(void); + bool sendAc(void); + bool sendAc(const stdAc::state_t desired, const stdAc::state_t *prev = NULL); + bool sendAc(const decode_type_t vendor, const int16_t model, + const bool power, const stdAc::opmode_t mode, const float degrees, + const bool celsius, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); + static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b); + static bool strToBool(const char *str, const bool def = false); + static int16_t strToModel(const char *str, const int16_t def = -1); + static stdAc::ac_command_t strToCommandType(const char *str, + const stdAc::ac_command_t def = stdAc::ac_command_t::kControlCommand); + static stdAc::opmode_t strToOpmode( + const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); + static stdAc::fanspeed_t strToFanspeed( + const char *str, + const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto); + static stdAc::swingv_t strToSwingV( + const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff); + static stdAc::swingh_t strToSwingH( + const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); + static String boolToString(const bool value); + static String commandTypeToString(const stdAc::ac_command_t cmdType); + static String opmodeToString(const stdAc::opmode_t mode, + const bool ha = false); + static String fanspeedToString(const stdAc::fanspeed_t speed); + static String swingvToString(const stdAc::swingv_t swingv); + static String swinghToString(const stdAc::swingh_t swingh); + stdAc::state_t getState(void); + stdAc::state_t getStatePrev(void); + bool hasStateChanged(void); + stdAc::state_t next; ///< The state we want the device to be in after we send +#ifdef UNIT_TEST + /// @cond IGNORE + /// UT-specific + /// See @c OUTPUT_DECODE_RESULTS_FOR_UT macro description in IRac.cpp + std::shared_ptr _utReceiver = nullptr; + std::unique_ptr _lastDecodeResults = nullptr; + /// @endcond +#else + + private: +#endif // UNIT_TEST + uint16_t _pin; ///< The GPIO to use to transmit messages from. + 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_AIRTON + void airton(IRAirtonAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool light, const bool econo, const bool filter, + const int16_t sleep = -1); +#endif // SEND_AIRTON +#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, + const stdAc::fanspeed_t fan); +#endif // SEND_AMCOR +#if SEND_ARGO + void argo(IRArgoAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, const bool turbo, + const int16_t sleep = -1); + void argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, const bool night, + const bool econo, const bool turbo, const bool filter, const bool light); + void argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp); + void argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param, + const uint8_t value, bool safe = true); + void argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on, + const uint16_t currentTime, const uint16_t delayMinutes); +#endif // SEND_ARGO +#if SEND_BOSCH144 + void bosch144(IRBosch144AC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const bool quiet); +#endif // SEND_BOSCH144 +#if SEND_CARRIER_AC64 +void carrier64(IRCarrierAc64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep = -1); +#endif // SEND_CARRIER_AC64 +#if SEND_COOLIX + void coolix(IRCoolixAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool light, + const bool clean, const int16_t sleep = -1); +#endif // SEND_COOLIX +#if SEND_CORONA_AC + void corona(IRCoronaAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool econo); +#endif // SEND_CORONA_AC +#if SEND_DAIKIN + void daikin(IRDaikinESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool clean); +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + void daikin128(IRDaikin128 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool light, + const bool econo, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + void daikin152(IRDaikin152 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo); +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + void daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + void daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh); +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + void daikin2(IRDaikin2 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 +void daikin216(IRDaikin216 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo); +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN64 + void daikin64(IRDaikin64 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_DAIKIN64 +#if SEND_DELONGHI_AC + void delonghiac(IRDelonghiAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const bool turbo, const int16_t sleep = -1); +#endif // SEND_DELONGHI_AC +#if SEND_ECOCLIM +void ecoclim(IREcoclimAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_ECOCLIM +#if SEND_ELECTRA_AC +void electra(IRElectraAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool iFeel, const bool turbo, + const bool lighttoggle, const bool clean); +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, const int16_t sleep = -1); +#endif // SEND_FUJITSU_AC +#if SEND_FUJITSU_AC264 + void fujitsu264(IRFujitsuAC264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool quiet, const bool turbo, const bool econo, + const bool clean, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_FUJITSU_AC264 +#if SEND_GOODWEATHER + void goodweather(IRGoodweatherAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep = -1); +#endif // SEND_GOODWEATHER +#if SEND_GREE + void gree(IRGreeAC *ac, const gree_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool iFeel, const bool turbo, const bool econo, + const bool light, const bool clean, const int16_t sleep = -1); +#endif // SEND_GREE +#if SEND_HAIER_AC + void haier(IRHaierAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool filter, const int16_t sleep = -1, + const int16_t clock = -1); +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC160 + void haier160(IRHaierAC160 *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool quiet, const bool filter, + const bool clean, const bool light, const bool prevlight, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + void haier176(IRHaierAC176 *ac, + const haier_ac176_remote_model_t model, const bool on, + const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC_YRW02 + void haierYrwo2(IRHaierACYRW02 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool turbo, + const bool quiet, const bool filter, + const int16_t sleep = -1); +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + void hitachi(IRHitachiAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + void hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, + const bool on, const bool power_toggle, + const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool swing_toggle, const int16_t sleep = -1); +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + void hitachi264(IRHitachiAc264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + void hitachi344(IRHitachiAc344 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh); +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + void hitachi424(IRHitachiAc424 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv); +#endif // SEND_HITACHI_AC424 +#if SEND_KELON + void kelon(IRKelonAc *ac, const bool togglePower, const stdAc::opmode_t mode, + const int8_t dryGrade, const float degrees, + const stdAc::fanspeed_t fan, const bool toggleSwing, + const bool superCool, const int16_t sleep); +#endif // SEND_KELON +#if SEND_KELVINATOR + void kelvinator(IRKelvinatorAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool filter, const bool clean); +#endif // SEND_KELVINATOR +#if SEND_LG + void lg(IRLgAc *ac, const lg_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, + const stdAc::swingh_t swingh, const bool light); +#endif // SEND_LG +#if SEND_MIDEA + void midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const float sensorTemp, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool iFeel, const bool quiet, const bool quiet_prev, + const bool turbo, const bool econo, const bool light, + const bool clean, const int16_t sleep = -1); +#endif // SEND_MIDEA +#if SEND_MIRAGE + void mirage(IRMirageAc *ac, const stdAc::state_t state); +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + void mitsubishi(IRMitsubishiAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const int16_t clock = -1); +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 + void mitsubishi112(IRMitsubishi112 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet); +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHI136 + void mitsubishi136(IRMitsubishi136 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool quiet); +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHIHEAVY + void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool clean); + void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool filter, const bool clean, + const int16_t sleep = -1); +#endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + void neoclima(IRNeoclimaAc *ac, const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const bool filter, const int16_t sleep = -1); +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool filter, + const int16_t clock = -1); +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC32 + void panasonic32(IRPanasonicAc32 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_PANASONIC_AC32 +#if SEND_RHOSS + void rhoss(IRRhossAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swing); +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + void samsung(IRSamsungAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const bool prevpower = true, const int16_t prevsleep = -1, + const bool forceextended = true); +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + void sanyo(IRSanyoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const float sensorTemp, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool iFeel, const bool beep, + const int16_t sleep = -1); +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + void sanyo88(IRSanyoAc88 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool filter, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_SANYO_AC88 +#if SEND_SHARP_AC + void sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model, + const bool on, const bool prev_power, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev, + const bool turbo, const bool light, + const bool filter, const bool clean); +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + void tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool light, + const bool econo, const bool filter); +#endif // SEND_TCL112AC +#if SEND_TECHNIBEL_AC + void technibel(IRTechnibelAc *ac, + const bool on, const stdAc::opmode_t mode, const bool celsius, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const int16_t sleep = -1); +#endif // SEND_TECHNIBEL_AC +#if SEND_TECO + void teco(IRTecoAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool light, const int16_t sleep = -1); +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + void toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool econo, const bool filter); +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + void trotec(IRTrotecESP *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep = -1); +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + void trotec3550(IRTrotec3550 *ac, + const bool on, const stdAc::opmode_t mode, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv); +#endif // SEND_TROTEC_3550 +#if SEND_TRUMA + void truma(IRTrumaAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const bool quiet); +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + void vestel(IRVestelAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool filter, + const int16_t sleep = -1, const int16_t clock = -1, + const bool sendNormal = true); +#endif // SEND_VESTEL_AC +#if SEND_VOLTAS + void voltas(IRVoltas *ac, const voltas_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const int16_t sleep = -1); +#endif // SEND_VOLTAS +#if SEND_WHIRLPOOL_AC + void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep = -1, const int16_t clock = -1); +#endif // SEND_WHIRLPOOL_AC +#if SEND_TRANSCOLD + void transcold(IRTranscoldAc *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh); +#endif // SEND_TRANSCOLD +static stdAc::state_t cleanState(const stdAc::state_t state); +static stdAc::state_t handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev = NULL); +}; // IRac class + +/// Common functions for use with all A/Cs supported by the IRac class. +namespace IRAcUtils { + String resultAcToString(const decode_results * const results); + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev = NULL); +} // namespace IRAcUtils +#endif // IRAC_H_ diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 80c240cbb..3cf8cc7e8 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1,2089 +1,2089 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2015 Sebastien Warin -// Copyright 2017, 2019 David Conran - -#include "IRrecv.h" -#include -#ifndef UNIT_TEST -#if defined(ESP8266) -extern "C" { -#include -#include -} -#endif // ESP8266 -#include -#endif // UNIT_TEST -#include -#ifdef UNIT_TEST -#include -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "IRutils.h" - -#ifdef UNIT_TEST -#undef ICACHE_RAM_ATTR -#define ICACHE_RAM_ATTR -#endif - -#ifndef USE_IRAM_ATTR -#if defined(ESP8266) -#if defined(IRAM_ATTR) -#define USE_IRAM_ATTR IRAM_ATTR -#else // IRAM_ATTR -#define USE_IRAM_ATTR ICACHE_RAM_ATTR -#endif // IRAM_ATTR -#endif // ESP8266 -#if defined(ESP32) -#define USE_IRAM_ATTR IRAM_ATTR -#endif // ESP32 -#endif // USE_IRAM_ATTR - -#define ONCE 0 - -// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR -// code on ESP32 -// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code -// on ESP8266 -// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for -// sending IR code on ESP8266 - -// Globals -#ifndef UNIT_TEST -#if defined(ESP8266) -namespace _IRrecv { -static ETSTimer timer; -} // namespace _IRrecv -#endif // ESP8266 -#if defined(ESP32) -// We need a horrible timer hack for ESP32 Arduino framework < v2.0.0 -#if !defined(_ESP32_IRRECV_TIMER_HACK) -// Version check -#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) -// No need for the hack if we are running version >= 2.0.0 -#define _ESP32_IRRECV_TIMER_HACK false -#else // Version check -// If no ESP_ARDUINO_VERSION_MAJOR is defined, or less than 2, then we are -// using an old ESP32 core, so we need the hack. -#define _ESP32_IRRECV_TIMER_HACK true -#endif // Version check -#endif // !defined(_ESP32_IRRECV_TIMER_HACK) - -#if _ESP32_IRRECV_TIMER_HACK -// Required structs/types from: -// https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58 -// These are needed to be able to directly manipulate the timer registers from -// inside an ISR. This is very very ugly. -// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 -// Note: This will need to be updated if it ever changes. -// -// Start of Horrible Hack! -typedef struct { - union { - struct { - uint32_t reserved0: 10; - uint32_t alarm_en: 1; - /*When set alarm is enabled*/ - uint32_t level_int_en: 1; - /*When set level type interrupt will be generated during alarm*/ - uint32_t edge_int_en: 1; - /*When set edge type interrupt will be generated during alarm*/ - uint32_t divider: 16; - /*Timer clock (T0/1_clk) pre-scale value.*/ - uint32_t autoreload: 1; - /*When set timer 0/1 auto-reload at alarming is enabled*/ - uint32_t increase: 1; - /*When set timer 0/1 time-base counter increment. - When cleared timer 0 time-base counter decrement.*/ - uint32_t enable: 1; - /*When set timer 0/1 time-base counter is enabled*/ - }; - uint32_t val; - } config; - uint32_t cnt_low; - /*Register to store timer 0/1 time-base counter current value lower 32 - bits.*/ - uint32_t cnt_high; - /*Register to store timer 0 time-base counter current value higher 32 - bits.*/ - uint32_t update; - /*Write any value will trigger a timer 0 time-base counter value update - (timer 0 current value will be stored in registers above)*/ - uint32_t alarm_low; - /*Timer 0 time-base counter value lower 32 bits that will trigger the - alarm*/ - uint32_t alarm_high; - /*Timer 0 time-base counter value higher 32 bits that will trigger the - alarm*/ - uint32_t load_low; - /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ - uint32_t load_high; - /*higher 32 bits of the value that will load into timer 0 time-base - counter*/ - uint32_t reload; - /*Write any value will trigger timer 0 time-base counter reload*/ -} hw_timer_reg_t; - -typedef struct hw_timer_s { - hw_timer_reg_t * dev; - uint8_t num; - uint8_t group; - uint8_t timer; - portMUX_TYPE lock; -} hw_timer_t; -#endif // _ESP32_IRRECV_TIMER_HACK / End of Horrible Hack. - -namespace _IRrecv { -static hw_timer_t * timer = NULL; -} // namespace _IRrecv -#endif // ESP32 -using _IRrecv::timer; -#endif // UNIT_TEST - -namespace _IRrecv { // Namespace extension -#if defined(ESP32) -portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; -#endif // ESP32 -volatile irparams_t params; -irparams_t *params_save; // A copy of the interrupt state while decoding. -} // namespace _IRrecv - -#if defined(ESP32) -using _IRrecv::mux; -#endif // ESP32 -using _IRrecv::params; -using _IRrecv::params_save; - -#ifndef UNIT_TEST -#if defined(ESP8266) -/// Interrupt handler for when the timer runs out. -/// It signals to the library that capturing of IR data has stopped. -/// @param[in] arg Unused. (ESP8266 Only) -static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { - os_intr_lock(); -#endif // ESP8266 -/// @cond IGNORE -#if defined(ESP32) -/// Interrupt handler for when the timer runs out. -/// It signals to the library that capturing of IR data has stopped. -/// @note ESP32 version -static void USE_IRAM_ATTR read_timeout(void) { -/// @endcond - portENTER_CRITICAL(&mux); -#endif // ESP32 - if (params.rawlen) params.rcvstate = kStopState; -#if defined(ESP8266) - os_intr_unlock(); -#endif // ESP8266 -#if defined(ESP32) - portEXIT_CRITICAL(&mux); -#endif // ESP32 -} - -/// Interrupt handler for changes on the GPIO pin handling incoming IR messages. -static void USE_IRAM_ATTR gpio_intr() { - uint32_t now = micros(); - static uint32_t start = 0; - -#if defined(ESP8266) - uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); - os_timer_disarm(&timer); - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); -#endif // ESP8266 - - // Grab a local copy of rawlen to reduce instructions used in IRAM. - // This is an ugly premature optimisation code-wise, but we do everything we - // can to save IRAM. - // It seems referencing the value via the structure uses more instructions. - // Less instructions means faster and less IRAM used. - // N.B. It saves about 13 bytes of IRAM. - uint16_t rawlen = params.rawlen; - - if (rawlen >= params.bufsize) { - params.overflow = true; - params.rcvstate = kStopState; - } - - if (params.rcvstate == kStopState) return; - - if (params.rcvstate == kIdleState) { - params.rcvstate = kMarkState; - params.rawbuf[rawlen] = 1; - } else { - if (now < start) - params.rawbuf[rawlen] = (UINT32_MAX - start + now) / kRawTick; - else - params.rawbuf[rawlen] = (now - start) / kRawTick; - } - params.rawlen++; - - start = now; - -#if defined(ESP8266) - os_timer_arm(&timer, params.timeout, ONCE); -#endif // ESP8266 -#if defined(ESP32) - // Reset the timeout. - // -#if _ESP32_IRRECV_TIMER_HACK - // The following three lines of code are the equiv of: - // `timerWrite(timer, 0);` - // We can't call that routine safely from inside an ISR as that procedure - // is not stored in IRAM. Hence, we do it manually so that it's covered by - // USE_IRAM_ATTR in this ISR. - // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 - // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L106-L110 - timer->dev->load_high = (uint32_t) 0; - timer->dev->load_low = (uint32_t) 0; - timer->dev->reload = 1; - // The next line is the same, but instead replaces: - // `timerAlarmEnable(timer);` - // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 - // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178 - timer->dev->config.alarm_en = 1; -#else // _ESP32_IRRECV_TIMER_HACK - timerWrite(timer, 0); - timerAlarmEnable(timer); -#endif // _ESP32_IRRECV_TIMER_HACK -#endif // ESP32 -} -#endif // UNIT_TEST - -// Start of IRrecv class ------------------- - -/// Class constructor -/// Args: -/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is -/// connected to. -/// @param[in] bufsize Nr. of entries to have in the capture buffer. -/// (Default: kRawBuf) -/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop -/// capturing data. (Default: kTimeoutMs) -/// @param[in] save_buffer Use a second (save) buffer to decode from. -/// (Default: false) -/// @param[in] timer_num Nr. of the ESP32 timer to use. (0 to 3) (ESP32 Only) -/// or (0 to 1) (ESP32-C3) -#if defined(ESP32) -IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, - const uint8_t timeout, const bool save_buffer, - const uint8_t timer_num) { - // Ensure we use a valid timer number. - _timer_num = std::min(timer_num, - (uint8_t)( -#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS - SOC_TIMER_GROUP_TOTAL_TIMERS - 1)); -#else // SOC_TIMER_GROUP_TOTAL_TIMERS - 3)); -#endif // SOC_TIMER_GROUP_TOTAL_TIMERS -#else // ESP32 -/// @cond IGNORE -/// Class constructor -/// Args: -/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is -/// connected to. -/// @param[in] bufsize Nr. of entries to have in the capture buffer. -/// (Default: kRawBuf) -/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop -/// capturing data. (Default: kTimeoutMs) -/// @param[in] save_buffer Use a second (save) buffer to decode from. -/// (Default: false) -IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, - const uint8_t timeout, const bool save_buffer) { -/// @endcond -#endif // ESP32 - params.recvpin = recvpin; - params.bufsize = bufsize; - // Ensure we are going to be able to store all possible values in the - // capture buffer. - params.timeout = std::min(timeout, (uint8_t)kMaxTimeoutMs); - params.rawbuf = new uint16_t[bufsize]; - if (params.rawbuf == NULL) { - DPRINTLN( - "Could not allocate memory for the primary IR buffer.\n" - "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); -#ifndef UNIT_TEST - ESP.restart(); // Mem alloc failure. Reboot. -#endif - } - // If we have been asked to use a save buffer (for decoding), then create one. - if (save_buffer) { - params_save = new irparams_t; - params_save->rawbuf = new uint16_t[bufsize]; - // Check we allocated the memory successfully. - if (params_save->rawbuf == NULL) { - DPRINTLN( - "Could not allocate memory for the second IR buffer.\n" - "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); -#ifndef UNIT_TEST - ESP.restart(); // Mem alloc failure. Reboot. -#endif - } - } else { - params_save = NULL; - } -#if DECODE_HASH - _unknown_threshold = kUnknownThreshold; -#endif // DECODE_HASH - _tolerance = kTolerance; -} - -/// Class destructor -/// Cleans up after the object is no longer needed. -/// e.g. Frees up all memory used by the various buffers, and disables any -/// timers or interrupts used. -IRrecv::~IRrecv(void) { - disableIRIn(); -#if defined(ESP32) - if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. -#endif // ESP32 - delete[] params.rawbuf; - if (params_save != NULL) { - delete[] params_save->rawbuf; - delete params_save; - } -} - -/// Set up and (re)start the IR capture mechanism. -/// @param[in] pullup A flag indicating should the GPIO use the internal pullup -/// resistor. (Default: `false`. i.e. No.) -void IRrecv::enableIRIn(const bool pullup) { - // ESP32's seem to require explicitly setting the GPIO to INPUT etc. - // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. - if (pullup) { -#ifndef UNIT_TEST - pinMode(params.recvpin, INPUT_PULLUP); - } else { - pinMode(params.recvpin, INPUT); -#endif // UNIT_TEST - } -#if defined(ESP32) - // Initialise the ESP32 timer. - // 80MHz / 80 = 1 uSec granularity. - timer = timerBegin(_timer_num, 80, true); -#ifdef DEBUG - if (timer == NULL) { - DPRINT("FATAL: Unable enable system timer: "); - DPRINTLN((uint16_t)_timer_num); - } -#endif // DEBUG - assert(timer != NULL); // Check we actually got the timer. - // Set the timer so it only fires once, and set it's trigger in uSeconds. - timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); - // Note: Interrupt needs to be attached before it can be enabled or disabled. - // Note: EDGE (true) is not supported, use LEVEL (false). Ref: #1713 - // See: https://github.com/espressif/arduino-esp32/blob/caef4006af491130136b219c1205bdcf8f08bf2b/cores/esp32/esp32-hal-timer.c#L224-L227 - timerAttachInterrupt(timer, &read_timeout, false); -#endif // ESP32 - - // Initialise state machine variables - resume(); - -#ifndef UNIT_TEST -#if defined(ESP8266) - // Initialise ESP8266 timer. - os_timer_disarm(&timer); - os_timer_setfn(&timer, reinterpret_cast(read_timeout), - NULL); -#endif // ESP8266 - // Attach Interrupt - attachInterrupt(params.recvpin, gpio_intr, CHANGE); -#endif // UNIT_TEST -} - -/// Stop collection of any received IR data. -/// Disable any timers and interrupts. -void IRrecv::disableIRIn(void) { -#ifndef UNIT_TEST -#if defined(ESP8266) - os_timer_disarm(&timer); -#endif // ESP8266 -#if defined(ESP32) - timerAlarmDisable(timer); - timerDetachInterrupt(timer); - timerEnd(timer); -#endif // ESP32 - detachInterrupt(params.recvpin); -#endif // UNIT_TEST -} - -/// Pause collection of received IR data. -/// @see IRrecv class constructor -void IRrecv::pause(void) { - params.rcvstate = kStopState; - params.rawlen = 0; - params.overflow = false; -#if defined(ESP32) - gpio_intr_disable((gpio_num_t)params.recvpin); -#endif // ESP32 -} - -/// Resume collection of received IR data. -/// @note This is required if `decode()` is successful and `save_buffer` was -/// not set when the class was instanciated. -/// @see IRrecv class constructor -void IRrecv::resume(void) { - params.rcvstate = kIdleState; - params.rawlen = 0; - params.overflow = false; -#if defined(ESP32) - timerAlarmDisable(timer); - gpio_intr_enable((gpio_num_t)params.recvpin); -#endif // ESP32 -} - -/// Make a copy of the interrupt state & buffer data. -/// Needed because irparams is marked as volatile, thus memcpy() isn't allowed. -/// Only call this when you know the interrupt handlers won't modify anything. -/// i.e. In kStopState. -/// @param[in] src Pointer to an irparams_t structure to copy from. -/// @param[out] dst Pointer to an irparams_t structure to copy to. -void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { - // Typecast src and dst addresses to (char *) - char *csrc = (char *)src; // NOLINT(readability/casting) - char *cdst = (char *)dst; // NOLINT(readability/casting) - - // Save the pointer to the destination's rawbuf so we don't lose it as - // the for-loop/copy after this will overwrite it with src's rawbuf pointer. - // This isn't immediately obvious due to typecasting/different variable names. - uint16_t *dst_rawbuf_ptr; - dst_rawbuf_ptr = dst->rawbuf; - - // Copy contents of src[] to dst[] - for (uint16_t i = 0; i < sizeof(irparams_t); i++) cdst[i] = csrc[i]; - - // Restore the buffer pointer - dst->rawbuf = dst_rawbuf_ptr; - - // Copy the rawbuf - for (uint16_t i = 0; i < dst->bufsize; i++) dst->rawbuf[i] = src->rawbuf[i]; -} - -/// Obtain the maximum number of entries possible in the capture buffer. -/// i.e. It's size. -/// @return The size of the buffer that is in use by the object. -uint16_t IRrecv::getBufSize(void) { return params.bufsize; } - -#if DECODE_HASH -/// Set the minimum length we will consider for reporting UNKNOWN message types. -/// @param[in] length Min nr. of mark/space pulses required to be considered. -void IRrecv::setUnknownThreshold(const uint16_t length) { - _unknown_threshold = length; -} -#endif // DECODE_HASH - - -/// Set the base tolerance percentage for matching incoming IR messages. -/// @param[in] percent An integer percentage. (0-100) -void IRrecv::setTolerance(const uint8_t percent) { - _tolerance = std::min(percent, (uint8_t)100); -} - -/// Get the base tolerance percentage for matching incoming IR messages. -/// @return A integer percentage. -uint8_t IRrecv::getTolerance(void) { return _tolerance; } - -#if ENABLE_NOISE_FILTER_OPTION -/// Remove or merge pulses in the capture buffer that are too short. -/// @param[in,out] results Ptr to the decode_results we are going to filter. -/// @param[in] floor Only allow values in the buffer large than this. -/// (in microSeconds) -void IRrecv::crudeNoiseFilter(decode_results *results, const uint16_t floor) { - if (floor == 0) return; // Nothing to do. - const uint16_t kTickFloor = floor / kRawTick; - const uint16_t kBufSize = getBufSize(); - uint16_t offset = kStartOffset; - while (offset < results->rawlen && offset + 2 < kBufSize) { - uint16_t curr = results->rawbuf[offset]; - uint16_t next = results->rawbuf[offset + 1]; - uint16_t addition = curr + next; - if (curr < kTickFloor) { // Is it too short? - // Shuffle the buffer down. i.e. Remove the mark & space pair. - // Note: `memcpy()` can't be used as rawbuf is `volatile`. - for (uint16_t i = offset + 2; i <= results->rawlen && i < kBufSize; i++) - results->rawbuf[i - 2] = results->rawbuf[i]; - if (offset > 1) { // There is a previous pair we can add to. - // Merge this pair into into the previous space. - results->rawbuf[offset - 1] += addition; - } - results->rawlen -= 2; // Adjust the length. - } else { - offset++; // Move along. - } - } -} -#endif // ENABLE_NOISE_FILTER_OPTION - -/// Decodes the received IR message. -/// If the interrupt state is saved, we will immediately resume waiting -/// for the next IR message to avoid missing messages. -/// @note There is a trade-off here. Saving the state means less time lost until -/// we can receiving the next message vs. using more RAM. Choose appropriately. -/// @param[out] results A PTR to where the decoded IR message will be stored. -/// @param[out] save A PTR to an irparams_t instance in which to save -/// the interrupt's memory/state. NULL means don't save it. -/// @param[in] max_skip Maximum Nr. of pulses at the begining of a capture we -/// can skip when attempting to find a protocol we can successfully decode. -/// This parameter can dramatically improve detection of protocols -/// when there is light IR interference just before an incoming IR -/// message, however, it comes at a steep performace price. -/// (Default is 0. No skipping.) -/// @warning Increasing the `max_skip` value will dramatically (linearly) -/// increase the cpu time & usage to decode protocols. -/// e.g. 0 -> 1 will be a 2x increase in cpu usage/time. -/// 0 -> 2 will be a 3x increase etc. -/// If you are going to do this, consider disabling protocol decoding for -/// protocols you are not expecting. -/// @param[in] noise_floor Pulses below this size (in usecs) will be removed or -/// merged prior to any decoding. This is to try to remove noise/poor -/// readings & slightly increase the chances of a successful decode but at the -/// cost of data fidelity & integrity. -/// (Defaults to 0 usecs. i.e. Don't filter; which is safe!) -/// @warning DANGER: **Here Be Dragons!** -/// If you set the `noise_floor` value too high, it **WILL** break decoding -/// of some protocols. You have been warned! -/// **Any** non-zero value has the potential to **cook** the captured raw data -/// i.e. The raw data is going to lie to you. -/// It may obscure hardware, circuit, & environment issues thus making it -/// impossible to support you accurately or confidently. -/// Values of <= 50 usecs will probably be safe. -/// 51 - 100 usecs **might** be okay. -/// 100 - 150 usecs is "Danger, Will Robinson!". -/// 150 - 200 usecs expect broken protocols. -/// At 200+ usecs, you **have** protocols you can't decode!! -/// @return A boolean indicating if an IR message is ready or not. -bool IRrecv::decode(decode_results *results, irparams_t *save, - uint8_t max_skip, uint16_t noise_floor) { - // Proceed only if an IR message been received. -#ifndef UNIT_TEST - if (params.rcvstate != kStopState) return false; -#endif - - // Clear the entry we are currently pointing to when we got the timeout. - // i.e. Stopped collecting IR data. - // It's junk as we never wrote an entry to it and can only confuse decoding. - // This is done here rather than logically the best place in read_timeout() - // as it saves a few bytes of ICACHE_RAM as that routine is bound to an - // interrupt. decode() is not stored in ICACHE_RAM. - // Another better option would be to zero the entire irparams.rawbuf[] on - // resume() but that is a much more expensive operation compare to this. - // However, don't do this if rawbuf is already full as we stomp over the heap. - // See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516 - if (!params.overflow) params.rawbuf[params.rawlen] = 0; - - bool resumed = false; // Flag indicating if we have resumed. - - // If we were requested to use a save buffer previously, do so. - if (save == NULL) save = params_save; - - if (save == NULL) { - // We haven't been asked to copy it so use the existing memory. -#ifndef UNIT_TEST - results->rawbuf = params.rawbuf; - results->rawlen = params.rawlen; - results->overflow = params.overflow; -#endif - } else { - copyIrParams(¶ms, save); // Duplicate the interrupt's memory. - resume(); // It's now safe to rearm. The IR message won't be overridden. - resumed = true; - // Point the results at the saved copy. - results->rawbuf = save->rawbuf; - results->rawlen = save->rawlen; - results->overflow = save->overflow; - } - - // Reset any previously partially processed results. - results->decode_type = UNKNOWN; - results->bits = 0; - results->value = 0; - results->address = 0; - results->command = 0; - results->repeat = false; - -#if ENABLE_NOISE_FILTER_OPTION - crudeNoiseFilter(results, noise_floor); -#endif // ENABLE_NOISE_FILTER_OPTION - // Keep looking for protocols until we've run out of entries to skip or we - // find a valid protocol message. - for (uint16_t offset = kStartOffset; - offset <= (max_skip * 2) + kStartOffset; - offset += 2) { -#if DECODE_AIWA_RC_T501 - DPRINTLN("Attempting Aiwa RC T501 decode"); - // Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC() - // because the protocols are similar. This protocol is more specific than - // those ones, so should go before them. - if (decodeAiwaRCT501(results, offset)) return true; -#endif -#if DECODE_SANYO - DPRINTLN("Attempting Sanyo LC7461 decode"); - // Try decodeSanyoLC7461() before decodeNEC() because the protocols are - // similar in timings & structure, but the Sanyo one is much longer than the - // NEC protocol (42 vs 32 bits) so this one should be tried first to try to - // reduce false detection as a NEC packet. - if (decodeSanyoLC7461(results, offset)) return true; -#endif -#if DECODE_CARRIER_AC - DPRINTLN("Attempting Carrier AC decode"); - // Try decodeCarrierAC() before decodeNEC() because the protocols are - // similar in timings & structure, but the Carrier one is much longer than - // the NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodeCarrierAC(results, offset)) return true; -#endif -#if DECODE_PIONEER - DPRINTLN("Attempting Pioneer decode"); - // Try decodePioneer() before decodeNEC() because the protocols are - // similar in timings & structure, but the Pioneer one is much longer than - // the NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodePioneer(results, offset)) return true; -#endif -#if DECODE_EPSON - DPRINTLN("Attempting Epson decode"); - // Try decodeEpson() before decodeNEC() because the protocols are - // similar in timings & structure, but the Epson one is much longer than the - // NEC protocol (3x32 identical bits vs 1x32 bits) so this one should be tried - // first to try to reduce false detection as a NEC packet. - if (decodeEpson(results, offset)) return true; -#endif -#if DECODE_NEC - DPRINTLN("Attempting NEC decode"); - if (decodeNEC(results, offset)) return true; -#endif -#if DECODE_MILESTAG2 - DPRINTLN("Attempting MilesTag2 decode"); - // Try decodeMilestag2() before decodeSony() because the protocols are - // similar in timings & structure, but the Miles one differs in nbits - // so this one should be tried first to try to reduce false detection - if (decodeMilestag2(results, offset, kMilesTag2MsgBits) || - decodeMilestag2(results, offset, kMilesTag2ShotBits)) return true; -#endif -#if DECODE_SONY - DPRINTLN("Attempting Sony decode"); - if (decodeSony(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI - DPRINTLN("Attempting Mitsubishi decode"); - if (decodeMitsubishi(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI_AC - DPRINTLN("Attempting Mitsubishi AC decode"); - if (decodeMitsubishiAC(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI2 - DPRINTLN("Attempting Mitsubishi2 decode"); - if (decodeMitsubishi2(results, offset)) return true; -#endif -#if DECODE_RC5 - DPRINTLN("Attempting RC5 decode"); - if (decodeRC5(results, offset)) return true; -#endif -#if DECODE_RC6 - DPRINTLN("Attempting RC6 decode"); - if (decodeRC6(results, offset)) return true; -#endif -#if DECODE_RCMM - DPRINTLN("Attempting RC-MM decode"); - if (decodeRCMM(results, offset)) return true; -#endif -#if DECODE_FUJITSU_AC - // Fujitsu A/C needs to precede Panasonic and Denon as it has a short - // message which looks exactly the same as a Panasonic/Denon message. - DPRINTLN("Attempting Fujitsu A/C decode"); - if (decodeFujitsuAC(results, offset)) return true; -#endif -#if DECODE_FUJITSU_AC264 - // FujitsuAC264 should be checked before FujitsuAC - // Fujitsu A/C needs to precede Panasonic and Denon as it has a short - // message which looks exactly the same as a Panasonic/Denon message. - DPRINTLN("Attempting Fujitsu A/C264 decode"); - if (decodeFujitsuAC264(results, offset, kFujitsuAc264Bits) || - decodeFujitsuAC264(results, offset, kFujitsuAc264BitsMiddle) || - decodeFujitsuAC264(results, offset, kFujitsuAc264BitsShort)) - return true; -#endif -#if DECODE_DENON - // Denon needs to precede Panasonic as it is a special case of Panasonic. - DPRINTLN("Attempting Denon decode"); - if (decodeDenon(results, offset, kDenon48Bits) || - decodeDenon(results, offset, kDenonBits) || - decodeDenon(results, offset, kDenonLegacyBits)) - return true; -#endif -#if DECODE_PANASONIC - DPRINTLN("Attempting Panasonic (48-bit) decode"); - if (decodePanasonic(results, offset)) return true; - DPRINTLN("Attempting Panasonic (40-bit) decode"); - if (decodePanasonic(results, offset, kPanasonic40Bits, true, - kPanasonic40Manufacturer)) return true; -#endif // DECODE_PANASONIC -#if DECODE_LG - DPRINTLN("Attempting LG (28-bit) decode"); - if (decodeLG(results, offset, kLgBits, true)) return true; - DPRINTLN("Attempting LG (32-bit) decode"); - // LG32 should be tried before Samsung - if (decodeLG(results, offset, kLg32Bits, true)) return true; -#endif -#if DECODE_GICABLE - // Note: Needs to happen before JVC decode, because it looks similar except - // with a required NEC-like repeat code. - DPRINTLN("Attempting GICable decode"); - if (decodeGICable(results, offset)) return true; -#endif -#if DECODE_JVC - DPRINTLN("Attempting JVC decode"); - if (decodeJVC(results, offset)) return true; -#endif -#if DECODE_SAMSUNG - DPRINTLN("Attempting SAMSUNG decode"); - if (decodeSAMSUNG(results, offset)) return true; -#endif -#if DECODE_SAMSUNG36 - DPRINTLN("Attempting Samsung36 decode"); - if (decodeSamsung36(results, offset)) return true; -#endif -#if DECODE_WHYNTER - DPRINTLN("Attempting Whynter decode"); - if (decodeWhynter(results, offset)) return true; -#endif -#if DECODE_DISH - DPRINTLN("Attempting DISH decode"); - if (decodeDISH(results, offset)) return true; -#endif -#if DECODE_SHARP - DPRINTLN("Attempting Sharp decode"); - if (decodeSharp(results, offset)) return true; -#endif -#if DECODE_BOSCH144 - DPRINTLN("Attempting Bosch 144-bit decode"); - // Bosch is similar to Coolix, so it must be attempted before decodeCOOLIX. - if (decodeBosch144(results, offset)) return true; -#endif // DECODE_BOSCH144 -#if DECODE_COOLIX - DPRINTLN("Attempting Coolix 24-bit decode"); - if (decodeCOOLIX(results, offset)) return true; -#endif // DECODE_COOLIX -#if DECODE_NIKAI - DPRINTLN("Attempting Nikai decode"); - if (decodeNikai(results, offset)) return true; -#endif -#if DECODE_KELVINATOR - // Kelvinator based-devices use a similar code to Gree ones, to avoid false - // matches this needs to happen before decodeGree(). - DPRINTLN("Attempting Kelvinator decode"); - if (decodeKelvinator(results, offset)) return true; -#endif -#if DECODE_DAIKIN - DPRINTLN("Attempting Daikin decode"); - if (decodeDaikin(results, offset)) return true; -#endif -#if DECODE_DAIKIN2 - DPRINTLN("Attempting Daikin2 decode"); - if (decodeDaikin2(results, offset)) return true; -#endif -#if DECODE_DAIKIN216 - DPRINTLN("Attempting Daikin216 decode"); - if (decodeDaikin216(results, offset)) return true; -#endif -#if DECODE_TOSHIBA_AC - DPRINTLN("Attempting Toshiba AC 72bit decode"); - if (decodeToshibaAC(results, offset)) return true; - DPRINTLN("Attempting Toshiba AC 80bit decode"); - if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true; - DPRINTLN("Attempting Toshiba AC 56bit decode"); - if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true; -#endif -#if DECODE_MIDEA - DPRINTLN("Attempting Midea decode"); - if (decodeMidea(results, offset)) return true; -#endif -#if DECODE_MAGIQUEST - DPRINTLN("Attempting Magiquest decode"); - if (decodeMagiQuest(results, offset)) return true; -#endif - /* NOTE: Disabled due to poor quality. -#if DECODE_SANYO - // The Sanyo S866500B decoder is very poor quality & depricated. - // *IF* you are going to enable it, do it near last to avoid false positive - // matches. - DPRINTLN("Attempting Sanyo SA8650B decode"); - if (decodeSanyo(results, offset)) - return true; -#endif - */ -#if DECODE_NEC - // Some devices send NEC-like codes that don't follow the true NEC spec. - // This should detect those. e.g. Apple TV remote etc. - // This needs to be done after all other codes that use strict and some - // other protocols that are NEC-like as well, as turning off strict may - // cause this to match other valid protocols. - DPRINTLN("Attempting NEC (non-strict) decode"); - if (decodeNEC(results, offset, kNECBits, false)) { - results->decode_type = NEC_LIKE; - return true; - } -#endif -#if DECODE_LASERTAG - DPRINTLN("Attempting Lasertag decode"); - if (decodeLasertag(results, offset)) return true; -#endif -#if DECODE_GREE - // Gree based-devices use a similar code to Kelvinator ones, to avoid false - // matches this needs to happen after decodeKelvinator(). - DPRINTLN("Attempting Gree decode"); - if (decodeGree(results, offset)) return true; -#endif -#if DECODE_HAIER_AC - DPRINTLN("Attempting Haier AC decode"); - if (decodeHaierAC(results, offset)) return true; -#endif -#if DECODE_HAIER_AC_YRW02 - DPRINTLN("Attempting Haier AC YR-W02 decode"); - if (decodeHaierACYRW02(results, offset)) return true; -#endif -#if DECODE_HAIER_AC176 - DPRINTLN("Attempting Haier AC 176 bit decode"); - if (decodeHaierAC176(results, offset)) return true; -#endif // DECODE_HAIER_AC176 -#if DECODE_HITACHI_AC424 - // HitachiAc424 should be checked before HitachiAC, HitachiAC2, - // & HitachiAC184 - DPRINTLN("Attempting Hitachi AC 424 decode"); - if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true; -#endif // DECODE_HITACHI_AC424 -#if DECODE_MITSUBISHI136 - // Needs to happen before HitachiAc3 decode. - DPRINTLN("Attempting Mitsubishi136 decode"); - if (decodeMitsubishi136(results, offset)) return true; -#endif // DECODE_MITSUBISHI136 -#if DECODE_HITACHI_AC3 - // HitachiAc3 should be checked before HitachiAC & HitachiAC2 - // Attempt normal before the short version. - DPRINTLN("Attempting Hitachi AC3 decode"); - // Order these in decreasing bit size, as it is more optimal. - if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) || - decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) || - decodeHitachiAc3(results, offset, kHitachiAc3MinBits)) - return true; -#endif // DECODE_HITACHI_AC3 -#if DECODE_HITACHI_AC344 - // HitachiAC344 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC344 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false)) - return true; -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC264 - // HitachiAC264 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC264 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc264Bits, true, false)) - return true; -#endif // DECODE_HITACHI_AC264 -#if DECODE_HITACHI_AC296 - // HitachiAC296 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC296 decode"); - if (decodeHitachiAc296(results, offset, kHitachiAc296Bits, true)) - return true; -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC2 - // HitachiAC2 should be checked before HitachiAC - DPRINTLN("Attempting Hitachi AC2 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc2Bits)) return true; -#endif // DECODE_HITACHI_AC2 -#if DECODE_HITACHI_AC - DPRINTLN("Attempting Hitachi AC decode"); - if (decodeHitachiAC(results, offset, kHitachiAcBits)) return true; -#endif -#if DECODE_HITACHI_AC1 - DPRINTLN("Attempting Hitachi AC1 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc1Bits)) return true; -#endif -#if DECODE_WHIRLPOOL_AC - DPRINTLN("Attempting Whirlpool AC decode"); - if (decodeWhirlpoolAC(results, offset)) return true; -#endif -#if DECODE_SAMSUNG_AC - DPRINTLN("Attempting Samsung AC (extended) decode"); - // Check the extended size first, as it should fail fast due to longer - // length. - if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits)) return true; - // Now check for the more common length. - DPRINTLN("Attempting Samsung AC decode"); - if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true; -#endif -#if DECODE_ELECTRA_AC - DPRINTLN("Attempting Electra AC decode"); - if (decodeElectraAC(results, offset)) return true; -#endif -#if DECODE_PANASONIC_AC - DPRINTLN("Attempting Panasonic AC decode"); - if (decodePanasonicAC(results, offset)) return true; - DPRINTLN("Attempting Panasonic AC short decode"); - if (decodePanasonicAC(results, offset, kPanasonicAcShortBits)) return true; -#endif -#if DECODE_LUTRON - DPRINTLN("Attempting Lutron decode"); - if (decodeLutron(results, offset)) return true; -#endif -#if DECODE_MWM - DPRINTLN("Attempting MWM decode"); - if (decodeMWM(results, offset)) return true; -#endif -#if DECODE_VESTEL_AC - DPRINTLN("Attempting Vestel AC decode"); - if (decodeVestelAc(results, offset)) return true; -#endif -#if DECODE_MITSUBISHI112 || DECODE_TCL112AC - // Mitsubish112 and Tcl112 share the same decoder. - DPRINTLN("Attempting Mitsubishi112/TCL112AC decode"); - if (decodeMitsubishi112(results, offset)) return true; -#endif // DECODE_MITSUBISHI112 || DECODE_TCL112AC -#if DECODE_TECO - DPRINTLN("Attempting Teco decode"); - if (decodeTeco(results, offset)) return true; -#endif -#if DECODE_LEGOPF - DPRINTLN("Attempting LEGOPF decode"); - if (decodeLegoPf(results, offset)) return true; -#endif -#if DECODE_MITSUBISHIHEAVY - DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); - if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy152Bits)) - return true; - DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); - if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy88Bits)) - return true; -#endif -#if DECODE_ARGO - DPRINTLN("Attempting Argo WREM3 decode (AC Control)"); - if (decodeArgoWREM3(results, offset, kArgo3AcControlStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM3 decode (iFeel report)"); - if (decodeArgoWREM3(results, offset, kArgo3iFeelReportStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM3 decode (Config)"); - if (decodeArgoWREM3(results, offset, kArgo3ConfigStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM3 decode (Timer)"); - if (decodeArgoWREM3(results, offset, kArgo3TimerStateLength * 8, true)) - return true; - DPRINTLN("Attempting Argo WREM2 decode"); - if (decodeArgo(results, offset, kArgoBits) || - decodeArgo(results, offset, kArgoShortBits, false)) return true; -#endif // DECODE_ARGO -#if DECODE_SHARP_AC - DPRINTLN("Attempting SHARP_AC decode"); - if (decodeSharpAc(results, offset)) return true; -#endif -#if DECODE_GOODWEATHER - DPRINTLN("Attempting GOODWEATHER decode"); - if (decodeGoodweather(results, offset)) return true; -#endif // DECODE_GOODWEATHER -#if DECODE_INAX - DPRINTLN("Attempting Inax decode"); - if (decodeInax(results, offset)) return true; -#endif // DECODE_INAX -#if DECODE_TROTEC - DPRINTLN("Attempting Trotec decode"); - if (decodeTrotec(results, offset)) return true; -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - DPRINTLN("Attempting Trotec 3550 decode"); - if (decodeTrotec3550(results, offset)) return true; -#endif // DECODE_TROTEC_3550 -#if DECODE_DAIKIN160 - DPRINTLN("Attempting Daikin160 decode"); - if (decodeDaikin160(results, offset)) return true; -#endif // DECODE_DAIKIN160 -#if DECODE_NEOCLIMA - DPRINTLN("Attempting Neoclima decode"); - if (decodeNeoclima(results, offset)) return true; -#endif // DECODE_NEOCLIMA -#if DECODE_DAIKIN176 - DPRINTLN("Attempting Daikin176 decode"); - if (decodeDaikin176(results, offset)) return true; -#endif // DECODE_DAIKIN176 -#if DECODE_DAIKIN128 - DPRINTLN("Attempting Daikin128 decode"); - if (decodeDaikin128(results, offset)) return true; -#endif // DECODE_DAIKIN128 -#if DECODE_AMCOR - DPRINTLN("Attempting Amcor decode"); - if (decodeAmcor(results, offset)) return true; -#endif // DECODE_AMCOR -#if DECODE_DAIKIN152 - DPRINTLN("Attempting Daikin152 decode"); - if (decodeDaikin152(results, offset)) return true; -#endif // DECODE_DAIKIN152 -#if DECODE_SYMPHONY - DPRINTLN("Attempting Symphony decode"); - if (decodeSymphony(results, offset)) return true; -#endif // DECODE_SYMPHONY -#if DECODE_DAIKIN64 - DPRINTLN("Attempting Daikin64 decode"); - if (decodeDaikin64(results, offset)) return true; -#endif // DECODE_DAIKIN64 -#if DECODE_AIRWELL - DPRINTLN("Attempting Airwell decode"); - if (decodeAirwell(results, offset)) return true; -#endif // DECODE_AIRWELL -#if DECODE_DELONGHI_AC - DPRINTLN("Attempting Delonghi AC decode"); - if (decodeDelonghiAc(results, offset)) return true; -#endif // DECODE_DELONGHI_AC -#if DECODE_DOSHISHA - DPRINTLN("Attempting Doshisha decode"); - if (decodeDoshisha(results, offset)) return true; -#endif // DECODE_DOSHISHA -#if DECODE_TRUMA - // Needs to happen before decodeMultibrackets() as they can appear similar. - DPRINTLN("Attempting Truma decode"); - if (decodeTruma(results, offset)) return true; -#endif // DECODE_TRUMA -#if DECODE_MULTIBRACKETS - DPRINTLN("Attempting Multibrackets decode"); - if (decodeMultibrackets(results, offset)) return true; -#endif // DECODE_MULTIBRACKETS -#if DECODE_CARRIER_AC40 - DPRINTLN("Attempting Carrier 40bit decode"); - if (decodeCarrierAC40(results, offset)) return true; -#endif // DECODE_CARRIER_AC40 -#if DECODE_CARRIER_AC64 - DPRINTLN("Attempting Carrier 64bit decode"); - if (decodeCarrierAC64(results, offset)) return true; -#endif // DECODE_CARRIER_AC64 -#if DECODE_TECHNIBEL_AC - DPRINTLN("Attempting Technibel AC decode"); - if (decodeTechnibelAc(results, offset)) return true; -#endif // DECODE_TECHNIBEL_AC -#if DECODE_CORONA_AC - DPRINTLN("Attempting CoronaAc decode"); - if (decodeCoronaAc(results, offset)) return true; -#endif // DECODE_CORONA_AC -#if DECODE_MIDEA24 - DPRINTLN("Attempting Midea-Nec decode"); - if (decodeMidea24(results, offset)) return true; -#endif // DECODE_MIDEA24 -#if DECODE_ZEPEAL - DPRINTLN("Attempting Zepeal decode"); - if (decodeZepeal(results, offset)) return true; -#endif // DECODE_ZEPEAL -#if DECODE_SANYO_AC - DPRINTLN("Attempting Sanyo AC decode"); - if (decodeSanyoAc(results, offset)) return true; -#endif // DECODE_SANYO_AC -#if DECODE_VOLTAS - DPRINTLN("Attempting Voltas decode"); - if (decodeVoltas(results)) return true; -#endif // DECODE_VOLTAS -#if DECODE_METZ - DPRINTLN("Attempting Metz decode"); - if (decodeMetz(results, offset)) return true; -#endif // DECODE_METZ -#if DECODE_TRANSCOLD - DPRINTLN("Attempting Transcold decode"); - if (decodeTranscold(results, offset)) return true; -#endif // DECODE_TRANSCOLD -#if DECODE_MIRAGE - DPRINTLN("Attempting Mirage decode"); - if (decodeMirage(results, offset)) return true; -#endif // DECODE_MIRAGE -#if DECODE_ELITESCREENS - DPRINTLN("Attempting EliteScreens decode"); - if (decodeElitescreens(results, offset)) return true; -#endif // DECODE_ELITESCREENS -#if DECODE_PANASONIC_AC32 - DPRINTLN("Attempting Panasonic AC (32bit) long decode"); - if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits)) return true; - DPRINTLN("Attempting Panasonic AC (32bit) short decode"); - if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits / 2)) - return true; -#endif // DECODE_PANASONIC_AC32 -#if DECODE_ECOCLIM - DPRINTLN("Attempting Ecoclim decode"); - if (decodeEcoclim(results, offset, kEcoclimBits) || - decodeEcoclim(results, offset, kEcoclimShortBits)) return true; -#endif // DECODE_ECOCLIM -#if DECODE_XMP - DPRINTLN("Attempting XMP decode"); - if (decodeXmp(results, offset, kXmpBits)) return true; -#endif // DECODE_XMP -#if DECODE_TEKNOPOINT - DPRINTLN("Attempting Teknopoint decode"); - if (decodeTeknopoint(results, offset)) return true; -#endif // DECODE_TEKNOPOINT -#if DECODE_KELON168 - DPRINTLN("Attempting Kelon 168-bit decode"); - if (decodeKelon168(results, offset)) return true; -#endif // DECODE_KELON168 -#if DECODE_KELON - DPRINTLN("Attempting Kelon 48-bit decode"); - if (decodeKelon(results, offset)) return true; -#endif // DECODE_KELON -#if DECODE_SANYO_AC88 - DPRINTLN("Attempting SanyoAc88 decode"); - if (decodeSanyoAc88(results, offset)) return true; -#endif // DECODE_SANYO_AC88 -#if DECODE_BOSE - DPRINTLN("Attempting Bose decode"); - if (decodeBose(results, offset)) return true; -#endif // DECODE_BOSE -#if DECODE_ARRIS - DPRINTLN("Attempting Arris decode"); - if (decodeArris(results, offset)) return true; -#endif // DECODE_ARRIS -#if DECODE_RHOSS - DPRINTLN("Attempting Rhoss decode"); - if (decodeRhoss(results, offset)) return true; -#endif // DECODE_RHOSS -#if DECODE_AIRTON - DPRINTLN("Attempting Airton decode"); - if (decodeAirton(results, offset)) return true; -#endif // DECODE_AIRTON -#if DECODE_COOLIX48 - DPRINTLN("Attempting Coolix 48-bit decode"); - if (decodeCoolix48(results, offset)) return true; -#endif // DECODE_COOLIX48 -#if DECODE_DAIKIN200 - DPRINTLN("Attempting Daikin 200-bit decode"); - if (decodeDaikin200(results, offset)) return true; -#endif // DECODE_DAIKIN200 -#if DECODE_HAIER_AC160 - DPRINTLN("Attempting Haier AC 160 bit decode"); - if (decodeHaierAC160(results, offset)) return true; -#endif // DECODE_HAIER_AC160 -#if DECODE_CARRIER_AC128 - DPRINTLN("Attempting Carrier AC 128-bit decode"); - if (decodeCarrierAC128(results, offset)) return true; -#endif // DECODE_CARRIER_AC128 -#if DECODE_TOTO - DPRINTLN("Attempting Toto 48/24-bit decode"); - if (decodeToto(results, offset, kTotoLongBits) || // Long needs to be first - decodeToto(results, offset, kTotoShortBits)) return true; -#endif // DECODE_TOTO -#if DECODE_CLIMABUTLER - DPRINTLN("Attempting ClimaButler decode"); - if (decodeClimaButler(results)) return true; -#endif // DECODE_CLIMABUTLER -#if DECODE_TCL96AC - DPRINTLN("Attempting TCL AC 96-bit decode"); - if (decodeTcl96Ac(results, offset)) return true; -#endif // DECODE_TCL96AC -#if DECODE_SANYO_AC152 - DPRINTLN("Attempting Sanyo AC 152-bit decode"); - if (decodeSanyoAc152(results, offset)) return true; -#endif // DECODE_SANYO_AC152 -#if DECODE_DAIKIN312 - DPRINTLN("Attempting Daikin 312-bit decode"); - if (decodeDaikin312(results, offset)) return true; -#endif // DECODE_DAIKIN312 -#if DECODE_GORENJE - DPRINTLN("Attempting GORENJE decode"); - if (decodeGorenje(results, offset)) return true; -#endif // DECODE_GORENJE -#if DECODE_WOWWEE - DPRINTLN("Attempting WOWWEE decode"); - if (decodeWowwee(results, offset)) return true; -#endif // DECODE_WOWWEE -#if DECODE_CARRIER_AC84 - DPRINTLN("Attempting Carrier A/C 84-bit decode"); - if (decodeCarrierAC84(results, offset)) return true; -#endif // DECODE_CARRIER_AC84 -#if DECODE_YORK - DPRINTLN("Attempting York decode"); - if (decodeYork(results, offset, kYorkBits)) return true; -#endif // DECODE_YORK - // Typically new protocols are added above this line. - } -#if DECODE_HASH - // decodeHash returns a hash on any input. - // Thus, it needs to be last in the list. - // If you add any decodes, add them before this. - if (decodeHash(results)) { - return true; - } -#endif // DECODE_HASH - // Throw away and start over - if (!resumed) // Check if we have already resumed. - resume(); - return false; -} // NOLINT(readability/fn_size) - -/// Convert the tolerance percentage into something valid. -/// @param[in] percentage An integer percentage. -uint8_t IRrecv::_validTolerance(const uint8_t percentage) { - return (percentage > 100) ? _tolerance : percentage; -} - -/// Calculate the lower bound of the nr. of ticks. -/// @param[in] usecs Nr. of uSeconds. -/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% -/// @param[in] delta A non-scaling amount to reduce usecs by. -/// @return Nr. of ticks. -uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, - const uint16_t delta) { - // max() used to ensure the result can't drop below 0 before the cast. - return ((uint32_t)std::max( - (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), - (int32_t)0)); -} - -/// Calculate the upper bound of the nr. of ticks. -/// @param[in] usecs Nr. of uSeconds. -/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% -/// @param[in] delta A non-scaling amount to increase usecs by. -/// @return Nr. of ticks. -uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance, - const uint16_t delta) { - return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 + - delta); -} - -/// Check if we match a pulse(measured) with the desired within -/// +/-tolerance percent and/or +/- a fixed delta range. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] delta A non-scaling (+/-) error margin (in useconds). -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, - uint16_t delta) { - measured *= kRawTick; // Convert to uSecs. - DPRINT("Matching: "); - DPRINT(ticksLow(desired, tolerance, delta)); - DPRINT(" <= "); - DPRINT(measured); - DPRINT(" <= "); - DPRINTLN(ticksHigh(desired, tolerance, delta)); -#ifdef UNIT_TEST - // Sanity checks that we don't have values that cause integer over/underflow. - // Only performed during testing so there is no performance hit in normal - // operation. - assert(ticksLow(desired, tolerance, delta) <= desired); - // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) - assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); - // Check if our high mark is below where we started. This could happen. - // If there is a legit case, then this should be removed. - assert(ticksHigh(desired, tolerance, delta) >= desired); -#endif // UNIT_TEST - return (measured >= ticksLow(desired, tolerance, delta) && - measured <= ticksHigh(desired, tolerance, delta)); -} - -/// Check if we match a pulse(measured) of at least desired within -/// tolerance percent and/or a fixed delta margin. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] delta A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, - uint8_t tolerance, uint16_t delta) { - measured *= kRawTick; // Convert to uSecs. - DPRINT("Matching ATLEAST "); - DPRINT(measured); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(". Matching: "); - DPRINT(measured); - DPRINT(" >= "); - DPRINT(ticksLow(std::min(desired, (uint32_t)MS_TO_USEC(params.timeout)), - tolerance, delta)); - DPRINT(" [min("); - DPRINT(ticksLow(desired, tolerance, delta)); - DPRINT(", "); - DPRINT(ticksLow(MS_TO_USEC(params.timeout), tolerance, delta)); - DPRINTLN(")]"); -#ifdef UNIT_TEST - // Sanity checks that we don't have values that cause integer over/underflow. - // Only performed during testing so there is no performance hit in normal - // operation. - assert(ticksLow(desired, tolerance, delta) <= desired); - // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) - assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); - // Check if our high mark is below where we started. This could happen. - // If there is a legit case, then this should be removed. - assert(ticksHigh(desired, tolerance, delta) >= desired); -#endif // UNIT_TEST - // We really should never get a value of 0, except as the last value - // in the buffer. If that is the case, then assume infinity and return true. - if (measured == 0) return true; - return measured >= ticksLow(std::min(desired, - (uint32_t)MS_TO_USEC(params.timeout)), - tolerance, delta); -} - -/// Check if we match a mark signal(measured) with the desired within -/// +/-tolerance percent, after an expected is excess is added. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchMark(uint32_t measured, uint32_t desired, uint8_t tolerance, - int16_t excess) { - DPRINT("Matching MARK "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" + "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired + excess, tolerance); -} - -/// Check if we match a mark signal(measured) with the desired within a -/// range (in uSeconds) either side of the desired, after an expected is excess -/// is added. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] range The range limit from desired to accept in uSeconds. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchMarkRange(const uint32_t measured, const uint32_t desired, - const uint16_t range, const int16_t excess) { - DPRINT("Matching MARK "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" + "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired + excess, 0, range); -} - -/// Check if we match a space signal(measured) with the desired within -/// +/-tolerance percent, after an expected is excess is removed. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance, - int16_t excess) { - DPRINT("Matching SPACE "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" - "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired - excess, tolerance); -} - -/// Check if we match a space signal(measured) with the desired within a -/// range (in uSeconds) either side of the desired, after an expected is excess -/// is removed. -/// @param[in] measured The recorded period of the signal pulse. -/// @param[in] desired The expected period (in usecs) we are matching against. -/// @param[in] range The range limit from desired to accept in uSeconds. -/// @param[in] excess A non-scaling amount to reduce usecs by. -/// @return A Boolean. true if it matches, false if it doesn't. -bool IRrecv::matchSpaceRange(const uint32_t measured, const uint32_t desired, - const uint16_t range, const int16_t excess) { - DPRINT("Matching SPACE "); - DPRINT(measured * kRawTick); - DPRINT(" vs "); - DPRINT(desired); - DPRINT(" - "); - DPRINT(excess); - DPRINT(". "); - return match(measured, desired - excess, 0, range); -} - -#if DECODE_HASH -/// Compare two tick values. -/// @param[in] oldval Nr. of ticks. -/// @param[in] newval Nr. of ticks. -/// @return 0 if newval is shorter, 1 if it is equal, & 2 if it is longer. -/// @note Use a tolerance of 20% -uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) { - if (newval < oldval * 0.8) - return 0; - else if (oldval < newval * 0.8) - return 2; - else - return 1; -} - -/// Decode any arbitrary IR message into a 32-bit code value. -/// Instead of decoding using a standard encoding scheme -/// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. -/// -/// The algorithm: look at the sequence of MARK signals, and see if each one -/// is shorter (0), the same length (1), or longer (2) than the previous. -/// Do the same with the SPACE signals. Hash the resulting sequence of 0's, -/// 1's, and 2's to a 32-bit value. This will give a unique value for each -/// different code (probably), for most code systems. -/// @see http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html -/// @note This isn't a "real" decoding, just an arbitrary value. -/// Hopefully this code is unique for each button. -bool IRrecv::decodeHash(decode_results *results) { - // Require at least some samples to prevent triggering on noise - if (results->rawlen < _unknown_threshold) return false; - int32_t hash = kFnvBasis32; - // 'rawlen - 2' to avoid the look ahead from going out of bounds. - // Should probably be -3 to avoid comparing the trailing space entry, - // however it is left this way for compatibility with previously captured - // values. - for (uint16_t i = 1; i < results->rawlen - 2; i++) { - uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); - // Add value into the hash - hash = (hash * kFnvPrime32) ^ value; - } - results->value = hash & 0xFFFFFFFF; - results->bits = results->rawlen / 2; - results->address = 0; - results->command = 0; - results->decode_type = UNKNOWN; - return true; -} -#endif // DECODE_HASH - -/// Match & decode the typical data section of an IR message. -/// The data value is stored in the least significant bits reguardless of the -/// bit ordering requested. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] expectlastspace Do we expect a space at the end of the message? -/// @return A match_result_t structure containing the success (or not), the -/// data value, and how many buffer entries were used. -match_result_t IRrecv::matchData( - volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark, - const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance, const int16_t excess, const bool MSBfirst, - const bool expectlastspace) { - match_result_t result; - result.success = false; // Fail by default. - result.data = 0; - if (expectlastspace) { // We are expecting data with a final space. - for (result.used = 0; result.used < nbits * 2; - result.used += 2, data_ptr += 2) { - // Is the bit a '1'? - if (matchMark(*data_ptr, onemark, tolerance, excess) && - matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) { - result.data = (result.data << 1) | 1; - } else if (matchMark(*data_ptr, zeromark, tolerance, excess) && - matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) { - result.data <<= 1; // The bit is a '0'. - } else { - if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2); - return result; // It's neither, so fail. - } - } - result.success = true; - } else { // We are expecting data without a final space. - // Match all but the last bit, as it may not match easily. - result = matchData(data_ptr, nbits ? nbits - 1 : 0, onemark, onespace, - zeromark, zerospace, tolerance, excess, true, true); - if (result.success) { - // Is the bit a '1'? - if (matchMark(*(data_ptr + result.used), onemark, tolerance, excess)) - result.data = (result.data << 1) | 1; - else if (matchMark(*(data_ptr + result.used), zeromark, tolerance, - excess)) - result.data <<= 1; // The bit is a '0'. - else - result.success = false; - if (result.success) result.used++; - } - } - if (!MSBfirst) result.data = reverseBits(result.data, nbits); - return result; -} - -/// Match & decode the typical data section of an IR message. -/// The bytes are stored at result_ptr. The first byte in the result equates to -/// the first byte encountered, and so on. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbytes Nr. of data bytes we expect. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] expectlastspace Do we expect a space at the end of the message? -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, - const uint16_t remaining, const uint16_t nbytes, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance, const int16_t excess, - const bool MSBfirst, const bool expectlastspace) { - // Check if there is enough capture buffer to possibly have the desired bytes. - if (remaining + expectlastspace < (nbytes * 8 * 2) + 1) - return 0; // Nope, so abort. - uint16_t offset = 0; - for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { - bool lastspace = (byte_pos + 1 == nbytes) ? expectlastspace : true; - match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, - zeromark, zerospace, tolerance, excess, - MSBfirst, lastspace); - if (result.success == false) return 0; // Fail - result_ptr[byte_pos] = (uint8_t)result.data; - offset += result.used; - } - return offset; -} - -/// Match & decode a generic/typical IR message. -/// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag -/// `use_bits`. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @param[out] result_bits_ptr A pointer to where to start storing the bits we -/// decoded. -/// @param[out] result_bytes_ptr A pointer to where to start storing the bytes -/// we decoded. -/// @param[in] use_bits A flag indicating if we are to decode bits or bytes. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_bits_ptr, - uint8_t *result_bytes_ptr, - const bool use_bits, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - // If we are expecting byte sizes, check it's a factor of 8 or fail. - if (!use_bits && nbits % 8 != 0) return 0; - // Calculate if we expect a trailing space in the data section. - const bool kexpectspace = footermark || (onespace != zerospace); - // Calculate how much remaining buffer is required. - uint16_t min_remaining = nbits * 2 - (kexpectspace ? 0 : 1); - - if (hdrmark) min_remaining++; - if (hdrspace) min_remaining++; - if (footermark) min_remaining++; - // Don't need to extend for footerspace because it could be the end of message - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) return 0; // Nope, so abort. - uint16_t offset = 0; - - // Header - if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) - return 0; - if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, - excess)) - return 0; - - // Data - if (use_bits) { // Bits. - match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, - onemark, onespace, - zeromark, zerospace, tolerance, - excess, MSBfirst, kexpectspace); - if (!result.success) return 0; - *result_bits_ptr = result.data; - offset += result.used; - } else { // bytes - uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, - remaining - offset, nbits / 8, - onemark, onespace, - zeromark, zerospace, tolerance, - excess, MSBfirst, kexpectspace); - if (!data_used) return 0; - offset += data_used; - } - // Footer - if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, - excess)) - return 0; - // If we have something still to match & haven't reached the end of the buffer - if (footerspace && offset < remaining) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } else { - if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } - offset++; - } - return offset; -} - -/// Match & decode a generic/typical <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// -/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, - hdrmark, hdrspace, onemark, onespace, - zeromark, zerospace, footermark, footerspace, atleast, - tolerance, excess, MSBfirst); -} - -/// Match & decode a generic/typical > 64bit IR message. -/// The bytes are stored at result_ptr. The first byte in the result equates to -/// the first byte encountered, and so on. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. -/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. -/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. -/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, - uint8_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, - hdrmark, hdrspace, onemark, onespace, - zeromark, zerospace, footermark, footerspace, atleast, - tolerance, excess, MSBfirst); -} - -/// Match & decode a generic/typical constant bit time <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] one Nr. of uSeconds in an expected mark signal for a '1' bit. -/// @param[in] zero Nr. of uSeconds in an expected mark signal for a '0' bit. -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @note Parameters one + zero add up to the total time for a bit. -/// e.g. mark(one) + space(zero) is a `1`, mark(zero) + space(one) is a `0`. -uint16_t IRrecv::matchGenericConstBitTime(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t one, - const uint32_t zero, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst) { - uint16_t offset = 0; - uint64_t result = 0; - // If we expect a footermark, then this can be processed like normal. - if (footermark) - return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, - hdrmark, hdrspace, one, zero, zero, one, - footermark, footerspace, atleast, - tolerance, excess, MSBfirst); - // Overwise handle like normal, except for the last bit. and no footer. - uint16_t bits = (nbits > 0) ? nbits - 1 : 0; // Make sure we don't underflow. - offset = _matchGeneric(data_ptr, &result, NULL, true, remaining, bits, - hdrmark, hdrspace, one, zero, zero, one, 0, 0, false, - tolerance, excess, true); - if (!offset) return 0; // Didn't match. - // Now for the last bit. - if (remaining <= offset) return 0; // Not enough buffer. - result <<= 1; - bool last_bit = 0; - // Is the mark a '1' or a `0`? - if (matchMark(*(data_ptr + offset), one, tolerance, excess)) { // 1 - last_bit = 1; - result |= 1; - } else if (matchMark(*(data_ptr + offset), zero, tolerance, excess)) { // 0 - last_bit = 0; - } else { - return 0; // It's neither, so fail. - } - offset++; - uint32_t expected_space = (last_bit ? zero : one) + footerspace; - // If we are not at the end of the buffer, check for at least the expected - // space value. - if (remaining > offset) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), expected_space, tolerance, - excess)) - return false; - } else { - if (!matchSpace(*(data_ptr + offset), expected_space, tolerance)) - return false; - } - offset++; - } - if (!MSBfirst) result = reverseBits(result, nbits); - *result_ptr = result; - return offset; -} - -/// Match & decode a Manchester Code <= 64bit IR message. -/// The data is stored at result_ptr. -/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean -/// skip that requirement. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. -/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// i.e. 1/2 wavelength -/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. -/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap -/// signal. -/// @param[in] atleast Is the match on the footerspace a matchAtLeast or -/// matchSpace? -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf -uint16_t IRrecv::matchManchester(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t half_period, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst, - const bool GEThomas) { - uint16_t offset = 0; - uint16_t bank = 0; - uint16_t entry = 0; - - // Calculate how much remaining buffer is required. - // Shortest case is nbits. Longest case is 2 * nbits. - uint16_t min_remaining = nbits; - - if (hdrmark) min_remaining++; - if (hdrspace) min_remaining++; - if (footermark) min_remaining++; - // Don't need to extend for footerspace because it could be the end of message - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) return 0; // Nope, so abort. - - // Header - if (hdrmark) { - entry = *(data_ptr + offset++); - if (!hdrspace) { // If we have no Header Space ... - // Do we have a data 'mark' half period merged with the header mark? - if (matchMark(entry, hdrmark + half_period, - tolerance, excess)) { - // Looks like we do. - bank = entry * kRawTick - hdrmark; - } else if (!matchMark(entry, hdrmark, tolerance, excess)) { - return 0; // It's not a normal header mark, so fail. - } - } else if (!matchMark(entry, hdrmark, tolerance, excess)) { - return 0; // It's not a normal header mark, so fail. - } - } - if (hdrspace) { - entry = *(data_ptr + offset++); - // Check to see if the header space has merged with a data space half period - if (matchSpace(entry, hdrspace + half_period, tolerance, excess)) { - // Looks like we do. - bank = entry * kRawTick - hdrspace; - } else if (!matchSpace(entry, hdrspace, tolerance, excess)) { - return 0; // It's not a normal header space, so fail. - } - } - - if (!match(bank / kRawTick, half_period, tolerance, excess)) bank = 0; - // Data - uint16_t used = matchManchesterData(data_ptr + offset, result_ptr, - remaining - offset, nbits, half_period, - bank, tolerance, excess, MSBfirst, - GEThomas); - if (!used) return 0; // Data did match. - offset += used; - // Footer - if (footermark && - !(matchMark(*(data_ptr + offset), footermark + half_period, - tolerance, excess) || - matchMark(*(data_ptr + offset), footermark, tolerance, excess))) - return 0; - offset++; - // If we have something still to match & haven't reached the end of the buffer - if (footerspace && offset < remaining) { - if (atleast) { - if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) - return 0; - } else { - if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess) && - !matchSpace(*(data_ptr + offset), footerspace + half_period, - tolerance, excess)) - return 0; - } - offset++; - } - return offset; -} - -/// Match & decode a Manchester Code data (<= 64bits. -/// @param[in] data_ptr A pointer to where we are at in the capture buffer. -/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". -/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. -/// @param[in] remaining The size of the capture buffer remaining. -/// @param[in] nbits Nr. of data bits we expect. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// i.e. 1/2 wavelength -/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) -/// @param[in] starting_balance Amount of uSeconds to assume exists prior to -/// the current value pointed too. -/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) -/// @param[in] MSBfirst Bit order to save the data in. (Def: true) -/// true is Most Significant Bit First Order, false is Least Significant First -/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? -/// @return If successful, how many buffer entries were used. Otherwise 0. -/// @see https://en.wikipedia.org/wiki/Manchester_code -/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf -/// @todo Clean up and optimise this. It is just "get it working code" atm. -uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t half_period, - const uint16_t starting_balance, - const uint8_t tolerance, - const int16_t excess, - const bool MSBfirst, - const bool GEThomas) { - DPRINTLN("DEBUG: Entered matchManchesterData"); - uint16_t offset = 0; - uint64_t data = 0; - uint16_t nr_half_periods = 0; - const uint16_t expected_half_periods = nbits * 2; - // Flip the bit if we have a starting balance. ie. Carry over from the header. - bool currentBit = starting_balance ? !GEThomas : GEThomas; - const uint16_t raw_half_period = half_period / kRawTick; - - // Calculate how much remaining buffer is required. - // Shortest case is nbits. Longest case is 2 * nbits. - uint16_t min_remaining = nbits; - - // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) { - DPRINTLN("DEBUG: Ran out of capture buffer!"); - return 0; // Nope, so abort. - } - - // Convert to ticks. Optimisation: Saves on math/extra instructions later. - uint16_t bank = starting_balance / kRawTick; - - // Data - // Loop through the buffer till we run out of buffer, or nr of half periods. - // Possible patterns are: - // short + short = 1 bit (Add the value of the previous bit again) - // short + long + short = 2 bits (Add the previous bit again, then flip & add) - // short + long + long + short = 3 bits (add prev, flip & add, flip & add) - // We can't start with a long. - // - // The general approach is thus: - // Check we have a short interval, next or in the bank. - // If the next timing value is long, act according and reset the bank to - // a short balance. - // or - // If it is short, act accordingly and declare the bank empty. - // Repeat. - while ((offset < remaining || bank) && - nr_half_periods < expected_half_periods) { - // Get the next entry if we haven't anything existing to process. - DPRINT("DEBUG: Offset = "); - DPRINTLN(offset); - if (!bank) bank = *(data_ptr + offset++); - DPRINT("DEBUG: Bank = "); - DPRINTLN(bank * kRawTick); - // Check if we don't have a short interval. - DPRINTLN("DEBUG: Checking for short interval"); - if (!match(bank, half_period, tolerance, excess)) { - DPRINTLN("DEBUG: It is. Exiting"); - return 0; // Not valid. - } - // We've succeeded in matching half a period, so count it. - nr_half_periods++; - DPRINT("DEBUG: Half Periods = "); - DPRINTLN(nr_half_periods); - // We've now used up our bank, so refill it with the next item, unless we - // are at the end of the capture buffer. - // If we are assume a single half period of "space". - if (offset < remaining) { - DPRINT("DEBUG: Offset = "); - DPRINTLN(offset); - bank = *(data_ptr + offset++); - } else if (offset == remaining) { - bank = raw_half_period; - } else { - return 0; // We are out of buffer, so abort! - } - DPRINT("DEBUG: Bank = "); - DPRINTLN(bank * kRawTick); - - // Shift the data along and add our new bit. - DPRINT("DEBUG: Adding bit: "); - DPRINTLN((currentBit ? "1" : "0")); - data <<= 1; - data |= currentBit; - - // Check if we have a long interval. - if (match(bank, half_period * 2, tolerance, excess)) { - // It is, so flip the bit we need to append, and remove a half_period of - // time from the bank. - DPRINTLN("DEBUG: long interval detected"); - currentBit = !currentBit; - bank -= raw_half_period; - } else if (match(bank, half_period, tolerance, excess)) { - // It is a short interval, so eat up all the time and move on. - DPRINTLN("DEBUG: short interval detected"); - bank = 0; - } else if (nr_half_periods == expected_half_periods - 1 && - matchAtLeast(bank, half_period, tolerance, excess)) { - // We are at the end of the data & it is a short interval, so eat up all - // the time and move on. - bank = 0; - // Reduce the offset as we are at the end of the data doing a - // matchAtLeast() because we could be processing part of a footer. - offset--; - } else { - // The length isn't what we expected (neither long or short), so bail. - return 0; - } - nr_half_periods++; - } - - // Clean up and process the data. - if (!MSBfirst) data = reverseBits(data, nbits); - // Trim the data to size. - *result_ptr = GETBITS64(data, 0, nbits); - return offset; -} - -#if UNIT_TEST -/// Unit test helper to get access to the params structure. -volatile irparams_t *IRrecv::_getParamsPtr(void) { - return ¶ms; -} -#endif // UNIT_TEST -// End of IRrecv class ------------------- +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2015 Sebastien Warin +// Copyright 2017, 2019 David Conran + +#include "IRrecv.h" +#include +#ifndef UNIT_TEST +#if defined(ESP8266) +extern "C" { +#include +#include +} +#endif // ESP8266 +#include +#endif // UNIT_TEST +#include +#ifdef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "IRutils.h" + +#ifdef UNIT_TEST +#undef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR +#endif + +#ifndef USE_IRAM_ATTR +#if defined(ESP8266) +#if defined(IRAM_ATTR) +#define USE_IRAM_ATTR IRAM_ATTR +#else // IRAM_ATTR +#define USE_IRAM_ATTR ICACHE_RAM_ATTR +#endif // IRAM_ATTR +#endif // ESP8266 +#if defined(ESP32) +#define USE_IRAM_ATTR IRAM_ATTR +#endif // ESP32 +#endif // USE_IRAM_ATTR + +#define ONCE 0 + +// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR +// code on ESP32 +// Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code +// on ESP8266 +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for +// sending IR code on ESP8266 + +// Globals +#ifndef UNIT_TEST +#if defined(ESP8266) +namespace _IRrecv { +static ETSTimer timer; +} // namespace _IRrecv +#endif // ESP8266 +#if defined(ESP32) +// We need a horrible timer hack for ESP32 Arduino framework < v2.0.0 +#if !defined(_ESP32_IRRECV_TIMER_HACK) +// Version check +#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) +// No need for the hack if we are running version >= 2.0.0 +#define _ESP32_IRRECV_TIMER_HACK false +#else // Version check +// If no ESP_ARDUINO_VERSION_MAJOR is defined, or less than 2, then we are +// using an old ESP32 core, so we need the hack. +#define _ESP32_IRRECV_TIMER_HACK true +#endif // Version check +#endif // !defined(_ESP32_IRRECV_TIMER_HACK) + +#if _ESP32_IRRECV_TIMER_HACK +// Required structs/types from: +// https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58 +// These are needed to be able to directly manipulate the timer registers from +// inside an ISR. This is very very ugly. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 +// Note: This will need to be updated if it ever changes. +// +// Start of Horrible Hack! +typedef struct { + union { + struct { + uint32_t reserved0: 10; + uint32_t alarm_en: 1; + /*When set alarm is enabled*/ + uint32_t level_int_en: 1; + /*When set level type interrupt will be generated during alarm*/ + uint32_t edge_int_en: 1; + /*When set edge type interrupt will be generated during alarm*/ + uint32_t divider: 16; + /*Timer clock (T0/1_clk) pre-scale value.*/ + uint32_t autoreload: 1; + /*When set timer 0/1 auto-reload at alarming is enabled*/ + uint32_t increase: 1; + /*When set timer 0/1 time-base counter increment. + When cleared timer 0 time-base counter decrement.*/ + uint32_t enable: 1; + /*When set timer 0/1 time-base counter is enabled*/ + }; + uint32_t val; + } config; + uint32_t cnt_low; + /*Register to store timer 0/1 time-base counter current value lower 32 + bits.*/ + uint32_t cnt_high; + /*Register to store timer 0 time-base counter current value higher 32 + bits.*/ + uint32_t update; + /*Write any value will trigger a timer 0 time-base counter value update + (timer 0 current value will be stored in registers above)*/ + uint32_t alarm_low; + /*Timer 0 time-base counter value lower 32 bits that will trigger the + alarm*/ + uint32_t alarm_high; + /*Timer 0 time-base counter value higher 32 bits that will trigger the + alarm*/ + uint32_t load_low; + /*Lower 32 bits of the value that will load into timer 0 time-base counter*/ + uint32_t load_high; + /*higher 32 bits of the value that will load into timer 0 time-base + counter*/ + uint32_t reload; + /*Write any value will trigger timer 0 time-base counter reload*/ +} hw_timer_reg_t; + +typedef struct hw_timer_s { + hw_timer_reg_t * dev; + uint8_t num; + uint8_t group; + uint8_t timer; + portMUX_TYPE lock; +} hw_timer_t; +#endif // _ESP32_IRRECV_TIMER_HACK / End of Horrible Hack. + +namespace _IRrecv { +static hw_timer_t * timer = NULL; +} // namespace _IRrecv +#endif // ESP32 +using _IRrecv::timer; +#endif // UNIT_TEST + +namespace _IRrecv { // Namespace extension +#if defined(ESP32) +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; +#endif // ESP32 +volatile irparams_t params; +irparams_t *params_save; // A copy of the interrupt state while decoding. +} // namespace _IRrecv + +#if defined(ESP32) +using _IRrecv::mux; +#endif // ESP32 +using _IRrecv::params; +using _IRrecv::params_save; + +#ifndef UNIT_TEST +#if defined(ESP8266) +/// Interrupt handler for when the timer runs out. +/// It signals to the library that capturing of IR data has stopped. +/// @param[in] arg Unused. (ESP8266 Only) +static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { + os_intr_lock(); +#endif // ESP8266 +/// @cond IGNORE +#if defined(ESP32) +/// Interrupt handler for when the timer runs out. +/// It signals to the library that capturing of IR data has stopped. +/// @note ESP32 version +static void USE_IRAM_ATTR read_timeout(void) { +/// @endcond + portENTER_CRITICAL(&mux); +#endif // ESP32 + if (params.rawlen) params.rcvstate = kStopState; +#if defined(ESP8266) + os_intr_unlock(); +#endif // ESP8266 +#if defined(ESP32) + portEXIT_CRITICAL(&mux); +#endif // ESP32 +} + +/// Interrupt handler for changes on the GPIO pin handling incoming IR messages. +static void USE_IRAM_ATTR gpio_intr() { + uint32_t now = micros(); + static uint32_t start = 0; + +#if defined(ESP8266) + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + os_timer_disarm(&timer); + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); +#endif // ESP8266 + + // Grab a local copy of rawlen to reduce instructions used in IRAM. + // This is an ugly premature optimisation code-wise, but we do everything we + // can to save IRAM. + // It seems referencing the value via the structure uses more instructions. + // Less instructions means faster and less IRAM used. + // N.B. It saves about 13 bytes of IRAM. + uint16_t rawlen = params.rawlen; + + if (rawlen >= params.bufsize) { + params.overflow = true; + params.rcvstate = kStopState; + } + + if (params.rcvstate == kStopState) return; + + if (params.rcvstate == kIdleState) { + params.rcvstate = kMarkState; + params.rawbuf[rawlen] = 1; + } else { + if (now < start) + params.rawbuf[rawlen] = (UINT32_MAX - start + now) / kRawTick; + else + params.rawbuf[rawlen] = (now - start) / kRawTick; + } + params.rawlen++; + + start = now; + +#if defined(ESP8266) + os_timer_arm(&timer, params.timeout, ONCE); +#endif // ESP8266 +#if defined(ESP32) + // Reset the timeout. + // +#if _ESP32_IRRECV_TIMER_HACK + // The following three lines of code are the equiv of: + // `timerWrite(timer, 0);` + // We can't call that routine safely from inside an ISR as that procedure + // is not stored in IRAM. Hence, we do it manually so that it's covered by + // USE_IRAM_ATTR in this ISR. + // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 + // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L106-L110 + timer->dev->load_high = (uint32_t) 0; + timer->dev->load_low = (uint32_t) 0; + timer->dev->reload = 1; + // The next line is the same, but instead replaces: + // `timerAlarmEnable(timer);` + // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 + // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178 + timer->dev->config.alarm_en = 1; +#else // _ESP32_IRRECV_TIMER_HACK + timerWrite(timer, 0); + timerAlarmEnable(timer); +#endif // _ESP32_IRRECV_TIMER_HACK +#endif // ESP32 +} +#endif // UNIT_TEST + +// Start of IRrecv class ------------------- + +/// Class constructor +/// Args: +/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is +/// connected to. +/// @param[in] bufsize Nr. of entries to have in the capture buffer. +/// (Default: kRawBuf) +/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop +/// capturing data. (Default: kTimeoutMs) +/// @param[in] save_buffer Use a second (save) buffer to decode from. +/// (Default: false) +/// @param[in] timer_num Nr. of the ESP32 timer to use. (0 to 3) (ESP32 Only) +/// or (0 to 1) (ESP32-C3) +#if defined(ESP32) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer, + const uint8_t timer_num) { + // Ensure we use a valid timer number. + _timer_num = std::min(timer_num, + (uint8_t)( +#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS + SOC_TIMER_GROUP_TOTAL_TIMERS - 1)); +#else // SOC_TIMER_GROUP_TOTAL_TIMERS + 3)); +#endif // SOC_TIMER_GROUP_TOTAL_TIMERS +#else // ESP32 +/// @cond IGNORE +/// Class constructor +/// Args: +/// @param[in] recvpin The GPIO pin the IR receiver module's data pin is +/// connected to. +/// @param[in] bufsize Nr. of entries to have in the capture buffer. +/// (Default: kRawBuf) +/// @param[in] timeout Nr. of milli-Seconds of no signal before we stop +/// capturing data. (Default: kTimeoutMs) +/// @param[in] save_buffer Use a second (save) buffer to decode from. +/// (Default: false) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer) { +/// @endcond +#endif // ESP32 + params.recvpin = recvpin; + params.bufsize = bufsize; + // Ensure we are going to be able to store all possible values in the + // capture buffer. + params.timeout = std::min(timeout, (uint8_t)kMaxTimeoutMs); + params.rawbuf = new uint16_t[bufsize]; + if (params.rawbuf == NULL) { + DPRINTLN( + "Could not allocate memory for the primary IR buffer.\n" + "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); +#ifndef UNIT_TEST + ESP.restart(); // Mem alloc failure. Reboot. +#endif + } + // If we have been asked to use a save buffer (for decoding), then create one. + if (save_buffer) { + params_save = new irparams_t; + params_save->rawbuf = new uint16_t[bufsize]; + // Check we allocated the memory successfully. + if (params_save->rawbuf == NULL) { + DPRINTLN( + "Could not allocate memory for the second IR buffer.\n" + "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); +#ifndef UNIT_TEST + ESP.restart(); // Mem alloc failure. Reboot. +#endif + } + } else { + params_save = NULL; + } +#if DECODE_HASH + _unknown_threshold = kUnknownThreshold; +#endif // DECODE_HASH + _tolerance = kTolerance; +} + +/// Class destructor +/// Cleans up after the object is no longer needed. +/// e.g. Frees up all memory used by the various buffers, and disables any +/// timers or interrupts used. +IRrecv::~IRrecv(void) { + disableIRIn(); +#if defined(ESP32) + if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. +#endif // ESP32 + delete[] params.rawbuf; + if (params_save != NULL) { + delete[] params_save->rawbuf; + delete params_save; + } +} + +/// Set up and (re)start the IR capture mechanism. +/// @param[in] pullup A flag indicating should the GPIO use the internal pullup +/// resistor. (Default: `false`. i.e. No.) +void IRrecv::enableIRIn(const bool pullup) { + // ESP32's seem to require explicitly setting the GPIO to INPUT etc. + // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. + if (pullup) { +#ifndef UNIT_TEST + pinMode(params.recvpin, INPUT_PULLUP); + } else { + pinMode(params.recvpin, INPUT); +#endif // UNIT_TEST + } +#if defined(ESP32) + // Initialise the ESP32 timer. + // 80MHz / 80 = 1 uSec granularity. + timer = timerBegin(_timer_num, 80, true); +#ifdef DEBUG + if (timer == NULL) { + DPRINT("FATAL: Unable enable system timer: "); + DPRINTLN((uint16_t)_timer_num); + } +#endif // DEBUG + assert(timer != NULL); // Check we actually got the timer. + // Set the timer so it only fires once, and set it's trigger in uSeconds. + timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); + // Note: Interrupt needs to be attached before it can be enabled or disabled. + // Note: EDGE (true) is not supported, use LEVEL (false). Ref: #1713 + // See: https://github.com/espressif/arduino-esp32/blob/caef4006af491130136b219c1205bdcf8f08bf2b/cores/esp32/esp32-hal-timer.c#L224-L227 + timerAttachInterrupt(timer, &read_timeout, false); +#endif // ESP32 + + // Initialise state machine variables + resume(); + +#ifndef UNIT_TEST +#if defined(ESP8266) + // Initialise ESP8266 timer. + os_timer_disarm(&timer); + os_timer_setfn(&timer, reinterpret_cast(read_timeout), + NULL); +#endif // ESP8266 + // Attach Interrupt + attachInterrupt(params.recvpin, gpio_intr, CHANGE); +#endif // UNIT_TEST +} + +/// Stop collection of any received IR data. +/// Disable any timers and interrupts. +void IRrecv::disableIRIn(void) { +#ifndef UNIT_TEST +#if defined(ESP8266) + os_timer_disarm(&timer); +#endif // ESP8266 +#if defined(ESP32) + timerAlarmDisable(timer); + timerDetachInterrupt(timer); + timerEnd(timer); +#endif // ESP32 + detachInterrupt(params.recvpin); +#endif // UNIT_TEST +} + +/// Pause collection of received IR data. +/// @see IRrecv class constructor +void IRrecv::pause(void) { + params.rcvstate = kStopState; + params.rawlen = 0; + params.overflow = false; +#if defined(ESP32) + gpio_intr_disable((gpio_num_t)params.recvpin); +#endif // ESP32 +} + +/// Resume collection of received IR data. +/// @note This is required if `decode()` is successful and `save_buffer` was +/// not set when the class was instanciated. +/// @see IRrecv class constructor +void IRrecv::resume(void) { + params.rcvstate = kIdleState; + params.rawlen = 0; + params.overflow = false; +#if defined(ESP32) + timerAlarmDisable(timer); + gpio_intr_enable((gpio_num_t)params.recvpin); +#endif // ESP32 +} + +/// Make a copy of the interrupt state & buffer data. +/// Needed because irparams is marked as volatile, thus memcpy() isn't allowed. +/// Only call this when you know the interrupt handlers won't modify anything. +/// i.e. In kStopState. +/// @param[in] src Pointer to an irparams_t structure to copy from. +/// @param[out] dst Pointer to an irparams_t structure to copy to. +void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { + // Typecast src and dst addresses to (char *) + char *csrc = (char *)src; // NOLINT(readability/casting) + char *cdst = (char *)dst; // NOLINT(readability/casting) + + // Save the pointer to the destination's rawbuf so we don't lose it as + // the for-loop/copy after this will overwrite it with src's rawbuf pointer. + // This isn't immediately obvious due to typecasting/different variable names. + uint16_t *dst_rawbuf_ptr; + dst_rawbuf_ptr = dst->rawbuf; + + // Copy contents of src[] to dst[] + for (uint16_t i = 0; i < sizeof(irparams_t); i++) cdst[i] = csrc[i]; + + // Restore the buffer pointer + dst->rawbuf = dst_rawbuf_ptr; + + // Copy the rawbuf + for (uint16_t i = 0; i < dst->bufsize; i++) dst->rawbuf[i] = src->rawbuf[i]; +} + +/// Obtain the maximum number of entries possible in the capture buffer. +/// i.e. It's size. +/// @return The size of the buffer that is in use by the object. +uint16_t IRrecv::getBufSize(void) { return params.bufsize; } + +#if DECODE_HASH +/// Set the minimum length we will consider for reporting UNKNOWN message types. +/// @param[in] length Min nr. of mark/space pulses required to be considered. +void IRrecv::setUnknownThreshold(const uint16_t length) { + _unknown_threshold = length; +} +#endif // DECODE_HASH + + +/// Set the base tolerance percentage for matching incoming IR messages. +/// @param[in] percent An integer percentage. (0-100) +void IRrecv::setTolerance(const uint8_t percent) { + _tolerance = std::min(percent, (uint8_t)100); +} + +/// Get the base tolerance percentage for matching incoming IR messages. +/// @return A integer percentage. +uint8_t IRrecv::getTolerance(void) { return _tolerance; } + +#if ENABLE_NOISE_FILTER_OPTION +/// Remove or merge pulses in the capture buffer that are too short. +/// @param[in,out] results Ptr to the decode_results we are going to filter. +/// @param[in] floor Only allow values in the buffer large than this. +/// (in microSeconds) +void IRrecv::crudeNoiseFilter(decode_results *results, const uint16_t floor) { + if (floor == 0) return; // Nothing to do. + const uint16_t kTickFloor = floor / kRawTick; + const uint16_t kBufSize = getBufSize(); + uint16_t offset = kStartOffset; + while (offset < results->rawlen && offset + 2 < kBufSize) { + uint16_t curr = results->rawbuf[offset]; + uint16_t next = results->rawbuf[offset + 1]; + uint16_t addition = curr + next; + if (curr < kTickFloor) { // Is it too short? + // Shuffle the buffer down. i.e. Remove the mark & space pair. + // Note: `memcpy()` can't be used as rawbuf is `volatile`. + for (uint16_t i = offset + 2; i <= results->rawlen && i < kBufSize; i++) + results->rawbuf[i - 2] = results->rawbuf[i]; + if (offset > 1) { // There is a previous pair we can add to. + // Merge this pair into into the previous space. + results->rawbuf[offset - 1] += addition; + } + results->rawlen -= 2; // Adjust the length. + } else { + offset++; // Move along. + } + } +} +#endif // ENABLE_NOISE_FILTER_OPTION + +/// Decodes the received IR message. +/// If the interrupt state is saved, we will immediately resume waiting +/// for the next IR message to avoid missing messages. +/// @note There is a trade-off here. Saving the state means less time lost until +/// we can receiving the next message vs. using more RAM. Choose appropriately. +/// @param[out] results A PTR to where the decoded IR message will be stored. +/// @param[out] save A PTR to an irparams_t instance in which to save +/// the interrupt's memory/state. NULL means don't save it. +/// @param[in] max_skip Maximum Nr. of pulses at the begining of a capture we +/// can skip when attempting to find a protocol we can successfully decode. +/// This parameter can dramatically improve detection of protocols +/// when there is light IR interference just before an incoming IR +/// message, however, it comes at a steep performace price. +/// (Default is 0. No skipping.) +/// @warning Increasing the `max_skip` value will dramatically (linearly) +/// increase the cpu time & usage to decode protocols. +/// e.g. 0 -> 1 will be a 2x increase in cpu usage/time. +/// 0 -> 2 will be a 3x increase etc. +/// If you are going to do this, consider disabling protocol decoding for +/// protocols you are not expecting. +/// @param[in] noise_floor Pulses below this size (in usecs) will be removed or +/// merged prior to any decoding. This is to try to remove noise/poor +/// readings & slightly increase the chances of a successful decode but at the +/// cost of data fidelity & integrity. +/// (Defaults to 0 usecs. i.e. Don't filter; which is safe!) +/// @warning DANGER: **Here Be Dragons!** +/// If you set the `noise_floor` value too high, it **WILL** break decoding +/// of some protocols. You have been warned! +/// **Any** non-zero value has the potential to **cook** the captured raw data +/// i.e. The raw data is going to lie to you. +/// It may obscure hardware, circuit, & environment issues thus making it +/// impossible to support you accurately or confidently. +/// Values of <= 50 usecs will probably be safe. +/// 51 - 100 usecs **might** be okay. +/// 100 - 150 usecs is "Danger, Will Robinson!". +/// 150 - 200 usecs expect broken protocols. +/// At 200+ usecs, you **have** protocols you can't decode!! +/// @return A boolean indicating if an IR message is ready or not. +bool IRrecv::decode(decode_results *results, irparams_t *save, + uint8_t max_skip, uint16_t noise_floor) { + // Proceed only if an IR message been received. +#ifndef UNIT_TEST + if (params.rcvstate != kStopState) return false; +#endif + + // Clear the entry we are currently pointing to when we got the timeout. + // i.e. Stopped collecting IR data. + // It's junk as we never wrote an entry to it and can only confuse decoding. + // This is done here rather than logically the best place in read_timeout() + // as it saves a few bytes of ICACHE_RAM as that routine is bound to an + // interrupt. decode() is not stored in ICACHE_RAM. + // Another better option would be to zero the entire irparams.rawbuf[] on + // resume() but that is a much more expensive operation compare to this. + // However, don't do this if rawbuf is already full as we stomp over the heap. + // See: https://github.com/crankyoldgit/IRremoteESP8266/issues/1516 + if (!params.overflow) params.rawbuf[params.rawlen] = 0; + + bool resumed = false; // Flag indicating if we have resumed. + + // If we were requested to use a save buffer previously, do so. + if (save == NULL) save = params_save; + + if (save == NULL) { + // We haven't been asked to copy it so use the existing memory. +#ifndef UNIT_TEST + results->rawbuf = params.rawbuf; + results->rawlen = params.rawlen; + results->overflow = params.overflow; +#endif + } else { + copyIrParams(¶ms, save); // Duplicate the interrupt's memory. + resume(); // It's now safe to rearm. The IR message won't be overridden. + resumed = true; + // Point the results at the saved copy. + results->rawbuf = save->rawbuf; + results->rawlen = save->rawlen; + results->overflow = save->overflow; + } + + // Reset any previously partially processed results. + results->decode_type = UNKNOWN; + results->bits = 0; + results->value = 0; + results->address = 0; + results->command = 0; + results->repeat = false; + +#if ENABLE_NOISE_FILTER_OPTION + crudeNoiseFilter(results, noise_floor); +#endif // ENABLE_NOISE_FILTER_OPTION + // Keep looking for protocols until we've run out of entries to skip or we + // find a valid protocol message. + for (uint16_t offset = kStartOffset; + offset <= (max_skip * 2) + kStartOffset; + offset += 2) { +#if DECODE_AIWA_RC_T501 + DPRINTLN("Attempting Aiwa RC T501 decode"); + // Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC() + // because the protocols are similar. This protocol is more specific than + // those ones, so should go before them. + if (decodeAiwaRCT501(results, offset)) return true; +#endif +#if DECODE_SANYO + DPRINTLN("Attempting Sanyo LC7461 decode"); + // Try decodeSanyoLC7461() before decodeNEC() because the protocols are + // similar in timings & structure, but the Sanyo one is much longer than the + // NEC protocol (42 vs 32 bits) so this one should be tried first to try to + // reduce false detection as a NEC packet. + if (decodeSanyoLC7461(results, offset)) return true; +#endif +#if DECODE_CARRIER_AC + DPRINTLN("Attempting Carrier AC decode"); + // Try decodeCarrierAC() before decodeNEC() because the protocols are + // similar in timings & structure, but the Carrier one is much longer than + // the NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodeCarrierAC(results, offset)) return true; +#endif +#if DECODE_PIONEER + DPRINTLN("Attempting Pioneer decode"); + // Try decodePioneer() before decodeNEC() because the protocols are + // similar in timings & structure, but the Pioneer one is much longer than + // the NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodePioneer(results, offset)) return true; +#endif +#if DECODE_EPSON + DPRINTLN("Attempting Epson decode"); + // Try decodeEpson() before decodeNEC() because the protocols are + // similar in timings & structure, but the Epson one is much longer than the + // NEC protocol (3x32 identical bits vs 1x32 bits) so this one should be tried + // first to try to reduce false detection as a NEC packet. + if (decodeEpson(results, offset)) return true; +#endif +#if DECODE_NEC + DPRINTLN("Attempting NEC decode"); + if (decodeNEC(results, offset)) return true; +#endif +#if DECODE_MILESTAG2 + DPRINTLN("Attempting MilesTag2 decode"); + // Try decodeMilestag2() before decodeSony() because the protocols are + // similar in timings & structure, but the Miles one differs in nbits + // so this one should be tried first to try to reduce false detection + if (decodeMilestag2(results, offset, kMilesTag2MsgBits) || + decodeMilestag2(results, offset, kMilesTag2ShotBits)) return true; +#endif +#if DECODE_SONY + DPRINTLN("Attempting Sony decode"); + if (decodeSony(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI + DPRINTLN("Attempting Mitsubishi decode"); + if (decodeMitsubishi(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI_AC + DPRINTLN("Attempting Mitsubishi AC decode"); + if (decodeMitsubishiAC(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI2 + DPRINTLN("Attempting Mitsubishi2 decode"); + if (decodeMitsubishi2(results, offset)) return true; +#endif +#if DECODE_RC5 + DPRINTLN("Attempting RC5 decode"); + if (decodeRC5(results, offset)) return true; +#endif +#if DECODE_RC6 + DPRINTLN("Attempting RC6 decode"); + if (decodeRC6(results, offset)) return true; +#endif +#if DECODE_RCMM + DPRINTLN("Attempting RC-MM decode"); + if (decodeRCMM(results, offset)) return true; +#endif +#if DECODE_FUJITSU_AC + // Fujitsu A/C needs to precede Panasonic and Denon as it has a short + // message which looks exactly the same as a Panasonic/Denon message. + DPRINTLN("Attempting Fujitsu A/C decode"); + if (decodeFujitsuAC(results, offset)) return true; +#endif +#if DECODE_FUJITSU_AC264 + // FujitsuAC264 should be checked before FujitsuAC + // Fujitsu A/C needs to precede Panasonic and Denon as it has a short + // message which looks exactly the same as a Panasonic/Denon message. + DPRINTLN("Attempting Fujitsu A/C264 decode"); + if (decodeFujitsuAC264(results, offset, kFujitsuAc264Bits) || + decodeFujitsuAC264(results, offset, kFujitsuAc264BitsMiddle) || + decodeFujitsuAC264(results, offset, kFujitsuAc264BitsShort)) + return true; +#endif +#if DECODE_DENON + // Denon needs to precede Panasonic as it is a special case of Panasonic. + DPRINTLN("Attempting Denon decode"); + if (decodeDenon(results, offset, kDenon48Bits) || + decodeDenon(results, offset, kDenonBits) || + decodeDenon(results, offset, kDenonLegacyBits)) + return true; +#endif +#if DECODE_PANASONIC + DPRINTLN("Attempting Panasonic (48-bit) decode"); + if (decodePanasonic(results, offset)) return true; + DPRINTLN("Attempting Panasonic (40-bit) decode"); + if (decodePanasonic(results, offset, kPanasonic40Bits, true, + kPanasonic40Manufacturer)) return true; +#endif // DECODE_PANASONIC +#if DECODE_LG + DPRINTLN("Attempting LG (28-bit) decode"); + if (decodeLG(results, offset, kLgBits, true)) return true; + DPRINTLN("Attempting LG (32-bit) decode"); + // LG32 should be tried before Samsung + if (decodeLG(results, offset, kLg32Bits, true)) return true; +#endif +#if DECODE_GICABLE + // Note: Needs to happen before JVC decode, because it looks similar except + // with a required NEC-like repeat code. + DPRINTLN("Attempting GICable decode"); + if (decodeGICable(results, offset)) return true; +#endif +#if DECODE_JVC + DPRINTLN("Attempting JVC decode"); + if (decodeJVC(results, offset)) return true; +#endif +#if DECODE_SAMSUNG + DPRINTLN("Attempting SAMSUNG decode"); + if (decodeSAMSUNG(results, offset)) return true; +#endif +#if DECODE_SAMSUNG36 + DPRINTLN("Attempting Samsung36 decode"); + if (decodeSamsung36(results, offset)) return true; +#endif +#if DECODE_WHYNTER + DPRINTLN("Attempting Whynter decode"); + if (decodeWhynter(results, offset)) return true; +#endif +#if DECODE_DISH + DPRINTLN("Attempting DISH decode"); + if (decodeDISH(results, offset)) return true; +#endif +#if DECODE_SHARP + DPRINTLN("Attempting Sharp decode"); + if (decodeSharp(results, offset)) return true; +#endif +#if DECODE_BOSCH144 + DPRINTLN("Attempting Bosch 144-bit decode"); + // Bosch is similar to Coolix, so it must be attempted before decodeCOOLIX. + if (decodeBosch144(results, offset)) return true; +#endif // DECODE_BOSCH144 +#if DECODE_COOLIX + DPRINTLN("Attempting Coolix 24-bit decode"); + if (decodeCOOLIX(results, offset)) return true; +#endif // DECODE_COOLIX +#if DECODE_NIKAI + DPRINTLN("Attempting Nikai decode"); + if (decodeNikai(results, offset)) return true; +#endif +#if DECODE_KELVINATOR + // Kelvinator based-devices use a similar code to Gree ones, to avoid false + // matches this needs to happen before decodeGree(). + DPRINTLN("Attempting Kelvinator decode"); + if (decodeKelvinator(results, offset)) return true; +#endif +#if DECODE_DAIKIN + DPRINTLN("Attempting Daikin decode"); + if (decodeDaikin(results, offset)) return true; +#endif +#if DECODE_DAIKIN2 + DPRINTLN("Attempting Daikin2 decode"); + if (decodeDaikin2(results, offset)) return true; +#endif +#if DECODE_DAIKIN216 + DPRINTLN("Attempting Daikin216 decode"); + if (decodeDaikin216(results, offset)) return true; +#endif +#if DECODE_TOSHIBA_AC + DPRINTLN("Attempting Toshiba AC 72bit decode"); + if (decodeToshibaAC(results, offset)) return true; + DPRINTLN("Attempting Toshiba AC 80bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true; + DPRINTLN("Attempting Toshiba AC 56bit decode"); + if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true; +#endif +#if DECODE_MIDEA + DPRINTLN("Attempting Midea decode"); + if (decodeMidea(results, offset)) return true; +#endif +#if DECODE_MAGIQUEST + DPRINTLN("Attempting Magiquest decode"); + if (decodeMagiQuest(results, offset)) return true; +#endif + /* NOTE: Disabled due to poor quality. +#if DECODE_SANYO + // The Sanyo S866500B decoder is very poor quality & depricated. + // *IF* you are going to enable it, do it near last to avoid false positive + // matches. + DPRINTLN("Attempting Sanyo SA8650B decode"); + if (decodeSanyo(results, offset)) + return true; +#endif + */ +#if DECODE_NEC + // Some devices send NEC-like codes that don't follow the true NEC spec. + // This should detect those. e.g. Apple TV remote etc. + // This needs to be done after all other codes that use strict and some + // other protocols that are NEC-like as well, as turning off strict may + // cause this to match other valid protocols. + DPRINTLN("Attempting NEC (non-strict) decode"); + if (decodeNEC(results, offset, kNECBits, false)) { + results->decode_type = NEC_LIKE; + return true; + } +#endif +#if DECODE_LASERTAG + DPRINTLN("Attempting Lasertag decode"); + if (decodeLasertag(results, offset)) return true; +#endif +#if DECODE_GREE + // Gree based-devices use a similar code to Kelvinator ones, to avoid false + // matches this needs to happen after decodeKelvinator(). + DPRINTLN("Attempting Gree decode"); + if (decodeGree(results, offset)) return true; +#endif +#if DECODE_HAIER_AC + DPRINTLN("Attempting Haier AC decode"); + if (decodeHaierAC(results, offset)) return true; +#endif +#if DECODE_HAIER_AC_YRW02 + DPRINTLN("Attempting Haier AC YR-W02 decode"); + if (decodeHaierACYRW02(results, offset)) return true; +#endif +#if DECODE_HAIER_AC176 + DPRINTLN("Attempting Haier AC 176 bit decode"); + if (decodeHaierAC176(results, offset)) return true; +#endif // DECODE_HAIER_AC176 +#if DECODE_HITACHI_AC424 + // HitachiAc424 should be checked before HitachiAC, HitachiAC2, + // & HitachiAC184 + DPRINTLN("Attempting Hitachi AC 424 decode"); + if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true; +#endif // DECODE_HITACHI_AC424 +#if DECODE_MITSUBISHI136 + // Needs to happen before HitachiAc3 decode. + DPRINTLN("Attempting Mitsubishi136 decode"); + if (decodeMitsubishi136(results, offset)) return true; +#endif // DECODE_MITSUBISHI136 +#if DECODE_HITACHI_AC3 + // HitachiAc3 should be checked before HitachiAC & HitachiAC2 + // Attempt normal before the short version. + DPRINTLN("Attempting Hitachi AC3 decode"); + // Order these in decreasing bit size, as it is more optimal. + if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) || + decodeHitachiAc3(results, offset, kHitachiAc3MinBits)) + return true; +#endif // DECODE_HITACHI_AC3 +#if DECODE_HITACHI_AC344 + // HitachiAC344 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC344 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false)) + return true; +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC264 + // HitachiAC264 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC264 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc264Bits, true, false)) + return true; +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + // HitachiAC296 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC296 decode"); + if (decodeHitachiAc296(results, offset, kHitachiAc296Bits, true)) + return true; +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC2 + // HitachiAC2 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC2 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc2Bits)) return true; +#endif // DECODE_HITACHI_AC2 +#if DECODE_HITACHI_AC + DPRINTLN("Attempting Hitachi AC decode"); + if (decodeHitachiAC(results, offset, kHitachiAcBits)) return true; +#endif +#if DECODE_HITACHI_AC1 + DPRINTLN("Attempting Hitachi AC1 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc1Bits)) return true; +#endif +#if DECODE_WHIRLPOOL_AC + DPRINTLN("Attempting Whirlpool AC decode"); + if (decodeWhirlpoolAC(results, offset)) return true; +#endif +#if DECODE_SAMSUNG_AC + DPRINTLN("Attempting Samsung AC (extended) decode"); + // Check the extended size first, as it should fail fast due to longer + // length. + if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits)) return true; + // Now check for the more common length. + DPRINTLN("Attempting Samsung AC decode"); + if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true; +#endif +#if DECODE_ELECTRA_AC + DPRINTLN("Attempting Electra AC decode"); + if (decodeElectraAC(results, offset)) return true; +#endif +#if DECODE_PANASONIC_AC + DPRINTLN("Attempting Panasonic AC decode"); + if (decodePanasonicAC(results, offset)) return true; + DPRINTLN("Attempting Panasonic AC short decode"); + if (decodePanasonicAC(results, offset, kPanasonicAcShortBits)) return true; +#endif +#if DECODE_LUTRON + DPRINTLN("Attempting Lutron decode"); + if (decodeLutron(results, offset)) return true; +#endif +#if DECODE_MWM + DPRINTLN("Attempting MWM decode"); + if (decodeMWM(results, offset)) return true; +#endif +#if DECODE_VESTEL_AC + DPRINTLN("Attempting Vestel AC decode"); + if (decodeVestelAc(results, offset)) return true; +#endif +#if DECODE_MITSUBISHI112 || DECODE_TCL112AC + // Mitsubish112 and Tcl112 share the same decoder. + DPRINTLN("Attempting Mitsubishi112/TCL112AC decode"); + if (decodeMitsubishi112(results, offset)) return true; +#endif // DECODE_MITSUBISHI112 || DECODE_TCL112AC +#if DECODE_TECO + DPRINTLN("Attempting Teco decode"); + if (decodeTeco(results, offset)) return true; +#endif +#if DECODE_LEGOPF + DPRINTLN("Attempting LEGOPF decode"); + if (decodeLegoPf(results, offset)) return true; +#endif +#if DECODE_MITSUBISHIHEAVY + DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); + if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy152Bits)) + return true; + DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); + if (decodeMitsubishiHeavy(results, offset, kMitsubishiHeavy88Bits)) + return true; +#endif +#if DECODE_ARGO + DPRINTLN("Attempting Argo WREM3 decode (AC Control)"); + if (decodeArgoWREM3(results, offset, kArgo3AcControlStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM3 decode (iFeel report)"); + if (decodeArgoWREM3(results, offset, kArgo3iFeelReportStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM3 decode (Config)"); + if (decodeArgoWREM3(results, offset, kArgo3ConfigStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM3 decode (Timer)"); + if (decodeArgoWREM3(results, offset, kArgo3TimerStateLength * 8, true)) + return true; + DPRINTLN("Attempting Argo WREM2 decode"); + if (decodeArgo(results, offset, kArgoBits) || + decodeArgo(results, offset, kArgoShortBits, false)) return true; +#endif // DECODE_ARGO +#if DECODE_SHARP_AC + DPRINTLN("Attempting SHARP_AC decode"); + if (decodeSharpAc(results, offset)) return true; +#endif +#if DECODE_GOODWEATHER + DPRINTLN("Attempting GOODWEATHER decode"); + if (decodeGoodweather(results, offset)) return true; +#endif // DECODE_GOODWEATHER +#if DECODE_INAX + DPRINTLN("Attempting Inax decode"); + if (decodeInax(results, offset)) return true; +#endif // DECODE_INAX +#if DECODE_TROTEC + DPRINTLN("Attempting Trotec decode"); + if (decodeTrotec(results, offset)) return true; +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + DPRINTLN("Attempting Trotec 3550 decode"); + if (decodeTrotec3550(results, offset)) return true; +#endif // DECODE_TROTEC_3550 +#if DECODE_DAIKIN160 + DPRINTLN("Attempting Daikin160 decode"); + if (decodeDaikin160(results, offset)) return true; +#endif // DECODE_DAIKIN160 +#if DECODE_NEOCLIMA + DPRINTLN("Attempting Neoclima decode"); + if (decodeNeoclima(results, offset)) return true; +#endif // DECODE_NEOCLIMA +#if DECODE_DAIKIN176 + DPRINTLN("Attempting Daikin176 decode"); + if (decodeDaikin176(results, offset)) return true; +#endif // DECODE_DAIKIN176 +#if DECODE_DAIKIN128 + DPRINTLN("Attempting Daikin128 decode"); + if (decodeDaikin128(results, offset)) return true; +#endif // DECODE_DAIKIN128 +#if DECODE_AMCOR + DPRINTLN("Attempting Amcor decode"); + if (decodeAmcor(results, offset)) return true; +#endif // DECODE_AMCOR +#if DECODE_DAIKIN152 + DPRINTLN("Attempting Daikin152 decode"); + if (decodeDaikin152(results, offset)) return true; +#endif // DECODE_DAIKIN152 +#if DECODE_SYMPHONY + DPRINTLN("Attempting Symphony decode"); + if (decodeSymphony(results, offset)) return true; +#endif // DECODE_SYMPHONY +#if DECODE_DAIKIN64 + DPRINTLN("Attempting Daikin64 decode"); + if (decodeDaikin64(results, offset)) return true; +#endif // DECODE_DAIKIN64 +#if DECODE_AIRWELL + DPRINTLN("Attempting Airwell decode"); + if (decodeAirwell(results, offset)) return true; +#endif // DECODE_AIRWELL +#if DECODE_DELONGHI_AC + DPRINTLN("Attempting Delonghi AC decode"); + if (decodeDelonghiAc(results, offset)) return true; +#endif // DECODE_DELONGHI_AC +#if DECODE_DOSHISHA + DPRINTLN("Attempting Doshisha decode"); + if (decodeDoshisha(results, offset)) return true; +#endif // DECODE_DOSHISHA +#if DECODE_TRUMA + // Needs to happen before decodeMultibrackets() as they can appear similar. + DPRINTLN("Attempting Truma decode"); + if (decodeTruma(results, offset)) return true; +#endif // DECODE_TRUMA +#if DECODE_MULTIBRACKETS + DPRINTLN("Attempting Multibrackets decode"); + if (decodeMultibrackets(results, offset)) return true; +#endif // DECODE_MULTIBRACKETS +#if DECODE_CARRIER_AC40 + DPRINTLN("Attempting Carrier 40bit decode"); + if (decodeCarrierAC40(results, offset)) return true; +#endif // DECODE_CARRIER_AC40 +#if DECODE_CARRIER_AC64 + DPRINTLN("Attempting Carrier 64bit decode"); + if (decodeCarrierAC64(results, offset)) return true; +#endif // DECODE_CARRIER_AC64 +#if DECODE_TECHNIBEL_AC + DPRINTLN("Attempting Technibel AC decode"); + if (decodeTechnibelAc(results, offset)) return true; +#endif // DECODE_TECHNIBEL_AC +#if DECODE_CORONA_AC + DPRINTLN("Attempting CoronaAc decode"); + if (decodeCoronaAc(results, offset)) return true; +#endif // DECODE_CORONA_AC +#if DECODE_MIDEA24 + DPRINTLN("Attempting Midea-Nec decode"); + if (decodeMidea24(results, offset)) return true; +#endif // DECODE_MIDEA24 +#if DECODE_ZEPEAL + DPRINTLN("Attempting Zepeal decode"); + if (decodeZepeal(results, offset)) return true; +#endif // DECODE_ZEPEAL +#if DECODE_SANYO_AC + DPRINTLN("Attempting Sanyo AC decode"); + if (decodeSanyoAc(results, offset)) return true; +#endif // DECODE_SANYO_AC +#if DECODE_VOLTAS + DPRINTLN("Attempting Voltas decode"); + if (decodeVoltas(results)) return true; +#endif // DECODE_VOLTAS +#if DECODE_METZ + DPRINTLN("Attempting Metz decode"); + if (decodeMetz(results, offset)) return true; +#endif // DECODE_METZ +#if DECODE_TRANSCOLD + DPRINTLN("Attempting Transcold decode"); + if (decodeTranscold(results, offset)) return true; +#endif // DECODE_TRANSCOLD +#if DECODE_MIRAGE + DPRINTLN("Attempting Mirage decode"); + if (decodeMirage(results, offset)) return true; +#endif // DECODE_MIRAGE +#if DECODE_ELITESCREENS + DPRINTLN("Attempting EliteScreens decode"); + if (decodeElitescreens(results, offset)) return true; +#endif // DECODE_ELITESCREENS +#if DECODE_PANASONIC_AC32 + DPRINTLN("Attempting Panasonic AC (32bit) long decode"); + if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits)) return true; + DPRINTLN("Attempting Panasonic AC (32bit) short decode"); + if (decodePanasonicAC32(results, offset, kPanasonicAc32Bits / 2)) + return true; +#endif // DECODE_PANASONIC_AC32 +#if DECODE_ECOCLIM + DPRINTLN("Attempting Ecoclim decode"); + if (decodeEcoclim(results, offset, kEcoclimBits) || + decodeEcoclim(results, offset, kEcoclimShortBits)) return true; +#endif // DECODE_ECOCLIM +#if DECODE_XMP + DPRINTLN("Attempting XMP decode"); + if (decodeXmp(results, offset, kXmpBits)) return true; +#endif // DECODE_XMP +#if DECODE_TEKNOPOINT + DPRINTLN("Attempting Teknopoint decode"); + if (decodeTeknopoint(results, offset)) return true; +#endif // DECODE_TEKNOPOINT +#if DECODE_KELON168 + DPRINTLN("Attempting Kelon 168-bit decode"); + if (decodeKelon168(results, offset)) return true; +#endif // DECODE_KELON168 +#if DECODE_KELON + DPRINTLN("Attempting Kelon 48-bit decode"); + if (decodeKelon(results, offset)) return true; +#endif // DECODE_KELON +#if DECODE_SANYO_AC88 + DPRINTLN("Attempting SanyoAc88 decode"); + if (decodeSanyoAc88(results, offset)) return true; +#endif // DECODE_SANYO_AC88 +#if DECODE_BOSE + DPRINTLN("Attempting Bose decode"); + if (decodeBose(results, offset)) return true; +#endif // DECODE_BOSE +#if DECODE_ARRIS + DPRINTLN("Attempting Arris decode"); + if (decodeArris(results, offset)) return true; +#endif // DECODE_ARRIS +#if DECODE_RHOSS + DPRINTLN("Attempting Rhoss decode"); + if (decodeRhoss(results, offset)) return true; +#endif // DECODE_RHOSS +#if DECODE_AIRTON + DPRINTLN("Attempting Airton decode"); + if (decodeAirton(results, offset)) return true; +#endif // DECODE_AIRTON +#if DECODE_COOLIX48 + DPRINTLN("Attempting Coolix 48-bit decode"); + if (decodeCoolix48(results, offset)) return true; +#endif // DECODE_COOLIX48 +#if DECODE_DAIKIN200 + DPRINTLN("Attempting Daikin 200-bit decode"); + if (decodeDaikin200(results, offset)) return true; +#endif // DECODE_DAIKIN200 +#if DECODE_HAIER_AC160 + DPRINTLN("Attempting Haier AC 160 bit decode"); + if (decodeHaierAC160(results, offset)) return true; +#endif // DECODE_HAIER_AC160 +#if DECODE_CARRIER_AC128 + DPRINTLN("Attempting Carrier AC 128-bit decode"); + if (decodeCarrierAC128(results, offset)) return true; +#endif // DECODE_CARRIER_AC128 +#if DECODE_TOTO + DPRINTLN("Attempting Toto 48/24-bit decode"); + if (decodeToto(results, offset, kTotoLongBits) || // Long needs to be first + decodeToto(results, offset, kTotoShortBits)) return true; +#endif // DECODE_TOTO +#if DECODE_CLIMABUTLER + DPRINTLN("Attempting ClimaButler decode"); + if (decodeClimaButler(results)) return true; +#endif // DECODE_CLIMABUTLER +#if DECODE_TCL96AC + DPRINTLN("Attempting TCL AC 96-bit decode"); + if (decodeTcl96Ac(results, offset)) return true; +#endif // DECODE_TCL96AC +#if DECODE_SANYO_AC152 + DPRINTLN("Attempting Sanyo AC 152-bit decode"); + if (decodeSanyoAc152(results, offset)) return true; +#endif // DECODE_SANYO_AC152 +#if DECODE_DAIKIN312 + DPRINTLN("Attempting Daikin 312-bit decode"); + if (decodeDaikin312(results, offset)) return true; +#endif // DECODE_DAIKIN312 +#if DECODE_GORENJE + DPRINTLN("Attempting GORENJE decode"); + if (decodeGorenje(results, offset)) return true; +#endif // DECODE_GORENJE +#if DECODE_WOWWEE + DPRINTLN("Attempting WOWWEE decode"); + if (decodeWowwee(results, offset)) return true; +#endif // DECODE_WOWWEE +#if DECODE_CARRIER_AC84 + DPRINTLN("Attempting Carrier A/C 84-bit decode"); + if (decodeCarrierAC84(results, offset)) return true; +#endif // DECODE_CARRIER_AC84 +#if DECODE_YORK + DPRINTLN("Attempting York decode"); + if (decodeYork(results, offset, kYorkBits)) return true; +#endif // DECODE_YORK + // Typically new protocols are added above this line. + } +#if DECODE_HASH + // decodeHash returns a hash on any input. + // Thus, it needs to be last in the list. + // If you add any decodes, add them before this. + if (decodeHash(results)) { + return true; + } +#endif // DECODE_HASH + // Throw away and start over + if (!resumed) // Check if we have already resumed. + resume(); + return false; +} // NOLINT(readability/fn_size) + +/// Convert the tolerance percentage into something valid. +/// @param[in] percentage An integer percentage. +uint8_t IRrecv::_validTolerance(const uint8_t percentage) { + return (percentage > 100) ? _tolerance : percentage; +} + +/// Calculate the lower bound of the nr. of ticks. +/// @param[in] usecs Nr. of uSeconds. +/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% +/// @param[in] delta A non-scaling amount to reduce usecs by. +/// @return Nr. of ticks. +uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { + // max() used to ensure the result can't drop below 0 before the cast. + return ((uint32_t)std::max( + (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), + (int32_t)0)); +} + +/// Calculate the upper bound of the nr. of ticks. +/// @param[in] usecs Nr. of uSeconds. +/// @param[in] tolerance Percent as an integer. e.g. 10 is 10% +/// @param[in] delta A non-scaling amount to increase usecs by. +/// @return Nr. of ticks. +uint32_t IRrecv::ticksHigh(const uint32_t usecs, const uint8_t tolerance, + const uint16_t delta) { + return ((uint32_t)(usecs * (1.0 + _validTolerance(tolerance) / 100.0)) + 1 + + delta); +} + +/// Check if we match a pulse(measured) with the desired within +/// +/-tolerance percent and/or +/- a fixed delta range. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] delta A non-scaling (+/-) error margin (in useconds). +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, + uint16_t delta) { + measured *= kRawTick; // Convert to uSecs. + DPRINT("Matching: "); + DPRINT(ticksLow(desired, tolerance, delta)); + DPRINT(" <= "); + DPRINT(measured); + DPRINT(" <= "); + DPRINTLN(ticksHigh(desired, tolerance, delta)); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST + return (measured >= ticksLow(desired, tolerance, delta) && + measured <= ticksHigh(desired, tolerance, delta)); +} + +/// Check if we match a pulse(measured) of at least desired within +/// tolerance percent and/or a fixed delta margin. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] delta A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, + uint8_t tolerance, uint16_t delta) { + measured *= kRawTick; // Convert to uSecs. + DPRINT("Matching ATLEAST "); + DPRINT(measured); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(". Matching: "); + DPRINT(measured); + DPRINT(" >= "); + DPRINT(ticksLow(std::min(desired, (uint32_t)MS_TO_USEC(params.timeout)), + tolerance, delta)); + DPRINT(" [min("); + DPRINT(ticksLow(desired, tolerance, delta)); + DPRINT(", "); + DPRINT(ticksLow(MS_TO_USEC(params.timeout), tolerance, delta)); + DPRINTLN(")]"); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST + // We really should never get a value of 0, except as the last value + // in the buffer. If that is the case, then assume infinity and return true. + if (measured == 0) return true; + return measured >= ticksLow(std::min(desired, + (uint32_t)MS_TO_USEC(params.timeout)), + tolerance, delta); +} + +/// Check if we match a mark signal(measured) with the desired within +/// +/-tolerance percent, after an expected is excess is added. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchMark(uint32_t measured, uint32_t desired, uint8_t tolerance, + int16_t excess) { + DPRINT("Matching MARK "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" + "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired + excess, tolerance); +} + +/// Check if we match a mark signal(measured) with the desired within a +/// range (in uSeconds) either side of the desired, after an expected is excess +/// is added. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] range The range limit from desired to accept in uSeconds. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchMarkRange(const uint32_t measured, const uint32_t desired, + const uint16_t range, const int16_t excess) { + DPRINT("Matching MARK "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" + "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired + excess, 0, range); +} + +/// Check if we match a space signal(measured) with the desired within +/// +/-tolerance percent, after an expected is excess is removed. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] tolerance A percentage expressed as an integer. e.g. 10 is 10%. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance, + int16_t excess) { + DPRINT("Matching SPACE "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" - "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired - excess, tolerance); +} + +/// Check if we match a space signal(measured) with the desired within a +/// range (in uSeconds) either side of the desired, after an expected is excess +/// is removed. +/// @param[in] measured The recorded period of the signal pulse. +/// @param[in] desired The expected period (in usecs) we are matching against. +/// @param[in] range The range limit from desired to accept in uSeconds. +/// @param[in] excess A non-scaling amount to reduce usecs by. +/// @return A Boolean. true if it matches, false if it doesn't. +bool IRrecv::matchSpaceRange(const uint32_t measured, const uint32_t desired, + const uint16_t range, const int16_t excess) { + DPRINT("Matching SPACE "); + DPRINT(measured * kRawTick); + DPRINT(" vs "); + DPRINT(desired); + DPRINT(" - "); + DPRINT(excess); + DPRINT(". "); + return match(measured, desired - excess, 0, range); +} + +#if DECODE_HASH +/// Compare two tick values. +/// @param[in] oldval Nr. of ticks. +/// @param[in] newval Nr. of ticks. +/// @return 0 if newval is shorter, 1 if it is equal, & 2 if it is longer. +/// @note Use a tolerance of 20% +uint16_t IRrecv::compare(const uint16_t oldval, const uint16_t newval) { + if (newval < oldval * 0.8) + return 0; + else if (oldval < newval * 0.8) + return 2; + else + return 1; +} + +/// Decode any arbitrary IR message into a 32-bit code value. +/// Instead of decoding using a standard encoding scheme +/// (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. +/// +/// The algorithm: look at the sequence of MARK signals, and see if each one +/// is shorter (0), the same length (1), or longer (2) than the previous. +/// Do the same with the SPACE signals. Hash the resulting sequence of 0's, +/// 1's, and 2's to a 32-bit value. This will give a unique value for each +/// different code (probably), for most code systems. +/// @see http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html +/// @note This isn't a "real" decoding, just an arbitrary value. +/// Hopefully this code is unique for each button. +bool IRrecv::decodeHash(decode_results *results) { + // Require at least some samples to prevent triggering on noise + if (results->rawlen < _unknown_threshold) return false; + int32_t hash = kFnvBasis32; + // 'rawlen - 2' to avoid the look ahead from going out of bounds. + // Should probably be -3 to avoid comparing the trailing space entry, + // however it is left this way for compatibility with previously captured + // values. + for (uint16_t i = 1; i < results->rawlen - 2; i++) { + uint16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); + // Add value into the hash + hash = (hash * kFnvPrime32) ^ value; + } + results->value = hash & 0xFFFFFFFF; + results->bits = results->rawlen / 2; + results->address = 0; + results->command = 0; + results->decode_type = UNKNOWN; + return true; +} +#endif // DECODE_HASH + +/// Match & decode the typical data section of an IR message. +/// The data value is stored in the least significant bits reguardless of the +/// bit ordering requested. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] expectlastspace Do we expect a space at the end of the message? +/// @return A match_result_t structure containing the success (or not), the +/// data value, and how many buffer entries were used. +match_result_t IRrecv::matchData( + volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark, + const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance, const int16_t excess, const bool MSBfirst, + const bool expectlastspace) { + match_result_t result; + result.success = false; // Fail by default. + result.data = 0; + if (expectlastspace) { // We are expecting data with a final space. + for (result.used = 0; result.used < nbits * 2; + result.used += 2, data_ptr += 2) { + // Is the bit a '1'? + if (matchMark(*data_ptr, onemark, tolerance, excess) && + matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) { + result.data = (result.data << 1) | 1; + } else if (matchMark(*data_ptr, zeromark, tolerance, excess) && + matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) { + result.data <<= 1; // The bit is a '0'. + } else { + if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2); + return result; // It's neither, so fail. + } + } + result.success = true; + } else { // We are expecting data without a final space. + // Match all but the last bit, as it may not match easily. + result = matchData(data_ptr, nbits ? nbits - 1 : 0, onemark, onespace, + zeromark, zerospace, tolerance, excess, true, true); + if (result.success) { + // Is the bit a '1'? + if (matchMark(*(data_ptr + result.used), onemark, tolerance, excess)) + result.data = (result.data << 1) | 1; + else if (matchMark(*(data_ptr + result.used), zeromark, tolerance, + excess)) + result.data <<= 1; // The bit is a '0'. + else + result.success = false; + if (result.success) result.used++; + } + } + if (!MSBfirst) result.data = reverseBits(result.data, nbits); + return result; +} + +/// Match & decode the typical data section of an IR message. +/// The bytes are stored at result_ptr. The first byte in the result equates to +/// the first byte encountered, and so on. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbytes Nr. of data bytes we expect. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] expectlastspace Do we expect a space at the end of the message? +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance, const int16_t excess, + const bool MSBfirst, const bool expectlastspace) { + // Check if there is enough capture buffer to possibly have the desired bytes. + if (remaining + expectlastspace < (nbytes * 8 * 2) + 1) + return 0; // Nope, so abort. + uint16_t offset = 0; + for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { + bool lastspace = (byte_pos + 1 == nbytes) ? expectlastspace : true; + match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, + zeromark, zerospace, tolerance, excess, + MSBfirst, lastspace); + if (result.success == false) return 0; // Fail + result_ptr[byte_pos] = (uint8_t)result.data; + offset += result.used; + } + return offset; +} + +/// Match & decode a generic/typical IR message. +/// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag +/// `use_bits`. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @param[out] result_bits_ptr A pointer to where to start storing the bits we +/// decoded. +/// @param[out] result_bytes_ptr A pointer to where to start storing the bytes +/// we decoded. +/// @param[in] use_bits A flag indicating if we are to decode bits or bytes. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_bytes_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + // If we are expecting byte sizes, check it's a factor of 8 or fail. + if (!use_bits && nbits % 8 != 0) return 0; + // Calculate if we expect a trailing space in the data section. + const bool kexpectspace = footermark || (onespace != zerospace); + // Calculate how much remaining buffer is required. + uint16_t min_remaining = nbits * 2 - (kexpectspace ? 0 : 1); + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + uint16_t offset = 0; + + // Header + if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) + return 0; + if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, + excess)) + return 0; + + // Data + if (use_bits) { // Bits. + match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst, kexpectspace); + if (!result.success) return 0; + *result_bits_ptr = result.data; + offset += result.used; + } else { // bytes + uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, + remaining - offset, nbits / 8, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst, kexpectspace); + if (!data_used) return 0; + offset += data_used; + } + // Footer + if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, + excess)) + return 0; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +/// Match & decode a generic/typical <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// +/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +/// Match & decode a generic/typical > 64bit IR message. +/// The bytes are stored at result_ptr. The first byte in the result equates to +/// the first byte encountered, and so on. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr: A pointer to where we are at in the capture buffer. +/// @param[out] result_ptr A ptr to where to start storing the bytes we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] onemark Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] onespace Nr. of uSecs in an expected space signal for a '1' bit. +/// @param[in] zeromark Nr. of uSecs in an expected mark signal for a '0' bit. +/// @param[in] zerospace Nr. of uSecs in an expected space signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint8_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +/// Match & decode a generic/typical constant bit time <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] one Nr. of uSeconds in an expected mark signal for a '1' bit. +/// @param[in] zero Nr. of uSeconds in an expected mark signal for a '0' bit. +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @note Parameters one + zero add up to the total time for a bit. +/// e.g. mark(one) + space(zero) is a `1`, mark(zero) + space(one) is a `0`. +uint16_t IRrecv::matchGenericConstBitTime(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t one, + const uint32_t zero, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + uint16_t offset = 0; + uint64_t result = 0; + // If we expect a footermark, then this can be processed like normal. + if (footermark) + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, one, zero, zero, one, + footermark, footerspace, atleast, + tolerance, excess, MSBfirst); + // Overwise handle like normal, except for the last bit. and no footer. + uint16_t bits = (nbits > 0) ? nbits - 1 : 0; // Make sure we don't underflow. + offset = _matchGeneric(data_ptr, &result, NULL, true, remaining, bits, + hdrmark, hdrspace, one, zero, zero, one, 0, 0, false, + tolerance, excess, true); + if (!offset) return 0; // Didn't match. + // Now for the last bit. + if (remaining <= offset) return 0; // Not enough buffer. + result <<= 1; + bool last_bit = 0; + // Is the mark a '1' or a `0`? + if (matchMark(*(data_ptr + offset), one, tolerance, excess)) { // 1 + last_bit = 1; + result |= 1; + } else if (matchMark(*(data_ptr + offset), zero, tolerance, excess)) { // 0 + last_bit = 0; + } else { + return 0; // It's neither, so fail. + } + offset++; + uint32_t expected_space = (last_bit ? zero : one) + footerspace; + // If we are not at the end of the buffer, check for at least the expected + // space value. + if (remaining > offset) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), expected_space, tolerance, + excess)) + return false; + } else { + if (!matchSpace(*(data_ptr + offset), expected_space, tolerance)) + return false; + } + offset++; + } + if (!MSBfirst) result = reverseBits(result, nbits); + *result_ptr = result; + return offset; +} + +/// Match & decode a Manchester Code <= 64bit IR message. +/// The data is stored at result_ptr. +/// @note Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean +/// skip that requirement. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] hdrmark Nr. of uSeconds for the expected header mark signal. +/// @param[in] hdrspace Nr. of uSeconds for the expected header space signal. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// i.e. 1/2 wavelength +/// @param[in] footermark Nr. of uSeconds for the expected footer mark signal. +/// @param[in] footerspace Nr. of uSeconds for the expected footer space/gap +/// signal. +/// @param[in] atleast Is the match on the footerspace a matchAtLeast or +/// matchSpace? +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf +uint16_t IRrecv::matchManchester(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t half_period, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst, + const bool GEThomas) { + uint16_t offset = 0; + uint16_t bank = 0; + uint16_t entry = 0; + + // Calculate how much remaining buffer is required. + // Shortest case is nbits. Longest case is 2 * nbits. + uint16_t min_remaining = nbits; + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + + // Header + if (hdrmark) { + entry = *(data_ptr + offset++); + if (!hdrspace) { // If we have no Header Space ... + // Do we have a data 'mark' half period merged with the header mark? + if (matchMark(entry, hdrmark + half_period, + tolerance, excess)) { + // Looks like we do. + bank = entry * kRawTick - hdrmark; + } else if (!matchMark(entry, hdrmark, tolerance, excess)) { + return 0; // It's not a normal header mark, so fail. + } + } else if (!matchMark(entry, hdrmark, tolerance, excess)) { + return 0; // It's not a normal header mark, so fail. + } + } + if (hdrspace) { + entry = *(data_ptr + offset++); + // Check to see if the header space has merged with a data space half period + if (matchSpace(entry, hdrspace + half_period, tolerance, excess)) { + // Looks like we do. + bank = entry * kRawTick - hdrspace; + } else if (!matchSpace(entry, hdrspace, tolerance, excess)) { + return 0; // It's not a normal header space, so fail. + } + } + + if (!match(bank / kRawTick, half_period, tolerance, excess)) bank = 0; + // Data + uint16_t used = matchManchesterData(data_ptr + offset, result_ptr, + remaining - offset, nbits, half_period, + bank, tolerance, excess, MSBfirst, + GEThomas); + if (!used) return 0; // Data did match. + offset += used; + // Footer + if (footermark && + !(matchMark(*(data_ptr + offset), footermark + half_period, + tolerance, excess) || + matchMark(*(data_ptr + offset), footermark, tolerance, excess))) + return 0; + offset++; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess) && + !matchSpace(*(data_ptr + offset), footerspace + half_period, + tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +/// Match & decode a Manchester Code data (<= 64bits. +/// @param[in] data_ptr A pointer to where we are at in the capture buffer. +/// @note `data_ptr` is assumed to be pointing to a "Mark", not a "Space". +/// @param[out] result_ptr A ptr to where to start storing the bits we decoded. +/// @param[in] remaining The size of the capture buffer remaining. +/// @param[in] nbits Nr. of data bits we expect. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// i.e. 1/2 wavelength +/// @param[in] tolerance Percentage error margin to allow. (Default: kUseDefTol) +/// @param[in] starting_balance Amount of uSeconds to assume exists prior to +/// the current value pointed too. +/// @param[in] excess Nr. of uSeconds. (Def: kMarkExcess) +/// @param[in] MSBfirst Bit order to save the data in. (Def: true) +/// true is Most Significant Bit First Order, false is Least Significant First +/// @param[in] GEThomas Use G.E. Thomas (true) or IEEE 802.3 (false) convention? +/// @return If successful, how many buffer entries were used. Otherwise 0. +/// @see https://en.wikipedia.org/wiki/Manchester_code +/// @see http://ww1.microchip.com/downloads/en/AppNotes/Atmel-9164-Manchester-Coding-Basics_Application-Note.pdf +/// @todo Clean up and optimise this. It is just "get it working code" atm. +uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t half_period, + const uint16_t starting_balance, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst, + const bool GEThomas) { + DPRINTLN("DEBUG: Entered matchManchesterData"); + uint16_t offset = 0; + uint64_t data = 0; + uint16_t nr_half_periods = 0; + const uint16_t expected_half_periods = nbits * 2; + // Flip the bit if we have a starting balance. ie. Carry over from the header. + bool currentBit = starting_balance ? !GEThomas : GEThomas; + const uint16_t raw_half_period = half_period / kRawTick; + + // Calculate how much remaining buffer is required. + // Shortest case is nbits. Longest case is 2 * nbits. + uint16_t min_remaining = nbits; + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) { + DPRINTLN("DEBUG: Ran out of capture buffer!"); + return 0; // Nope, so abort. + } + + // Convert to ticks. Optimisation: Saves on math/extra instructions later. + uint16_t bank = starting_balance / kRawTick; + + // Data + // Loop through the buffer till we run out of buffer, or nr of half periods. + // Possible patterns are: + // short + short = 1 bit (Add the value of the previous bit again) + // short + long + short = 2 bits (Add the previous bit again, then flip & add) + // short + long + long + short = 3 bits (add prev, flip & add, flip & add) + // We can't start with a long. + // + // The general approach is thus: + // Check we have a short interval, next or in the bank. + // If the next timing value is long, act according and reset the bank to + // a short balance. + // or + // If it is short, act accordingly and declare the bank empty. + // Repeat. + while ((offset < remaining || bank) && + nr_half_periods < expected_half_periods) { + // Get the next entry if we haven't anything existing to process. + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); + if (!bank) bank = *(data_ptr + offset++); + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); + // Check if we don't have a short interval. + DPRINTLN("DEBUG: Checking for short interval"); + if (!match(bank, half_period, tolerance, excess)) { + DPRINTLN("DEBUG: It is. Exiting"); + return 0; // Not valid. + } + // We've succeeded in matching half a period, so count it. + nr_half_periods++; + DPRINT("DEBUG: Half Periods = "); + DPRINTLN(nr_half_periods); + // We've now used up our bank, so refill it with the next item, unless we + // are at the end of the capture buffer. + // If we are assume a single half period of "space". + if (offset < remaining) { + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); + bank = *(data_ptr + offset++); + } else if (offset == remaining) { + bank = raw_half_period; + } else { + return 0; // We are out of buffer, so abort! + } + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); + + // Shift the data along and add our new bit. + DPRINT("DEBUG: Adding bit: "); + DPRINTLN((currentBit ? "1" : "0")); + data <<= 1; + data |= currentBit; + + // Check if we have a long interval. + if (match(bank, half_period * 2, tolerance, excess)) { + // It is, so flip the bit we need to append, and remove a half_period of + // time from the bank. + DPRINTLN("DEBUG: long interval detected"); + currentBit = !currentBit; + bank -= raw_half_period; + } else if (match(bank, half_period, tolerance, excess)) { + // It is a short interval, so eat up all the time and move on. + DPRINTLN("DEBUG: short interval detected"); + bank = 0; + } else if (nr_half_periods == expected_half_periods - 1 && + matchAtLeast(bank, half_period, tolerance, excess)) { + // We are at the end of the data & it is a short interval, so eat up all + // the time and move on. + bank = 0; + // Reduce the offset as we are at the end of the data doing a + // matchAtLeast() because we could be processing part of a footer. + offset--; + } else { + // The length isn't what we expected (neither long or short), so bail. + return 0; + } + nr_half_periods++; + } + + // Clean up and process the data. + if (!MSBfirst) data = reverseBits(data, nbits); + // Trim the data to size. + *result_ptr = GETBITS64(data, 0, nbits); + return offset; +} + +#if UNIT_TEST +/// Unit test helper to get access to the params structure. +volatile irparams_t *IRrecv::_getParamsPtr(void) { + return ¶ms; +} +#endif // UNIT_TEST +// End of IRrecv class ------------------- diff --git a/src/IRrecv.h b/src/IRrecv.h index 616eb48b3..41e2eabe2 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -1,894 +1,894 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2015 Sebastien Warin -// Copyright 2017 David Conran - -#ifndef IRRECV_H_ -#define IRRECV_H_ - -#ifndef UNIT_TEST -#include -#endif -#include -#define __STDC_LIMIT_MACROS -#include -#include "IRremoteESP8266.h" - -// Constants -const uint16_t kHeader = 2; // Usual nr. of header entries. -const uint16_t kFooter = 2; // Usual nr. of footer (stop bits) entries. -const uint16_t kStartOffset = 1; // Usual rawbuf entry to start from. -#define MS_TO_USEC(x) ((x) * 1000U) // Convert milli-Seconds to micro-Seconds. -// Marks tend to be 100us too long, and spaces 100us too short -// when received due to sensor lag. -const uint16_t kMarkExcess = 50; -const uint16_t kRawBuf = 100; // Default length of raw capture buffer -const uint64_t kRepeat = UINT64_MAX; -// Default min size of reported UNKNOWN messages. -const uint16_t kUnknownThreshold = 6; - -// receiver states -const uint8_t kIdleState = 2; -const uint8_t kMarkState = 3; -const uint8_t kSpaceState = 4; -const uint8_t kStopState = 5; -const uint8_t kTolerance = 25; // default percent tolerance in measurements. -const uint8_t kUseDefTol = 255; // Indicate to use the class default tolerance. -const uint16_t kRawTick = 2; // Capture tick to uSec factor. -#define RAWTICK kRawTick // Deprecated. For legacy user code support only. -// How long (ms) before we give up wait for more data? -// Don't exceed kMaxTimeoutMs without a good reason. -// That is the capture buffers maximum value size. (UINT16_MAX / kRawTick) -// Typically messages/protocols tend to repeat around the 100ms timeframe, -// thus we should timeout before that to give us some time to try to decode -// before we need to start capturing a possible new message. -// Typically 15ms suits most applications. However, some protocols demand a -// higher value. e.g. 90ms for XMP-1 and some aircon units. -const uint8_t kTimeoutMs = 15; // In MilliSeconds. -#define TIMEOUT_MS kTimeoutMs // For legacy documentation. -const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1)); - -// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param -const uint32_t kFnvPrime32 = 16777619UL; -const uint32_t kFnvBasis32 = 2166136261UL; - -#ifdef ESP32 -// Which of the ESP32 timers to use by default. -// (3 for most ESP32s, 1 for ESP32-C3s) -#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS -const uint8_t kDefaultESP32Timer = SOC_TIMER_GROUP_TOTAL_TIMERS - 1; -#else // SOC_TIMER_GROUP_TOTAL_TIMERS -const uint8_t kDefaultESP32Timer = 3; -#endif // SOC_TIMER_GROUP_TOTAL_TIMERS -#endif // ESP32 - -#if DECODE_AC -// Hitachi AC is the current largest state size. -const uint16_t kStateSizeMax = kHitachiAc2StateLength; -#else // DECODE_AC -// Just define something (a uint64_t) -const uint16_t kStateSizeMax = sizeof(uint64_t); -#endif // DECODE_AC - -// Types - -/// Information for the interrupt handler -typedef struct { - uint8_t recvpin; // pin for IR data from detector - uint8_t rcvstate; // state machine - uint16_t timer; // state timer, counts 50uS ticks. - uint16_t bufsize; // max. nr. of entries in the capture buffer. - uint16_t *rawbuf; // raw data - // uint16_t is used for rawlen as it saves 3 bytes of iram in the interrupt - // handler. Don't ask why, I don't know. It just does. - uint16_t rawlen; // counter of entries in rawbuf. - uint8_t overflow; // Buffer overflow indicator. - uint8_t timeout; // Nr. of milliSeconds before we give up. -} irparams_t; - -/// Results from a data match -typedef struct { - bool success; // Was the match successful? - uint64_t data; // The data found. - uint16_t used; // How many buffer positions were used. -} match_result_t; - -// Classes - -/// Results returned from the decoder -class decode_results { - public: - decode_type_t decode_type; // NEC, SONY, RC5, UNKNOWN - // value, address, & command are all mutually exclusive with state. - // i.e. They MUST NOT be used at the same time as state, so we can use a union - // structure to save us a handful of valuable bytes of memory. - union { - struct { - uint64_t value; // Decoded value - uint32_t address; // Decoded device address. - uint32_t command; // Decoded command. - }; - uint8_t state[kStateSizeMax]; // Multi-byte results. - }; - uint16_t bits; // Number of bits in decoded value - volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks - uint16_t rawlen; // Number of records in rawbuf. - bool overflow; - bool repeat; // Is the result a repeat code? -}; - -/// Class for receiving IR messages. -class IRrecv { - public: -#if defined(ESP32) - explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, - const uint8_t timeout = kTimeoutMs, - const bool save_buffer = false, - const uint8_t timer_num = kDefaultESP32Timer); // Constructor -#else // ESP32 - explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, - const uint8_t timeout = kTimeoutMs, - const bool save_buffer = false); // Constructor -#endif // ESP32 - ~IRrecv(void); // Destructor - void setTolerance(const uint8_t percent = kTolerance); - uint8_t getTolerance(void); - bool decode(decode_results *results, irparams_t *save = NULL, - uint8_t max_skip = 0, uint16_t noise_floor = 0); - void enableIRIn(const bool pullup = false); - void disableIRIn(void); - void pause(void); - void resume(void); - uint16_t getBufSize(void); -#if DECODE_HASH - void setUnknownThreshold(const uint16_t length); -#endif - bool match(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - bool matchMark(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess); - bool matchMarkRange(const uint32_t measured, const uint32_t desired, - const uint16_t range = 100, - const int16_t excess = kMarkExcess); - bool matchSpace(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess); - bool matchSpaceRange(const uint32_t measured, const uint32_t desired, - const uint16_t range = 100, - const int16_t excess = kMarkExcess); -#ifndef UNIT_TEST - - private: -#endif - irparams_t *irparams_save; - uint8_t _tolerance; -#if defined(ESP32) - uint8_t _timer_num; -#endif // defined(ESP32) -#if DECODE_HASH - uint16_t _unknown_threshold; -#endif -#ifdef UNIT_TEST - volatile irparams_t *_getParamsPtr(void); -#endif // UNIT_TEST - // These are called by decode - uint8_t _validTolerance(const uint8_t percentage); - void copyIrParams(volatile irparams_t *src, irparams_t *dst); - uint16_t compare(const uint16_t oldval, const uint16_t newval); - uint32_t ticksLow(const uint32_t usecs, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - uint32_t ticksHigh(const uint32_t usecs, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - bool matchAtLeast(const uint32_t measured, const uint32_t desired, - const uint8_t tolerance = kUseDefTol, - const uint16_t delta = 0); - uint16_t _matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_bits_ptr, - uint8_t *result_ptr, - const bool use_bits, - const uint16_t remaining, - const uint16_t required, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t onemark, - const uint32_t onespace, - const uint16_t zeromark, - const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool expectlastspace = true); - uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, - const uint16_t remaining, const uint16_t nbytes, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool expectlastspace = true); - uint16_t matchGeneric(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, const uint16_t nbits, - const uint16_t hdrmark, const uint32_t hdrspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr, - const uint16_t remaining, const uint16_t nbits, - const uint16_t hdrmark, const uint32_t hdrspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - uint16_t matchGenericConstBitTime(volatile uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t one, - const uint32_t zero, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true); - uint16_t matchManchesterData(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t half_period, - const uint16_t starting_balance = 0, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool GEThomas = true); - uint16_t matchManchester(volatile const uint16_t *data_ptr, - uint64_t *result_ptr, - const uint16_t remaining, - const uint16_t nbits, - const uint16_t hdrmark, - const uint32_t hdrspace, - const uint16_t clock_period, - const uint16_t footermark, - const uint32_t footerspace, - const bool atleast = false, - const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const bool MSBfirst = true, - const bool GEThomas = true); - void crudeNoiseFilter(decode_results *results, const uint16_t floor = 0); - bool decodeHash(decode_results *results); -#if DECODE_VOLTAS - bool decodeVoltas(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kVoltasBits, - const bool strict = true); -#endif // DECODE_VOLTAS -#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO) - bool decodeNEC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kNECBits, const bool strict = true); -#endif -#if DECODE_ARGO - bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kArgoBits, const bool strict = true); - bool decodeArgoWREM3(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kArgo3AcControlStateLength * 8, - const bool strict = true); -#endif // DECODE_ARGO -#if DECODE_ARRIS - bool decodeArris(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kArrisBits, const bool strict = true); -#endif // DECODE_ARRIS -#if DECODE_SONY - bool decodeSony(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSonyMinBits, - const bool strict = false); -#endif -#if DECODE_SANYO - // DISABLED due to poor quality. - // bool decodeSanyo(decode_results *results, uint16_t offset = kStartOffset, - // uint16_t nbits = kSanyoSA8650BBits, - // bool strict = false); - bool decodeSanyoLC7461(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoLC7461Bits, - const bool strict = true); -#endif -#if DECODE_SANYO_AC - bool decodeSanyoAc(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoAcBits, - const bool strict = true); -#endif // DECODE_SANYO_AC -#if DECODE_SANYO_AC88 - bool decodeSanyoAc88(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoAc88Bits, - const bool strict = true); -#endif // DECODE_SANYO_AC88 -#if DECODE_SANYO_AC152 - bool decodeSanyoAc152(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kSanyoAc152Bits, - const bool strict = true); -#endif // DECODE_SANYO_AC152 -#if DECODE_MITSUBISHI - bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiBits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHI2 - bool decodeMitsubishi2(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiBits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHI_AC - bool decodeMitsubishiAC(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiACBits, - const bool strict = false); -#endif -#if DECODE_MITSUBISHI136 - bool decodeMitsubishi136(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishi136Bits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHI112 - bool decodeMitsubishi112(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishi112Bits, - const bool strict = true); -#endif -#if DECODE_MITSUBISHIHEAVY - bool decodeMitsubishiHeavy(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMitsubishiHeavy152Bits, - const bool strict = true); -#endif -#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG || DECODE_MWM) - int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, - uint16_t bitTime, const uint8_t tolerance = kUseDefTol, - const int16_t excess = kMarkExcess, - const uint16_t delta = 0, const uint8_t maxwidth = 3); -#endif -#if DECODE_RC5 - bool decodeRC5(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRC5XBits, - const bool strict = true); -#endif -#if DECODE_RC6 - bool decodeRC6(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRC6Mode0Bits, - const bool strict = false); -#endif -#if DECODE_RCMM - bool decodeRCMM(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRCMMBits, - const bool strict = false); -#endif -#if (DECODE_PANASONIC || DECODE_DENON) - bool decodePanasonic(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kPanasonicBits, - const bool strict = false, - const uint32_t manufacturer = kPanasonicManufacturer); -#endif -#if DECODE_LG - bool decodeLG(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLgBits, - const bool strict = false); -#endif -#if DECODE_INAX - bool decodeInax(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kInaxBits, - const bool strict = true); -#endif // DECODE_INAX -#if DECODE_JVC - bool decodeJVC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kJvcBits, - const bool strict = true); -#endif -#if DECODE_SAMSUNG - bool decodeSAMSUNG(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSamsungBits, - const bool strict = true); -#endif -#if DECODE_SAMSUNG - bool decodeSamsung36(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSamsung36Bits, - const bool strict = true); -#endif -#if DECODE_SAMSUNG_AC - bool decodeSamsungAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSamsungAcBits, - const bool strict = true); -#endif -#if DECODE_WHYNTER - bool decodeWhynter(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kWhynterBits, - const bool strict = true); -#endif -#if DECODE_COOLIX - bool decodeCOOLIX(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCoolixBits, - const bool strict = true); -#endif // DECODE_COOLIX -#if DECODE_COOLIX48 - bool decodeCoolix48(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCoolix48Bits, - const bool strict = true); -#endif // DECODE_COOLIX48 -#if DECODE_DENON - bool decodeDenon(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDenonBits, - const bool strict = true); -#endif -#if DECODE_DISH - bool decodeDISH(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDishBits, - const bool strict = true); -#endif -#if (DECODE_SHARP || DECODE_DENON) - bool decodeSharp(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSharpBits, - const bool strict = true, const bool expansion = true); -#endif -#if DECODE_SHARP_AC - bool decodeSharpAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSharpAcBits, - const bool strict = true); -#endif -#if DECODE_AIWA_RC_T501 - bool decodeAiwaRCT501(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAiwaRcT501Bits, - const bool strict = true); -#endif -#if DECODE_NIKAI - bool decodeNikai(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kNikaiBits, - const bool strict = true); -#endif -#if DECODE_MAGIQUEST - bool decodeMagiQuest(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMagiquestBits, - const bool strict = true); -#endif -#if DECODE_KELVINATOR - bool decodeKelvinator(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kKelvinatorBits, - const bool strict = true); -#endif -#if DECODE_DAIKIN - bool decodeDaikin(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikinBits, - const bool strict = true); -#endif -#if DECODE_DAIKIN64 - bool decodeDaikin64(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin64Bits, - const bool strict = true); -#endif // DECODE_DAIKIN64 -#if DECODE_DAIKIN128 - bool decodeDaikin128(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin128Bits, - const bool strict = true); -#endif // DECODE_DAIKIN128 -#if DECODE_DAIKIN152 - bool decodeDaikin152(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin152Bits, - const bool strict = true); -#endif // DECODE_DAIKIN152 -#if DECODE_DAIKIN160 - bool decodeDaikin160(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin160Bits, - const bool strict = true); -#endif // DECODE_DAIKIN160 -#if DECODE_DAIKIN176 - bool decodeDaikin176(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin176Bits, - const bool strict = true); -#endif // DECODE_DAIKIN176 -#if DECODE_DAIKIN2 - bool decodeDaikin2(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin2Bits, - const bool strict = true); -#endif -#if DECODE_DAIKIN200 - bool decodeDaikin200(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin200Bits, - const bool strict = true); -#endif // DECODE_DAIKIN200 -#if DECODE_DAIKIN216 - bool decodeDaikin216(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin216Bits, - const bool strict = true); -#endif // DECODE_DAIKIN216 -#if DECODE_DAIKIN312 - bool decodeDaikin312(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDaikin312Bits, - const bool strict = true); -#endif // DECODE_DAIKIN312 -#if DECODE_TOSHIBA_AC - bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kToshibaACBits, - const bool strict = true); -#endif -#if DECODE_TROTEC - bool decodeTrotec(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTrotecBits, - const bool strict = true); -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - bool decodeTrotec3550(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTrotecBits, - const bool strict = true); -#endif // DECODE_TROTEC_3550 -#if DECODE_MIDEA - bool decodeMidea(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMideaBits, - const bool strict = true); -#endif // DECODE_MIDEA -#if DECODE_MIDEA24 - bool decodeMidea24(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMidea24Bits, - const bool strict = true); -#endif // DECODE_MIDEA24 -#if DECODE_FUJITSU_AC - bool decodeFujitsuAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kFujitsuAcBits, - const bool strict = false); -#endif -#if DECODE_FUJITSU_AC264 - bool decodeFujitsuAC264(decode_results* results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kFujitsuAc264Bits, - const bool strict = true); -#endif // DECODE_FUJITSU_AC264 -#if DECODE_LASERTAG - bool decodeLasertag(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLasertagBits, - const bool strict = true); -#endif -#if DECODE_MILESTAG2 - bool decodeMilestag2(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMilesTag2ShotBits, - const bool strict = true); -#endif -#if DECODE_CARRIER_AC - bool decodeCarrierAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAcBits, - const bool strict = true); -#endif // DECODE_CARRIER_AC -#if DECODE_CARRIER_AC40 - bool decodeCarrierAC40(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc40Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC40 -#if DECODE_CARRIER_AC84 - bool decodeCarrierAC84(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc84Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC84 -#if DECODE_CARRIER_AC64 - bool decodeCarrierAC64(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc64Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC64 -#if DECODE_CARRIER_AC128 - bool decodeCarrierAC128(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kCarrierAc128Bits, - const bool strict = true); -#endif // DECODE_CARRIER_AC128 -#if DECODE_GOODWEATHER - bool decodeGoodweather(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kGoodweatherBits, - const bool strict = true); -#endif // DECODE_GOODWEATHER -#if DECODE_GORENJE - bool decodeGorenje(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kGorenjeBits, - const bool strict = true); -#endif // DECODE_GORENJE -#if DECODE_GREE - bool decodeGree(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kGreeBits, - const bool strict = true); -#endif -#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \ - DECODE_HAIER_AC176) - bool decodeHaierAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierACBits, - const bool strict = true); -#endif -#if DECODE_HAIER_AC_YRW02 - bool decodeHaierACYRW02(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierACYRW02Bits, - const bool strict = true); -#endif -#if DECODE_HAIER_AC160 - bool decodeHaierAC160(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierAC160Bits, - const bool strict = true); -#endif // DECODE_HAIER_AC160 -#if DECODE_HAIER_AC176 - bool decodeHaierAC176(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHaierAC176Bits, - const bool strict = true); -#endif // DECODE_HAIER_AC176 -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \ - DECODE_HITACHI_AC344) - bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAcBits, - const bool strict = true, const bool MSBfirst = true); -#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || - // DECODE_HITACHI_AC344) -#if DECODE_HITACHI_AC1 - bool decodeHitachiAC1(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc1Bits, - const bool strict = true); -#endif -#if DECODE_HITACHI_AC3 - bool decodeHitachiAc3(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc3Bits, - const bool strict = true); -#endif // DECODE_HITACHI_AC3 -#if DECODE_HITACHI_AC296 - bool decodeHitachiAc296(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc296Bits, - const bool strict = true); -#endif // DECODE_HITACHI_AC296 -#if DECODE_HITACHI_AC424 - bool decodeHitachiAc424(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kHitachiAc424Bits, - const bool strict = true); -#endif // DECODE_HITACHI_AC424 -#if DECODE_GICABLE - bool decodeGICable(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kGicableBits, - const bool strict = true); -#endif -#if DECODE_WHIRLPOOL_AC - bool decodeWhirlpoolAC(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kWhirlpoolAcBits, - const bool strict = true); -#endif -#if DECODE_LUTRON - bool decodeLutron(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLutronBits, - const bool strict = true); -#endif -#if DECODE_ELECTRA_AC - bool decodeElectraAC(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kElectraAcBits, - const bool strict = true); -#endif -#if DECODE_PANASONIC_AC - bool decodePanasonicAC(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kPanasonicAcBits, - const bool strict = true); -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - bool decodePanasonicAC32(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kPanasonicAc32Bits, - const bool strict = true); -#endif // DECODE_PANASONIC_AC32 -#if DECODE_PIONEER - bool decodePioneer(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kPioneerBits, - const bool strict = true); -#endif -#if DECODE_MWM - bool decodeMWM(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = 24, - const bool strict = true); -#endif -#if DECODE_VESTEL_AC - bool decodeVestelAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kVestelAcBits, - const bool strict = true); -#endif -#if DECODE_TECO - bool decodeTeco(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTecoBits, - const bool strict = false); -#endif -#if DECODE_LEGOPF - bool decodeLegoPf(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kLegoPfBits, - const bool strict = true); -#endif -#if DECODE_NEOCLIMA - bool decodeNeoclima(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kNeoclimaBits, - const bool strict = true); -#endif // DECODE_NEOCLIMA -#if DECODE_AMCOR - bool decodeAmcor(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAmcorBits, - const bool strict = true); -#endif // DECODE_AMCOR -#if DECODE_EPSON - bool decodeEpson(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kEpsonBits, - const bool strict = true); -#endif // DECODE_EPSON -#if DECODE_SYMPHONY - bool decodeSymphony(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kSymphonyBits, - const bool strict = true); -#endif // DECODE_SYMPHONY -#if DECODE_AIRWELL - bool decodeAirwell(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAirwellBits, - const bool strict = true); -#endif // DECODE_AIRWELL -#if DECODE_DELONGHI_AC - bool decodeDelonghiAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDelonghiAcBits, - const bool strict = true); -#endif // DECODE_DELONGHI_AC -#if DECODE_DOSHISHA - bool decodeDoshisha(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDoshishaBits, - const bool strict = true); -#endif // DECODE_DOSHISHA -#if DECODE_MULTIBRACKETS - bool decodeMultibrackets(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMultibracketsBits, - const bool strict = true); -#endif // DECODE_MULTIBRACKETS -#if DECODE_TECHNIBEL_AC - bool decodeTechnibelAc(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kTechnibelAcBits, - const bool strict = true); -#endif // DECODE_TECHNIBEL_AC -#if DECODE_CORONA_AC - bool decodeCoronaAc(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kCoronaAcBitsShort, - const bool strict = true); -#endif // DECODE_CORONA_AC -#if DECODE_ZEPEAL - bool decodeZepeal(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kZepealBits, - const bool strict = true); -#endif // DECODE_ZEPEAL -#if DECODE_METZ - bool decodeMetz(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kMetzBits, - const bool strict = true); -#endif // DECODE_METZ -#if DECODE_TRANSCOLD - bool decodeTranscold(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTranscoldBits, - const bool strict = true); -#endif // DECODE_TRANSCOLD -#if DECODE_MIRAGE - bool decodeMirage(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kMirageBits, - const bool strict = true); -#endif // DECODE_MIRAGE -#if DECODE_ELITESCREENS - bool decodeElitescreens(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kEliteScreensBits, - const bool strict = true); -#endif // DECODE_ELITESCREENS -#if DECODE_ECOCLIM - bool decodeEcoclim(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kEcoclimBits, - const bool strict = true); -#endif // DECODE_ECOCLIM -#if DECODE_XMP - bool decodeXmp(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kXmpBits, const bool strict = true); -#endif // DECODE_XMP -#if DECODE_TRUMA - bool decodeTruma(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTrumaBits, const bool strict = true); -#endif // DECODE_TRUMA -#if DECODE_TEKNOPOINT - bool decodeTeknopoint(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTeknopointBits, - const bool strict = true); -#endif // DECODE_TEKNOPOINT -#if DECODE_KELON - bool decodeKelon(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kKelonBits, const bool strict = true); -#endif // DECODE_KELON -#if DECODE_KELON168 - bool decodeKelon168(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kKelon168Bits, - const bool strict = true); -#endif // DECODE_KELON168 -#if DECODE_BOSE - bool decodeBose(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kBoseBits, const bool strict = true); -#endif // DECODE_BOSE -#if DECODE_RHOSS - bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kRhossBits, const bool strict = true); -#endif // DECODE_RHOSS -#if DECODE_AIRTON - bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kAirtonBits, - const bool strict = true); -#endif // DECODE_AIRTON -#if DECODE_TOTO - bool decodeToto(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kTotoBits, - const bool strict = true); -#endif // DECODE_TOTO -#if DECODE_CLIMABUTLER - bool decodeClimaButler(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kClimaButlerBits, - const bool strict = true); -#endif // DECODE_CLIMABUTLER -#if DECODE_TCL96AC - bool decodeTcl96Ac(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kTcl96AcBits, - const bool strict = true); -#endif // DECODE_TCL96AC -#if DECODE_BOSCH144 - bool decodeBosch144(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kBosch144Bits, - const bool strict = true); -#endif // DECODE_BOSCH144 -#if DECODE_WOWWEE - bool decodeWowwee(decode_results *results, - uint16_t offset = kStartOffset, - const uint16_t nbits = kWowweeBits, - const bool strict = true); -#endif // DECODE_WOWWEE -#if DECODE_YORK - bool decodeYork(decode_results *results, - uint16_t kStartOffset, - const uint16_t kYorkBits, - const bool strict = true); -#endif // DECODE_YORK -}; - -#endif // IRRECV_H_ +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2015 Sebastien Warin +// Copyright 2017 David Conran + +#ifndef IRRECV_H_ +#define IRRECV_H_ + +#ifndef UNIT_TEST +#include +#endif +#include +#define __STDC_LIMIT_MACROS +#include +#include "IRremoteESP8266.h" + +// Constants +const uint16_t kHeader = 2; // Usual nr. of header entries. +const uint16_t kFooter = 2; // Usual nr. of footer (stop bits) entries. +const uint16_t kStartOffset = 1; // Usual rawbuf entry to start from. +#define MS_TO_USEC(x) ((x) * 1000U) // Convert milli-Seconds to micro-Seconds. +// Marks tend to be 100us too long, and spaces 100us too short +// when received due to sensor lag. +const uint16_t kMarkExcess = 50; +const uint16_t kRawBuf = 100; // Default length of raw capture buffer +const uint64_t kRepeat = UINT64_MAX; +// Default min size of reported UNKNOWN messages. +const uint16_t kUnknownThreshold = 6; + +// receiver states +const uint8_t kIdleState = 2; +const uint8_t kMarkState = 3; +const uint8_t kSpaceState = 4; +const uint8_t kStopState = 5; +const uint8_t kTolerance = 25; // default percent tolerance in measurements. +const uint8_t kUseDefTol = 255; // Indicate to use the class default tolerance. +const uint16_t kRawTick = 2; // Capture tick to uSec factor. +#define RAWTICK kRawTick // Deprecated. For legacy user code support only. +// How long (ms) before we give up wait for more data? +// Don't exceed kMaxTimeoutMs without a good reason. +// That is the capture buffers maximum value size. (UINT16_MAX / kRawTick) +// Typically messages/protocols tend to repeat around the 100ms timeframe, +// thus we should timeout before that to give us some time to try to decode +// before we need to start capturing a possible new message. +// Typically 15ms suits most applications. However, some protocols demand a +// higher value. e.g. 90ms for XMP-1 and some aircon units. +const uint8_t kTimeoutMs = 15; // In MilliSeconds. +#define TIMEOUT_MS kTimeoutMs // For legacy documentation. +const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1)); + +// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param +const uint32_t kFnvPrime32 = 16777619UL; +const uint32_t kFnvBasis32 = 2166136261UL; + +#ifdef ESP32 +// Which of the ESP32 timers to use by default. +// (3 for most ESP32s, 1 for ESP32-C3s) +#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS +const uint8_t kDefaultESP32Timer = SOC_TIMER_GROUP_TOTAL_TIMERS - 1; +#else // SOC_TIMER_GROUP_TOTAL_TIMERS +const uint8_t kDefaultESP32Timer = 3; +#endif // SOC_TIMER_GROUP_TOTAL_TIMERS +#endif // ESP32 + +#if DECODE_AC +// Hitachi AC is the current largest state size. +const uint16_t kStateSizeMax = kHitachiAc2StateLength; +#else // DECODE_AC +// Just define something (a uint64_t) +const uint16_t kStateSizeMax = sizeof(uint64_t); +#endif // DECODE_AC + +// Types + +/// Information for the interrupt handler +typedef struct { + uint8_t recvpin; // pin for IR data from detector + uint8_t rcvstate; // state machine + uint16_t timer; // state timer, counts 50uS ticks. + uint16_t bufsize; // max. nr. of entries in the capture buffer. + uint16_t *rawbuf; // raw data + // uint16_t is used for rawlen as it saves 3 bytes of iram in the interrupt + // handler. Don't ask why, I don't know. It just does. + uint16_t rawlen; // counter of entries in rawbuf. + uint8_t overflow; // Buffer overflow indicator. + uint8_t timeout; // Nr. of milliSeconds before we give up. +} irparams_t; + +/// Results from a data match +typedef struct { + bool success; // Was the match successful? + uint64_t data; // The data found. + uint16_t used; // How many buffer positions were used. +} match_result_t; + +// Classes + +/// Results returned from the decoder +class decode_results { + public: + decode_type_t decode_type; // NEC, SONY, RC5, UNKNOWN + // value, address, & command are all mutually exclusive with state. + // i.e. They MUST NOT be used at the same time as state, so we can use a union + // structure to save us a handful of valuable bytes of memory. + union { + struct { + uint64_t value; // Decoded value + uint32_t address; // Decoded device address. + uint32_t command; // Decoded command. + }; + uint8_t state[kStateSizeMax]; // Multi-byte results. + }; + uint16_t bits; // Number of bits in decoded value + volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks + uint16_t rawlen; // Number of records in rawbuf. + bool overflow; + bool repeat; // Is the result a repeat code? +}; + +/// Class for receiving IR messages. +class IRrecv { + public: +#if defined(ESP32) + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false, + const uint8_t timer_num = kDefaultESP32Timer); // Constructor +#else // ESP32 + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false); // Constructor +#endif // ESP32 + ~IRrecv(void); // Destructor + void setTolerance(const uint8_t percent = kTolerance); + uint8_t getTolerance(void); + bool decode(decode_results *results, irparams_t *save = NULL, + uint8_t max_skip = 0, uint16_t noise_floor = 0); + void enableIRIn(const bool pullup = false); + void disableIRIn(void); + void pause(void); + void resume(void); + uint16_t getBufSize(void); +#if DECODE_HASH + void setUnknownThreshold(const uint16_t length); +#endif + bool match(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + bool matchMark(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess); + bool matchMarkRange(const uint32_t measured, const uint32_t desired, + const uint16_t range = 100, + const int16_t excess = kMarkExcess); + bool matchSpace(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess); + bool matchSpaceRange(const uint32_t measured, const uint32_t desired, + const uint16_t range = 100, + const int16_t excess = kMarkExcess); +#ifndef UNIT_TEST + + private: +#endif + irparams_t *irparams_save; + uint8_t _tolerance; +#if defined(ESP32) + uint8_t _timer_num; +#endif // defined(ESP32) +#if DECODE_HASH + uint16_t _unknown_threshold; +#endif +#ifdef UNIT_TEST + volatile irparams_t *_getParamsPtr(void); +#endif // UNIT_TEST + // These are called by decode + uint8_t _validTolerance(const uint8_t percentage); + void copyIrParams(volatile irparams_t *src, irparams_t *dst); + uint16_t compare(const uint16_t oldval, const uint16_t newval); + uint32_t ticksLow(const uint32_t usecs, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + uint32_t ticksHigh(const uint32_t usecs, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + bool matchAtLeast(const uint32_t measured, const uint32_t desired, + const uint8_t tolerance = kUseDefTol, + const uint16_t delta = 0); + uint16_t _matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t required, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool expectlastspace = true); + uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool expectlastspace = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGenericConstBitTime(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t one, + const uint32_t zero, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchManchesterData(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t half_period, + const uint16_t starting_balance = 0, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool GEThomas = true); + uint16_t matchManchester(volatile const uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t clock_period, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true, + const bool GEThomas = true); + void crudeNoiseFilter(decode_results *results, const uint16_t floor = 0); + bool decodeHash(decode_results *results); +#if DECODE_VOLTAS + bool decodeVoltas(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kVoltasBits, + const bool strict = true); +#endif // DECODE_VOLTAS +#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO) + bool decodeNEC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kNECBits, const bool strict = true); +#endif +#if DECODE_ARGO + bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kArgoBits, const bool strict = true); + bool decodeArgoWREM3(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kArgo3AcControlStateLength * 8, + const bool strict = true); +#endif // DECODE_ARGO +#if DECODE_ARRIS + bool decodeArris(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kArrisBits, const bool strict = true); +#endif // DECODE_ARRIS +#if DECODE_SONY + bool decodeSony(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSonyMinBits, + const bool strict = false); +#endif +#if DECODE_SANYO + // DISABLED due to poor quality. + // bool decodeSanyo(decode_results *results, uint16_t offset = kStartOffset, + // uint16_t nbits = kSanyoSA8650BBits, + // bool strict = false); + bool decodeSanyoLC7461(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoLC7461Bits, + const bool strict = true); +#endif +#if DECODE_SANYO_AC + bool decodeSanyoAc(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAcBits, + const bool strict = true); +#endif // DECODE_SANYO_AC +#if DECODE_SANYO_AC88 + bool decodeSanyoAc88(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAc88Bits, + const bool strict = true); +#endif // DECODE_SANYO_AC88 +#if DECODE_SANYO_AC152 + bool decodeSanyoAc152(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAc152Bits, + const bool strict = true); +#endif // DECODE_SANYO_AC152 +#if DECODE_MITSUBISHI + bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiBits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHI2 + bool decodeMitsubishi2(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiBits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHI_AC + bool decodeMitsubishiAC(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiACBits, + const bool strict = false); +#endif +#if DECODE_MITSUBISHI136 + bool decodeMitsubishi136(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishi136Bits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHI112 + bool decodeMitsubishi112(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishi112Bits, + const bool strict = true); +#endif +#if DECODE_MITSUBISHIHEAVY + bool decodeMitsubishiHeavy(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMitsubishiHeavy152Bits, + const bool strict = true); +#endif +#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG || DECODE_MWM) + int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, + uint16_t bitTime, const uint8_t tolerance = kUseDefTol, + const int16_t excess = kMarkExcess, + const uint16_t delta = 0, const uint8_t maxwidth = 3); +#endif +#if DECODE_RC5 + bool decodeRC5(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRC5XBits, + const bool strict = true); +#endif +#if DECODE_RC6 + bool decodeRC6(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRC6Mode0Bits, + const bool strict = false); +#endif +#if DECODE_RCMM + bool decodeRCMM(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRCMMBits, + const bool strict = false); +#endif +#if (DECODE_PANASONIC || DECODE_DENON) + bool decodePanasonic(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kPanasonicBits, + const bool strict = false, + const uint32_t manufacturer = kPanasonicManufacturer); +#endif +#if DECODE_LG + bool decodeLG(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLgBits, + const bool strict = false); +#endif +#if DECODE_INAX + bool decodeInax(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kInaxBits, + const bool strict = true); +#endif // DECODE_INAX +#if DECODE_JVC + bool decodeJVC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kJvcBits, + const bool strict = true); +#endif +#if DECODE_SAMSUNG + bool decodeSAMSUNG(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSamsungBits, + const bool strict = true); +#endif +#if DECODE_SAMSUNG + bool decodeSamsung36(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSamsung36Bits, + const bool strict = true); +#endif +#if DECODE_SAMSUNG_AC + bool decodeSamsungAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSamsungAcBits, + const bool strict = true); +#endif +#if DECODE_WHYNTER + bool decodeWhynter(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kWhynterBits, + const bool strict = true); +#endif +#if DECODE_COOLIX + bool decodeCOOLIX(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCoolixBits, + const bool strict = true); +#endif // DECODE_COOLIX +#if DECODE_COOLIX48 + bool decodeCoolix48(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCoolix48Bits, + const bool strict = true); +#endif // DECODE_COOLIX48 +#if DECODE_DENON + bool decodeDenon(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDenonBits, + const bool strict = true); +#endif +#if DECODE_DISH + bool decodeDISH(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDishBits, + const bool strict = true); +#endif +#if (DECODE_SHARP || DECODE_DENON) + bool decodeSharp(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSharpBits, + const bool strict = true, const bool expansion = true); +#endif +#if DECODE_SHARP_AC + bool decodeSharpAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSharpAcBits, + const bool strict = true); +#endif +#if DECODE_AIWA_RC_T501 + bool decodeAiwaRCT501(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAiwaRcT501Bits, + const bool strict = true); +#endif +#if DECODE_NIKAI + bool decodeNikai(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kNikaiBits, + const bool strict = true); +#endif +#if DECODE_MAGIQUEST + bool decodeMagiQuest(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMagiquestBits, + const bool strict = true); +#endif +#if DECODE_KELVINATOR + bool decodeKelvinator(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kKelvinatorBits, + const bool strict = true); +#endif +#if DECODE_DAIKIN + bool decodeDaikin(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikinBits, + const bool strict = true); +#endif +#if DECODE_DAIKIN64 + bool decodeDaikin64(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin64Bits, + const bool strict = true); +#endif // DECODE_DAIKIN64 +#if DECODE_DAIKIN128 + bool decodeDaikin128(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin128Bits, + const bool strict = true); +#endif // DECODE_DAIKIN128 +#if DECODE_DAIKIN152 + bool decodeDaikin152(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin152Bits, + const bool strict = true); +#endif // DECODE_DAIKIN152 +#if DECODE_DAIKIN160 + bool decodeDaikin160(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin160Bits, + const bool strict = true); +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + bool decodeDaikin176(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin176Bits, + const bool strict = true); +#endif // DECODE_DAIKIN176 +#if DECODE_DAIKIN2 + bool decodeDaikin2(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin2Bits, + const bool strict = true); +#endif +#if DECODE_DAIKIN200 + bool decodeDaikin200(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin200Bits, + const bool strict = true); +#endif // DECODE_DAIKIN200 +#if DECODE_DAIKIN216 + bool decodeDaikin216(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin216Bits, + const bool strict = true); +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN312 + bool decodeDaikin312(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin312Bits, + const bool strict = true); +#endif // DECODE_DAIKIN312 +#if DECODE_TOSHIBA_AC + bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kToshibaACBits, + const bool strict = true); +#endif +#if DECODE_TROTEC + bool decodeTrotec(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTrotecBits, + const bool strict = true); +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + bool decodeTrotec3550(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTrotecBits, + const bool strict = true); +#endif // DECODE_TROTEC_3550 +#if DECODE_MIDEA + bool decodeMidea(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMideaBits, + const bool strict = true); +#endif // DECODE_MIDEA +#if DECODE_MIDEA24 + bool decodeMidea24(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMidea24Bits, + const bool strict = true); +#endif // DECODE_MIDEA24 +#if DECODE_FUJITSU_AC + bool decodeFujitsuAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kFujitsuAcBits, + const bool strict = false); +#endif +#if DECODE_FUJITSU_AC264 + bool decodeFujitsuAC264(decode_results* results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kFujitsuAc264Bits, + const bool strict = true); +#endif // DECODE_FUJITSU_AC264 +#if DECODE_LASERTAG + bool decodeLasertag(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLasertagBits, + const bool strict = true); +#endif +#if DECODE_MILESTAG2 + bool decodeMilestag2(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMilesTag2ShotBits, + const bool strict = true); +#endif +#if DECODE_CARRIER_AC + bool decodeCarrierAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAcBits, + const bool strict = true); +#endif // DECODE_CARRIER_AC +#if DECODE_CARRIER_AC40 + bool decodeCarrierAC40(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc40Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC40 +#if DECODE_CARRIER_AC84 + bool decodeCarrierAC84(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc84Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC84 +#if DECODE_CARRIER_AC64 + bool decodeCarrierAC64(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc64Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC64 +#if DECODE_CARRIER_AC128 + bool decodeCarrierAC128(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc128Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC128 +#if DECODE_GOODWEATHER + bool decodeGoodweather(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kGoodweatherBits, + const bool strict = true); +#endif // DECODE_GOODWEATHER +#if DECODE_GORENJE + bool decodeGorenje(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kGorenjeBits, + const bool strict = true); +#endif // DECODE_GORENJE +#if DECODE_GREE + bool decodeGree(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kGreeBits, + const bool strict = true); +#endif +#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \ + DECODE_HAIER_AC176) + bool decodeHaierAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierACBits, + const bool strict = true); +#endif +#if DECODE_HAIER_AC_YRW02 + bool decodeHaierACYRW02(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierACYRW02Bits, + const bool strict = true); +#endif +#if DECODE_HAIER_AC160 + bool decodeHaierAC160(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierAC160Bits, + const bool strict = true); +#endif // DECODE_HAIER_AC160 +#if DECODE_HAIER_AC176 + bool decodeHaierAC176(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierAC176Bits, + const bool strict = true); +#endif // DECODE_HAIER_AC176 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \ + DECODE_HITACHI_AC344) + bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAcBits, + const bool strict = true, const bool MSBfirst = true); +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || + // DECODE_HITACHI_AC344) +#if DECODE_HITACHI_AC1 + bool decodeHitachiAC1(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc1Bits, + const bool strict = true); +#endif +#if DECODE_HITACHI_AC3 + bool decodeHitachiAc3(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc3Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC3 +#if DECODE_HITACHI_AC296 + bool decodeHitachiAc296(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc296Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC296 +#if DECODE_HITACHI_AC424 + bool decodeHitachiAc424(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc424Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC424 +#if DECODE_GICABLE + bool decodeGICable(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kGicableBits, + const bool strict = true); +#endif +#if DECODE_WHIRLPOOL_AC + bool decodeWhirlpoolAC(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kWhirlpoolAcBits, + const bool strict = true); +#endif +#if DECODE_LUTRON + bool decodeLutron(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLutronBits, + const bool strict = true); +#endif +#if DECODE_ELECTRA_AC + bool decodeElectraAC(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kElectraAcBits, + const bool strict = true); +#endif +#if DECODE_PANASONIC_AC + bool decodePanasonicAC(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kPanasonicAcBits, + const bool strict = true); +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + bool decodePanasonicAC32(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kPanasonicAc32Bits, + const bool strict = true); +#endif // DECODE_PANASONIC_AC32 +#if DECODE_PIONEER + bool decodePioneer(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kPioneerBits, + const bool strict = true); +#endif +#if DECODE_MWM + bool decodeMWM(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = 24, + const bool strict = true); +#endif +#if DECODE_VESTEL_AC + bool decodeVestelAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kVestelAcBits, + const bool strict = true); +#endif +#if DECODE_TECO + bool decodeTeco(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTecoBits, + const bool strict = false); +#endif +#if DECODE_LEGOPF + bool decodeLegoPf(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kLegoPfBits, + const bool strict = true); +#endif +#if DECODE_NEOCLIMA + bool decodeNeoclima(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kNeoclimaBits, + const bool strict = true); +#endif // DECODE_NEOCLIMA +#if DECODE_AMCOR + bool decodeAmcor(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAmcorBits, + const bool strict = true); +#endif // DECODE_AMCOR +#if DECODE_EPSON + bool decodeEpson(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kEpsonBits, + const bool strict = true); +#endif // DECODE_EPSON +#if DECODE_SYMPHONY + bool decodeSymphony(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kSymphonyBits, + const bool strict = true); +#endif // DECODE_SYMPHONY +#if DECODE_AIRWELL + bool decodeAirwell(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAirwellBits, + const bool strict = true); +#endif // DECODE_AIRWELL +#if DECODE_DELONGHI_AC + bool decodeDelonghiAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDelonghiAcBits, + const bool strict = true); +#endif // DECODE_DELONGHI_AC +#if DECODE_DOSHISHA + bool decodeDoshisha(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDoshishaBits, + const bool strict = true); +#endif // DECODE_DOSHISHA +#if DECODE_MULTIBRACKETS + bool decodeMultibrackets(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMultibracketsBits, + const bool strict = true); +#endif // DECODE_MULTIBRACKETS +#if DECODE_TECHNIBEL_AC + bool decodeTechnibelAc(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kTechnibelAcBits, + const bool strict = true); +#endif // DECODE_TECHNIBEL_AC +#if DECODE_CORONA_AC + bool decodeCoronaAc(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCoronaAcBitsShort, + const bool strict = true); +#endif // DECODE_CORONA_AC +#if DECODE_ZEPEAL + bool decodeZepeal(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kZepealBits, + const bool strict = true); +#endif // DECODE_ZEPEAL +#if DECODE_METZ + bool decodeMetz(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kMetzBits, + const bool strict = true); +#endif // DECODE_METZ +#if DECODE_TRANSCOLD + bool decodeTranscold(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTranscoldBits, + const bool strict = true); +#endif // DECODE_TRANSCOLD +#if DECODE_MIRAGE + bool decodeMirage(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMirageBits, + const bool strict = true); +#endif // DECODE_MIRAGE +#if DECODE_ELITESCREENS + bool decodeElitescreens(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kEliteScreensBits, + const bool strict = true); +#endif // DECODE_ELITESCREENS +#if DECODE_ECOCLIM + bool decodeEcoclim(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kEcoclimBits, + const bool strict = true); +#endif // DECODE_ECOCLIM +#if DECODE_XMP + bool decodeXmp(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kXmpBits, const bool strict = true); +#endif // DECODE_XMP +#if DECODE_TRUMA + bool decodeTruma(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTrumaBits, const bool strict = true); +#endif // DECODE_TRUMA +#if DECODE_TEKNOPOINT + bool decodeTeknopoint(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTeknopointBits, + const bool strict = true); +#endif // DECODE_TEKNOPOINT +#if DECODE_KELON + bool decodeKelon(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kKelonBits, const bool strict = true); +#endif // DECODE_KELON +#if DECODE_KELON168 + bool decodeKelon168(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kKelon168Bits, + const bool strict = true); +#endif // DECODE_KELON168 +#if DECODE_BOSE + bool decodeBose(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kBoseBits, const bool strict = true); +#endif // DECODE_BOSE +#if DECODE_RHOSS + bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kRhossBits, const bool strict = true); +#endif // DECODE_RHOSS +#if DECODE_AIRTON + bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAirtonBits, + const bool strict = true); +#endif // DECODE_AIRTON +#if DECODE_TOTO + bool decodeToto(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTotoBits, + const bool strict = true); +#endif // DECODE_TOTO +#if DECODE_CLIMABUTLER + bool decodeClimaButler(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kClimaButlerBits, + const bool strict = true); +#endif // DECODE_CLIMABUTLER +#if DECODE_TCL96AC + bool decodeTcl96Ac(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kTcl96AcBits, + const bool strict = true); +#endif // DECODE_TCL96AC +#if DECODE_BOSCH144 + bool decodeBosch144(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kBosch144Bits, + const bool strict = true); +#endif // DECODE_BOSCH144 +#if DECODE_WOWWEE + bool decodeWowwee(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kWowweeBits, + const bool strict = true); +#endif // DECODE_WOWWEE +#if DECODE_YORK + bool decodeYork(decode_results *results, + uint16_t kStartOffset, + const uint16_t kYorkBits, + const bool strict = true); +#endif // DECODE_YORK +}; + +#endif // IRRECV_H_ diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 06512fbf4..c40ae4f1f 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -1,1539 +1,1539 @@ - /*************************************************** - * IRremote for ESP8266 - * - * Based on the IRremote library for Arduino by Ken Shirriff - * Version 0.11 August, 2009 - * Copyright 2009 Ken Shirriff - * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html - * - * Edited by Mitra to add new controller SANYO - * - * Interrupt code based on NECIRrcv by Joe Knapp - * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 - * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ - * - * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) - * LG added by Darryl Smith (based on the JVC protocol) - * Whynter A/C ARC-110WD added by Francesco Meschia - * Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit - * Denon: sendDenon, decodeDenon added by Massimiliano Pinto - (from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp) - * Kelvinator A/C and Sherwood added by crankyoldgit - * Mitsubishi (TV) sending added by crankyoldgit - * Pronto code sending added by crankyoldgit - * Mitsubishi & Toshiba A/C added by crankyoldgit - * (derived from https://github.com/r45635/HVAC-IR-Control) - * DISH decode by marcosamarinho - * Gree Heatpump sending added by Ville Skyttä (scop) - * (derived from https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp) - * Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for sending IR code on ESP8266 - * Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266 - * - * Updated by sillyfrog for Daikin, adopted from - * (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/) - * Fujitsu A/C code added by jonnygraham - * Trotec AC code by stufisher - * Carrier & Haier AC code by crankyoldgit - * Vestel AC code by Erdem U. Altınyurt - * Teco AC code by Fabien Valthier (hcoohb) - * Mitsubishi 112 AC Code by kuchel77 - * Kelon AC code by Davide Depau (Depau) - * - * GPL license, all text above must be included in any redistribution - ****************************************************/ - -#ifndef IRREMOTEESP8266_H_ -#define IRREMOTEESP8266_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifdef UNIT_TEST -#include -#include -#endif // UNIT_TEST - -// Library Version Information -// Major version number (X.x.x) -#define _IRREMOTEESP8266_VERSION_MAJOR 2 -// Minor version number (x.X.x) -#define _IRREMOTEESP8266_VERSION_MINOR 8 -// Patch version number (x.x.X) -#define _IRREMOTEESP8266_VERSION_PATCH 6 -// Macro to convert version info into an integer -#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \ - (((major) << 16) | ((minor) << 8) | (patch)) -// Macro to convert literal into a string -#define MKSTR_HELPER(x) #x -#define MKSTR(x) MKSTR_HELPER(x) -// Integer version -#define _IRREMOTEESP8266_VERSION _IRREMOTEESP8266_VERSION_VAL(\ - _IRREMOTEESP8266_VERSION_MAJOR, \ - _IRREMOTEESP8266_VERSION_MINOR, \ - _IRREMOTEESP8266_VERSION_PATCH) -// String version -#define _IRREMOTEESP8266_VERSION_STR MKSTR(_IRREMOTEESP8266_VERSION_MAJOR) "." \ - MKSTR(_IRREMOTEESP8266_VERSION_MINOR) "." \ - MKSTR(_IRREMOTEESP8266_VERSION_PATCH) -// String version (DEPRECATED) -#define _IRREMOTEESP8266_VERSION_ _IRREMOTEESP8266_VERSION_STR - -// Set the language & locale for the library. See the `locale` dir for options. -#ifndef _IR_LOCALE_ -#define _IR_LOCALE_ en-AU -#endif // _IR_LOCALE_ - -// Do we enable all the protocols by default (true), or disable them (false)? -// This allows users of the library to disable or enable all protocols at -// compile-time with `-D_IR_ENABLE_DEFAULT_=true` or -// `-D_IR_ENABLE_DEFAULT_=false` compiler flags respectively. -// Everything is included by default. -// e.g. If you only want to enable use of he NEC protocol to save program space, -// you would use something like: -// `-D_IR_ENABLE_DEFAULT_=false -DDECODE_NEC=true -DSEND_NEC=true` -// -// or alter your 'platform.ini' file accordingly: -// ``` -// build_flags = -D_IR_ENABLE_DEFAULT_=false -// -DDECODE_NEC=true -// -DSEND_NEC=true -// ``` -// If you want to enable support for every protocol *except* _decoding_ the -// Kelvinator protocol, you would use: -// `-DDECODE_KELVINATOR=false` -#ifndef _IR_ENABLE_DEFAULT_ -#define _IR_ENABLE_DEFAULT_ true // Unless set externally, the default is on. -#endif // _IR_ENABLE_DEFAULT_ - -// Supported IR protocols -// Each protocol you include costs memory and, during decode, costs time -// Disable (set to false) all the protocols you do not need/want! -// The Air Conditioner protocols are the most expensive memory-wise. -// - -// Semi-unique code for unknown messages -#ifndef DECODE_HASH -#define DECODE_HASH _IR_ENABLE_DEFAULT_ -#endif // DECODE_HASH - -#ifndef SEND_RAW -#define SEND_RAW _IR_ENABLE_DEFAULT_ -#endif // SEND_RAW - -#ifndef DECODE_NEC -#define DECODE_NEC _IR_ENABLE_DEFAULT_ -#endif // DECODE_NEC -#ifndef SEND_NEC -#define SEND_NEC _IR_ENABLE_DEFAULT_ -#endif // SEND_NEC - -#ifndef DECODE_SHERWOOD -#define DECODE_SHERWOOD false // Not applicable. Actually is DECODE_NEC -#endif // DECODE_SHERWOOD -#ifndef SEND_SHERWOOD -#define SEND_SHERWOOD _IR_ENABLE_DEFAULT_ -#endif // SEND_SHERWOOD - -#ifndef DECODE_RC5 -#define DECODE_RC5 _IR_ENABLE_DEFAULT_ -#endif // DECODE_RC5 -#ifndef SEND_RC5 -#define SEND_RC5 _IR_ENABLE_DEFAULT_ -#endif // SEND_RC5 - -#ifndef DECODE_RC6 -#define DECODE_RC6 _IR_ENABLE_DEFAULT_ -#endif // DECODE_RC6 -#ifndef SEND_RC6 -#define SEND_RC6 _IR_ENABLE_DEFAULT_ -#endif // SEND_RC6 - -#ifndef DECODE_RCMM -#define DECODE_RCMM _IR_ENABLE_DEFAULT_ -#endif // DECODE_RCMM -#ifndef SEND_RCMM -#define SEND_RCMM _IR_ENABLE_DEFAULT_ -#endif // SEND_RCMM - -#ifndef DECODE_SONY -#define DECODE_SONY _IR_ENABLE_DEFAULT_ -#endif // DECODE_SONY -#ifndef SEND_SONY -#define SEND_SONY _IR_ENABLE_DEFAULT_ -#endif // SEND_SONY - -#ifndef DECODE_PANASONIC -#define DECODE_PANASONIC _IR_ENABLE_DEFAULT_ -#endif // DECODE_PANASONIC -#ifndef SEND_PANASONIC -#define SEND_PANASONIC _IR_ENABLE_DEFAULT_ -#endif // SEND_PANASONIC - -#ifndef DECODE_JVC -#define DECODE_JVC _IR_ENABLE_DEFAULT_ -#endif // DECODE_JVC -#ifndef SEND_JVC -#define SEND_JVC _IR_ENABLE_DEFAULT_ -#endif // SEND_JVC - -#ifndef DECODE_SAMSUNG -#define DECODE_SAMSUNG _IR_ENABLE_DEFAULT_ -#endif // DECODE_SAMSUNG -#ifndef SEND_SAMSUNG -#define SEND_SAMSUNG _IR_ENABLE_DEFAULT_ -#endif // SEND_SAMSUNG - -#ifndef DECODE_SAMSUNG36 -#define DECODE_SAMSUNG36 _IR_ENABLE_DEFAULT_ -#endif // DECODE_SAMSUNG36 -#ifndef SEND_SAMSUNG36 -#define SEND_SAMSUNG36 _IR_ENABLE_DEFAULT_ -#endif // SEND_SAMSUNG36 - -#ifndef DECODE_SAMSUNG_AC -#define DECODE_SAMSUNG_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_SAMSUNG_AC -#ifndef SEND_SAMSUNG_AC -#define SEND_SAMSUNG_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_SAMSUNG_AC - -#ifndef DECODE_WHYNTER -#define DECODE_WHYNTER _IR_ENABLE_DEFAULT_ -#endif // DECODE_WHYNTER -#ifndef SEND_WHYNTER -#define SEND_WHYNTER _IR_ENABLE_DEFAULT_ -#endif // SEND_WHYNTER - -#ifndef DECODE_AIWA_RC_T501 -#define DECODE_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ -#endif // DECODE_AIWA_RC_T501 -#ifndef SEND_AIWA_RC_T501 -#define SEND_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ -#endif // SEND_AIWA_RC_T501 - -#ifndef DECODE_LG -#define DECODE_LG _IR_ENABLE_DEFAULT_ -#endif // DECODE_LG -#ifndef SEND_LG -#define SEND_LG _IR_ENABLE_DEFAULT_ -#endif // SEND_LG - -#ifndef DECODE_SANYO -#define DECODE_SANYO _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO -#ifndef SEND_SANYO -#define SEND_SANYO _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO - -#ifndef DECODE_SANYO_AC -#define DECODE_SANYO_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO_AC -#ifndef SEND_SANYO_AC -#define SEND_SANYO_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO_AC - -#ifndef DECODE_SANYO_AC88 -#define DECODE_SANYO_AC88 _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO_AC88 -#ifndef SEND_SANYO_AC88 -#define SEND_SANYO_AC88 _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO_AC88 - -#ifndef DECODE_SANYO_AC152 -#define DECODE_SANYO_AC152 _IR_ENABLE_DEFAULT_ -#endif // DECODE_SANYO_AC152 -#ifndef SEND_SANYO_AC152 -#define SEND_SANYO_AC152 _IR_ENABLE_DEFAULT_ -#endif // SEND_SANYO_AC152 - -#ifndef DECODE_MITSUBISHI -#define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI -#ifndef SEND_MITSUBISHI -#define SEND_MITSUBISHI _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI - -#ifndef DECODE_MITSUBISHI2 -#define DECODE_MITSUBISHI2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI2 -#ifndef SEND_MITSUBISHI2 -#define SEND_MITSUBISHI2 _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI2 - -#ifndef DECODE_DISH -#define DECODE_DISH _IR_ENABLE_DEFAULT_ -#endif // DECODE_DISH -#ifndef SEND_DISH -#define SEND_DISH _IR_ENABLE_DEFAULT_ -#endif // SEND_DISH - -#ifndef DECODE_SHARP -#define DECODE_SHARP _IR_ENABLE_DEFAULT_ -#endif // DECODE_SHARP -#ifndef SEND_SHARP -#define SEND_SHARP _IR_ENABLE_DEFAULT_ -#endif // SEND_SHARP - -#ifndef DECODE_SHARP_AC -#define DECODE_SHARP_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_SHARP_AC -#ifndef SEND_SHARP_AC -#define SEND_SHARP_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_SHARP_AC - -#ifndef DECODE_DENON -#define DECODE_DENON _IR_ENABLE_DEFAULT_ -#endif // DECODE_DENON -#ifndef SEND_DENON -#define SEND_DENON _IR_ENABLE_DEFAULT_ -#endif // SEND_DENON - -#ifndef DECODE_KELVINATOR -#define DECODE_KELVINATOR _IR_ENABLE_DEFAULT_ -#endif // DECODE_KELVINATOR -#ifndef SEND_KELVINATOR -#define SEND_KELVINATOR _IR_ENABLE_DEFAULT_ -#endif // SEND_KELVINATOR - -#ifndef DECODE_MITSUBISHI_AC -#define DECODE_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI_AC -#ifndef SEND_MITSUBISHI_AC -#define SEND_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI_AC - -#ifndef DECODE_MITSUBISHI136 -#define DECODE_MITSUBISHI136 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI136 -#ifndef SEND_MITSUBISHI136 -#define SEND_MITSUBISHI136 _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI136 - -#ifndef DECODE_MITSUBISHI112 -#define DECODE_MITSUBISHI112 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHI112 -#ifndef SEND_MITSUBISHI112 -#define SEND_MITSUBISHI112 _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHI112 - -#ifndef DECODE_FUJITSU_AC -#define DECODE_FUJITSU_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_FUJITSU_AC -#ifndef SEND_FUJITSU_AC -#define SEND_FUJITSU_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_FUJITSU_AC - -#ifndef DECODE_INAX -#define DECODE_INAX _IR_ENABLE_DEFAULT_ -#endif // DECODE_INAX -#ifndef SEND_INAX -#define SEND_INAX _IR_ENABLE_DEFAULT_ -#endif // SEND_INAX - -#ifndef DECODE_DAIKIN -#define DECODE_DAIKIN _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN -#ifndef SEND_DAIKIN -#define SEND_DAIKIN _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN - -#ifndef DECODE_COOLIX -#define DECODE_COOLIX _IR_ENABLE_DEFAULT_ -#endif // DECODE_COOLIX -#ifndef SEND_COOLIX -#define SEND_COOLIX _IR_ENABLE_DEFAULT_ -#endif // SEND_COOLIX - -#ifndef DECODE_COOLIX48 -#define DECODE_COOLIX48 _IR_ENABLE_DEFAULT_ -#endif // DECODE_COOLIX48 -#ifndef SEND_COOLIX48 -#define SEND_COOLIX48 _IR_ENABLE_DEFAULT_ -#endif // SEND_COOLIX48 - -#ifndef DECODE_GLOBALCACHE -#define DECODE_GLOBALCACHE false // Not applicable. -#endif // DECODE_GLOBALCACHE -#ifndef SEND_GLOBALCACHE -#define SEND_GLOBALCACHE _IR_ENABLE_DEFAULT_ -#endif // SEND_GLOBALCACHE - -#ifndef DECODE_GOODWEATHER -#define DECODE_GOODWEATHER _IR_ENABLE_DEFAULT_ -#endif // DECODE_GOODWEATHER -#ifndef SEND_GOODWEATHER -#define SEND_GOODWEATHER _IR_ENABLE_DEFAULT_ -#endif // SEND_GOODWEATHER - -#ifndef DECODE_GREE -#define DECODE_GREE _IR_ENABLE_DEFAULT_ -#endif // DECODE_GREE -#ifndef SEND_GREE -#define SEND_GREE _IR_ENABLE_DEFAULT_ -#endif // SEND_GREE - -#ifndef DECODE_PRONTO -#define DECODE_PRONTO false // Not applicable. -#endif // DECODE_PRONTO -#ifndef SEND_PRONTO -#define SEND_PRONTO _IR_ENABLE_DEFAULT_ -#endif // SEND_PRONTO - -#ifndef DECODE_ARGO -#define DECODE_ARGO _IR_ENABLE_DEFAULT_ -#endif // DECODE_ARGO -#ifndef SEND_ARGO -#define SEND_ARGO _IR_ENABLE_DEFAULT_ -#endif // SEND_ARGO - -#ifndef DECODE_TROTEC -#define DECODE_TROTEC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TROTEC -#ifndef SEND_TROTEC -#define SEND_TROTEC _IR_ENABLE_DEFAULT_ -#endif // SEND_TROTEC - -#ifndef DECODE_TROTEC_3550 -#define DECODE_TROTEC_3550 _IR_ENABLE_DEFAULT_ -#endif // DECODE_TROTEC_3550 -#ifndef SEND_TROTEC_3550 -#define SEND_TROTEC_3550 _IR_ENABLE_DEFAULT_ -#endif // SEND_TROTEC_3550 - -#ifndef DECODE_NIKAI -#define DECODE_NIKAI _IR_ENABLE_DEFAULT_ -#endif // DECODE_NIKAI -#ifndef SEND_NIKAI -#define SEND_NIKAI _IR_ENABLE_DEFAULT_ -#endif // SEND_NIKAI - -#ifndef DECODE_TOSHIBA_AC -#define DECODE_TOSHIBA_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TOSHIBA_AC -#ifndef SEND_TOSHIBA_AC -#define SEND_TOSHIBA_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TOSHIBA_AC - -#ifndef DECODE_MAGIQUEST -#define DECODE_MAGIQUEST _IR_ENABLE_DEFAULT_ -#endif // DECODE_MAGIQUEST -#ifndef SEND_MAGIQUEST -#define SEND_MAGIQUEST _IR_ENABLE_DEFAULT_ -#endif // SEND_MAGIQUEST - -#ifndef DECODE_MIDEA -#define DECODE_MIDEA _IR_ENABLE_DEFAULT_ -#endif // DECODE_MIDEA -#ifndef SEND_MIDEA -#define SEND_MIDEA _IR_ENABLE_DEFAULT_ -#endif // SEND_MIDEA - -#ifndef DECODE_MIDEA24 -#define DECODE_MIDEA24 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MIDEA24 -#ifndef SEND_MIDEA24 -#define SEND_MIDEA24 _IR_ENABLE_DEFAULT_ -#endif // SEND_MIDEA24 - -#ifndef DECODE_LASERTAG -#define DECODE_LASERTAG _IR_ENABLE_DEFAULT_ -#endif // DECODE_LASERTAG -#ifndef SEND_LASERTAG -#define SEND_LASERTAG _IR_ENABLE_DEFAULT_ -#endif // SEND_LASERTAG - -#ifndef DECODE_CARRIER_AC -#define DECODE_CARRIER_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC -#ifndef SEND_CARRIER_AC -#define SEND_CARRIER_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC - -#ifndef DECODE_CARRIER_AC40 -#define DECODE_CARRIER_AC40 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC40 -#ifndef SEND_CARRIER_AC40 -#define SEND_CARRIER_AC40 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC40 - -#ifndef DECODE_CARRIER_AC64 -#define DECODE_CARRIER_AC64 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC64 -#ifndef SEND_CARRIER_AC64 -#define SEND_CARRIER_AC64 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC64 - -#ifndef DECODE_CARRIER_AC128 -#define DECODE_CARRIER_AC128 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC128 -#ifndef SEND_CARRIER_AC128 -#define SEND_CARRIER_AC128 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC128 - -#ifndef DECODE_HAIER_AC -#define DECODE_HAIER_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC -#ifndef SEND_HAIER_AC -#define SEND_HAIER_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC - -#ifndef DECODE_HITACHI_AC -#define DECODE_HITACHI_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC -#ifndef SEND_HITACHI_AC -#define SEND_HITACHI_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC - -#ifndef DECODE_HITACHI_AC1 -#define DECODE_HITACHI_AC1 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC1 -#ifndef SEND_HITACHI_AC1 -#define SEND_HITACHI_AC1 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC1 - -#ifndef DECODE_HITACHI_AC2 -#define DECODE_HITACHI_AC2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC2 -#ifndef SEND_HITACHI_AC2 -#define SEND_HITACHI_AC2 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC2 - -#ifndef DECODE_HITACHI_AC3 -#define DECODE_HITACHI_AC3 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC3 -#ifndef SEND_HITACHI_AC3 -#define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC3 - -#ifndef DECODE_HITACHI_AC264 -#define DECODE_HITACHI_AC264 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC264 -#ifndef SEND_HITACHI_AC264 -#define SEND_HITACHI_AC264 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC264 - -#ifndef DECODE_HITACHI_AC296 -#define DECODE_HITACHI_AC296 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC296 -#ifndef SEND_HITACHI_AC296 -#define SEND_HITACHI_AC296 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC296 - -#ifndef DECODE_HITACHI_AC344 -#define DECODE_HITACHI_AC344 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC344 -#ifndef SEND_HITACHI_AC344 -#define SEND_HITACHI_AC344 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC344 - -#ifndef DECODE_HITACHI_AC424 -#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HITACHI_AC424 -#ifndef SEND_HITACHI_AC424 -#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_ -#endif // SEND_HITACHI_AC424 - -#ifndef DECODE_GICABLE -#define DECODE_GICABLE _IR_ENABLE_DEFAULT_ -#endif // DECODE_GICABLE -#ifndef SEND_GICABLE -#define SEND_GICABLE _IR_ENABLE_DEFAULT_ -#endif // SEND_GICABLE - -#ifndef DECODE_HAIER_AC_YRW02 -#define DECODE_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC_YRW02 -#ifndef SEND_HAIER_AC_YRW02 -#define SEND_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC_YRW02 - -#ifndef DECODE_WHIRLPOOL_AC -#define DECODE_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_WHIRLPOOL_AC -#ifndef SEND_WHIRLPOOL_AC -#define SEND_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_WHIRLPOOL_AC - -#ifndef DECODE_LUTRON -#define DECODE_LUTRON _IR_ENABLE_DEFAULT_ -#endif // DECODE_LUTRON -#ifndef SEND_LUTRON -#define SEND_LUTRON _IR_ENABLE_DEFAULT_ -#endif // SEND_LUTRON - -#ifndef DECODE_ELECTRA_AC -#define DECODE_ELECTRA_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_ELECTRA_AC -#ifndef SEND_ELECTRA_AC -#define SEND_ELECTRA_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_ELECTRA_AC - -#ifndef DECODE_PANASONIC_AC -#define DECODE_PANASONIC_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_PANASONIC_AC -#ifndef SEND_PANASONIC_AC -#define SEND_PANASONIC_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_PANASONIC_AC - -#ifndef DECODE_PANASONIC_AC32 -#define DECODE_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ -#endif // DECODE_PANASONIC_AC32 -#ifndef SEND_PANASONIC_AC32 -#define SEND_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ -#endif // SEND_PANASONIC_AC32 - -#ifndef DECODE_MWM -#define DECODE_MWM _IR_ENABLE_DEFAULT_ -#endif // DECODE_MWM -#ifndef SEND_MWM -#define SEND_MWM _IR_ENABLE_DEFAULT_ -#endif // SEND_MWM - -#ifndef DECODE_PIONEER -#define DECODE_PIONEER _IR_ENABLE_DEFAULT_ -#endif // DECODE_PIONEER -#ifndef SEND_PIONEER -#define SEND_PIONEER _IR_ENABLE_DEFAULT_ -#endif // SEND_PIONEER - -#ifndef DECODE_DAIKIN2 -#define DECODE_DAIKIN2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN2 -#ifndef SEND_DAIKIN2 -#define SEND_DAIKIN2 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN2 - -#ifndef DECODE_VESTEL_AC -#define DECODE_VESTEL_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_VESTEL_AC -#ifndef SEND_VESTEL_AC -#define SEND_VESTEL_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_VESTEL_AC - -#ifndef DECODE_TECO -#define DECODE_TECO _IR_ENABLE_DEFAULT_ -#endif // DECODE_TECO -#ifndef SEND_TECO -#define SEND_TECO _IR_ENABLE_DEFAULT_ -#endif // SEND_TECO - -#ifndef DECODE_TCL96AC -#define DECODE_TCL96AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TCL96AC -#ifndef SEND_TCL96AC -#define SEND_TCL96AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TCL96AC - -#ifndef DECODE_TCL112AC -#define DECODE_TCL112AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TCL112AC -#ifndef SEND_TCL112AC -#define SEND_TCL112AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TCL112AC - -#ifndef DECODE_LEGOPF -#define DECODE_LEGOPF _IR_ENABLE_DEFAULT_ -#endif // DECODE_LEGOPF -#ifndef SEND_LEGOPF -#define SEND_LEGOPF _IR_ENABLE_DEFAULT_ -#endif // SEND_LEGOPF - -#ifndef DECODE_MITSUBISHIHEAVY -#define DECODE_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ -#endif // DECODE_MITSUBISHIHEAVY -#ifndef SEND_MITSUBISHIHEAVY -#define SEND_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ -#endif // SEND_MITSUBISHIHEAVY - -#ifndef DECODE_DAIKIN216 -#define DECODE_DAIKIN216 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN216 -#ifndef SEND_DAIKIN216 -#define SEND_DAIKIN216 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN216 - -#ifndef DECODE_DAIKIN160 -#define DECODE_DAIKIN160 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN160 -#ifndef SEND_DAIKIN160 -#define SEND_DAIKIN160 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN160 - -#ifndef DECODE_NEOCLIMA -#define DECODE_NEOCLIMA _IR_ENABLE_DEFAULT_ -#endif // DECODE_NEOCLIMA -#ifndef SEND_NEOCLIMA -#define SEND_NEOCLIMA _IR_ENABLE_DEFAULT_ -#endif // SEND_NEOCLIMA - -#ifndef DECODE_DAIKIN176 -#define DECODE_DAIKIN176 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN176 -#ifndef SEND_DAIKIN176 -#define SEND_DAIKIN176 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN176 - -#ifndef DECODE_DAIKIN128 -#define DECODE_DAIKIN128 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN128 -#ifndef SEND_DAIKIN128 -#define SEND_DAIKIN128 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN128 - -#ifndef DECODE_AMCOR -#define DECODE_AMCOR _IR_ENABLE_DEFAULT_ -#endif // DECODE_AMCOR -#ifndef SEND_AMCOR -#define SEND_AMCOR _IR_ENABLE_DEFAULT_ -#endif // SEND_AMCOR - -#ifndef DECODE_DAIKIN152 -#define DECODE_DAIKIN152 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN152 -#ifndef SEND_DAIKIN152 -#define SEND_DAIKIN152 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN152 - -#ifndef DECODE_EPSON -#define DECODE_EPSON _IR_ENABLE_DEFAULT_ -#endif // DECODE_EPSON -#ifndef SEND_EPSON -#define SEND_EPSON _IR_ENABLE_DEFAULT_ -#endif // SEND_EPSON - -#ifndef DECODE_SYMPHONY -#define DECODE_SYMPHONY _IR_ENABLE_DEFAULT_ -#endif // DECODE_SYMPHONY -#ifndef SEND_SYMPHONY -#define SEND_SYMPHONY _IR_ENABLE_DEFAULT_ -#endif // SEND_SYMPHONY - -#ifndef DECODE_DAIKIN64 -#define DECODE_DAIKIN64 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN64 -#ifndef SEND_DAIKIN64 -#define SEND_DAIKIN64 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN64 - -#ifndef DECODE_AIRWELL -#define DECODE_AIRWELL _IR_ENABLE_DEFAULT_ -#endif // DECODE_AIRWELL -#ifndef SEND_AIRWELL -#define SEND_AIRWELL _IR_ENABLE_DEFAULT_ -#endif // SEND_AIRWELL - -#ifndef DECODE_DELONGHI_AC -#define DECODE_DELONGHI_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_DELONGHI_AC -#ifndef SEND_DELONGHI_AC -#define SEND_DELONGHI_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_DELONGHI_AC - -#ifndef DECODE_DOSHISHA -#define DECODE_DOSHISHA _IR_ENABLE_DEFAULT_ -#endif // DECODE_DOSHISHA -#ifndef SEND_DOSHISHA -#define SEND_DOSHISHA _IR_ENABLE_DEFAULT_ -#endif // SEND_DOSHISHA - -#ifndef DECODE_MULTIBRACKETS -#define DECODE_MULTIBRACKETS _IR_ENABLE_DEFAULT_ -#endif // DECODE_MULTIBRACKETS -#ifndef SEND_MULTIBRACKETS -#define SEND_MULTIBRACKETS _IR_ENABLE_DEFAULT_ -#endif // SEND_MULTIBRACKETS - -#ifndef DECODE_TECHNIBEL_AC -#define DECODE_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_TECHNIBEL_AC -#ifndef SEND_TECHNIBEL_AC -#define SEND_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_TECHNIBEL_AC - -#ifndef DECODE_CORONA_AC -#define DECODE_CORONA_AC _IR_ENABLE_DEFAULT_ -#endif // DECODE_CORONA_AC -#ifndef SEND_CORONA_AC -#define SEND_CORONA_AC _IR_ENABLE_DEFAULT_ -#endif // SEND_CORONA_AC - -#ifndef DECODE_ZEPEAL -#define DECODE_ZEPEAL _IR_ENABLE_DEFAULT_ -#endif // DECODE_ZEPEAL -#ifndef SEND_ZEPEAL -#define SEND_ZEPEAL _IR_ENABLE_DEFAULT_ -#endif // SEND_ZEPEAL - -#ifndef DECODE_VOLTAS -#define DECODE_VOLTAS _IR_ENABLE_DEFAULT_ -#endif // DECODE_VOLTAS -#ifndef SEND_VOLTAS -#define SEND_VOLTAS _IR_ENABLE_DEFAULT_ -#endif // SEND_VOLTAS - -#ifndef DECODE_METZ -#define DECODE_METZ _IR_ENABLE_DEFAULT_ -#endif // DECODE_METZ -#ifndef SEND_METZ -#define SEND_METZ _IR_ENABLE_DEFAULT_ -#endif // SEND_METZ - -#ifndef DECODE_TRANSCOLD -#define DECODE_TRANSCOLD _IR_ENABLE_DEFAULT_ -#endif // DECODE_TRANSCOLD -#ifndef SEND_TRANSCOLD -#define SEND_TRANSCOLD _IR_ENABLE_DEFAULT_ -#endif // SEND_TRANSCOLD - -#ifndef DECODE_MIRAGE -#define DECODE_MIRAGE _IR_ENABLE_DEFAULT_ -#endif // DECODE_MIRAGE -#ifndef SEND_MIRAGE -#define SEND_MIRAGE _IR_ENABLE_DEFAULT_ -#endif // SEND_MIRAGE - -#ifndef DECODE_ELITESCREENS -#define DECODE_ELITESCREENS _IR_ENABLE_DEFAULT_ -#endif // DECODE_ELITESCREENS -#ifndef SEND_ELITESCREENS -#define SEND_ELITESCREENS _IR_ENABLE_DEFAULT_ -#endif // SEND_ELITESCREENS - -#ifndef DECODE_MILESTAG2 -#define DECODE_MILESTAG2 _IR_ENABLE_DEFAULT_ -#endif // DECODE_MILESTAG2 -#ifndef SEND_MILESTAG2 -#define SEND_MILESTAG2 _IR_ENABLE_DEFAULT_ -#endif // SEND_MILESTAG2 - -#ifndef DECODE_ECOCLIM -#define DECODE_ECOCLIM _IR_ENABLE_DEFAULT_ -#endif // DECODE_ECOCLIM -#ifndef SEND_ECOCLIM -#define SEND_ECOCLIM _IR_ENABLE_DEFAULT_ -#endif // SEND_ECOCLIM - -#ifndef DECODE_XMP -#define DECODE_XMP _IR_ENABLE_DEFAULT_ -#endif // DECODE_XMP -#ifndef SEND_XMP -#define SEND_XMP _IR_ENABLE_DEFAULT_ -#endif // SEND_XMP - -#ifndef DECODE_TRUMA -#define DECODE_TRUMA _IR_ENABLE_DEFAULT_ -#endif // DECODE_TRUMA -#ifndef SEND_TRUMA -#define SEND_TRUMA _IR_ENABLE_DEFAULT_ -#endif // SEND_TRUMA - -#ifndef DECODE_HAIER_AC176 -#define DECODE_HAIER_AC176 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC176 -#ifndef SEND_HAIER_AC176 -#define SEND_HAIER_AC176 _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC176 - -#ifndef DECODE_TEKNOPOINT -#define DECODE_TEKNOPOINT _IR_ENABLE_DEFAULT_ -#endif // DECODE_TEKNOPOINT -#ifndef SEND_TEKNOPOINT -#define SEND_TEKNOPOINT _IR_ENABLE_DEFAULT_ -#endif // SEND_TEKNOPOINT - -#ifndef DECODE_KELON -#define DECODE_KELON _IR_ENABLE_DEFAULT_ -#endif // DECODE_KELON -#ifndef SEND_KELON -#define SEND_KELON _IR_ENABLE_DEFAULT_ -#endif // SEND_KELON - -#ifndef DECODE_BOSE -#define DECODE_BOSE _IR_ENABLE_DEFAULT_ -#endif // DECODE_BOSE -#ifndef SEND_BOSE -#define SEND_BOSE _IR_ENABLE_DEFAULT_ -#endif // SEND_BOSE - -#ifndef DECODE_ARRIS -#define DECODE_ARRIS _IR_ENABLE_DEFAULT_ -#endif // DECODE_ARRIS -#ifndef SEND_ARRIS -#define SEND_ARRIS _IR_ENABLE_DEFAULT_ -#endif // SEND_ARRIS - -#ifndef DECODE_RHOSS -#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ -#endif // DECODE_RHOSS -#ifndef SEND_RHOSS -#define SEND_RHOSS _IR_ENABLE_DEFAULT_ -#endif // SEND_RHOSS - -#ifndef DECODE_AIRTON -#define DECODE_AIRTON _IR_ENABLE_DEFAULT_ -#endif // DECODE_AIRTON -#ifndef SEND_AIRTON -#define SEND_AIRTON _IR_ENABLE_DEFAULT_ -#endif // SEND_AIRTON - -#ifndef DECODE_KELON168 -#define DECODE_KELON168 _IR_ENABLE_DEFAULT_ -#endif // DECODE_KELON168 -#ifndef SEND_KELON168 -#define SEND_KELON168 _IR_ENABLE_DEFAULT_ -#endif // SEND_KELON168 - -#ifndef DECODE_DAIKIN200 -#define DECODE_DAIKIN200 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN200 -#ifndef SEND_DAIKIN200 -#define SEND_DAIKIN200 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN200 - -#ifndef DECODE_HAIER_AC160 -#define DECODE_HAIER_AC160 _IR_ENABLE_DEFAULT_ -#endif // DECODE_HAIER_AC160 -#ifndef SEND_HAIER_AC160 -#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_ -#endif // SEND_HAIER_AC160 - -#ifndef DECODE_TOTO -#define DECODE_TOTO _IR_ENABLE_DEFAULT_ -#endif // DECODE_TOTO -#ifndef SEND_TOTO -#define SEND_TOTO _IR_ENABLE_DEFAULT_ -#endif // SEND_TOTO - -#ifndef DECODE_CLIMABUTLER -#define DECODE_CLIMABUTLER _IR_ENABLE_DEFAULT_ -#endif // DECODE_CLIMABUTLER -#ifndef SEND_CLIMABUTLER -#define SEND_CLIMABUTLER _IR_ENABLE_DEFAULT_ -#endif // SEND_CLIMABUTLER - -#ifndef DECODE_BOSCH144 -#define DECODE_BOSCH144 _IR_ENABLE_DEFAULT_ -#endif // DECODE_BOSCH144 -#ifndef SEND_BOSCH144 -#define SEND_BOSCH144 _IR_ENABLE_DEFAULT_ -#endif // SEND_BOSCH144 - -#ifndef DECODE_DAIKIN312 -#define DECODE_DAIKIN312 _IR_ENABLE_DEFAULT_ -#endif // DECODE_DAIKIN312 -#ifndef SEND_DAIKIN312 -#define SEND_DAIKIN312 _IR_ENABLE_DEFAULT_ -#endif // SEND_DAIKIN312 - -#ifndef DECODE_GORENJE -#define DECODE_GORENJE _IR_ENABLE_DEFAULT_ -#endif // DECODE_GORENJE -#ifndef SEND_GORENJE -#define SEND_GORENJE _IR_ENABLE_DEFAULT_ -#endif // SEND_GORENJE - -#ifndef DECODE_WOWWEE -#define DECODE_WOWWEE _IR_ENABLE_DEFAULT_ -#endif // DECODE_WOWWEE -#ifndef SEND_WOWWEE -#define SEND_WOWWEE _IR_ENABLE_DEFAULT_ -#endif // SEND_WOWWEE - -#ifndef DECODE_CARRIER_AC84 -#define DECODE_CARRIER_AC84 _IR_ENABLE_DEFAULT_ -#endif // DECODE_CARRIER_AC84 -#ifndef SEND_CARRIER_AC84 -#define SEND_CARRIER_AC84 _IR_ENABLE_DEFAULT_ -#endif // SEND_CARRIER_AC84 - -#ifndef DECODE_YORK -#define DECODE_YORK _IR_ENABLE_DEFAULT_ -#endif // DECODE_YORK -#ifndef SEND_YORK -#define SEND_YORK _IR_ENABLE_DEFAULT_ -#endif // SEND_YORK - -#ifndef DECODE_FUJITSU_AC264 -#define DECODE_FUJITSU_AC264 _IR_ENABLE_DEFAULT_ -#endif // DECODE_FUJITSU_AC264 -#ifndef SEND_FUJITSU_AC264 -#define SEND_FUJITSU_AC264 _IR_ENABLE_DEFAULT_ -#endif // SEND_FUJITSU_AC264 - -#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ - DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ - DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ - DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ - DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ - DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ - DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ - DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ - DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ - DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \ - DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \ - DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \ - DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \ - DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \ - DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \ - DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \ - DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \ - DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \ - DECODE_CARRIER_AC84 || DECODE_YORK || DECODE_FUJITSU_AC264 || \ - false) - // Add any DECODE to the above if it uses result->state (see kStateSizeMax) - // you might also want to add the protocol to hasACState function -#define DECODE_AC true // We need some common infrastructure for decoding A/Cs. -#else -#define DECODE_AC false // We don't need that infrastructure. -#endif - -// Use millisecond 'delay()' calls where we can to avoid tripping the WDT. -// Note: If you plan to send IR messages in the callbacks of the AsyncWebserver -// library, you need to set ALLOW_DELAY_CALLS to false. -// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/430 -#ifndef ALLOW_DELAY_CALLS -#define ALLOW_DELAY_CALLS true -#endif // ALLOW_DELAY_CALLS - -// Enable a run-time settable high-pass filter on captured data **before** -// trying any protocol decoding. -// i.e. Try to remove/merge any really short pulses detected in the raw data. -// Note: Even when this option is enabled, it is _off_ by default, and requires -// a user who knows what they are doing to enable it. -// The option to disable this feature is here if your project is _really_ -// tight on resources. i.e. Saves a small handful of bytes and cpu time. -// WARNING: If you use this feature at runtime, you can no longer trust the -// **raw** data captured. It will now have been slightly **cooked**! -// DANGER: If you set the `noise_floor` value too high, it **WILL** break -// decoding of some protocols. You have been warned. Here Be Dragons! -// -// See: `irrecv::decode()` in IRrecv.cpp for more info. -#ifndef ENABLE_NOISE_FILTER_OPTION -#define ENABLE_NOISE_FILTER_OPTION true -#endif // ENABLE_NOISE_FILTER_OPTION - -/// Enumerator for defining and numbering of supported IR protocol. -/// @note Always add to the end of the list and should never remove entries -/// or change order. Projects may save the type number for later usage -/// so numbering should always stay the same. -enum decode_type_t { - UNKNOWN = -1, - UNUSED = 0, - RC5, - RC6, - NEC, - SONY, - PANASONIC, // (5) - JVC, - SAMSUNG, - WHYNTER, - AIWA_RC_T501, - LG, // (10) - SANYO, - MITSUBISHI, - DISH, - SHARP, - COOLIX, // (15) - DAIKIN, - DENON, - KELVINATOR, - SHERWOOD, - MITSUBISHI_AC, // (20) - RCMM, - SANYO_LC7461, - RC5X, - GREE, - PRONTO, // Technically not a protocol, but an encoding. (25) - NEC_LIKE, - ARGO, - TROTEC, - NIKAI, - RAW, // Technically not a protocol, but an encoding. (30) - GLOBALCACHE, // Technically not a protocol, but an encoding. - TOSHIBA_AC, - FUJITSU_AC, - MIDEA, - MAGIQUEST, // (35) - LASERTAG, - CARRIER_AC, - HAIER_AC, - MITSUBISHI2, - HITACHI_AC, // (40) - HITACHI_AC1, - HITACHI_AC2, - GICABLE, - HAIER_AC_YRW02, - WHIRLPOOL_AC, // (45) - SAMSUNG_AC, - LUTRON, - ELECTRA_AC, - PANASONIC_AC, - PIONEER, // (50) - LG2, - MWM, - DAIKIN2, - VESTEL_AC, - TECO, // (55) - SAMSUNG36, - TCL112AC, - LEGOPF, - MITSUBISHI_HEAVY_88, - MITSUBISHI_HEAVY_152, // 60 - DAIKIN216, - SHARP_AC, - GOODWEATHER, - INAX, - DAIKIN160, // 65 - NEOCLIMA, - DAIKIN176, - DAIKIN128, - AMCOR, - DAIKIN152, // 70 - MITSUBISHI136, - MITSUBISHI112, - HITACHI_AC424, - SONY_38K, - EPSON, // 75 - SYMPHONY, - HITACHI_AC3, - DAIKIN64, - AIRWELL, - DELONGHI_AC, // 80 - DOSHISHA, - MULTIBRACKETS, - CARRIER_AC40, - CARRIER_AC64, - HITACHI_AC344, // 85 - CORONA_AC, - MIDEA24, - ZEPEAL, - SANYO_AC, - VOLTAS, // 90 - METZ, - TRANSCOLD, - TECHNIBEL_AC, - MIRAGE, - ELITESCREENS, // 95 - PANASONIC_AC32, - MILESTAG2, - ECOCLIM, - XMP, - TRUMA, // 100 - HAIER_AC176, - TEKNOPOINT, - KELON, - TROTEC_3550, - SANYO_AC88, // 105 - BOSE, - ARRIS, - RHOSS, - AIRTON, - COOLIX48, // 110 - HITACHI_AC264, - KELON168, - HITACHI_AC296, - DAIKIN200, - HAIER_AC160, // 115 - CARRIER_AC128, - TOTO, - CLIMABUTLER, - TCL96AC, - BOSCH144, // 120 - SANYO_AC152, - DAIKIN312, - GORENJE, - WOWWEE, - CARRIER_AC84, // 125 - YORK, - FUJITSU_AC264, - // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = FUJITSU_AC264, -}; - -// Message lengths & required repeat values -const uint16_t kNoRepeat = 0; -const uint16_t kSingleRepeat = 1; - -const uint16_t kAirtonBits = 56; -const uint16_t kAirtonDefaultRepeat = kNoRepeat; -const uint16_t kAirwellBits = 34; -const uint16_t kAirwellMinRepeats = 2; -const uint16_t kAiwaRcT501Bits = 15; -const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; -const uint16_t kAlokaBits = 32; -const uint16_t kAmcorStateLength = 8; -const uint16_t kAmcorBits = kAmcorStateLength * 8; -const uint16_t kAmcorDefaultRepeat = kSingleRepeat; -const uint16_t kArgoStateLength = 12; -const uint16_t kArgoShortStateLength = 4; -const uint16_t kArgoBits = kArgoStateLength * 8; -const uint16_t kArgoShortBits = kArgoShortStateLength * 8; -const uint16_t kArgo3AcControlStateLength = 6; // Bytes -const uint16_t kArgo3iFeelReportStateLength = 2; // Bytes -const uint16_t kArgo3TimerStateLength = 9; // Bytes -const uint16_t kArgo3ConfigStateLength = 4; // Bytes -const uint16_t kArgoDefaultRepeat = kNoRepeat; -const uint16_t kArrisBits = 32; -const uint16_t kBosch144StateLength = 18; -const uint16_t kBosch144Bits = kBosch144StateLength * 8; -const uint16_t kCoolixBits = 24; -const uint16_t kCoolix48Bits = kCoolixBits * 2; -const uint16_t kCoolixDefaultRepeat = kSingleRepeat; -const uint16_t kCarrierAcBits = 32; -const uint16_t kCarrierAcMinRepeat = kNoRepeat; -const uint16_t kCarrierAc40Bits = 40; -const uint16_t kCarrierAc40MinRepeat = 2; -const uint16_t kCarrierAc64Bits = 64; -const uint16_t kCarrierAc64MinRepeat = kNoRepeat; -const uint16_t kCarrierAc84StateLength = 11; -const uint16_t kCarrierAc84Bits = kCarrierAc84StateLength * 8 - 4; -const uint16_t kCarrierAc84MinRepeat = kNoRepeat; -const uint16_t kCarrierAc128StateLength = 16; -const uint16_t kCarrierAc128Bits = kCarrierAc128StateLength * 8; -const uint16_t kCarrierAc128MinRepeat = kNoRepeat; -const uint16_t kCoronaAcStateLengthShort = 7; -const uint16_t kCoronaAcStateLength = kCoronaAcStateLengthShort * 3; -const uint16_t kCoronaAcBitsShort = kCoronaAcStateLengthShort * 8; -const uint16_t kCoronaAcBits = kCoronaAcStateLength * 8; -const uint16_t kDaikinStateLength = 35; -const uint16_t kDaikinBits = kDaikinStateLength * 8; -const uint16_t kDaikinStateLengthShort = kDaikinStateLength - 8; -const uint16_t kDaikinBitsShort = kDaikinStateLengthShort * 8; -const uint16_t kDaikinDefaultRepeat = kNoRepeat; -const uint16_t kDaikin2StateLength = 39; -const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; -const uint16_t kDaikin2DefaultRepeat = kNoRepeat; -const uint16_t kDaikin64Bits = 64; -const uint16_t kDaikin64DefaultRepeat = kNoRepeat; -const uint16_t kDaikin160StateLength = 20; -const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; -const uint16_t kDaikin160DefaultRepeat = kNoRepeat; -const uint16_t kDaikin128StateLength = 16; -const uint16_t kDaikin128Bits = kDaikin128StateLength * 8; -const uint16_t kDaikin128DefaultRepeat = kNoRepeat; -const uint16_t kDaikin152StateLength = 19; -const uint16_t kDaikin152Bits = kDaikin152StateLength * 8; -const uint16_t kDaikin152DefaultRepeat = kNoRepeat; -const uint16_t kDaikin176StateLength = 22; -const uint16_t kDaikin176Bits = kDaikin176StateLength * 8; -const uint16_t kDaikin176DefaultRepeat = kNoRepeat; -const uint16_t kDaikin200StateLength = 25; -const uint16_t kDaikin200Bits = kDaikin200StateLength * 8; -const uint16_t kDaikin200DefaultRepeat = kNoRepeat; -const uint16_t kDaikin216StateLength = 27; -const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; -const uint16_t kDaikin216DefaultRepeat = kNoRepeat; -const uint16_t kDaikin312StateLength = 39; -const uint16_t kDaikin312Bits = kDaikin312StateLength * 8; -const uint16_t kDaikin312DefaultRepeat = kNoRepeat; -const uint16_t kDelonghiAcBits = 64; -const uint16_t kDelonghiAcDefaultRepeat = kNoRepeat; -const uint16_t kTechnibelAcBits = 56; -const uint16_t kTechnibelAcDefaultRepeat = kNoRepeat; -const uint16_t kDenonBits = 15; -const uint16_t kDenon48Bits = 48; -const uint16_t kDenonLegacyBits = 14; -const uint16_t kDishBits = 16; -const uint16_t kDishMinRepeat = 3; -const uint16_t kDoshishaBits = 40; -const uint16_t kEcoclimBits = 56; -const uint16_t kEcoclimShortBits = 15; -const uint16_t kEpsonBits = 32; -const uint16_t kEpsonMinRepeat = 2; -const uint16_t kElectraAcStateLength = 13; -const uint16_t kElectraAcBits = kElectraAcStateLength * 8; -const uint16_t kElectraAcMinRepeat = kNoRepeat; -const uint16_t kEliteScreensBits = 32; -const uint16_t kEliteScreensDefaultRepeat = kSingleRepeat; -const uint16_t kFujitsuAcMinRepeat = kNoRepeat; -const uint16_t kFujitsuAcStateLength = 16; -const uint16_t kFujitsuAcStateLengthShort = 7; -const uint16_t kFujitsuAcBits = kFujitsuAcStateLength * 8; -const uint16_t kFujitsuAcMinBits = (kFujitsuAcStateLengthShort - 1) * 8; -const uint16_t kFujitsuAc264DefaultRepeat = kNoRepeat; -const uint16_t kFujitsuAc264StateLength = 33; -const uint16_t kFujitsuAc264StateLengthMiddle = 16; -const uint16_t kFujitsuAc264StateLengthShort = 7; -const uint16_t kFujitsuAc264Bits = kFujitsuAc264StateLength * 8; -const uint16_t kFujitsuAc264BitsMiddle = kFujitsuAc264StateLengthMiddle * 8; -const uint16_t kFujitsuAc264BitsShort = kFujitsuAc264StateLengthShort * 8; -const uint16_t kGicableBits = 16; -const uint16_t kGicableMinRepeat = kSingleRepeat; -const uint16_t kGoodweatherBits = 48; -const uint16_t kGoodweatherMinRepeat = kNoRepeat; -const uint16_t kGorenjeBits = 8; -const uint16_t kGreeStateLength = 8; -const uint16_t kGreeBits = kGreeStateLength * 8; -const uint16_t kGreeDefaultRepeat = kNoRepeat; -const uint16_t kHaierACStateLength = 9; -const uint16_t kHaierACBits = kHaierACStateLength * 8; -const uint16_t kHaierAcDefaultRepeat = kNoRepeat; -const uint16_t kHaierACYRW02StateLength = 14; -const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8; -const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat; -const uint16_t kHaierAC160StateLength = 20; -const uint16_t kHaierAC160Bits = kHaierAC160StateLength * 8; -const uint16_t kHaierAc160DefaultRepeat = kNoRepeat; -const uint16_t kHaierAC176StateLength = 22; -const uint16_t kHaierAC176Bits = kHaierAC176StateLength * 8; -const uint16_t kHaierAc176DefaultRepeat = kNoRepeat; -const uint16_t kHitachiAcStateLength = 28; -const uint16_t kHitachiAcBits = kHitachiAcStateLength * 8; -const uint16_t kHitachiAcDefaultRepeat = kNoRepeat; -const uint16_t kHitachiAc1StateLength = 13; -const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; -const uint16_t kHitachiAc2StateLength = 53; -const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; -const uint16_t kHitachiAc3StateLength = 27; -const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8; -const uint16_t kHitachiAc3MinStateLength = 15; -const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8; -const uint16_t kHitachiAc264StateLength = 33; -const uint16_t kHitachiAc264Bits = kHitachiAc264StateLength * 8; -const uint16_t kHitachiAc296StateLength = 37; -const uint16_t kHitachiAc296Bits = kHitachiAc296StateLength * 8; -const uint16_t kHitachiAc344StateLength = 43; -const uint16_t kHitachiAc344Bits = kHitachiAc344StateLength * 8; -const uint16_t kHitachiAc424StateLength = 53; -const uint16_t kHitachiAc424Bits = kHitachiAc424StateLength * 8; -const uint16_t kInaxBits = 24; -const uint16_t kInaxMinRepeat = kSingleRepeat; -const uint16_t kJvcBits = 16; -const uint16_t kKelonBits = 48; -const uint16_t kKelon168StateLength = 21; -const uint16_t kKelon168Bits = kKelon168StateLength * 8; -const uint16_t kKelvinatorStateLength = 16; -const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; -const uint16_t kKelvinatorDefaultRepeat = kNoRepeat; -const uint16_t kLasertagBits = 13; -const uint16_t kLasertagMinRepeat = kNoRepeat; -const uint16_t kLegoPfBits = 16; -const uint16_t kLegoPfMinRepeat = kNoRepeat; -const uint16_t kLgBits = 28; -const uint16_t kLg32Bits = 32; -const uint16_t kLgDefaultRepeat = kNoRepeat; -const uint16_t kLutronBits = 35; -const uint16_t kMagiquestBits = 56; -const uint16_t kMetzBits = 19; -const uint16_t kMetzMinRepeat = kNoRepeat; -const uint16_t kMideaBits = 48; -const uint16_t kMideaMinRepeat = kNoRepeat; -const uint16_t kMidea24Bits = 24; -const uint16_t kMidea24MinRepeat = kSingleRepeat; -const uint16_t kMirageStateLength = 15; -const uint16_t kMirageBits = kMirageStateLength * 8; -const uint16_t kMirageMinRepeat = kNoRepeat; -const uint16_t kMitsubishiBits = 16; -// TODO(anyone): Verify that the Mitsubishi repeat is really needed. -// Based on marcosamarinho's code. -const uint16_t kMitsubishiMinRepeat = kSingleRepeat; -const uint16_t kMitsubishiACStateLength = 18; -const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8; -const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; -const uint16_t kMitsubishi136StateLength = 17; -const uint16_t kMitsubishi136Bits = kMitsubishi136StateLength * 8; -const uint16_t kMitsubishi136MinRepeat = kNoRepeat; -const uint16_t kMitsubishi112StateLength = 14; -const uint16_t kMitsubishi112Bits = kMitsubishi112StateLength * 8; -const uint16_t kMitsubishi112MinRepeat = kNoRepeat; -const uint16_t kMitsubishiHeavy88StateLength = 11; -const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8; -const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; -const uint16_t kMitsubishiHeavy152StateLength = 19; -const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; -const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; -const uint16_t kMultibracketsBits = 8; -const uint16_t kMultibracketsDefaultRepeat = kSingleRepeat; -const uint16_t kNikaiBits = 24; -const uint16_t kNECBits = 32; -const uint16_t kNeoclimaStateLength = 12; -const uint16_t kNeoclimaBits = kNeoclimaStateLength * 8; -const uint16_t kNeoclimaMinRepeat = kNoRepeat; -const uint16_t kPanasonicBits = 48; -const uint32_t kPanasonicManufacturer = 0x4004; -const uint32_t kPanasonic40Manufacturer = 0x34; -const uint16_t kPanasonic40Bits = 40; -const uint16_t kPanasonicAcStateLength = 27; -const uint16_t kPanasonicAcStateShortLength = 16; -const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8; -const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8; -const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat; -const uint16_t kPanasonicAc32Bits = 32; -const uint16_t kPioneerBits = 64; -const uint16_t kProntoMinLength = 6; -const uint16_t kRC5RawBits = 14; -const uint16_t kRC5Bits = kRC5RawBits - 2; -const uint16_t kRC5XBits = kRC5RawBits - 1; -const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. -const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. -const uint16_t kRCMMBits = 24; -const uint16_t kSamsungBits = 32; -const uint16_t kSamsung36Bits = 36; -const uint16_t kSamsungAcStateLength = 14; -const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8; -const uint16_t kSamsungAcExtendedStateLength = 21; -const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8; -const uint16_t kSamsungAcDefaultRepeat = kNoRepeat; -const uint16_t kSanyoAcStateLength = 9; -const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8; -const uint16_t kSanyoAc88StateLength = 11; -const uint16_t kSanyoAc88Bits = kSanyoAc88StateLength * 8; -const uint16_t kSanyoAc88MinRepeat = 2; -const uint16_t kSanyoAc152StateLength = 19; -const uint16_t kSanyoAc152Bits = kSanyoAc152StateLength * 8; -const uint16_t kSanyoAc152MinRepeat = kNoRepeat; -const uint16_t kSanyoSA8650BBits = 12; -const uint16_t kSanyoLC7461AddressBits = 13; -const uint16_t kSanyoLC7461CommandBits = 8; -const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits + - kSanyoLC7461CommandBits) * 2; -const uint8_t kSharpAddressBits = 5; -const uint8_t kSharpCommandBits = 8; -const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15 -const uint16_t kSharpAcStateLength = 13; -const uint16_t kSharpAcBits = kSharpAcStateLength * 8; // 104 -const uint16_t kSharpAcDefaultRepeat = kNoRepeat; -const uint8_t kSherwoodBits = kNECBits; -const uint16_t kSherwoodMinRepeat = kSingleRepeat; -const uint16_t kSony12Bits = 12; -const uint16_t kSony15Bits = 15; -const uint16_t kSony20Bits = 20; -const uint16_t kSonyMinBits = 12; -const uint16_t kSonyMinRepeat = 2; -const uint16_t kSymphonyBits = 12; -const uint16_t kSymphonyDefaultRepeat = 3; -const uint16_t kTcl96AcStateLength = 12; -const uint16_t kTcl96AcBits = kTcl96AcStateLength * 8; -const uint16_t kTcl96AcDefaultRepeat = kNoRepeat; -const uint16_t kTcl112AcStateLength = 14; -const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; -const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; -const uint16_t kTecoBits = 35; -const uint16_t kTecoDefaultRepeat = kNoRepeat; -const uint16_t kTeknopointStateLength = 14; -const uint16_t kTeknopointBits = kTeknopointStateLength * 8; -const uint16_t kToshibaACStateLength = 9; -const uint16_t kToshibaACBits = kToshibaACStateLength * 8; -const uint16_t kToshibaACMinRepeat = kSingleRepeat; -const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2; -const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8; -const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1; -const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8; -const uint16_t kTotoBits = 24; -const uint16_t kTotoShortBits = kTotoBits; -const uint16_t kTotoLongBits = kTotoShortBits * 2; -const uint16_t kTotoDefaultRepeat = kSingleRepeat; -const uint16_t kTranscoldBits = 24; -const uint16_t kTranscoldDefaultRepeat = kNoRepeat; -const uint16_t kTrotecStateLength = 9; -const uint16_t kTrotecBits = kTrotecStateLength * 8; -const uint16_t kTrotecDefaultRepeat = kNoRepeat; -const uint16_t kTrumaBits = 56; -const uint16_t kWhirlpoolAcStateLength = 21; -const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; -const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; -const uint16_t kWhynterBits = 32; -const uint16_t kWowweeBits = 11; -const uint16_t kWowweeDefaultRepeat = kNoRepeat; -const uint8_t kVestelAcBits = 56; -const uint16_t kXmpBits = 64; -const uint16_t kZepealBits = 16; -const uint16_t kZepealMinRepeat = 4; -const uint16_t kVoltasBits = 80; -const uint16_t kVoltasStateLength = 10; -const uint16_t kMilesTag2ShotBits = 14; -const uint16_t kMilesTag2MsgBits = 24; -const uint16_t kMilesMinRepeat = 0; -const uint16_t kBoseBits = 16; -const uint16_t kRhossStateLength = 12; -const uint16_t kRhossBits = kRhossStateLength * 8; -const uint16_t kRhossDefaultRepeat = 0; -const uint16_t kClimaButlerBits = 52; -const uint16_t kYorkBits = 136; -const uint16_t kYorkStateLength = 17; - - -// Legacy defines. (Deprecated) -#define AIWA_RC_T501_BITS kAiwaRcT501Bits -#define ARGO_COMMAND_LENGTH kArgoStateLength -#define COOLIX_BITS kCoolixBits -#define CARRIER_AC_BITS kCarrierAcBits -#define DAIKIN_COMMAND_LENGTH kDaikinStateLength -#define DENON_BITS kDenonBits -#define DENON_48_BITS kDenon48Bits -#define DENON_LEGACY_BITS kDenonLegacyBits -#define DISH_BITS kDishBits -#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat -#define FUJITSU_AC_STATE_LENGTH kFujitsuAcStateLength -#define FUJITSU_AC_STATE_LENGTH_SHORT kFujitsuAcStateLengthShort -#define FUJITSU_AC_BITS kFujitsuAcBits -#define FUJITSU_AC_MIN_BITS kFujitsuAcMinBits -#define GICABLE_BITS kGicableBits -#define GREE_STATE_LENGTH kGreeStateLength -#define HAIER_AC_STATE_LENGTH kHaierACStateLength -#define HAIER_AC_YRW02_STATE_LENGTH kHaierACYRW02StateLength -#define HITACHI_AC_STATE_LENGTH kHitachiAcStateLength -#define HITACHI_AC_BITS kHitachiAcBits -#define HITACHI_AC1_STATE_LENGTH kHitachiAc1StateLength -#define HITACHI_AC1_BITS kHitachiAc1Bits -#define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength -#define HITACHI_AC2_BITS kHitachiAc2Bits -#define HITACHI_AC296_STATE_LENGTH kHitachiAc296StateLength -#define HITACHI_AC296_BITS kHitachiAc296Bits -#define JVC_BITS kJvcBits -#define KELVINATOR_STATE_LENGTH kKelvinatorStateLength -#define LASERTAG_BITS kLasertagBits -#define LG_BITS kLgBits -#define LG32_BITS kLg32Bits -#define MAGIQUEST_BITS kMagiquestBits -#define MIDEA_BITS kMideaBits -#define MITSUBISHI_BITS kMitsubishiBits -#define MITSUBISHI_AC_STATE_LENGTH kMitsubishiACStateLength -#define NEC_BITS kNECBits -#define NIKAI_BITS kNikaiBits -#define PANASONIC_BITS kPanasonicBits -#define RC5_BITS kRC5Bits -#define RC5X_BITS kRC5XBits -#define RC6_MODE0_BITS kRC6Mode0Bits -#define RC6_36_BITS kRC6_36Bits -#define RCMM_BITS kRCMMBits -#define SANYO_LC7461_BITS kSanyoLC7461Bits -#define SAMSUNG_BITS kSamsungBits -#define SANYO_SA8650B_BITS kSanyoSA8650BBits -#define SHARP_BITS kSharpBits -#define SHERWOOD_BITS kSherwoodBits -#define SONY_12_BITS kSony12Bits -#define SONY_15_BITS kSony15Bits -#define SONY_20_BITS kSony20Bits -#define TOSHIBA_AC_STATE_LENGTH kToshibaACStateLength -#define TROTEC_COMMAND_LENGTH kTrotecStateLength -#define WHYNTER_BITS kWhynterBits - -// Turn on Debugging information by uncommenting the following line. -// #define DEBUG 1 - -#ifdef DEBUG -#ifdef UNIT_TEST -#define DPRINT(x) do { std::cout << x; } while (0) -#define DPRINTLN(x) do { std::cout << x << std::endl; } while (0) -#endif // UNIT_TEST -#ifdef ARDUINO -#define DPRINT(x) do { Serial.print(x); } while (0) -#define DPRINTLN(x) do { Serial.println(x); } while (0) -#endif // ARDUINO -#else // DEBUG -#define DPRINT(x) -#define DPRINTLN(x) -#endif // DEBUG - -#ifdef UNIT_TEST -#ifndef F -// Create a no-op F() macro so the code base still compiles outside of the -// Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out -// the code base. That macro stores constants in Flash (PROGMEM) memory. -// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/667 -#define F(x) x -#endif // F -typedef std::string String; -#endif // UNIT_TEST - -#endif // IRREMOTEESP8266_H_ + /*************************************************** + * IRremote for ESP8266 + * + * Based on the IRremote library for Arduino by Ken Shirriff + * Version 0.11 August, 2009 + * Copyright 2009 Ken Shirriff + * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html + * + * Edited by Mitra to add new controller SANYO + * + * Interrupt code based on NECIRrcv by Joe Knapp + * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 + * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ + * + * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) + * LG added by Darryl Smith (based on the JVC protocol) + * Whynter A/C ARC-110WD added by Francesco Meschia + * Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit + * Denon: sendDenon, decodeDenon added by Massimiliano Pinto + (from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp) + * Kelvinator A/C and Sherwood added by crankyoldgit + * Mitsubishi (TV) sending added by crankyoldgit + * Pronto code sending added by crankyoldgit + * Mitsubishi & Toshiba A/C added by crankyoldgit + * (derived from https://github.com/r45635/HVAC-IR-Control) + * DISH decode by marcosamarinho + * Gree Heatpump sending added by Ville Skyttä (scop) + * (derived from https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp) + * Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for sending IR code on ESP8266 + * Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266 + * + * Updated by sillyfrog for Daikin, adopted from + * (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/) + * Fujitsu A/C code added by jonnygraham + * Trotec AC code by stufisher + * Carrier & Haier AC code by crankyoldgit + * Vestel AC code by Erdem U. Altınyurt + * Teco AC code by Fabien Valthier (hcoohb) + * Mitsubishi 112 AC Code by kuchel77 + * Kelon AC code by Davide Depau (Depau) + * + * GPL license, all text above must be included in any redistribution + ****************************************************/ + +#ifndef IRREMOTEESP8266_H_ +#define IRREMOTEESP8266_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifdef UNIT_TEST +#include +#include +#endif // UNIT_TEST + +// Library Version Information +// Major version number (X.x.x) +#define _IRREMOTEESP8266_VERSION_MAJOR 2 +// Minor version number (x.X.x) +#define _IRREMOTEESP8266_VERSION_MINOR 8 +// Patch version number (x.x.X) +#define _IRREMOTEESP8266_VERSION_PATCH 6 +// Macro to convert version info into an integer +#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \ + (((major) << 16) | ((minor) << 8) | (patch)) +// Macro to convert literal into a string +#define MKSTR_HELPER(x) #x +#define MKSTR(x) MKSTR_HELPER(x) +// Integer version +#define _IRREMOTEESP8266_VERSION _IRREMOTEESP8266_VERSION_VAL(\ + _IRREMOTEESP8266_VERSION_MAJOR, \ + _IRREMOTEESP8266_VERSION_MINOR, \ + _IRREMOTEESP8266_VERSION_PATCH) +// String version +#define _IRREMOTEESP8266_VERSION_STR MKSTR(_IRREMOTEESP8266_VERSION_MAJOR) "." \ + MKSTR(_IRREMOTEESP8266_VERSION_MINOR) "." \ + MKSTR(_IRREMOTEESP8266_VERSION_PATCH) +// String version (DEPRECATED) +#define _IRREMOTEESP8266_VERSION_ _IRREMOTEESP8266_VERSION_STR + +// Set the language & locale for the library. See the `locale` dir for options. +#ifndef _IR_LOCALE_ +#define _IR_LOCALE_ en-AU +#endif // _IR_LOCALE_ + +// Do we enable all the protocols by default (true), or disable them (false)? +// This allows users of the library to disable or enable all protocols at +// compile-time with `-D_IR_ENABLE_DEFAULT_=true` or +// `-D_IR_ENABLE_DEFAULT_=false` compiler flags respectively. +// Everything is included by default. +// e.g. If you only want to enable use of he NEC protocol to save program space, +// you would use something like: +// `-D_IR_ENABLE_DEFAULT_=false -DDECODE_NEC=true -DSEND_NEC=true` +// +// or alter your 'platform.ini' file accordingly: +// ``` +// build_flags = -D_IR_ENABLE_DEFAULT_=false +// -DDECODE_NEC=true +// -DSEND_NEC=true +// ``` +// If you want to enable support for every protocol *except* _decoding_ the +// Kelvinator protocol, you would use: +// `-DDECODE_KELVINATOR=false` +#ifndef _IR_ENABLE_DEFAULT_ +#define _IR_ENABLE_DEFAULT_ true // Unless set externally, the default is on. +#endif // _IR_ENABLE_DEFAULT_ + +// Supported IR protocols +// Each protocol you include costs memory and, during decode, costs time +// Disable (set to false) all the protocols you do not need/want! +// The Air Conditioner protocols are the most expensive memory-wise. +// + +// Semi-unique code for unknown messages +#ifndef DECODE_HASH +#define DECODE_HASH _IR_ENABLE_DEFAULT_ +#endif // DECODE_HASH + +#ifndef SEND_RAW +#define SEND_RAW _IR_ENABLE_DEFAULT_ +#endif // SEND_RAW + +#ifndef DECODE_NEC +#define DECODE_NEC _IR_ENABLE_DEFAULT_ +#endif // DECODE_NEC +#ifndef SEND_NEC +#define SEND_NEC _IR_ENABLE_DEFAULT_ +#endif // SEND_NEC + +#ifndef DECODE_SHERWOOD +#define DECODE_SHERWOOD false // Not applicable. Actually is DECODE_NEC +#endif // DECODE_SHERWOOD +#ifndef SEND_SHERWOOD +#define SEND_SHERWOOD _IR_ENABLE_DEFAULT_ +#endif // SEND_SHERWOOD + +#ifndef DECODE_RC5 +#define DECODE_RC5 _IR_ENABLE_DEFAULT_ +#endif // DECODE_RC5 +#ifndef SEND_RC5 +#define SEND_RC5 _IR_ENABLE_DEFAULT_ +#endif // SEND_RC5 + +#ifndef DECODE_RC6 +#define DECODE_RC6 _IR_ENABLE_DEFAULT_ +#endif // DECODE_RC6 +#ifndef SEND_RC6 +#define SEND_RC6 _IR_ENABLE_DEFAULT_ +#endif // SEND_RC6 + +#ifndef DECODE_RCMM +#define DECODE_RCMM _IR_ENABLE_DEFAULT_ +#endif // DECODE_RCMM +#ifndef SEND_RCMM +#define SEND_RCMM _IR_ENABLE_DEFAULT_ +#endif // SEND_RCMM + +#ifndef DECODE_SONY +#define DECODE_SONY _IR_ENABLE_DEFAULT_ +#endif // DECODE_SONY +#ifndef SEND_SONY +#define SEND_SONY _IR_ENABLE_DEFAULT_ +#endif // SEND_SONY + +#ifndef DECODE_PANASONIC +#define DECODE_PANASONIC _IR_ENABLE_DEFAULT_ +#endif // DECODE_PANASONIC +#ifndef SEND_PANASONIC +#define SEND_PANASONIC _IR_ENABLE_DEFAULT_ +#endif // SEND_PANASONIC + +#ifndef DECODE_JVC +#define DECODE_JVC _IR_ENABLE_DEFAULT_ +#endif // DECODE_JVC +#ifndef SEND_JVC +#define SEND_JVC _IR_ENABLE_DEFAULT_ +#endif // SEND_JVC + +#ifndef DECODE_SAMSUNG +#define DECODE_SAMSUNG _IR_ENABLE_DEFAULT_ +#endif // DECODE_SAMSUNG +#ifndef SEND_SAMSUNG +#define SEND_SAMSUNG _IR_ENABLE_DEFAULT_ +#endif // SEND_SAMSUNG + +#ifndef DECODE_SAMSUNG36 +#define DECODE_SAMSUNG36 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SAMSUNG36 +#ifndef SEND_SAMSUNG36 +#define SEND_SAMSUNG36 _IR_ENABLE_DEFAULT_ +#endif // SEND_SAMSUNG36 + +#ifndef DECODE_SAMSUNG_AC +#define DECODE_SAMSUNG_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_SAMSUNG_AC +#ifndef SEND_SAMSUNG_AC +#define SEND_SAMSUNG_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_SAMSUNG_AC + +#ifndef DECODE_WHYNTER +#define DECODE_WHYNTER _IR_ENABLE_DEFAULT_ +#endif // DECODE_WHYNTER +#ifndef SEND_WHYNTER +#define SEND_WHYNTER _IR_ENABLE_DEFAULT_ +#endif // SEND_WHYNTER + +#ifndef DECODE_AIWA_RC_T501 +#define DECODE_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIWA_RC_T501 +#ifndef SEND_AIWA_RC_T501 +#define SEND_AIWA_RC_T501 _IR_ENABLE_DEFAULT_ +#endif // SEND_AIWA_RC_T501 + +#ifndef DECODE_LG +#define DECODE_LG _IR_ENABLE_DEFAULT_ +#endif // DECODE_LG +#ifndef SEND_LG +#define SEND_LG _IR_ENABLE_DEFAULT_ +#endif // SEND_LG + +#ifndef DECODE_SANYO +#define DECODE_SANYO _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO +#ifndef SEND_SANYO +#define SEND_SANYO _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO + +#ifndef DECODE_SANYO_AC +#define DECODE_SANYO_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC +#ifndef SEND_SANYO_AC +#define SEND_SANYO_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC + +#ifndef DECODE_SANYO_AC88 +#define DECODE_SANYO_AC88 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC88 +#ifndef SEND_SANYO_AC88 +#define SEND_SANYO_AC88 _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC88 + +#ifndef DECODE_SANYO_AC152 +#define DECODE_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC152 +#ifndef SEND_SANYO_AC152 +#define SEND_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC152 + +#ifndef DECODE_MITSUBISHI +#define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI +#ifndef SEND_MITSUBISHI +#define SEND_MITSUBISHI _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI + +#ifndef DECODE_MITSUBISHI2 +#define DECODE_MITSUBISHI2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI2 +#ifndef SEND_MITSUBISHI2 +#define SEND_MITSUBISHI2 _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI2 + +#ifndef DECODE_DISH +#define DECODE_DISH _IR_ENABLE_DEFAULT_ +#endif // DECODE_DISH +#ifndef SEND_DISH +#define SEND_DISH _IR_ENABLE_DEFAULT_ +#endif // SEND_DISH + +#ifndef DECODE_SHARP +#define DECODE_SHARP _IR_ENABLE_DEFAULT_ +#endif // DECODE_SHARP +#ifndef SEND_SHARP +#define SEND_SHARP _IR_ENABLE_DEFAULT_ +#endif // SEND_SHARP + +#ifndef DECODE_SHARP_AC +#define DECODE_SHARP_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_SHARP_AC +#ifndef SEND_SHARP_AC +#define SEND_SHARP_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_SHARP_AC + +#ifndef DECODE_DENON +#define DECODE_DENON _IR_ENABLE_DEFAULT_ +#endif // DECODE_DENON +#ifndef SEND_DENON +#define SEND_DENON _IR_ENABLE_DEFAULT_ +#endif // SEND_DENON + +#ifndef DECODE_KELVINATOR +#define DECODE_KELVINATOR _IR_ENABLE_DEFAULT_ +#endif // DECODE_KELVINATOR +#ifndef SEND_KELVINATOR +#define SEND_KELVINATOR _IR_ENABLE_DEFAULT_ +#endif // SEND_KELVINATOR + +#ifndef DECODE_MITSUBISHI_AC +#define DECODE_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI_AC +#ifndef SEND_MITSUBISHI_AC +#define SEND_MITSUBISHI_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI_AC + +#ifndef DECODE_MITSUBISHI136 +#define DECODE_MITSUBISHI136 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI136 +#ifndef SEND_MITSUBISHI136 +#define SEND_MITSUBISHI136 _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI136 + +#ifndef DECODE_MITSUBISHI112 +#define DECODE_MITSUBISHI112 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHI112 +#ifndef SEND_MITSUBISHI112 +#define SEND_MITSUBISHI112 _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHI112 + +#ifndef DECODE_FUJITSU_AC +#define DECODE_FUJITSU_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_FUJITSU_AC +#ifndef SEND_FUJITSU_AC +#define SEND_FUJITSU_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_FUJITSU_AC + +#ifndef DECODE_INAX +#define DECODE_INAX _IR_ENABLE_DEFAULT_ +#endif // DECODE_INAX +#ifndef SEND_INAX +#define SEND_INAX _IR_ENABLE_DEFAULT_ +#endif // SEND_INAX + +#ifndef DECODE_DAIKIN +#define DECODE_DAIKIN _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN +#ifndef SEND_DAIKIN +#define SEND_DAIKIN _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN + +#ifndef DECODE_COOLIX +#define DECODE_COOLIX _IR_ENABLE_DEFAULT_ +#endif // DECODE_COOLIX +#ifndef SEND_COOLIX +#define SEND_COOLIX _IR_ENABLE_DEFAULT_ +#endif // SEND_COOLIX + +#ifndef DECODE_COOLIX48 +#define DECODE_COOLIX48 _IR_ENABLE_DEFAULT_ +#endif // DECODE_COOLIX48 +#ifndef SEND_COOLIX48 +#define SEND_COOLIX48 _IR_ENABLE_DEFAULT_ +#endif // SEND_COOLIX48 + +#ifndef DECODE_GLOBALCACHE +#define DECODE_GLOBALCACHE false // Not applicable. +#endif // DECODE_GLOBALCACHE +#ifndef SEND_GLOBALCACHE +#define SEND_GLOBALCACHE _IR_ENABLE_DEFAULT_ +#endif // SEND_GLOBALCACHE + +#ifndef DECODE_GOODWEATHER +#define DECODE_GOODWEATHER _IR_ENABLE_DEFAULT_ +#endif // DECODE_GOODWEATHER +#ifndef SEND_GOODWEATHER +#define SEND_GOODWEATHER _IR_ENABLE_DEFAULT_ +#endif // SEND_GOODWEATHER + +#ifndef DECODE_GREE +#define DECODE_GREE _IR_ENABLE_DEFAULT_ +#endif // DECODE_GREE +#ifndef SEND_GREE +#define SEND_GREE _IR_ENABLE_DEFAULT_ +#endif // SEND_GREE + +#ifndef DECODE_PRONTO +#define DECODE_PRONTO false // Not applicable. +#endif // DECODE_PRONTO +#ifndef SEND_PRONTO +#define SEND_PRONTO _IR_ENABLE_DEFAULT_ +#endif // SEND_PRONTO + +#ifndef DECODE_ARGO +#define DECODE_ARGO _IR_ENABLE_DEFAULT_ +#endif // DECODE_ARGO +#ifndef SEND_ARGO +#define SEND_ARGO _IR_ENABLE_DEFAULT_ +#endif // SEND_ARGO + +#ifndef DECODE_TROTEC +#define DECODE_TROTEC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TROTEC +#ifndef SEND_TROTEC +#define SEND_TROTEC _IR_ENABLE_DEFAULT_ +#endif // SEND_TROTEC + +#ifndef DECODE_TROTEC_3550 +#define DECODE_TROTEC_3550 _IR_ENABLE_DEFAULT_ +#endif // DECODE_TROTEC_3550 +#ifndef SEND_TROTEC_3550 +#define SEND_TROTEC_3550 _IR_ENABLE_DEFAULT_ +#endif // SEND_TROTEC_3550 + +#ifndef DECODE_NIKAI +#define DECODE_NIKAI _IR_ENABLE_DEFAULT_ +#endif // DECODE_NIKAI +#ifndef SEND_NIKAI +#define SEND_NIKAI _IR_ENABLE_DEFAULT_ +#endif // SEND_NIKAI + +#ifndef DECODE_TOSHIBA_AC +#define DECODE_TOSHIBA_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TOSHIBA_AC +#ifndef SEND_TOSHIBA_AC +#define SEND_TOSHIBA_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TOSHIBA_AC + +#ifndef DECODE_MAGIQUEST +#define DECODE_MAGIQUEST _IR_ENABLE_DEFAULT_ +#endif // DECODE_MAGIQUEST +#ifndef SEND_MAGIQUEST +#define SEND_MAGIQUEST _IR_ENABLE_DEFAULT_ +#endif // SEND_MAGIQUEST + +#ifndef DECODE_MIDEA +#define DECODE_MIDEA _IR_ENABLE_DEFAULT_ +#endif // DECODE_MIDEA +#ifndef SEND_MIDEA +#define SEND_MIDEA _IR_ENABLE_DEFAULT_ +#endif // SEND_MIDEA + +#ifndef DECODE_MIDEA24 +#define DECODE_MIDEA24 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MIDEA24 +#ifndef SEND_MIDEA24 +#define SEND_MIDEA24 _IR_ENABLE_DEFAULT_ +#endif // SEND_MIDEA24 + +#ifndef DECODE_LASERTAG +#define DECODE_LASERTAG _IR_ENABLE_DEFAULT_ +#endif // DECODE_LASERTAG +#ifndef SEND_LASERTAG +#define SEND_LASERTAG _IR_ENABLE_DEFAULT_ +#endif // SEND_LASERTAG + +#ifndef DECODE_CARRIER_AC +#define DECODE_CARRIER_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC +#ifndef SEND_CARRIER_AC +#define SEND_CARRIER_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC + +#ifndef DECODE_CARRIER_AC40 +#define DECODE_CARRIER_AC40 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC40 +#ifndef SEND_CARRIER_AC40 +#define SEND_CARRIER_AC40 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC40 + +#ifndef DECODE_CARRIER_AC64 +#define DECODE_CARRIER_AC64 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC64 +#ifndef SEND_CARRIER_AC64 +#define SEND_CARRIER_AC64 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC64 + +#ifndef DECODE_CARRIER_AC128 +#define DECODE_CARRIER_AC128 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC128 +#ifndef SEND_CARRIER_AC128 +#define SEND_CARRIER_AC128 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC128 + +#ifndef DECODE_HAIER_AC +#define DECODE_HAIER_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC +#ifndef SEND_HAIER_AC +#define SEND_HAIER_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC + +#ifndef DECODE_HITACHI_AC +#define DECODE_HITACHI_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC +#ifndef SEND_HITACHI_AC +#define SEND_HITACHI_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC + +#ifndef DECODE_HITACHI_AC1 +#define DECODE_HITACHI_AC1 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC1 +#ifndef SEND_HITACHI_AC1 +#define SEND_HITACHI_AC1 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC1 + +#ifndef DECODE_HITACHI_AC2 +#define DECODE_HITACHI_AC2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC2 +#ifndef SEND_HITACHI_AC2 +#define SEND_HITACHI_AC2 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC2 + +#ifndef DECODE_HITACHI_AC3 +#define DECODE_HITACHI_AC3 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC3 +#ifndef SEND_HITACHI_AC3 +#define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC3 + +#ifndef DECODE_HITACHI_AC264 +#define DECODE_HITACHI_AC264 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC264 +#ifndef SEND_HITACHI_AC264 +#define SEND_HITACHI_AC264 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC264 + +#ifndef DECODE_HITACHI_AC296 +#define DECODE_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC296 +#ifndef SEND_HITACHI_AC296 +#define SEND_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC296 + +#ifndef DECODE_HITACHI_AC344 +#define DECODE_HITACHI_AC344 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC344 +#ifndef SEND_HITACHI_AC344 +#define SEND_HITACHI_AC344 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC344 + +#ifndef DECODE_HITACHI_AC424 +#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC424 +#ifndef SEND_HITACHI_AC424 +#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC424 + +#ifndef DECODE_GICABLE +#define DECODE_GICABLE _IR_ENABLE_DEFAULT_ +#endif // DECODE_GICABLE +#ifndef SEND_GICABLE +#define SEND_GICABLE _IR_ENABLE_DEFAULT_ +#endif // SEND_GICABLE + +#ifndef DECODE_HAIER_AC_YRW02 +#define DECODE_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC_YRW02 +#ifndef SEND_HAIER_AC_YRW02 +#define SEND_HAIER_AC_YRW02 _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC_YRW02 + +#ifndef DECODE_WHIRLPOOL_AC +#define DECODE_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_WHIRLPOOL_AC +#ifndef SEND_WHIRLPOOL_AC +#define SEND_WHIRLPOOL_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_WHIRLPOOL_AC + +#ifndef DECODE_LUTRON +#define DECODE_LUTRON _IR_ENABLE_DEFAULT_ +#endif // DECODE_LUTRON +#ifndef SEND_LUTRON +#define SEND_LUTRON _IR_ENABLE_DEFAULT_ +#endif // SEND_LUTRON + +#ifndef DECODE_ELECTRA_AC +#define DECODE_ELECTRA_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_ELECTRA_AC +#ifndef SEND_ELECTRA_AC +#define SEND_ELECTRA_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_ELECTRA_AC + +#ifndef DECODE_PANASONIC_AC +#define DECODE_PANASONIC_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_PANASONIC_AC +#ifndef SEND_PANASONIC_AC +#define SEND_PANASONIC_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_PANASONIC_AC + +#ifndef DECODE_PANASONIC_AC32 +#define DECODE_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ +#endif // DECODE_PANASONIC_AC32 +#ifndef SEND_PANASONIC_AC32 +#define SEND_PANASONIC_AC32 _IR_ENABLE_DEFAULT_ +#endif // SEND_PANASONIC_AC32 + +#ifndef DECODE_MWM +#define DECODE_MWM _IR_ENABLE_DEFAULT_ +#endif // DECODE_MWM +#ifndef SEND_MWM +#define SEND_MWM _IR_ENABLE_DEFAULT_ +#endif // SEND_MWM + +#ifndef DECODE_PIONEER +#define DECODE_PIONEER _IR_ENABLE_DEFAULT_ +#endif // DECODE_PIONEER +#ifndef SEND_PIONEER +#define SEND_PIONEER _IR_ENABLE_DEFAULT_ +#endif // SEND_PIONEER + +#ifndef DECODE_DAIKIN2 +#define DECODE_DAIKIN2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN2 +#ifndef SEND_DAIKIN2 +#define SEND_DAIKIN2 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN2 + +#ifndef DECODE_VESTEL_AC +#define DECODE_VESTEL_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_VESTEL_AC +#ifndef SEND_VESTEL_AC +#define SEND_VESTEL_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_VESTEL_AC + +#ifndef DECODE_TECO +#define DECODE_TECO _IR_ENABLE_DEFAULT_ +#endif // DECODE_TECO +#ifndef SEND_TECO +#define SEND_TECO _IR_ENABLE_DEFAULT_ +#endif // SEND_TECO + +#ifndef DECODE_TCL96AC +#define DECODE_TCL96AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TCL96AC +#ifndef SEND_TCL96AC +#define SEND_TCL96AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TCL96AC + +#ifndef DECODE_TCL112AC +#define DECODE_TCL112AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TCL112AC +#ifndef SEND_TCL112AC +#define SEND_TCL112AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TCL112AC + +#ifndef DECODE_LEGOPF +#define DECODE_LEGOPF _IR_ENABLE_DEFAULT_ +#endif // DECODE_LEGOPF +#ifndef SEND_LEGOPF +#define SEND_LEGOPF _IR_ENABLE_DEFAULT_ +#endif // SEND_LEGOPF + +#ifndef DECODE_MITSUBISHIHEAVY +#define DECODE_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ +#endif // DECODE_MITSUBISHIHEAVY +#ifndef SEND_MITSUBISHIHEAVY +#define SEND_MITSUBISHIHEAVY _IR_ENABLE_DEFAULT_ +#endif // SEND_MITSUBISHIHEAVY + +#ifndef DECODE_DAIKIN216 +#define DECODE_DAIKIN216 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN216 +#ifndef SEND_DAIKIN216 +#define SEND_DAIKIN216 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN216 + +#ifndef DECODE_DAIKIN160 +#define DECODE_DAIKIN160 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN160 +#ifndef SEND_DAIKIN160 +#define SEND_DAIKIN160 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN160 + +#ifndef DECODE_NEOCLIMA +#define DECODE_NEOCLIMA _IR_ENABLE_DEFAULT_ +#endif // DECODE_NEOCLIMA +#ifndef SEND_NEOCLIMA +#define SEND_NEOCLIMA _IR_ENABLE_DEFAULT_ +#endif // SEND_NEOCLIMA + +#ifndef DECODE_DAIKIN176 +#define DECODE_DAIKIN176 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN176 +#ifndef SEND_DAIKIN176 +#define SEND_DAIKIN176 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN176 + +#ifndef DECODE_DAIKIN128 +#define DECODE_DAIKIN128 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN128 +#ifndef SEND_DAIKIN128 +#define SEND_DAIKIN128 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN128 + +#ifndef DECODE_AMCOR +#define DECODE_AMCOR _IR_ENABLE_DEFAULT_ +#endif // DECODE_AMCOR +#ifndef SEND_AMCOR +#define SEND_AMCOR _IR_ENABLE_DEFAULT_ +#endif // SEND_AMCOR + +#ifndef DECODE_DAIKIN152 +#define DECODE_DAIKIN152 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN152 +#ifndef SEND_DAIKIN152 +#define SEND_DAIKIN152 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN152 + +#ifndef DECODE_EPSON +#define DECODE_EPSON _IR_ENABLE_DEFAULT_ +#endif // DECODE_EPSON +#ifndef SEND_EPSON +#define SEND_EPSON _IR_ENABLE_DEFAULT_ +#endif // SEND_EPSON + +#ifndef DECODE_SYMPHONY +#define DECODE_SYMPHONY _IR_ENABLE_DEFAULT_ +#endif // DECODE_SYMPHONY +#ifndef SEND_SYMPHONY +#define SEND_SYMPHONY _IR_ENABLE_DEFAULT_ +#endif // SEND_SYMPHONY + +#ifndef DECODE_DAIKIN64 +#define DECODE_DAIKIN64 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN64 +#ifndef SEND_DAIKIN64 +#define SEND_DAIKIN64 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN64 + +#ifndef DECODE_AIRWELL +#define DECODE_AIRWELL _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIRWELL +#ifndef SEND_AIRWELL +#define SEND_AIRWELL _IR_ENABLE_DEFAULT_ +#endif // SEND_AIRWELL + +#ifndef DECODE_DELONGHI_AC +#define DECODE_DELONGHI_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_DELONGHI_AC +#ifndef SEND_DELONGHI_AC +#define SEND_DELONGHI_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_DELONGHI_AC + +#ifndef DECODE_DOSHISHA +#define DECODE_DOSHISHA _IR_ENABLE_DEFAULT_ +#endif // DECODE_DOSHISHA +#ifndef SEND_DOSHISHA +#define SEND_DOSHISHA _IR_ENABLE_DEFAULT_ +#endif // SEND_DOSHISHA + +#ifndef DECODE_MULTIBRACKETS +#define DECODE_MULTIBRACKETS _IR_ENABLE_DEFAULT_ +#endif // DECODE_MULTIBRACKETS +#ifndef SEND_MULTIBRACKETS +#define SEND_MULTIBRACKETS _IR_ENABLE_DEFAULT_ +#endif // SEND_MULTIBRACKETS + +#ifndef DECODE_TECHNIBEL_AC +#define DECODE_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TECHNIBEL_AC +#ifndef SEND_TECHNIBEL_AC +#define SEND_TECHNIBEL_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TECHNIBEL_AC + +#ifndef DECODE_CORONA_AC +#define DECODE_CORONA_AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_CORONA_AC +#ifndef SEND_CORONA_AC +#define SEND_CORONA_AC _IR_ENABLE_DEFAULT_ +#endif // SEND_CORONA_AC + +#ifndef DECODE_ZEPEAL +#define DECODE_ZEPEAL _IR_ENABLE_DEFAULT_ +#endif // DECODE_ZEPEAL +#ifndef SEND_ZEPEAL +#define SEND_ZEPEAL _IR_ENABLE_DEFAULT_ +#endif // SEND_ZEPEAL + +#ifndef DECODE_VOLTAS +#define DECODE_VOLTAS _IR_ENABLE_DEFAULT_ +#endif // DECODE_VOLTAS +#ifndef SEND_VOLTAS +#define SEND_VOLTAS _IR_ENABLE_DEFAULT_ +#endif // SEND_VOLTAS + +#ifndef DECODE_METZ +#define DECODE_METZ _IR_ENABLE_DEFAULT_ +#endif // DECODE_METZ +#ifndef SEND_METZ +#define SEND_METZ _IR_ENABLE_DEFAULT_ +#endif // SEND_METZ + +#ifndef DECODE_TRANSCOLD +#define DECODE_TRANSCOLD _IR_ENABLE_DEFAULT_ +#endif // DECODE_TRANSCOLD +#ifndef SEND_TRANSCOLD +#define SEND_TRANSCOLD _IR_ENABLE_DEFAULT_ +#endif // SEND_TRANSCOLD + +#ifndef DECODE_MIRAGE +#define DECODE_MIRAGE _IR_ENABLE_DEFAULT_ +#endif // DECODE_MIRAGE +#ifndef SEND_MIRAGE +#define SEND_MIRAGE _IR_ENABLE_DEFAULT_ +#endif // SEND_MIRAGE + +#ifndef DECODE_ELITESCREENS +#define DECODE_ELITESCREENS _IR_ENABLE_DEFAULT_ +#endif // DECODE_ELITESCREENS +#ifndef SEND_ELITESCREENS +#define SEND_ELITESCREENS _IR_ENABLE_DEFAULT_ +#endif // SEND_ELITESCREENS + +#ifndef DECODE_MILESTAG2 +#define DECODE_MILESTAG2 _IR_ENABLE_DEFAULT_ +#endif // DECODE_MILESTAG2 +#ifndef SEND_MILESTAG2 +#define SEND_MILESTAG2 _IR_ENABLE_DEFAULT_ +#endif // SEND_MILESTAG2 + +#ifndef DECODE_ECOCLIM +#define DECODE_ECOCLIM _IR_ENABLE_DEFAULT_ +#endif // DECODE_ECOCLIM +#ifndef SEND_ECOCLIM +#define SEND_ECOCLIM _IR_ENABLE_DEFAULT_ +#endif // SEND_ECOCLIM + +#ifndef DECODE_XMP +#define DECODE_XMP _IR_ENABLE_DEFAULT_ +#endif // DECODE_XMP +#ifndef SEND_XMP +#define SEND_XMP _IR_ENABLE_DEFAULT_ +#endif // SEND_XMP + +#ifndef DECODE_TRUMA +#define DECODE_TRUMA _IR_ENABLE_DEFAULT_ +#endif // DECODE_TRUMA +#ifndef SEND_TRUMA +#define SEND_TRUMA _IR_ENABLE_DEFAULT_ +#endif // SEND_TRUMA + +#ifndef DECODE_HAIER_AC176 +#define DECODE_HAIER_AC176 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC176 +#ifndef SEND_HAIER_AC176 +#define SEND_HAIER_AC176 _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC176 + +#ifndef DECODE_TEKNOPOINT +#define DECODE_TEKNOPOINT _IR_ENABLE_DEFAULT_ +#endif // DECODE_TEKNOPOINT +#ifndef SEND_TEKNOPOINT +#define SEND_TEKNOPOINT _IR_ENABLE_DEFAULT_ +#endif // SEND_TEKNOPOINT + +#ifndef DECODE_KELON +#define DECODE_KELON _IR_ENABLE_DEFAULT_ +#endif // DECODE_KELON +#ifndef SEND_KELON +#define SEND_KELON _IR_ENABLE_DEFAULT_ +#endif // SEND_KELON + +#ifndef DECODE_BOSE +#define DECODE_BOSE _IR_ENABLE_DEFAULT_ +#endif // DECODE_BOSE +#ifndef SEND_BOSE +#define SEND_BOSE _IR_ENABLE_DEFAULT_ +#endif // SEND_BOSE + +#ifndef DECODE_ARRIS +#define DECODE_ARRIS _IR_ENABLE_DEFAULT_ +#endif // DECODE_ARRIS +#ifndef SEND_ARRIS +#define SEND_ARRIS _IR_ENABLE_DEFAULT_ +#endif // SEND_ARRIS + +#ifndef DECODE_RHOSS +#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ +#endif // DECODE_RHOSS +#ifndef SEND_RHOSS +#define SEND_RHOSS _IR_ENABLE_DEFAULT_ +#endif // SEND_RHOSS + +#ifndef DECODE_AIRTON +#define DECODE_AIRTON _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIRTON +#ifndef SEND_AIRTON +#define SEND_AIRTON _IR_ENABLE_DEFAULT_ +#endif // SEND_AIRTON + +#ifndef DECODE_KELON168 +#define DECODE_KELON168 _IR_ENABLE_DEFAULT_ +#endif // DECODE_KELON168 +#ifndef SEND_KELON168 +#define SEND_KELON168 _IR_ENABLE_DEFAULT_ +#endif // SEND_KELON168 + +#ifndef DECODE_DAIKIN200 +#define DECODE_DAIKIN200 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN200 +#ifndef SEND_DAIKIN200 +#define SEND_DAIKIN200 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN200 + +#ifndef DECODE_HAIER_AC160 +#define DECODE_HAIER_AC160 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC160 +#ifndef SEND_HAIER_AC160 +#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC160 + +#ifndef DECODE_TOTO +#define DECODE_TOTO _IR_ENABLE_DEFAULT_ +#endif // DECODE_TOTO +#ifndef SEND_TOTO +#define SEND_TOTO _IR_ENABLE_DEFAULT_ +#endif // SEND_TOTO + +#ifndef DECODE_CLIMABUTLER +#define DECODE_CLIMABUTLER _IR_ENABLE_DEFAULT_ +#endif // DECODE_CLIMABUTLER +#ifndef SEND_CLIMABUTLER +#define SEND_CLIMABUTLER _IR_ENABLE_DEFAULT_ +#endif // SEND_CLIMABUTLER + +#ifndef DECODE_BOSCH144 +#define DECODE_BOSCH144 _IR_ENABLE_DEFAULT_ +#endif // DECODE_BOSCH144 +#ifndef SEND_BOSCH144 +#define SEND_BOSCH144 _IR_ENABLE_DEFAULT_ +#endif // SEND_BOSCH144 + +#ifndef DECODE_DAIKIN312 +#define DECODE_DAIKIN312 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN312 +#ifndef SEND_DAIKIN312 +#define SEND_DAIKIN312 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN312 + +#ifndef DECODE_GORENJE +#define DECODE_GORENJE _IR_ENABLE_DEFAULT_ +#endif // DECODE_GORENJE +#ifndef SEND_GORENJE +#define SEND_GORENJE _IR_ENABLE_DEFAULT_ +#endif // SEND_GORENJE + +#ifndef DECODE_WOWWEE +#define DECODE_WOWWEE _IR_ENABLE_DEFAULT_ +#endif // DECODE_WOWWEE +#ifndef SEND_WOWWEE +#define SEND_WOWWEE _IR_ENABLE_DEFAULT_ +#endif // SEND_WOWWEE + +#ifndef DECODE_CARRIER_AC84 +#define DECODE_CARRIER_AC84 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC84 +#ifndef SEND_CARRIER_AC84 +#define SEND_CARRIER_AC84 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC84 + +#ifndef DECODE_YORK +#define DECODE_YORK _IR_ENABLE_DEFAULT_ +#endif // DECODE_YORK +#ifndef SEND_YORK +#define SEND_YORK _IR_ENABLE_DEFAULT_ +#endif // SEND_YORK + +#ifndef DECODE_FUJITSU_AC264 +#define DECODE_FUJITSU_AC264 _IR_ENABLE_DEFAULT_ +#endif // DECODE_FUJITSU_AC264 +#ifndef SEND_FUJITSU_AC264 +#define SEND_FUJITSU_AC264 _IR_ENABLE_DEFAULT_ +#endif // SEND_FUJITSU_AC264 + +#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ + DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ + DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ + DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ + DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ + DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ + DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ + DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ + DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ + DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \ + DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \ + DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \ + DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \ + DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \ + DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \ + DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \ + DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \ + DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \ + DECODE_CARRIER_AC84 || DECODE_YORK || DECODE_FUJITSU_AC264 || \ + false) + // Add any DECODE to the above if it uses result->state (see kStateSizeMax) + // you might also want to add the protocol to hasACState function +#define DECODE_AC true // We need some common infrastructure for decoding A/Cs. +#else +#define DECODE_AC false // We don't need that infrastructure. +#endif + +// Use millisecond 'delay()' calls where we can to avoid tripping the WDT. +// Note: If you plan to send IR messages in the callbacks of the AsyncWebserver +// library, you need to set ALLOW_DELAY_CALLS to false. +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/430 +#ifndef ALLOW_DELAY_CALLS +#define ALLOW_DELAY_CALLS true +#endif // ALLOW_DELAY_CALLS + +// Enable a run-time settable high-pass filter on captured data **before** +// trying any protocol decoding. +// i.e. Try to remove/merge any really short pulses detected in the raw data. +// Note: Even when this option is enabled, it is _off_ by default, and requires +// a user who knows what they are doing to enable it. +// The option to disable this feature is here if your project is _really_ +// tight on resources. i.e. Saves a small handful of bytes and cpu time. +// WARNING: If you use this feature at runtime, you can no longer trust the +// **raw** data captured. It will now have been slightly **cooked**! +// DANGER: If you set the `noise_floor` value too high, it **WILL** break +// decoding of some protocols. You have been warned. Here Be Dragons! +// +// See: `irrecv::decode()` in IRrecv.cpp for more info. +#ifndef ENABLE_NOISE_FILTER_OPTION +#define ENABLE_NOISE_FILTER_OPTION true +#endif // ENABLE_NOISE_FILTER_OPTION + +/// Enumerator for defining and numbering of supported IR protocol. +/// @note Always add to the end of the list and should never remove entries +/// or change order. Projects may save the type number for later usage +/// so numbering should always stay the same. +enum decode_type_t { + UNKNOWN = -1, + UNUSED = 0, + RC5, + RC6, + NEC, + SONY, + PANASONIC, // (5) + JVC, + SAMSUNG, + WHYNTER, + AIWA_RC_T501, + LG, // (10) + SANYO, + MITSUBISHI, + DISH, + SHARP, + COOLIX, // (15) + DAIKIN, + DENON, + KELVINATOR, + SHERWOOD, + MITSUBISHI_AC, // (20) + RCMM, + SANYO_LC7461, + RC5X, + GREE, + PRONTO, // Technically not a protocol, but an encoding. (25) + NEC_LIKE, + ARGO, + TROTEC, + NIKAI, + RAW, // Technically not a protocol, but an encoding. (30) + GLOBALCACHE, // Technically not a protocol, but an encoding. + TOSHIBA_AC, + FUJITSU_AC, + MIDEA, + MAGIQUEST, // (35) + LASERTAG, + CARRIER_AC, + HAIER_AC, + MITSUBISHI2, + HITACHI_AC, // (40) + HITACHI_AC1, + HITACHI_AC2, + GICABLE, + HAIER_AC_YRW02, + WHIRLPOOL_AC, // (45) + SAMSUNG_AC, + LUTRON, + ELECTRA_AC, + PANASONIC_AC, + PIONEER, // (50) + LG2, + MWM, + DAIKIN2, + VESTEL_AC, + TECO, // (55) + SAMSUNG36, + TCL112AC, + LEGOPF, + MITSUBISHI_HEAVY_88, + MITSUBISHI_HEAVY_152, // 60 + DAIKIN216, + SHARP_AC, + GOODWEATHER, + INAX, + DAIKIN160, // 65 + NEOCLIMA, + DAIKIN176, + DAIKIN128, + AMCOR, + DAIKIN152, // 70 + MITSUBISHI136, + MITSUBISHI112, + HITACHI_AC424, + SONY_38K, + EPSON, // 75 + SYMPHONY, + HITACHI_AC3, + DAIKIN64, + AIRWELL, + DELONGHI_AC, // 80 + DOSHISHA, + MULTIBRACKETS, + CARRIER_AC40, + CARRIER_AC64, + HITACHI_AC344, // 85 + CORONA_AC, + MIDEA24, + ZEPEAL, + SANYO_AC, + VOLTAS, // 90 + METZ, + TRANSCOLD, + TECHNIBEL_AC, + MIRAGE, + ELITESCREENS, // 95 + PANASONIC_AC32, + MILESTAG2, + ECOCLIM, + XMP, + TRUMA, // 100 + HAIER_AC176, + TEKNOPOINT, + KELON, + TROTEC_3550, + SANYO_AC88, // 105 + BOSE, + ARRIS, + RHOSS, + AIRTON, + COOLIX48, // 110 + HITACHI_AC264, + KELON168, + HITACHI_AC296, + DAIKIN200, + HAIER_AC160, // 115 + CARRIER_AC128, + TOTO, + CLIMABUTLER, + TCL96AC, + BOSCH144, // 120 + SANYO_AC152, + DAIKIN312, + GORENJE, + WOWWEE, + CARRIER_AC84, // 125 + YORK, + FUJITSU_AC264, + // Add new entries before this one, and update it to point to the last entry. + kLastDecodeType = FUJITSU_AC264, +}; + +// Message lengths & required repeat values +const uint16_t kNoRepeat = 0; +const uint16_t kSingleRepeat = 1; + +const uint16_t kAirtonBits = 56; +const uint16_t kAirtonDefaultRepeat = kNoRepeat; +const uint16_t kAirwellBits = 34; +const uint16_t kAirwellMinRepeats = 2; +const uint16_t kAiwaRcT501Bits = 15; +const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; +const uint16_t kAlokaBits = 32; +const uint16_t kAmcorStateLength = 8; +const uint16_t kAmcorBits = kAmcorStateLength * 8; +const uint16_t kAmcorDefaultRepeat = kSingleRepeat; +const uint16_t kArgoStateLength = 12; +const uint16_t kArgoShortStateLength = 4; +const uint16_t kArgoBits = kArgoStateLength * 8; +const uint16_t kArgoShortBits = kArgoShortStateLength * 8; +const uint16_t kArgo3AcControlStateLength = 6; // Bytes +const uint16_t kArgo3iFeelReportStateLength = 2; // Bytes +const uint16_t kArgo3TimerStateLength = 9; // Bytes +const uint16_t kArgo3ConfigStateLength = 4; // Bytes +const uint16_t kArgoDefaultRepeat = kNoRepeat; +const uint16_t kArrisBits = 32; +const uint16_t kBosch144StateLength = 18; +const uint16_t kBosch144Bits = kBosch144StateLength * 8; +const uint16_t kCoolixBits = 24; +const uint16_t kCoolix48Bits = kCoolixBits * 2; +const uint16_t kCoolixDefaultRepeat = kSingleRepeat; +const uint16_t kCarrierAcBits = 32; +const uint16_t kCarrierAcMinRepeat = kNoRepeat; +const uint16_t kCarrierAc40Bits = 40; +const uint16_t kCarrierAc40MinRepeat = 2; +const uint16_t kCarrierAc64Bits = 64; +const uint16_t kCarrierAc64MinRepeat = kNoRepeat; +const uint16_t kCarrierAc84StateLength = 11; +const uint16_t kCarrierAc84Bits = kCarrierAc84StateLength * 8 - 4; +const uint16_t kCarrierAc84MinRepeat = kNoRepeat; +const uint16_t kCarrierAc128StateLength = 16; +const uint16_t kCarrierAc128Bits = kCarrierAc128StateLength * 8; +const uint16_t kCarrierAc128MinRepeat = kNoRepeat; +const uint16_t kCoronaAcStateLengthShort = 7; +const uint16_t kCoronaAcStateLength = kCoronaAcStateLengthShort * 3; +const uint16_t kCoronaAcBitsShort = kCoronaAcStateLengthShort * 8; +const uint16_t kCoronaAcBits = kCoronaAcStateLength * 8; +const uint16_t kDaikinStateLength = 35; +const uint16_t kDaikinBits = kDaikinStateLength * 8; +const uint16_t kDaikinStateLengthShort = kDaikinStateLength - 8; +const uint16_t kDaikinBitsShort = kDaikinStateLengthShort * 8; +const uint16_t kDaikinDefaultRepeat = kNoRepeat; +const uint16_t kDaikin2StateLength = 39; +const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; +const uint16_t kDaikin2DefaultRepeat = kNoRepeat; +const uint16_t kDaikin64Bits = 64; +const uint16_t kDaikin64DefaultRepeat = kNoRepeat; +const uint16_t kDaikin160StateLength = 20; +const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; +const uint16_t kDaikin160DefaultRepeat = kNoRepeat; +const uint16_t kDaikin128StateLength = 16; +const uint16_t kDaikin128Bits = kDaikin128StateLength * 8; +const uint16_t kDaikin128DefaultRepeat = kNoRepeat; +const uint16_t kDaikin152StateLength = 19; +const uint16_t kDaikin152Bits = kDaikin152StateLength * 8; +const uint16_t kDaikin152DefaultRepeat = kNoRepeat; +const uint16_t kDaikin176StateLength = 22; +const uint16_t kDaikin176Bits = kDaikin176StateLength * 8; +const uint16_t kDaikin176DefaultRepeat = kNoRepeat; +const uint16_t kDaikin200StateLength = 25; +const uint16_t kDaikin200Bits = kDaikin200StateLength * 8; +const uint16_t kDaikin200DefaultRepeat = kNoRepeat; +const uint16_t kDaikin216StateLength = 27; +const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; +const uint16_t kDaikin216DefaultRepeat = kNoRepeat; +const uint16_t kDaikin312StateLength = 39; +const uint16_t kDaikin312Bits = kDaikin312StateLength * 8; +const uint16_t kDaikin312DefaultRepeat = kNoRepeat; +const uint16_t kDelonghiAcBits = 64; +const uint16_t kDelonghiAcDefaultRepeat = kNoRepeat; +const uint16_t kTechnibelAcBits = 56; +const uint16_t kTechnibelAcDefaultRepeat = kNoRepeat; +const uint16_t kDenonBits = 15; +const uint16_t kDenon48Bits = 48; +const uint16_t kDenonLegacyBits = 14; +const uint16_t kDishBits = 16; +const uint16_t kDishMinRepeat = 3; +const uint16_t kDoshishaBits = 40; +const uint16_t kEcoclimBits = 56; +const uint16_t kEcoclimShortBits = 15; +const uint16_t kEpsonBits = 32; +const uint16_t kEpsonMinRepeat = 2; +const uint16_t kElectraAcStateLength = 13; +const uint16_t kElectraAcBits = kElectraAcStateLength * 8; +const uint16_t kElectraAcMinRepeat = kNoRepeat; +const uint16_t kEliteScreensBits = 32; +const uint16_t kEliteScreensDefaultRepeat = kSingleRepeat; +const uint16_t kFujitsuAcMinRepeat = kNoRepeat; +const uint16_t kFujitsuAcStateLength = 16; +const uint16_t kFujitsuAcStateLengthShort = 7; +const uint16_t kFujitsuAcBits = kFujitsuAcStateLength * 8; +const uint16_t kFujitsuAcMinBits = (kFujitsuAcStateLengthShort - 1) * 8; +const uint16_t kFujitsuAc264DefaultRepeat = kNoRepeat; +const uint16_t kFujitsuAc264StateLength = 33; +const uint16_t kFujitsuAc264StateLengthMiddle = 16; +const uint16_t kFujitsuAc264StateLengthShort = 7; +const uint16_t kFujitsuAc264Bits = kFujitsuAc264StateLength * 8; +const uint16_t kFujitsuAc264BitsMiddle = kFujitsuAc264StateLengthMiddle * 8; +const uint16_t kFujitsuAc264BitsShort = kFujitsuAc264StateLengthShort * 8; +const uint16_t kGicableBits = 16; +const uint16_t kGicableMinRepeat = kSingleRepeat; +const uint16_t kGoodweatherBits = 48; +const uint16_t kGoodweatherMinRepeat = kNoRepeat; +const uint16_t kGorenjeBits = 8; +const uint16_t kGreeStateLength = 8; +const uint16_t kGreeBits = kGreeStateLength * 8; +const uint16_t kGreeDefaultRepeat = kNoRepeat; +const uint16_t kHaierACStateLength = 9; +const uint16_t kHaierACBits = kHaierACStateLength * 8; +const uint16_t kHaierAcDefaultRepeat = kNoRepeat; +const uint16_t kHaierACYRW02StateLength = 14; +const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8; +const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat; +const uint16_t kHaierAC160StateLength = 20; +const uint16_t kHaierAC160Bits = kHaierAC160StateLength * 8; +const uint16_t kHaierAc160DefaultRepeat = kNoRepeat; +const uint16_t kHaierAC176StateLength = 22; +const uint16_t kHaierAC176Bits = kHaierAC176StateLength * 8; +const uint16_t kHaierAc176DefaultRepeat = kNoRepeat; +const uint16_t kHitachiAcStateLength = 28; +const uint16_t kHitachiAcBits = kHitachiAcStateLength * 8; +const uint16_t kHitachiAcDefaultRepeat = kNoRepeat; +const uint16_t kHitachiAc1StateLength = 13; +const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8; +const uint16_t kHitachiAc2StateLength = 53; +const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8; +const uint16_t kHitachiAc3StateLength = 27; +const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8; +const uint16_t kHitachiAc3MinStateLength = 15; +const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8; +const uint16_t kHitachiAc264StateLength = 33; +const uint16_t kHitachiAc264Bits = kHitachiAc264StateLength * 8; +const uint16_t kHitachiAc296StateLength = 37; +const uint16_t kHitachiAc296Bits = kHitachiAc296StateLength * 8; +const uint16_t kHitachiAc344StateLength = 43; +const uint16_t kHitachiAc344Bits = kHitachiAc344StateLength * 8; +const uint16_t kHitachiAc424StateLength = 53; +const uint16_t kHitachiAc424Bits = kHitachiAc424StateLength * 8; +const uint16_t kInaxBits = 24; +const uint16_t kInaxMinRepeat = kSingleRepeat; +const uint16_t kJvcBits = 16; +const uint16_t kKelonBits = 48; +const uint16_t kKelon168StateLength = 21; +const uint16_t kKelon168Bits = kKelon168StateLength * 8; +const uint16_t kKelvinatorStateLength = 16; +const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; +const uint16_t kKelvinatorDefaultRepeat = kNoRepeat; +const uint16_t kLasertagBits = 13; +const uint16_t kLasertagMinRepeat = kNoRepeat; +const uint16_t kLegoPfBits = 16; +const uint16_t kLegoPfMinRepeat = kNoRepeat; +const uint16_t kLgBits = 28; +const uint16_t kLg32Bits = 32; +const uint16_t kLgDefaultRepeat = kNoRepeat; +const uint16_t kLutronBits = 35; +const uint16_t kMagiquestBits = 56; +const uint16_t kMetzBits = 19; +const uint16_t kMetzMinRepeat = kNoRepeat; +const uint16_t kMideaBits = 48; +const uint16_t kMideaMinRepeat = kNoRepeat; +const uint16_t kMidea24Bits = 24; +const uint16_t kMidea24MinRepeat = kSingleRepeat; +const uint16_t kMirageStateLength = 15; +const uint16_t kMirageBits = kMirageStateLength * 8; +const uint16_t kMirageMinRepeat = kNoRepeat; +const uint16_t kMitsubishiBits = 16; +// TODO(anyone): Verify that the Mitsubishi repeat is really needed. +// Based on marcosamarinho's code. +const uint16_t kMitsubishiMinRepeat = kSingleRepeat; +const uint16_t kMitsubishiACStateLength = 18; +const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8; +const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; +const uint16_t kMitsubishi136StateLength = 17; +const uint16_t kMitsubishi136Bits = kMitsubishi136StateLength * 8; +const uint16_t kMitsubishi136MinRepeat = kNoRepeat; +const uint16_t kMitsubishi112StateLength = 14; +const uint16_t kMitsubishi112Bits = kMitsubishi112StateLength * 8; +const uint16_t kMitsubishi112MinRepeat = kNoRepeat; +const uint16_t kMitsubishiHeavy88StateLength = 11; +const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8; +const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; +const uint16_t kMitsubishiHeavy152StateLength = 19; +const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; +const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; +const uint16_t kMultibracketsBits = 8; +const uint16_t kMultibracketsDefaultRepeat = kSingleRepeat; +const uint16_t kNikaiBits = 24; +const uint16_t kNECBits = 32; +const uint16_t kNeoclimaStateLength = 12; +const uint16_t kNeoclimaBits = kNeoclimaStateLength * 8; +const uint16_t kNeoclimaMinRepeat = kNoRepeat; +const uint16_t kPanasonicBits = 48; +const uint32_t kPanasonicManufacturer = 0x4004; +const uint32_t kPanasonic40Manufacturer = 0x34; +const uint16_t kPanasonic40Bits = 40; +const uint16_t kPanasonicAcStateLength = 27; +const uint16_t kPanasonicAcStateShortLength = 16; +const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8; +const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8; +const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat; +const uint16_t kPanasonicAc32Bits = 32; +const uint16_t kPioneerBits = 64; +const uint16_t kProntoMinLength = 6; +const uint16_t kRC5RawBits = 14; +const uint16_t kRC5Bits = kRC5RawBits - 2; +const uint16_t kRC5XBits = kRC5RawBits - 1; +const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. +const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. +const uint16_t kRCMMBits = 24; +const uint16_t kSamsungBits = 32; +const uint16_t kSamsung36Bits = 36; +const uint16_t kSamsungAcStateLength = 14; +const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8; +const uint16_t kSamsungAcExtendedStateLength = 21; +const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8; +const uint16_t kSamsungAcDefaultRepeat = kNoRepeat; +const uint16_t kSanyoAcStateLength = 9; +const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8; +const uint16_t kSanyoAc88StateLength = 11; +const uint16_t kSanyoAc88Bits = kSanyoAc88StateLength * 8; +const uint16_t kSanyoAc88MinRepeat = 2; +const uint16_t kSanyoAc152StateLength = 19; +const uint16_t kSanyoAc152Bits = kSanyoAc152StateLength * 8; +const uint16_t kSanyoAc152MinRepeat = kNoRepeat; +const uint16_t kSanyoSA8650BBits = 12; +const uint16_t kSanyoLC7461AddressBits = 13; +const uint16_t kSanyoLC7461CommandBits = 8; +const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits + + kSanyoLC7461CommandBits) * 2; +const uint8_t kSharpAddressBits = 5; +const uint8_t kSharpCommandBits = 8; +const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15 +const uint16_t kSharpAcStateLength = 13; +const uint16_t kSharpAcBits = kSharpAcStateLength * 8; // 104 +const uint16_t kSharpAcDefaultRepeat = kNoRepeat; +const uint8_t kSherwoodBits = kNECBits; +const uint16_t kSherwoodMinRepeat = kSingleRepeat; +const uint16_t kSony12Bits = 12; +const uint16_t kSony15Bits = 15; +const uint16_t kSony20Bits = 20; +const uint16_t kSonyMinBits = 12; +const uint16_t kSonyMinRepeat = 2; +const uint16_t kSymphonyBits = 12; +const uint16_t kSymphonyDefaultRepeat = 3; +const uint16_t kTcl96AcStateLength = 12; +const uint16_t kTcl96AcBits = kTcl96AcStateLength * 8; +const uint16_t kTcl96AcDefaultRepeat = kNoRepeat; +const uint16_t kTcl112AcStateLength = 14; +const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; +const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; +const uint16_t kTecoBits = 35; +const uint16_t kTecoDefaultRepeat = kNoRepeat; +const uint16_t kTeknopointStateLength = 14; +const uint16_t kTeknopointBits = kTeknopointStateLength * 8; +const uint16_t kToshibaACStateLength = 9; +const uint16_t kToshibaACBits = kToshibaACStateLength * 8; +const uint16_t kToshibaACMinRepeat = kSingleRepeat; +const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2; +const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8; +const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1; +const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8; +const uint16_t kTotoBits = 24; +const uint16_t kTotoShortBits = kTotoBits; +const uint16_t kTotoLongBits = kTotoShortBits * 2; +const uint16_t kTotoDefaultRepeat = kSingleRepeat; +const uint16_t kTranscoldBits = 24; +const uint16_t kTranscoldDefaultRepeat = kNoRepeat; +const uint16_t kTrotecStateLength = 9; +const uint16_t kTrotecBits = kTrotecStateLength * 8; +const uint16_t kTrotecDefaultRepeat = kNoRepeat; +const uint16_t kTrumaBits = 56; +const uint16_t kWhirlpoolAcStateLength = 21; +const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; +const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; +const uint16_t kWhynterBits = 32; +const uint16_t kWowweeBits = 11; +const uint16_t kWowweeDefaultRepeat = kNoRepeat; +const uint8_t kVestelAcBits = 56; +const uint16_t kXmpBits = 64; +const uint16_t kZepealBits = 16; +const uint16_t kZepealMinRepeat = 4; +const uint16_t kVoltasBits = 80; +const uint16_t kVoltasStateLength = 10; +const uint16_t kMilesTag2ShotBits = 14; +const uint16_t kMilesTag2MsgBits = 24; +const uint16_t kMilesMinRepeat = 0; +const uint16_t kBoseBits = 16; +const uint16_t kRhossStateLength = 12; +const uint16_t kRhossBits = kRhossStateLength * 8; +const uint16_t kRhossDefaultRepeat = 0; +const uint16_t kClimaButlerBits = 52; +const uint16_t kYorkBits = 136; +const uint16_t kYorkStateLength = 17; + + +// Legacy defines. (Deprecated) +#define AIWA_RC_T501_BITS kAiwaRcT501Bits +#define ARGO_COMMAND_LENGTH kArgoStateLength +#define COOLIX_BITS kCoolixBits +#define CARRIER_AC_BITS kCarrierAcBits +#define DAIKIN_COMMAND_LENGTH kDaikinStateLength +#define DENON_BITS kDenonBits +#define DENON_48_BITS kDenon48Bits +#define DENON_LEGACY_BITS kDenonLegacyBits +#define DISH_BITS kDishBits +#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat +#define FUJITSU_AC_STATE_LENGTH kFujitsuAcStateLength +#define FUJITSU_AC_STATE_LENGTH_SHORT kFujitsuAcStateLengthShort +#define FUJITSU_AC_BITS kFujitsuAcBits +#define FUJITSU_AC_MIN_BITS kFujitsuAcMinBits +#define GICABLE_BITS kGicableBits +#define GREE_STATE_LENGTH kGreeStateLength +#define HAIER_AC_STATE_LENGTH kHaierACStateLength +#define HAIER_AC_YRW02_STATE_LENGTH kHaierACYRW02StateLength +#define HITACHI_AC_STATE_LENGTH kHitachiAcStateLength +#define HITACHI_AC_BITS kHitachiAcBits +#define HITACHI_AC1_STATE_LENGTH kHitachiAc1StateLength +#define HITACHI_AC1_BITS kHitachiAc1Bits +#define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength +#define HITACHI_AC2_BITS kHitachiAc2Bits +#define HITACHI_AC296_STATE_LENGTH kHitachiAc296StateLength +#define HITACHI_AC296_BITS kHitachiAc296Bits +#define JVC_BITS kJvcBits +#define KELVINATOR_STATE_LENGTH kKelvinatorStateLength +#define LASERTAG_BITS kLasertagBits +#define LG_BITS kLgBits +#define LG32_BITS kLg32Bits +#define MAGIQUEST_BITS kMagiquestBits +#define MIDEA_BITS kMideaBits +#define MITSUBISHI_BITS kMitsubishiBits +#define MITSUBISHI_AC_STATE_LENGTH kMitsubishiACStateLength +#define NEC_BITS kNECBits +#define NIKAI_BITS kNikaiBits +#define PANASONIC_BITS kPanasonicBits +#define RC5_BITS kRC5Bits +#define RC5X_BITS kRC5XBits +#define RC6_MODE0_BITS kRC6Mode0Bits +#define RC6_36_BITS kRC6_36Bits +#define RCMM_BITS kRCMMBits +#define SANYO_LC7461_BITS kSanyoLC7461Bits +#define SAMSUNG_BITS kSamsungBits +#define SANYO_SA8650B_BITS kSanyoSA8650BBits +#define SHARP_BITS kSharpBits +#define SHERWOOD_BITS kSherwoodBits +#define SONY_12_BITS kSony12Bits +#define SONY_15_BITS kSony15Bits +#define SONY_20_BITS kSony20Bits +#define TOSHIBA_AC_STATE_LENGTH kToshibaACStateLength +#define TROTEC_COMMAND_LENGTH kTrotecStateLength +#define WHYNTER_BITS kWhynterBits + +// Turn on Debugging information by uncommenting the following line. +// #define DEBUG 1 + +#ifdef DEBUG +#ifdef UNIT_TEST +#define DPRINT(x) do { std::cout << x; } while (0) +#define DPRINTLN(x) do { std::cout << x << std::endl; } while (0) +#endif // UNIT_TEST +#ifdef ARDUINO +#define DPRINT(x) do { Serial.print(x); } while (0) +#define DPRINTLN(x) do { Serial.println(x); } while (0) +#endif // ARDUINO +#else // DEBUG +#define DPRINT(x) +#define DPRINTLN(x) +#endif // DEBUG + +#ifdef UNIT_TEST +#ifndef F +// Create a no-op F() macro so the code base still compiles outside of the +// Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out +// the code base. That macro stores constants in Flash (PROGMEM) memory. +// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/667 +#define F(x) x +#endif // F +typedef std::string String; +#endif // UNIT_TEST + +#endif // IRREMOTEESP8266_H_ diff --git a/src/IRsend.cpp b/src/IRsend.cpp index bb7c1852f..c9503de4c 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -1,1448 +1,1448 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2017,2019 David Conran - -#include "IRsend.h" -#ifndef UNIT_TEST -#include -#else -#define __STDC_LIMIT_MACROS -#include -#endif -#include -#ifdef UNIT_TEST -#include -#endif -#include "IRtimer.h" - -/// Constructor for an IRsend object. -/// @param[in] IRsendPin Which GPIO pin to use when sending an IR command. -/// @param[in] inverted Optional flag to invert the output. (default = false) -/// e.g. LED is illuminated when GPIO is LOW rather than HIGH. -/// @warning Setting `inverted` to something other than the default could -/// easily destroy your IR LED if you are overdriving it. -/// Unless you *REALLY* know what you are doing, don't change this. -/// @param[in] use_modulation Do we do frequency modulation during transmission? -/// i.e. If not, assume a 100% duty cycle. Ignore attempts to change the -/// duty cycle etc. -IRsend::IRsend(uint16_t IRsendPin, bool inverted, bool use_modulation) - : IRpin(IRsendPin), periodOffset(kPeriodOffset) { - if (inverted) { - outputOn = LOW; - outputOff = HIGH; - } else { - outputOn = HIGH; - outputOff = LOW; - } - modulation = use_modulation; - if (modulation) - _dutycycle = kDutyDefault; - else - _dutycycle = kDutyMax; -} - -/// Enable the pin for output. -void IRsend::begin() { -#ifndef UNIT_TEST - pinMode(IRpin, OUTPUT); -#endif - ledOff(); // Ensure the LED is in a known safe state when we start. -} - -/// Turn off the IR LED. -void IRsend::ledOff() { -#ifndef UNIT_TEST - digitalWrite(IRpin, outputOff); -#endif -} - -/// Turn on the IR LED. -void IRsend::ledOn() { -#ifndef UNIT_TEST - digitalWrite(IRpin, outputOn); -#endif -} - -/// Calculate the period for a given frequency. -/// @param[in] hz Frequency in Hz. -/// @param[in] use_offset Should we use the calculated offset or not? -/// @return nr. of uSeconds. -/// @note (T = 1/f) -uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { - if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. - uint32_t period = - (1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz). - // Apply the offset and ensure we don't result in a <= 0 value. - if (use_offset) - return std::max((uint32_t)1, period + periodOffset); - else - return std::max((uint32_t)1, period); -} - -/// Set the output frequency modulation and duty cycle. -/// @param[in] freq The freq we want to modulate at. -/// Assumes < 1000 means kHz else Hz. -/// @param[in] duty Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// This is ignored if modulation is disabled at object instantiation. -/// @note Integer timing functions & math mean we can't do fractions of -/// microseconds timing. Thus minor changes to the freq & duty values may have -/// limited effect. You've been warned. -void IRsend::enableIROut(uint32_t freq, uint8_t duty) { - // Set the duty cycle to use if we want freq. modulation. - if (modulation) { - _dutycycle = std::min(duty, kDutyMax); - } else { - _dutycycle = kDutyMax; - } - if (freq < 1000) // Were we given kHz? Supports the old call usage. - freq *= 1000; -#ifdef UNIT_TEST - _freq_unittest = freq; -#endif // UNIT_TEST - uint32_t period = calcUSecPeriod(freq); - // Nr. of uSeconds the LED will be on per pulse. - onTimePeriod = (period * _dutycycle) / kDutyMax; - // Nr. of uSeconds the LED will be off per pulse. - offTimePeriod = period - onTimePeriod; -} - -#if ALLOW_DELAY_CALLS -/// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds(). -/// @param[in] usec Nr. of uSeconds to delay for. -void IRsend::_delayMicroseconds(uint32_t usec) { - // delayMicroseconds() is only accurate to 16383us. - // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds - if (usec <= kMaxAccurateUsecDelay) { -#ifndef UNIT_TEST - delayMicroseconds(usec); -#endif - } else { -#ifndef UNIT_TEST - // Invoke a delay(), where possible, to avoid triggering the WDT. - delay(usec / 1000UL); // Delay for as many whole milliseconds as we can. - // Delay the remaining sub-millisecond. - delayMicroseconds(static_cast(usec % 1000UL)); -#endif - } -} -#else // ALLOW_DELAY_CALLS -/// A version of delayMicroseconds() that handles large values and does NOT use -/// the watch-dog friendly delay() calls where appropriate. -/// @note Use this only if you know what you are doing as it may cause the WDT -/// to reset the ESP8266. -void IRsend::_delayMicroseconds(uint32_t usec) { - for (; usec > kMaxAccurateUsecDelay; usec -= kMaxAccurateUsecDelay) -#ifndef UNIT_TEST - delayMicroseconds(kMaxAccurateUsecDelay); - delayMicroseconds(static_cast(usec)); -#endif // UNIT_TEST -} -#endif // ALLOW_DELAY_CALLS - -/// Modulate the IR LED for the given period (usec) and at the duty cycle set. -/// @param[in] usec The period of time to modulate the IR LED for, in -/// microseconds. -/// @return Nr. of pulses actually sent. -/// @note -/// The ESP8266 has no good way to do hardware PWM, so we have to do it all -/// in software. There is a horrible kludge/brilliant hack to use the second -/// serial TX line to do fairly accurate hardware PWM, but it is only -/// available on a single specific GPIO and only available on some modules. -/// e.g. It's not available on the ESP-01 module. -/// Hence, for greater compatibility & choice, we don't use that method. -/// Ref: -/// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ -uint16_t IRsend::mark(uint16_t usec) { - // Handle the simple case of no required frequency modulation. - if (!modulation || _dutycycle >= 100) { - ledOn(); - _delayMicroseconds(usec); - ledOff(); - return 1; - } - - // Not simple, so do it assuming frequency modulation. - uint16_t counter = 0; - IRtimer usecTimer = IRtimer(); - // Cache the time taken so far. This saves us calling time, and we can be - // assured that we can't have odd math problems. i.e. unsigned under/overflow. - uint32_t elapsed = usecTimer.elapsed(); - - while (elapsed < usec) { // Loop until we've met/exceeded our required time. - ledOn(); - // Calculate how long we should pulse on for. - // e.g. Are we to close to the end of our requested mark time (usec)? - _delayMicroseconds(std::min((uint32_t)onTimePeriod, usec - elapsed)); - ledOff(); - counter++; - if (elapsed + onTimePeriod >= usec) - return counter; // LED is now off & we've passed our allotted time. - // Wait for the lesser of the rest of the duty cycle, or the time remaining. - _delayMicroseconds( - std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod)); - elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. - } - return counter; -} - -/// Turn the pin (LED) off for a given time. -/// Sends an IR space for the specified number of microseconds. -/// A space is no output, so the PWM output is disabled. -/// @param[in] time Time in microseconds (us). -void IRsend::space(uint32_t time) { - ledOff(); - if (time == 0) return; - _delayMicroseconds(time); -} - -/// Calculate & set any offsets to account for execution times during sending. -/// -/// @param[in] hz The frequency to calibrate at >= 1000Hz. Default is 38000Hz. -/// @return The calculated period offset (in uSeconds) which is now in use. -/// e.g. -5. -/// @note This will generate an 65535us mark() IR LED signal. -/// This only needs to be called once, if at all. -int8_t IRsend::calibrate(uint16_t hz) { - if (hz < 1000) // Were we given kHz? Supports the old call usage. - hz *= 1000; - periodOffset = 0; // Turn off any existing offset while we calibrate. - enableIROut(hz); - IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. - uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) - uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. - // While it shouldn't be necessary, assume at least 1 pulse, to avoid a - // divide by 0 situation. - pulses = std::max(pulses, (uint16_t)1U); - uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. - // Assuming 38kHz for the example calculations: - // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. - // e.g. 65535.0us / 26us = 2520.5769 - // This should have caused approx 2520 loops through the main loop in mark(). - // The average over that many interations should give us a reasonable - // approximation at what offset we need to use to account for instruction - // execution times. - // - // Calculate the actual period from the actual time & the actual pulses - // generated. - double_t actualPeriod = (double_t)timeTaken / (double_t)pulses; - // Store the difference between the actual time per period vs. calculated. - periodOffset = (int8_t)((double_t)calcPeriod - actualPeriod); - return periodOffset; -} - -/// Generic method for sending data that is common to most protocols. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -void IRsend::sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, - uint32_t zerospace, uint64_t data, uint16_t nbits, - bool MSBfirst) { - if (nbits == 0) // If we are asked to send nothing, just return. - return; - if (MSBfirst) { // Send the MSB first. - // Send 0's until we get down to a bit size we can actually manage. - while (nbits > sizeof(data) * 8) { - mark(zeromark); - space(zerospace); - nbits--; - } - // Send the supplied data. - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) { // Send a 1 - mark(onemark); - space(onespace); - } else { // Send a 0 - mark(zeromark); - space(zerospace); - } - } else { // Send the Least Significant Bit (LSB) first / MSB last. - for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1) - if (data & 1) { // Send a 1 - mark(onemark); - space(onespace); - } else { // Send a 0 - mark(zeromark); - space(zerospace); - } - } -} - -/// Generic method for sending simple protocol messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint64_t data, const uint16_t nbits, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle) { - sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace, - footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat, - dutycycle); -} - -/// Generic method for sending simple protocol messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] mesgtime Min. nr. of usecs a single message needs to be. -/// This is effectively the min. total length of a single message. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint32_t mesgtime, const uint64_t data, - const uint16_t nbits, const uint16_t frequency, - const bool MSBfirst, const uint16_t repeat, - const uint8_t dutycycle) { - // Setup - enableIROut(frequency, dutycycle); - IRtimer usecs = IRtimer(); - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - usecs.reset(); - - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - - // Data - sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst); - - // Footer - if (footermark) mark(footermark); - uint32_t elapsed = usecs.elapsed(); - // Avoid potential unsigned integer underflow. e.g. when mesgtime is 0. - if (elapsed >= mesgtime) - space(gap); - else - space(std::max(gap, mesgtime - elapsed)); - } -} - -/// Generic method for sending simple protocol messages. -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. -/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. -/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. -/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. -/// This is effectively the gap between messages. -/// A value of 0 means no gap space. -/// @param[in] dataptr Pointer to the data to be transmitted. -/// @param[in] nbytes Nr. of bytes of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint8_t *dataptr, const uint16_t nbytes, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle) { - // Setup - enableIROut(frequency, dutycycle); - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - - // Data - for (uint16_t i = 0; i < nbytes; i++) - sendData(onemark, onespace, zeromark, zerospace, *(dataptr + i), 8, - MSBfirst); - - // Footer - if (footermark) mark(footermark); - space(gap); - } -} - -/// Generic method for sending Manchester code data. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// of bits in data. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// (1/2 wavelength) -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). -void IRsend::sendManchesterData(const uint16_t half_period, - const uint64_t data, - const uint16_t nbits, const bool MSBfirst, - const bool GEThomas) { - if (nbits == 0) return; // Nothing to send. - uint16_t bits = nbits; - uint64_t copy = (GEThomas) ? data : ~data; - - if (MSBfirst) { // Send the MSB first. - // Send 0's until we get down to a bit size we can actually manage. - if (bits > (sizeof(data) * 8)) { - sendManchesterData(half_period, 0ULL, bits - sizeof(data) * 8, MSBfirst, - GEThomas); - bits = sizeof(data) * 8; - } - // Send the supplied data. - for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) - if (copy & mask) { - mark(half_period); - space(half_period); - } else { - space(half_period); - mark(half_period); - } - } else { // Send the Least Significant Bit (LSB) first / MSB last. - for (bits = 0; bits < nbits; bits++, copy >>= 1) - if (copy & 1) { - mark(half_period); - space(half_period); - } else { - space(half_period); - mark(half_period); - } - } -} - -/// Generic method for sending Manchester code messages. -/// Will send leading or trailing 0's if the nbits is larger than the number -/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header -/// mark. A value of 0 means no header mark. -/// @param[in] headerspace Nr. of usecs for the led to be off after the header -/// mark. A value of 0 means no header space. -/// @param[in] half_period Nr. of uSeconds for half the clock's period. -/// (1/2 wavelength) -/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer -/// mark. A value of 0 means no footer mark. -/// @param[in] gap Min. nr. of usecs for the led to be off after the footer -/// mark. This is effectively the absolute minimum gap between messages. -/// @param[in] data The data to be transmitted. -/// @param[in] nbits Nr. of bits of data to be sent. -/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) -/// @param[in] MSBfirst Flag for bit transmission order. -/// Defaults to MSB->LSB order. -/// @param[in] repeat Nr. of extra times the message will be sent. -/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages -/// @param[in] dutycycle Percentage duty cycle of the LED. -/// e.g. 25 = 25% = 1/4 on, 3/4 off. -/// If you are not sure, try 50 percent. -/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). -/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. -/// Most common value is 38000 or 38, for 38kHz. -void IRsend::sendManchester(const uint16_t headermark, - const uint32_t headerspace, - const uint16_t half_period, - const uint16_t footermark, const uint32_t gap, - const uint64_t data, const uint16_t nbits, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle, - const bool GEThomas) { - // Setup - enableIROut(frequency, dutycycle); - - // We always send a message, even for repeat=0, hence '<= repeat'. - for (uint16_t r = 0; r <= repeat; r++) { - // Header - if (headermark) mark(headermark); - if (headerspace) space(headerspace); - // Data - sendManchesterData(half_period, data, nbits, MSBfirst, GEThomas); - // Footer - if (footermark) mark(footermark); - if (gap) space(gap); - } -} - -#if SEND_RAW -/// Send a raw IRremote message. -/// -/// @param[in] buf An array of uint16_t's that has microseconds elements. -/// @param[in] len Nr. of elements in the buf[] array. -/// @param[in] hz Frequency to send the message at. (kHz < 1000; Hz >= 1000) -/// @note Even elements are Mark times (On), Odd elements are Space times (Off). -/// Ref: -/// examples/IRrecvDumpV2/IRrecvDumpV2.ino (or later) -void IRsend::sendRaw(const uint16_t buf[], const uint16_t len, - const uint16_t hz) { - // Set IR carrier frequency - enableIROut(hz); - for (uint16_t i = 0; i < len; i++) { - if (i & 1) { // Odd bit. - space(buf[i]); - } else { // Even bit. - mark(buf[i]); - } - } - ledOff(); // We potentially have ended with a mark(), so turn of the LED. -} -#endif // SEND_RAW - -/// Get the minimum number of repeats for a given protocol. -/// @param[in] protocol Protocol number/type of the message you want to send. -/// @return The number of repeats required. -uint16_t IRsend::minRepeats(const decode_type_t protocol) { - switch (protocol) { - // Single repeats - case AIWA_RC_T501: - case AMCOR: - case COOLIX: - case COOLIX48: - case ELITESCREENS: - case GICABLE: - case INAX: - case MIDEA24: - case MITSUBISHI: - case MITSUBISHI2: - case MITSUBISHI_AC: - case MULTIBRACKETS: - case SHERWOOD: - case TOSHIBA_AC: - case TOTO: - return kSingleRepeat; - // Special - case AIRWELL: - return kAirwellMinRepeats; - case CARRIER_AC40: - return kCarrierAc40MinRepeat; - case DISH: - return kDishMinRepeat; - case EPSON: - return kEpsonMinRepeat; - case SANYO_AC88: - return kSanyoAc88MinRepeat; - case SONY: - return kSonyMinRepeat; - case SONY_38K: - return kSonyMinRepeat + 1; - case SYMPHONY: - return kSymphonyDefaultRepeat; - case ZEPEAL: - return kZepealMinRepeat; - default: - return kNoRepeat; - } -} - -/// Get the default number of bits for a given protocol. -/// @param[in] protocol Protocol number/type you want the default bit size for. -/// @return The number of bits. -uint16_t IRsend::defaultBits(const decode_type_t protocol) { - switch (protocol) { - case MULTIBRACKETS: - case GORENJE: - return 8; - case WOWWEE: - return 11; - case RC5: - case SYMPHONY: - return 12; - case LASERTAG: - case RC5X: - return 13; - case AIWA_RC_T501: - case DENON: - case SHARP: - return 15; - case BOSE: - case DISH: - case GICABLE: - case JVC: - case LEGOPF: - case MITSUBISHI: - case MITSUBISHI2: - case ZEPEAL: - return 16; - case METZ: - return 19; - case RC6: - case SONY: - case SONY_38K: - return 20; - case COOLIX: - case INAX: - case MIDEA24: - case NIKAI: - case RCMM: - case TOTO: - case TRANSCOLD: - return 24; - case LG: - case LG2: - return 28; - case ARRIS: - case CARRIER_AC: - case ELITESCREENS: - case EPSON: - case NEC: - case NEC_LIKE: - case PANASONIC_AC32: - case SAMSUNG: - case SHERWOOD: - case WHYNTER: - return 32; - case AIRWELL: - return 34; - case LUTRON: - case TECO: - return 35; - case SAMSUNG36: - return 36; - case CARRIER_AC40: - return kCarrierAc40Bits; // 40 - case DOSHISHA: - return kDoshishaBits; // 40 - case SANYO_LC7461: - return kSanyoLC7461Bits; // 42 - case COOLIX48: - case GOODWEATHER: - case KELON: - case MIDEA: - case PANASONIC: - return 48; - case CLIMABUTLER: - return kClimaButlerBits; // 52 - case AIRTON: - case ECOCLIM: - case MAGIQUEST: - case VESTEL_AC: - case TECHNIBEL_AC: - case TRUMA: - return 56; - case AMCOR: - case CARRIER_AC64: - case DELONGHI_AC: - case PIONEER: - return 64; - case ARGO: - return kArgoBits; - case BOSCH144: - return kBosch144Bits; - case CORONA_AC: - return kCoronaAcBits; - case CARRIER_AC84: - return kCarrierAc84Bits; - case CARRIER_AC128: - return kCarrierAc128Bits; - case DAIKIN: - return kDaikinBits; - case DAIKIN128: - return kDaikin128Bits; - case DAIKIN152: - return kDaikin152Bits; - case DAIKIN160: - return kDaikin160Bits; - case DAIKIN176: - return kDaikin176Bits; - case DAIKIN2: - return kDaikin2Bits; - case DAIKIN200: - return kDaikin200Bits; - case DAIKIN216: - return kDaikin216Bits; - case DAIKIN312: - return kDaikin312Bits; - case DAIKIN64: - return kDaikin64Bits; - case ELECTRA_AC: - return kElectraAcBits; - case GREE: - return kGreeBits; - case HAIER_AC: - return kHaierACBits; - case HAIER_AC_YRW02: - return kHaierACYRW02Bits; - case HAIER_AC160: - return kHaierAC160Bits; - case HAIER_AC176: - return kHaierAC176Bits; - case HITACHI_AC: - return kHitachiAcBits; - case HITACHI_AC1: - return kHitachiAc1Bits; - case HITACHI_AC2: - return kHitachiAc2Bits; - case HITACHI_AC3: - return kHitachiAc3Bits; - case HITACHI_AC264: - return kHitachiAc264Bits; - case HITACHI_AC296: - return kHitachiAc296Bits; - case HITACHI_AC344: - return kHitachiAc344Bits; - case HITACHI_AC424: - return kHitachiAc424Bits; - case KELON168: - return kKelon168Bits; - case KELVINATOR: - return kKelvinatorBits; - case MILESTAG2: - return kMilesTag2ShotBits; - case MIRAGE: - return kMirageBits; - case MITSUBISHI_AC: - return kMitsubishiACBits; - case MITSUBISHI136: - return kMitsubishi136Bits; - case MITSUBISHI112: - return kMitsubishi112Bits; - case MITSUBISHI_HEAVY_152: - return kMitsubishiHeavy152Bits; - case MITSUBISHI_HEAVY_88: - return kMitsubishiHeavy88Bits; - case NEOCLIMA: - return kNeoclimaBits; - case PANASONIC_AC: - return kPanasonicAcBits; - case RHOSS: - return kRhossBits; - case SAMSUNG_AC: - return kSamsungAcBits; - case SANYO_AC: - return kSanyoAcBits; - case SANYO_AC88: - return kSanyoAc88Bits; - case SANYO_AC152: - return kSanyoAc152Bits; - case SHARP_AC: - return kSharpAcBits; - case TCL96AC: - return kTcl96AcBits; - case TCL112AC: - return kTcl112AcBits; - case TEKNOPOINT: - return kTeknopointBits; - case TOSHIBA_AC: - return kToshibaACBits; - case TROTEC: - case TROTEC_3550: - return kTrotecBits; - case VOLTAS: - return kVoltasBits; - case WHIRLPOOL_AC: - return kWhirlpoolAcBits; - case XMP: - return kXmpBits; - case YORK: - return kYorkBits; - case FUJITSU_AC264: - return kFujitsuAc264Bits; - // No default amount of bits. - case FUJITSU_AC: - case MWM: - default: - return 0; - } -} - -/// Send a simple (up to 64 bits) IR message of a given type. -/// An unknown/unsupported type will send nothing. -/// @param[in] type Protocol number/type of the message you want to send. -/// @param[in] data The data you want to send (up to 64 bits). -/// @param[in] nbits How many bits long the message is to be. -/// @param[in] repeat How many repeats to do? -/// @return True if it is a type we can attempt to send, false if not. -bool IRsend::send(const decode_type_t type, const uint64_t data, - const uint16_t nbits, const uint16_t repeat) { - uint16_t min_repeat __attribute__((unused)) = - std::max(IRsend::minRepeats(type), repeat); - switch (type) { -#if SEND_AIRTON - case AIRTON: - sendAirton(data, nbits, min_repeat); - break; -#endif // SEND_AIRTON -#if SEND_AIRWELL - case AIRWELL: - sendAirwell(data, nbits, min_repeat); - break; -#endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: - sendAiwaRCT501(data, nbits, min_repeat); - break; -#endif // SEND_AIWA_RC_T501 -#if SEND_ARRIS - case ARRIS: - sendArris(data, nbits, min_repeat); - break; -#endif // SEND_ARRIS -#if SEND_BOSE - case BOSE: - sendBose(data, nbits, min_repeat); - break; -#endif // SEND_BOSE -#if SEND_CARRIER_AC - case CARRIER_AC: - sendCarrierAC(data, nbits, min_repeat); - break; -#endif -#if SEND_CARRIER_AC40 - case CARRIER_AC40: - sendCarrierAC40(data, nbits, min_repeat); - break; -#endif // SEND_CARRIER_AC40 -#if SEND_CARRIER_AC64 - case CARRIER_AC64: - sendCarrierAC64(data, nbits, min_repeat); - break; -#endif // SEND_CARRIER_AC64 -#if SEND_CLIMABUTLER - case CLIMABUTLER: - sendClimaButler(data, nbits, min_repeat); - break; -#endif // SEND_CLIMABUTLER -#if SEND_COOLIX - case COOLIX: - sendCOOLIX(data, nbits, min_repeat); - break; -#endif // SEND_COOLIX -#if SEND_COOLIX48 - case COOLIX48: - sendCoolix48(data, nbits, min_repeat); - break; -#endif // SEND_COOLIX48 -#if SEND_DAIKIN64 - case DAIKIN64: - sendDaikin64(data, nbits, min_repeat); - break; -#endif -#if SEND_DELONGHI_AC - case DELONGHI_AC: - sendDelonghiAc(data, nbits, min_repeat); - break; -#endif -#if SEND_DENON - case DENON: - sendDenon(data, nbits, min_repeat); - break; -#endif -#if SEND_DISH - case DISH: - sendDISH(data, nbits, min_repeat); - break; -#endif -#if SEND_DOSHISHA - case DOSHISHA: - sendDoshisha(data, nbits, min_repeat); - break; -#endif -#if SEND_ECOCLIM - case ECOCLIM: - sendEcoclim(data, nbits, min_repeat); - break; -#endif // SEND_ECOCLIM -#if SEND_ELITESCREENS - case ELITESCREENS: - sendElitescreens(data, nbits, min_repeat); - break; -#endif // SEND_ELITESCREENS -#if SEND_EPSON - case EPSON: - sendEpson(data, nbits, min_repeat); - break; -#endif -#if SEND_GICABLE - case GICABLE: - sendGICable(data, nbits, min_repeat); - break; -#endif -#if SEND_GOODWEATHER - case GOODWEATHER: - sendGoodweather(data, nbits, min_repeat); - break; -#endif -#if SEND_GORENJE - case GORENJE: - sendGorenje(data, nbits, min_repeat); - break; -#endif -#if SEND_GREE - case GREE: - sendGree(data, nbits, min_repeat); - break; -#endif -#if SEND_INAX - case INAX: - sendInax(data, nbits, min_repeat); - break; -#endif // SEND_INAX -#if SEND_JVC - case JVC: - sendJVC(data, nbits, min_repeat); - break; -#endif -#if SEND_KELON - case KELON: - sendKelon(data, nbits, min_repeat); - break; -#endif // SEND_KELON -#if SEND_LASERTAG - case LASERTAG: - sendLasertag(data, nbits, min_repeat); - break; -#endif -#if SEND_LEGOPF - case LEGOPF: - sendLegoPf(data, nbits, min_repeat); - break; -#endif -#if SEND_LG - case LG: - sendLG(data, nbits, min_repeat); - break; - case LG2: - sendLG2(data, nbits, min_repeat); - break; -#endif -#if SEND_LUTRON - case LUTRON: - sendLutron(data, nbits, min_repeat); - break; -#endif -#if SEND_MAGIQUEST - case MAGIQUEST: - sendMagiQuest(data, nbits, min_repeat); - break; -#endif // SEND_MAGIQUEST -#if SEND_METZ - case METZ: - sendMetz(data, nbits, min_repeat); - break; -#endif // SEND_METZ -#if SEND_MIDEA - case MIDEA: - sendMidea(data, nbits, min_repeat); - break; -#endif // SEND_MIDEA -#if SEND_MIDEA24 - case MIDEA24: - sendMidea24(data, nbits, min_repeat); - break; -#endif // SEND_MIDEA24 -#if SEND_MILESTAG2 - case MILESTAG2: - sendMilestag2(data, nbits, min_repeat); - break; -#endif // SEND_MILESTAG2 -#if SEND_MITSUBISHI - case MITSUBISHI: - sendMitsubishi(data, nbits, min_repeat); - break; -#endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: - sendMitsubishi2(data, nbits, min_repeat); - break; -#endif -#if SEND_MULTIBRACKETS - case MULTIBRACKETS: - sendMultibrackets(data, nbits, min_repeat); - break; -#endif -#if SEND_NIKAI - case NIKAI: - sendNikai(data, nbits, min_repeat); - break; -#endif -#if SEND_NEC - case NEC: - case NEC_LIKE: - sendNEC(data, nbits, min_repeat); - break; -#endif -#if SEND_PANASONIC - case PANASONIC: - sendPanasonic64(data, nbits, min_repeat); - break; -#endif // SEND_PANASONIC -#if SEND_PANASONIC_AC32 - case PANASONIC_AC32: - sendPanasonicAC32(data, nbits, min_repeat); - break; -#endif // SEND_PANASONIC_AC32 -#if SEND_PIONEER - case PIONEER: - sendPioneer(data, nbits, min_repeat); - break; -#endif -#if SEND_RC5 - case RC5: - case RC5X: - sendRC5(data, nbits, min_repeat); - break; -#endif -#if SEND_RC6 - case RC6: - sendRC6(data, nbits, min_repeat); - break; -#endif -#if SEND_RCMM - case RCMM: - sendRCMM(data, nbits, min_repeat); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: - sendSAMSUNG(data, nbits, min_repeat); - break; -#endif -#if SEND_SAMSUNG36 - case SAMSUNG36: - sendSamsung36(data, nbits, min_repeat); - break; -#endif -#if SEND_SANYO - case SANYO_LC7461: - sendSanyoLC7461(data, nbits, min_repeat); - break; -#endif -#if SEND_SHARP - case SHARP: - sendSharpRaw(data, nbits, min_repeat); - break; -#endif -#if SEND_SHERWOOD - case SHERWOOD: - sendSherwood(data, nbits, min_repeat); - break; -#endif -#if SEND_SONY - case SONY: - sendSony(data, nbits, min_repeat); - break; - case SONY_38K: - sendSony38(data, nbits, min_repeat); - break; -#endif -#if SEND_SYMPHONY - case SYMPHONY: - sendSymphony(data, nbits, min_repeat); - break; -#endif -#if SEND_TECHNIBEL_AC - case TECHNIBEL_AC: - sendTechnibelAc(data, nbits, min_repeat); - break; -#endif -#if SEND_TECO - case TECO: - sendTeco(data, nbits, min_repeat); - break; -#endif // SEND_TECO -#if SEND_TOTO - case TOTO: - sendToto(data, nbits, min_repeat); - break; -#endif // SEND_TOTO -#if SEND_TRANSCOLD - case TRANSCOLD: - sendTranscold(data, nbits, min_repeat); - break; -#endif // SEND_TRANSCOLD -#if SEND_TRUMA - case TRUMA: - sendTruma(data, nbits, min_repeat); - break; -#endif // SEND_TRUMA -#if SEND_VESTEL_AC - case VESTEL_AC: - sendVestelAc(data, nbits, min_repeat); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: - sendWhynter(data, nbits, min_repeat); - break; -#endif -#if SEND_WOWWEE - case WOWWEE: - sendWowwee(data, nbits, min_repeat); - break; -#endif // SEND_WOWWEE -#if SEND_XMP - case XMP: - sendXmp(data, nbits, min_repeat); - break; -#endif -#if SEND_ZEPEAL - case ZEPEAL: - sendZepeal(data, nbits, min_repeat); - break; -#endif // SEND_ZEPEAL - default: - return false; - } - return true; -} - -/// Send a complex (>= 64 bits) IR message of a given type. -/// An unknown/unsupported type will send nothing. -/// @param[in] type Protocol number/type of the message you want to send. -/// @param[in] state A pointer to the array of bytes that make up the state[]. -/// @param[in] nbytes How many bytes are in the state. -/// @return True if it is a type we can attempt to send, false if not. -bool IRsend::send(const decode_type_t type, const uint8_t *state, - const uint16_t nbytes) { - switch (type) { -#if SEND_VOLTAS - case VOLTAS: - sendVoltas(state, nbytes); - break; -#endif // SEND_VOLTAS -#if SEND_AMCOR - case AMCOR: - sendAmcor(state, nbytes); - break; -#endif -#if SEND_ARGO - case ARGO: - sendArgo(state, nbytes); - break; -#endif // SEND_ARGO -#if SEND_BOSCH144 - case BOSCH144: - sendBosch144(state, nbytes); - break; -#endif // SEND_BOSCH144 -#if SEND_CARRIER_AC84 - case CARRIER_AC84: - sendCarrierAC84(state, nbytes); - break; -#endif // SEND_CARRIER_AC84 -#if SEND_CARRIER_AC128 - case CARRIER_AC128: - sendCarrierAC128(state, nbytes); - break; -#endif // SEND_CARRIER_AC128 -#if SEND_CORONA_AC - case CORONA_AC: - sendCoronaAc(state, nbytes); - break; -#endif // SEND_ARGO -#if SEND_DAIKIN - case DAIKIN: - sendDaikin(state, nbytes); - break; -#endif // SEND_DAIKIN -#if SEND_DAIKIN128 - case DAIKIN128: - sendDaikin128(state, nbytes); - break; -#endif // SEND_DAIKIN128 -#if SEND_DAIKIN152 - case DAIKIN152: - sendDaikin152(state, nbytes); - break; -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - case DAIKIN160: - sendDaikin160(state, nbytes); - break; -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - case DAIKIN176: - sendDaikin176(state, nbytes); - break; -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - case DAIKIN2: - sendDaikin2(state, nbytes); - break; -#endif // SEND_DAIKIN2 -#if SEND_DAIKIN200 - case DAIKIN200: - sendDaikin200(state, nbytes); - break; -#endif // SEND_DAIKIN200 -#if SEND_DAIKIN216 - case DAIKIN216: - sendDaikin216(state, nbytes); - break; -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN312 - case DAIKIN312: - sendDaikin312(state, nbytes); - break; -#endif // SEND_DAIKIN312 -#if SEND_ELECTRA_AC - case ELECTRA_AC: - sendElectraAC(state, nbytes); - break; -#endif // SEND_ELECTRA_AC -#if SEND_FUJITSU_AC - case FUJITSU_AC: - sendFujitsuAC(state, nbytes); - break; -#endif // SEND_FUJITSU_AC -#if SEND_FUJITSU_AC264 - case FUJITSU_AC264: - sendFujitsuAC264(state, nbytes); - break; -#endif -#if SEND_GREE - case GREE: - sendGree(state, nbytes); - break; -#endif // SEND_GREE -#if SEND_HAIER_AC - case HAIER_AC: - sendHaierAC(state, nbytes); - break; -#endif // SEND_HAIER_AC -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - sendHaierACYRW02(state, nbytes); - break; -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HAIER_AC160 - case HAIER_AC160: - sendHaierAC160(state, nbytes); - break; -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - case HAIER_AC176: - sendHaierAC176(state, nbytes); - break; -#endif // SEND_HAIER_AC176 -#if SEND_HITACHI_AC - case HITACHI_AC: - sendHitachiAC(state, nbytes); - break; -#endif // SEND_HITACHI_AC -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - sendHitachiAC1(state, nbytes); - break; -#endif // SEND_HITACHI_AC1 -#if SEND_HITACHI_AC2 - case HITACHI_AC2: - sendHitachiAC2(state, nbytes); - break; -#endif // SEND_HITACHI_AC2 -#if SEND_HITACHI_AC3 - case HITACHI_AC3: - sendHitachiAc3(state, nbytes); - break; -#endif // SEND_HITACHI_AC3 -#if SEND_HITACHI_AC264 - case HITACHI_AC264: - sendHitachiAc264(state, nbytes); - break; -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - case HITACHI_AC296: - sendHitachiAc296(state, nbytes); - break; -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - case HITACHI_AC344: - sendHitachiAc344(state, nbytes); - break; -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - case HITACHI_AC424: - sendHitachiAc424(state, nbytes); - break; -#endif // SEND_HITACHI_AC424 -#if SEND_KELON168 - case KELON168: - sendKelon168(state, nbytes); - break; -#endif // SEND_KELON168 -#if SEND_KELVINATOR - case KELVINATOR: - sendKelvinator(state, nbytes); - break; -#endif // SEND_KELVINATOR -#if SEND_MIRAGE - case MIRAGE: - sendMirage(state, nbytes); - break; -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - sendMitsubishiAC(state, nbytes); - break; -#endif // SEND_MITSUBISHI_AC -#if SEND_MITSUBISHI136 - case MITSUBISHI136: - sendMitsubishi136(state, nbytes); - break; -#endif // SEND_MITSUBISHI136 -#if SEND_MITSUBISHI112 - case MITSUBISHI112: - sendMitsubishi112(state, nbytes); - break; -#endif // SEND_MITSUBISHI112 -#if SEND_MITSUBISHIHEAVY - case MITSUBISHI_HEAVY_88: - sendMitsubishiHeavy88(state, nbytes); - break; - case MITSUBISHI_HEAVY_152: - sendMitsubishiHeavy152(state, nbytes); - break; -#endif // SEND_MITSUBISHIHEAVY -#if SEND_MWM - case MWM: - sendMWM(state, nbytes); - break; -#endif // SEND_MWM -#if SEND_NEOCLIMA - case NEOCLIMA: - sendNeoclima(state, nbytes); - break; -#endif // SEND_NEOCLIMA -#if SEND_PANASONIC_AC - case PANASONIC_AC: - sendPanasonicAC(state, nbytes); - break; -#endif // SEND_PANASONIC_AC -#if SEND_RHOSS - case RHOSS: - sendRhoss(state, nbytes); - break; -#endif // SEND_RHOSS -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - sendSamsungAC(state, nbytes); - break; -#endif // SEND_SAMSUNG_AC -#if SEND_SANYO_AC - case SANYO_AC: - sendSanyoAc(state, nbytes); - break; -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - case SANYO_AC88: - sendSanyoAc88(state, nbytes); - break; -#endif // SEND_SANYO_AC88 -#if SEND_SANYO_AC152 - case SANYO_AC152: - sendSanyoAc152(state, nbytes); - break; -#endif // SEND_SANYO_AC152 -#if SEND_SHARP_AC - case SHARP_AC: - sendSharpAc(state, nbytes); - break; -#endif // SEND_SHARP_AC -#if SEND_TCL96AC - case TCL96AC: - sendTcl96Ac(state, nbytes); - break; -#endif // SEND_TCL96AC -#if SEND_TCL112AC - case TCL112AC: - sendTcl112Ac(state, nbytes); - break; -#endif // SEND_TCL112AC -#if SEND_TEKNOPOINT - case TEKNOPOINT: - sendTeknopoint(state, nbytes); - break; -#endif // SEND_TEKNOPOINT -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - sendToshibaAC(state, nbytes); - break; -#endif // SEND_TOSHIBA_AC -#if SEND_TROTEC - case TROTEC: - sendTrotec(state, nbytes); - break; -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - case TROTEC_3550: - sendTrotec3550(state, nbytes); - break; -#endif // SEND_TROTEC_3550 -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - sendWhirlpoolAC(state, nbytes); - break; -#endif // SEND_WHIRLPOOL_AC -#if SEND_YORK - case YORK: - sendYork(state, nbytes); - break; -#endif // SEND_YORK - default: - return false; - } - return true; -} +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2017,2019 David Conran + +#include "IRsend.h" +#ifndef UNIT_TEST +#include +#else +#define __STDC_LIMIT_MACROS +#include +#endif +#include +#ifdef UNIT_TEST +#include +#endif +#include "IRtimer.h" + +/// Constructor for an IRsend object. +/// @param[in] IRsendPin Which GPIO pin to use when sending an IR command. +/// @param[in] inverted Optional flag to invert the output. (default = false) +/// e.g. LED is illuminated when GPIO is LOW rather than HIGH. +/// @warning Setting `inverted` to something other than the default could +/// easily destroy your IR LED if you are overdriving it. +/// Unless you *REALLY* know what you are doing, don't change this. +/// @param[in] use_modulation Do we do frequency modulation during transmission? +/// i.e. If not, assume a 100% duty cycle. Ignore attempts to change the +/// duty cycle etc. +IRsend::IRsend(uint16_t IRsendPin, bool inverted, bool use_modulation) + : IRpin(IRsendPin), periodOffset(kPeriodOffset) { + if (inverted) { + outputOn = LOW; + outputOff = HIGH; + } else { + outputOn = HIGH; + outputOff = LOW; + } + modulation = use_modulation; + if (modulation) + _dutycycle = kDutyDefault; + else + _dutycycle = kDutyMax; +} + +/// Enable the pin for output. +void IRsend::begin() { +#ifndef UNIT_TEST + pinMode(IRpin, OUTPUT); +#endif + ledOff(); // Ensure the LED is in a known safe state when we start. +} + +/// Turn off the IR LED. +void IRsend::ledOff() { +#ifndef UNIT_TEST + digitalWrite(IRpin, outputOff); +#endif +} + +/// Turn on the IR LED. +void IRsend::ledOn() { +#ifndef UNIT_TEST + digitalWrite(IRpin, outputOn); +#endif +} + +/// Calculate the period for a given frequency. +/// @param[in] hz Frequency in Hz. +/// @param[in] use_offset Should we use the calculated offset or not? +/// @return nr. of uSeconds. +/// @note (T = 1/f) +uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { + if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. + uint32_t period = + (1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz). + // Apply the offset and ensure we don't result in a <= 0 value. + if (use_offset) + return std::max((uint32_t)1, period + periodOffset); + else + return std::max((uint32_t)1, period); +} + +/// Set the output frequency modulation and duty cycle. +/// @param[in] freq The freq we want to modulate at. +/// Assumes < 1000 means kHz else Hz. +/// @param[in] duty Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// This is ignored if modulation is disabled at object instantiation. +/// @note Integer timing functions & math mean we can't do fractions of +/// microseconds timing. Thus minor changes to the freq & duty values may have +/// limited effect. You've been warned. +void IRsend::enableIROut(uint32_t freq, uint8_t duty) { + // Set the duty cycle to use if we want freq. modulation. + if (modulation) { + _dutycycle = std::min(duty, kDutyMax); + } else { + _dutycycle = kDutyMax; + } + if (freq < 1000) // Were we given kHz? Supports the old call usage. + freq *= 1000; +#ifdef UNIT_TEST + _freq_unittest = freq; +#endif // UNIT_TEST + uint32_t period = calcUSecPeriod(freq); + // Nr. of uSeconds the LED will be on per pulse. + onTimePeriod = (period * _dutycycle) / kDutyMax; + // Nr. of uSeconds the LED will be off per pulse. + offTimePeriod = period - onTimePeriod; +} + +#if ALLOW_DELAY_CALLS +/// An ESP8266 RTOS watch-dog timer friendly version of delayMicroseconds(). +/// @param[in] usec Nr. of uSeconds to delay for. +void IRsend::_delayMicroseconds(uint32_t usec) { + // delayMicroseconds() is only accurate to 16383us. + // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds + if (usec <= kMaxAccurateUsecDelay) { +#ifndef UNIT_TEST + delayMicroseconds(usec); +#endif + } else { +#ifndef UNIT_TEST + // Invoke a delay(), where possible, to avoid triggering the WDT. + delay(usec / 1000UL); // Delay for as many whole milliseconds as we can. + // Delay the remaining sub-millisecond. + delayMicroseconds(static_cast(usec % 1000UL)); +#endif + } +} +#else // ALLOW_DELAY_CALLS +/// A version of delayMicroseconds() that handles large values and does NOT use +/// the watch-dog friendly delay() calls where appropriate. +/// @note Use this only if you know what you are doing as it may cause the WDT +/// to reset the ESP8266. +void IRsend::_delayMicroseconds(uint32_t usec) { + for (; usec > kMaxAccurateUsecDelay; usec -= kMaxAccurateUsecDelay) +#ifndef UNIT_TEST + delayMicroseconds(kMaxAccurateUsecDelay); + delayMicroseconds(static_cast(usec)); +#endif // UNIT_TEST +} +#endif // ALLOW_DELAY_CALLS + +/// Modulate the IR LED for the given period (usec) and at the duty cycle set. +/// @param[in] usec The period of time to modulate the IR LED for, in +/// microseconds. +/// @return Nr. of pulses actually sent. +/// @note +/// The ESP8266 has no good way to do hardware PWM, so we have to do it all +/// in software. There is a horrible kludge/brilliant hack to use the second +/// serial TX line to do fairly accurate hardware PWM, but it is only +/// available on a single specific GPIO and only available on some modules. +/// e.g. It's not available on the ESP-01 module. +/// Hence, for greater compatibility & choice, we don't use that method. +/// Ref: +/// https://www.analysir.com/blog/2017/01/29/updated-esp8266-nodemcu-backdoor-upwm-hack-for-ir-signals/ +uint16_t IRsend::mark(uint16_t usec) { + // Handle the simple case of no required frequency modulation. + if (!modulation || _dutycycle >= 100) { + ledOn(); + _delayMicroseconds(usec); + ledOff(); + return 1; + } + + // Not simple, so do it assuming frequency modulation. + uint16_t counter = 0; + IRtimer usecTimer = IRtimer(); + // Cache the time taken so far. This saves us calling time, and we can be + // assured that we can't have odd math problems. i.e. unsigned under/overflow. + uint32_t elapsed = usecTimer.elapsed(); + + while (elapsed < usec) { // Loop until we've met/exceeded our required time. + ledOn(); + // Calculate how long we should pulse on for. + // e.g. Are we to close to the end of our requested mark time (usec)? + _delayMicroseconds(std::min((uint32_t)onTimePeriod, usec - elapsed)); + ledOff(); + counter++; + if (elapsed + onTimePeriod >= usec) + return counter; // LED is now off & we've passed our allotted time. + // Wait for the lesser of the rest of the duty cycle, or the time remaining. + _delayMicroseconds( + std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod)); + elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. + } + return counter; +} + +/// Turn the pin (LED) off for a given time. +/// Sends an IR space for the specified number of microseconds. +/// A space is no output, so the PWM output is disabled. +/// @param[in] time Time in microseconds (us). +void IRsend::space(uint32_t time) { + ledOff(); + if (time == 0) return; + _delayMicroseconds(time); +} + +/// Calculate & set any offsets to account for execution times during sending. +/// +/// @param[in] hz The frequency to calibrate at >= 1000Hz. Default is 38000Hz. +/// @return The calculated period offset (in uSeconds) which is now in use. +/// e.g. -5. +/// @note This will generate an 65535us mark() IR LED signal. +/// This only needs to be called once, if at all. +int8_t IRsend::calibrate(uint16_t hz) { + if (hz < 1000) // Were we given kHz? Supports the old call usage. + hz *= 1000; + periodOffset = 0; // Turn off any existing offset while we calibrate. + enableIROut(hz); + IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. + uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) + uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. + // While it shouldn't be necessary, assume at least 1 pulse, to avoid a + // divide by 0 situation. + pulses = std::max(pulses, (uint16_t)1U); + uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. + // Assuming 38kHz for the example calculations: + // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. + // e.g. 65535.0us / 26us = 2520.5769 + // This should have caused approx 2520 loops through the main loop in mark(). + // The average over that many interations should give us a reasonable + // approximation at what offset we need to use to account for instruction + // execution times. + // + // Calculate the actual period from the actual time & the actual pulses + // generated. + double_t actualPeriod = (double_t)timeTaken / (double_t)pulses; + // Store the difference between the actual time per period vs. calculated. + periodOffset = (int8_t)((double_t)calcPeriod - actualPeriod); + return periodOffset; +} + +/// Generic method for sending data that is common to most protocols. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +void IRsend::sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, + uint32_t zerospace, uint64_t data, uint16_t nbits, + bool MSBfirst) { + if (nbits == 0) // If we are asked to send nothing, just return. + return; + if (MSBfirst) { // Send the MSB first. + // Send 0's until we get down to a bit size we can actually manage. + while (nbits > sizeof(data) * 8) { + mark(zeromark); + space(zerospace); + nbits--; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) + if (data & mask) { // Send a 1 + mark(onemark); + space(onespace); + } else { // Send a 0 + mark(zeromark); + space(zerospace); + } + } else { // Send the Least Significant Bit (LSB) first / MSB last. + for (uint16_t bit = 0; bit < nbits; bit++, data >>= 1) + if (data & 1) { // Send a 1 + mark(onemark); + space(onespace); + } else { // Send a 0 + mark(zeromark); + space(zerospace); + } + } +} + +/// Generic method for sending simple protocol messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle) { + sendGeneric(headermark, headerspace, onemark, onespace, zeromark, zerospace, + footermark, gap, 0U, data, nbits, frequency, MSBfirst, repeat, + dutycycle); +} + +/// Generic method for sending simple protocol messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] mesgtime Min. nr. of usecs a single message needs to be. +/// This is effectively the min. total length of a single message. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint32_t mesgtime, const uint64_t data, + const uint16_t nbits, const uint16_t frequency, + const bool MSBfirst, const uint16_t repeat, + const uint8_t dutycycle) { + // Setup + enableIROut(frequency, dutycycle); + IRtimer usecs = IRtimer(); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + usecs.reset(); + + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + sendData(onemark, onespace, zeromark, zerospace, data, nbits, MSBfirst); + + // Footer + if (footermark) mark(footermark); + uint32_t elapsed = usecs.elapsed(); + // Avoid potential unsigned integer underflow. e.g. when mesgtime is 0. + if (elapsed >= mesgtime) + space(gap); + else + space(std::max(gap, mesgtime - elapsed)); + } +} + +/// Generic method for sending simple protocol messages. +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] onemark Nr. of usecs for the led to be pulsed for a '1' bit. +/// @param[in] onespace Nr. of usecs for the led to be fully off for a '1' bit. +/// @param[in] zeromark Nr. of usecs for the led to be pulsed for a '0' bit. +/// @param[in] zerospace Nr. of usecs for the led to be fully off for a '0' bit. +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Nr. of usecs for the led to be off after the footer mark. +/// This is effectively the gap between messages. +/// A value of 0 means no gap space. +/// @param[in] dataptr Pointer to the data to be transmitted. +/// @param[in] nbytes Nr. of bytes of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint8_t *dataptr, const uint16_t nbytes, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle) { + // Setup + enableIROut(frequency, dutycycle); + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + + // Data + for (uint16_t i = 0; i < nbytes; i++) + sendData(onemark, onespace, zeromark, zerospace, *(dataptr + i), 8, + MSBfirst); + + // Footer + if (footermark) mark(footermark); + space(gap); + } +} + +/// Generic method for sending Manchester code data. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// of bits in data. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// (1/2 wavelength) +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). +void IRsend::sendManchesterData(const uint16_t half_period, + const uint64_t data, + const uint16_t nbits, const bool MSBfirst, + const bool GEThomas) { + if (nbits == 0) return; // Nothing to send. + uint16_t bits = nbits; + uint64_t copy = (GEThomas) ? data : ~data; + + if (MSBfirst) { // Send the MSB first. + // Send 0's until we get down to a bit size we can actually manage. + if (bits > (sizeof(data) * 8)) { + sendManchesterData(half_period, 0ULL, bits - sizeof(data) * 8, MSBfirst, + GEThomas); + bits = sizeof(data) * 8; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) + if (copy & mask) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } else { // Send the Least Significant Bit (LSB) first / MSB last. + for (bits = 0; bits < nbits; bits++, copy >>= 1) + if (copy & 1) { + mark(half_period); + space(half_period); + } else { + space(half_period); + mark(half_period); + } + } +} + +/// Generic method for sending Manchester code messages. +/// Will send leading or trailing 0's if the nbits is larger than the number +/// @param[in] headermark Nr. of usecs for the led to be pulsed for the header +/// mark. A value of 0 means no header mark. +/// @param[in] headerspace Nr. of usecs for the led to be off after the header +/// mark. A value of 0 means no header space. +/// @param[in] half_period Nr. of uSeconds for half the clock's period. +/// (1/2 wavelength) +/// @param[in] footermark Nr. of usecs for the led to be pulsed for the footer +/// mark. A value of 0 means no footer mark. +/// @param[in] gap Min. nr. of usecs for the led to be off after the footer +/// mark. This is effectively the absolute minimum gap between messages. +/// @param[in] data The data to be transmitted. +/// @param[in] nbits Nr. of bits of data to be sent. +/// @param[in] frequency The frequency we want to modulate at. (Hz/kHz) +/// @param[in] MSBfirst Flag for bit transmission order. +/// Defaults to MSB->LSB order. +/// @param[in] repeat Nr. of extra times the message will be sent. +/// e.g. 0 = 1 message sent, 1 = 1 initial + 1 repeat = 2 messages +/// @param[in] dutycycle Percentage duty cycle of the LED. +/// e.g. 25 = 25% = 1/4 on, 3/4 off. +/// If you are not sure, try 50 percent. +/// @param[in] GEThomas Use G.E. Thomas (true/default) or IEEE 802.3 (false). +/// @note Assumes a frequency < 1000 means kHz otherwise it is in Hz. +/// Most common value is 38000 or 38, for 38kHz. +void IRsend::sendManchester(const uint16_t headermark, + const uint32_t headerspace, + const uint16_t half_period, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle, + const bool GEThomas) { + // Setup + enableIROut(frequency, dutycycle); + + // We always send a message, even for repeat=0, hence '<= repeat'. + for (uint16_t r = 0; r <= repeat; r++) { + // Header + if (headermark) mark(headermark); + if (headerspace) space(headerspace); + // Data + sendManchesterData(half_period, data, nbits, MSBfirst, GEThomas); + // Footer + if (footermark) mark(footermark); + if (gap) space(gap); + } +} + +#if SEND_RAW +/// Send a raw IRremote message. +/// +/// @param[in] buf An array of uint16_t's that has microseconds elements. +/// @param[in] len Nr. of elements in the buf[] array. +/// @param[in] hz Frequency to send the message at. (kHz < 1000; Hz >= 1000) +/// @note Even elements are Mark times (On), Odd elements are Space times (Off). +/// Ref: +/// examples/IRrecvDumpV2/IRrecvDumpV2.ino (or later) +void IRsend::sendRaw(const uint16_t buf[], const uint16_t len, + const uint16_t hz) { + // Set IR carrier frequency + enableIROut(hz); + for (uint16_t i = 0; i < len; i++) { + if (i & 1) { // Odd bit. + space(buf[i]); + } else { // Even bit. + mark(buf[i]); + } + } + ledOff(); // We potentially have ended with a mark(), so turn of the LED. +} +#endif // SEND_RAW + +/// Get the minimum number of repeats for a given protocol. +/// @param[in] protocol Protocol number/type of the message you want to send. +/// @return The number of repeats required. +uint16_t IRsend::minRepeats(const decode_type_t protocol) { + switch (protocol) { + // Single repeats + case AIWA_RC_T501: + case AMCOR: + case COOLIX: + case COOLIX48: + case ELITESCREENS: + case GICABLE: + case INAX: + case MIDEA24: + case MITSUBISHI: + case MITSUBISHI2: + case MITSUBISHI_AC: + case MULTIBRACKETS: + case SHERWOOD: + case TOSHIBA_AC: + case TOTO: + return kSingleRepeat; + // Special + case AIRWELL: + return kAirwellMinRepeats; + case CARRIER_AC40: + return kCarrierAc40MinRepeat; + case DISH: + return kDishMinRepeat; + case EPSON: + return kEpsonMinRepeat; + case SANYO_AC88: + return kSanyoAc88MinRepeat; + case SONY: + return kSonyMinRepeat; + case SONY_38K: + return kSonyMinRepeat + 1; + case SYMPHONY: + return kSymphonyDefaultRepeat; + case ZEPEAL: + return kZepealMinRepeat; + default: + return kNoRepeat; + } +} + +/// Get the default number of bits for a given protocol. +/// @param[in] protocol Protocol number/type you want the default bit size for. +/// @return The number of bits. +uint16_t IRsend::defaultBits(const decode_type_t protocol) { + switch (protocol) { + case MULTIBRACKETS: + case GORENJE: + return 8; + case WOWWEE: + return 11; + case RC5: + case SYMPHONY: + return 12; + case LASERTAG: + case RC5X: + return 13; + case AIWA_RC_T501: + case DENON: + case SHARP: + return 15; + case BOSE: + case DISH: + case GICABLE: + case JVC: + case LEGOPF: + case MITSUBISHI: + case MITSUBISHI2: + case ZEPEAL: + return 16; + case METZ: + return 19; + case RC6: + case SONY: + case SONY_38K: + return 20; + case COOLIX: + case INAX: + case MIDEA24: + case NIKAI: + case RCMM: + case TOTO: + case TRANSCOLD: + return 24; + case LG: + case LG2: + return 28; + case ARRIS: + case CARRIER_AC: + case ELITESCREENS: + case EPSON: + case NEC: + case NEC_LIKE: + case PANASONIC_AC32: + case SAMSUNG: + case SHERWOOD: + case WHYNTER: + return 32; + case AIRWELL: + return 34; + case LUTRON: + case TECO: + return 35; + case SAMSUNG36: + return 36; + case CARRIER_AC40: + return kCarrierAc40Bits; // 40 + case DOSHISHA: + return kDoshishaBits; // 40 + case SANYO_LC7461: + return kSanyoLC7461Bits; // 42 + case COOLIX48: + case GOODWEATHER: + case KELON: + case MIDEA: + case PANASONIC: + return 48; + case CLIMABUTLER: + return kClimaButlerBits; // 52 + case AIRTON: + case ECOCLIM: + case MAGIQUEST: + case VESTEL_AC: + case TECHNIBEL_AC: + case TRUMA: + return 56; + case AMCOR: + case CARRIER_AC64: + case DELONGHI_AC: + case PIONEER: + return 64; + case ARGO: + return kArgoBits; + case BOSCH144: + return kBosch144Bits; + case CORONA_AC: + return kCoronaAcBits; + case CARRIER_AC84: + return kCarrierAc84Bits; + case CARRIER_AC128: + return kCarrierAc128Bits; + case DAIKIN: + return kDaikinBits; + case DAIKIN128: + return kDaikin128Bits; + case DAIKIN152: + return kDaikin152Bits; + case DAIKIN160: + return kDaikin160Bits; + case DAIKIN176: + return kDaikin176Bits; + case DAIKIN2: + return kDaikin2Bits; + case DAIKIN200: + return kDaikin200Bits; + case DAIKIN216: + return kDaikin216Bits; + case DAIKIN312: + return kDaikin312Bits; + case DAIKIN64: + return kDaikin64Bits; + case ELECTRA_AC: + return kElectraAcBits; + case GREE: + return kGreeBits; + case HAIER_AC: + return kHaierACBits; + case HAIER_AC_YRW02: + return kHaierACYRW02Bits; + case HAIER_AC160: + return kHaierAC160Bits; + case HAIER_AC176: + return kHaierAC176Bits; + case HITACHI_AC: + return kHitachiAcBits; + case HITACHI_AC1: + return kHitachiAc1Bits; + case HITACHI_AC2: + return kHitachiAc2Bits; + case HITACHI_AC3: + return kHitachiAc3Bits; + case HITACHI_AC264: + return kHitachiAc264Bits; + case HITACHI_AC296: + return kHitachiAc296Bits; + case HITACHI_AC344: + return kHitachiAc344Bits; + case HITACHI_AC424: + return kHitachiAc424Bits; + case KELON168: + return kKelon168Bits; + case KELVINATOR: + return kKelvinatorBits; + case MILESTAG2: + return kMilesTag2ShotBits; + case MIRAGE: + return kMirageBits; + case MITSUBISHI_AC: + return kMitsubishiACBits; + case MITSUBISHI136: + return kMitsubishi136Bits; + case MITSUBISHI112: + return kMitsubishi112Bits; + case MITSUBISHI_HEAVY_152: + return kMitsubishiHeavy152Bits; + case MITSUBISHI_HEAVY_88: + return kMitsubishiHeavy88Bits; + case NEOCLIMA: + return kNeoclimaBits; + case PANASONIC_AC: + return kPanasonicAcBits; + case RHOSS: + return kRhossBits; + case SAMSUNG_AC: + return kSamsungAcBits; + case SANYO_AC: + return kSanyoAcBits; + case SANYO_AC88: + return kSanyoAc88Bits; + case SANYO_AC152: + return kSanyoAc152Bits; + case SHARP_AC: + return kSharpAcBits; + case TCL96AC: + return kTcl96AcBits; + case TCL112AC: + return kTcl112AcBits; + case TEKNOPOINT: + return kTeknopointBits; + case TOSHIBA_AC: + return kToshibaACBits; + case TROTEC: + case TROTEC_3550: + return kTrotecBits; + case VOLTAS: + return kVoltasBits; + case WHIRLPOOL_AC: + return kWhirlpoolAcBits; + case XMP: + return kXmpBits; + case YORK: + return kYorkBits; + case FUJITSU_AC264: + return kFujitsuAc264Bits; + // No default amount of bits. + case FUJITSU_AC: + case MWM: + default: + return 0; + } +} + +/// Send a simple (up to 64 bits) IR message of a given type. +/// An unknown/unsupported type will send nothing. +/// @param[in] type Protocol number/type of the message you want to send. +/// @param[in] data The data you want to send (up to 64 bits). +/// @param[in] nbits How many bits long the message is to be. +/// @param[in] repeat How many repeats to do? +/// @return True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat) { + uint16_t min_repeat __attribute__((unused)) = + std::max(IRsend::minRepeats(type), repeat); + switch (type) { +#if SEND_AIRTON + case AIRTON: + sendAirton(data, nbits, min_repeat); + break; +#endif // SEND_AIRTON +#if SEND_AIRWELL + case AIRWELL: + sendAirwell(data, nbits, min_repeat); + break; +#endif +#if SEND_AIWA_RC_T501 + case AIWA_RC_T501: + sendAiwaRCT501(data, nbits, min_repeat); + break; +#endif // SEND_AIWA_RC_T501 +#if SEND_ARRIS + case ARRIS: + sendArris(data, nbits, min_repeat); + break; +#endif // SEND_ARRIS +#if SEND_BOSE + case BOSE: + sendBose(data, nbits, min_repeat); + break; +#endif // SEND_BOSE +#if SEND_CARRIER_AC + case CARRIER_AC: + sendCarrierAC(data, nbits, min_repeat); + break; +#endif +#if SEND_CARRIER_AC40 + case CARRIER_AC40: + sendCarrierAC40(data, nbits, min_repeat); + break; +#endif // SEND_CARRIER_AC40 +#if SEND_CARRIER_AC64 + case CARRIER_AC64: + sendCarrierAC64(data, nbits, min_repeat); + break; +#endif // SEND_CARRIER_AC64 +#if SEND_CLIMABUTLER + case CLIMABUTLER: + sendClimaButler(data, nbits, min_repeat); + break; +#endif // SEND_CLIMABUTLER +#if SEND_COOLIX + case COOLIX: + sendCOOLIX(data, nbits, min_repeat); + break; +#endif // SEND_COOLIX +#if SEND_COOLIX48 + case COOLIX48: + sendCoolix48(data, nbits, min_repeat); + break; +#endif // SEND_COOLIX48 +#if SEND_DAIKIN64 + case DAIKIN64: + sendDaikin64(data, nbits, min_repeat); + break; +#endif +#if SEND_DELONGHI_AC + case DELONGHI_AC: + sendDelonghiAc(data, nbits, min_repeat); + break; +#endif +#if SEND_DENON + case DENON: + sendDenon(data, nbits, min_repeat); + break; +#endif +#if SEND_DISH + case DISH: + sendDISH(data, nbits, min_repeat); + break; +#endif +#if SEND_DOSHISHA + case DOSHISHA: + sendDoshisha(data, nbits, min_repeat); + break; +#endif +#if SEND_ECOCLIM + case ECOCLIM: + sendEcoclim(data, nbits, min_repeat); + break; +#endif // SEND_ECOCLIM +#if SEND_ELITESCREENS + case ELITESCREENS: + sendElitescreens(data, nbits, min_repeat); + break; +#endif // SEND_ELITESCREENS +#if SEND_EPSON + case EPSON: + sendEpson(data, nbits, min_repeat); + break; +#endif +#if SEND_GICABLE + case GICABLE: + sendGICable(data, nbits, min_repeat); + break; +#endif +#if SEND_GOODWEATHER + case GOODWEATHER: + sendGoodweather(data, nbits, min_repeat); + break; +#endif +#if SEND_GORENJE + case GORENJE: + sendGorenje(data, nbits, min_repeat); + break; +#endif +#if SEND_GREE + case GREE: + sendGree(data, nbits, min_repeat); + break; +#endif +#if SEND_INAX + case INAX: + sendInax(data, nbits, min_repeat); + break; +#endif // SEND_INAX +#if SEND_JVC + case JVC: + sendJVC(data, nbits, min_repeat); + break; +#endif +#if SEND_KELON + case KELON: + sendKelon(data, nbits, min_repeat); + break; +#endif // SEND_KELON +#if SEND_LASERTAG + case LASERTAG: + sendLasertag(data, nbits, min_repeat); + break; +#endif +#if SEND_LEGOPF + case LEGOPF: + sendLegoPf(data, nbits, min_repeat); + break; +#endif +#if SEND_LG + case LG: + sendLG(data, nbits, min_repeat); + break; + case LG2: + sendLG2(data, nbits, min_repeat); + break; +#endif +#if SEND_LUTRON + case LUTRON: + sendLutron(data, nbits, min_repeat); + break; +#endif +#if SEND_MAGIQUEST + case MAGIQUEST: + sendMagiQuest(data, nbits, min_repeat); + break; +#endif // SEND_MAGIQUEST +#if SEND_METZ + case METZ: + sendMetz(data, nbits, min_repeat); + break; +#endif // SEND_METZ +#if SEND_MIDEA + case MIDEA: + sendMidea(data, nbits, min_repeat); + break; +#endif // SEND_MIDEA +#if SEND_MIDEA24 + case MIDEA24: + sendMidea24(data, nbits, min_repeat); + break; +#endif // SEND_MIDEA24 +#if SEND_MILESTAG2 + case MILESTAG2: + sendMilestag2(data, nbits, min_repeat); + break; +#endif // SEND_MILESTAG2 +#if SEND_MITSUBISHI + case MITSUBISHI: + sendMitsubishi(data, nbits, min_repeat); + break; +#endif +#if SEND_MITSUBISHI2 + case MITSUBISHI2: + sendMitsubishi2(data, nbits, min_repeat); + break; +#endif +#if SEND_MULTIBRACKETS + case MULTIBRACKETS: + sendMultibrackets(data, nbits, min_repeat); + break; +#endif +#if SEND_NIKAI + case NIKAI: + sendNikai(data, nbits, min_repeat); + break; +#endif +#if SEND_NEC + case NEC: + case NEC_LIKE: + sendNEC(data, nbits, min_repeat); + break; +#endif +#if SEND_PANASONIC + case PANASONIC: + sendPanasonic64(data, nbits, min_repeat); + break; +#endif // SEND_PANASONIC +#if SEND_PANASONIC_AC32 + case PANASONIC_AC32: + sendPanasonicAC32(data, nbits, min_repeat); + break; +#endif // SEND_PANASONIC_AC32 +#if SEND_PIONEER + case PIONEER: + sendPioneer(data, nbits, min_repeat); + break; +#endif +#if SEND_RC5 + case RC5: + case RC5X: + sendRC5(data, nbits, min_repeat); + break; +#endif +#if SEND_RC6 + case RC6: + sendRC6(data, nbits, min_repeat); + break; +#endif +#if SEND_RCMM + case RCMM: + sendRCMM(data, nbits, min_repeat); + break; +#endif +#if SEND_SAMSUNG + case SAMSUNG: + sendSAMSUNG(data, nbits, min_repeat); + break; +#endif +#if SEND_SAMSUNG36 + case SAMSUNG36: + sendSamsung36(data, nbits, min_repeat); + break; +#endif +#if SEND_SANYO + case SANYO_LC7461: + sendSanyoLC7461(data, nbits, min_repeat); + break; +#endif +#if SEND_SHARP + case SHARP: + sendSharpRaw(data, nbits, min_repeat); + break; +#endif +#if SEND_SHERWOOD + case SHERWOOD: + sendSherwood(data, nbits, min_repeat); + break; +#endif +#if SEND_SONY + case SONY: + sendSony(data, nbits, min_repeat); + break; + case SONY_38K: + sendSony38(data, nbits, min_repeat); + break; +#endif +#if SEND_SYMPHONY + case SYMPHONY: + sendSymphony(data, nbits, min_repeat); + break; +#endif +#if SEND_TECHNIBEL_AC + case TECHNIBEL_AC: + sendTechnibelAc(data, nbits, min_repeat); + break; +#endif +#if SEND_TECO + case TECO: + sendTeco(data, nbits, min_repeat); + break; +#endif // SEND_TECO +#if SEND_TOTO + case TOTO: + sendToto(data, nbits, min_repeat); + break; +#endif // SEND_TOTO +#if SEND_TRANSCOLD + case TRANSCOLD: + sendTranscold(data, nbits, min_repeat); + break; +#endif // SEND_TRANSCOLD +#if SEND_TRUMA + case TRUMA: + sendTruma(data, nbits, min_repeat); + break; +#endif // SEND_TRUMA +#if SEND_VESTEL_AC + case VESTEL_AC: + sendVestelAc(data, nbits, min_repeat); + break; +#endif +#if SEND_WHYNTER + case WHYNTER: + sendWhynter(data, nbits, min_repeat); + break; +#endif +#if SEND_WOWWEE + case WOWWEE: + sendWowwee(data, nbits, min_repeat); + break; +#endif // SEND_WOWWEE +#if SEND_XMP + case XMP: + sendXmp(data, nbits, min_repeat); + break; +#endif +#if SEND_ZEPEAL + case ZEPEAL: + sendZepeal(data, nbits, min_repeat); + break; +#endif // SEND_ZEPEAL + default: + return false; + } + return true; +} + +/// Send a complex (>= 64 bits) IR message of a given type. +/// An unknown/unsupported type will send nothing. +/// @param[in] type Protocol number/type of the message you want to send. +/// @param[in] state A pointer to the array of bytes that make up the state[]. +/// @param[in] nbytes How many bytes are in the state. +/// @return True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const uint8_t *state, + const uint16_t nbytes) { + switch (type) { +#if SEND_VOLTAS + case VOLTAS: + sendVoltas(state, nbytes); + break; +#endif // SEND_VOLTAS +#if SEND_AMCOR + case AMCOR: + sendAmcor(state, nbytes); + break; +#endif +#if SEND_ARGO + case ARGO: + sendArgo(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_BOSCH144 + case BOSCH144: + sendBosch144(state, nbytes); + break; +#endif // SEND_BOSCH144 +#if SEND_CARRIER_AC84 + case CARRIER_AC84: + sendCarrierAC84(state, nbytes); + break; +#endif // SEND_CARRIER_AC84 +#if SEND_CARRIER_AC128 + case CARRIER_AC128: + sendCarrierAC128(state, nbytes); + break; +#endif // SEND_CARRIER_AC128 +#if SEND_CORONA_AC + case CORONA_AC: + sendCoronaAc(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_DAIKIN + case DAIKIN: + sendDaikin(state, nbytes); + break; +#endif // SEND_DAIKIN +#if SEND_DAIKIN128 + case DAIKIN128: + sendDaikin128(state, nbytes); + break; +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + case DAIKIN152: + sendDaikin152(state, nbytes); + break; +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + case DAIKIN160: + sendDaikin160(state, nbytes); + break; +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + sendDaikin176(state, nbytes); + break; +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + sendDaikin2(state, nbytes); + break; +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN200 + case DAIKIN200: + sendDaikin200(state, nbytes); + break; +#endif // SEND_DAIKIN200 +#if SEND_DAIKIN216 + case DAIKIN216: + sendDaikin216(state, nbytes); + break; +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN312 + case DAIKIN312: + sendDaikin312(state, nbytes); + break; +#endif // SEND_DAIKIN312 +#if SEND_ELECTRA_AC + case ELECTRA_AC: + sendElectraAC(state, nbytes); + break; +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + sendFujitsuAC(state, nbytes); + break; +#endif // SEND_FUJITSU_AC +#if SEND_FUJITSU_AC264 + case FUJITSU_AC264: + sendFujitsuAC264(state, nbytes); + break; +#endif +#if SEND_GREE + case GREE: + sendGree(state, nbytes); + break; +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + sendHaierAC(state, nbytes); + break; +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + sendHaierACYRW02(state, nbytes); + break; +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HAIER_AC160 + case HAIER_AC160: + sendHaierAC160(state, nbytes); + break; +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + case HAIER_AC176: + sendHaierAC176(state, nbytes); + break; +#endif // SEND_HAIER_AC176 +#if SEND_HITACHI_AC + case HITACHI_AC: + sendHitachiAC(state, nbytes); + break; +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + sendHitachiAC1(state, nbytes); + break; +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC2 + case HITACHI_AC2: + sendHitachiAC2(state, nbytes); + break; +#endif // SEND_HITACHI_AC2 +#if SEND_HITACHI_AC3 + case HITACHI_AC3: + sendHitachiAc3(state, nbytes); + break; +#endif // SEND_HITACHI_AC3 +#if SEND_HITACHI_AC264 + case HITACHI_AC264: + sendHitachiAc264(state, nbytes); + break; +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + sendHitachiAc296(state, nbytes); + break; +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + case HITACHI_AC344: + sendHitachiAc344(state, nbytes); + break; +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + case HITACHI_AC424: + sendHitachiAc424(state, nbytes); + break; +#endif // SEND_HITACHI_AC424 +#if SEND_KELON168 + case KELON168: + sendKelon168(state, nbytes); + break; +#endif // SEND_KELON168 +#if SEND_KELVINATOR + case KELVINATOR: + sendKelvinator(state, nbytes); + break; +#endif // SEND_KELVINATOR +#if SEND_MIRAGE + case MIRAGE: + sendMirage(state, nbytes); + break; +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + sendMitsubishiAC(state, nbytes); + break; +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI136 + case MITSUBISHI136: + sendMitsubishi136(state, nbytes); + break; +#endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + sendMitsubishi112(state, nbytes); + break; +#endif // SEND_MITSUBISHI112 +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + sendMitsubishiHeavy88(state, nbytes); + break; + case MITSUBISHI_HEAVY_152: + sendMitsubishiHeavy152(state, nbytes); + break; +#endif // SEND_MITSUBISHIHEAVY +#if SEND_MWM + case MWM: + sendMWM(state, nbytes); + break; +#endif // SEND_MWM +#if SEND_NEOCLIMA + case NEOCLIMA: + sendNeoclima(state, nbytes); + break; +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + sendPanasonicAC(state, nbytes); + break; +#endif // SEND_PANASONIC_AC +#if SEND_RHOSS + case RHOSS: + sendRhoss(state, nbytes); + break; +#endif // SEND_RHOSS +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + sendSamsungAC(state, nbytes); + break; +#endif // SEND_SAMSUNG_AC +#if SEND_SANYO_AC + case SANYO_AC: + sendSanyoAc(state, nbytes); + break; +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + case SANYO_AC88: + sendSanyoAc88(state, nbytes); + break; +#endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + case SANYO_AC152: + sendSanyoAc152(state, nbytes); + break; +#endif // SEND_SANYO_AC152 +#if SEND_SHARP_AC + case SHARP_AC: + sendSharpAc(state, nbytes); + break; +#endif // SEND_SHARP_AC +#if SEND_TCL96AC + case TCL96AC: + sendTcl96Ac(state, nbytes); + break; +#endif // SEND_TCL96AC +#if SEND_TCL112AC + case TCL112AC: + sendTcl112Ac(state, nbytes); + break; +#endif // SEND_TCL112AC +#if SEND_TEKNOPOINT + case TEKNOPOINT: + sendTeknopoint(state, nbytes); + break; +#endif // SEND_TEKNOPOINT +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + sendToshibaAC(state, nbytes); + break; +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + sendTrotec(state, nbytes); + break; +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + case TROTEC_3550: + sendTrotec3550(state, nbytes); + break; +#endif // SEND_TROTEC_3550 +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + sendWhirlpoolAC(state, nbytes); + break; +#endif // SEND_WHIRLPOOL_AC +#if SEND_YORK + case YORK: + sendYork(state, nbytes); + break; +#endif // SEND_YORK + default: + return false; + } + return true; +} diff --git a/src/IRsend.h b/src/IRsend.h index c9f1bbe30..923f004e3 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -1,926 +1,926 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2015 Mark Szabo -// Copyright 2017 David Conran -#ifndef IRSEND_H_ -#define IRSEND_H_ - -#define __STDC_LIMIT_MACROS -#include -#include "IRremoteESP8266.h" - -// Originally from https://github.com/shirriff/Arduino-IRremote/ -// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for -// sending IR code on ESP8266 - -#if TEST || UNIT_TEST -#define VIRTUAL virtual -#else -#define VIRTUAL -#endif - -// Constants -// Offset (in microseconds) to use in Period time calculations to account for -// code excution time in producing the software PWM signal. -#if defined(ESP32) -// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz -const int8_t kPeriodOffset = -2; -#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens) -// Calculated on an ESP8266 NodeMCU v2 board using: -// v2.6.0 with v2.5.2 ESP core @ 160MHz -const int8_t kPeriodOffset = -2; -#else // (defined(ESP8266) && F_CPU == 160000000L) -// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz -const int8_t kPeriodOffset = -5; -#endif // (defined(ESP8266) && F_CPU == 160000000L) -const uint8_t kDutyDefault = 50; // Percentage -const uint8_t kDutyMax = 100; // Percentage -// delayMicroseconds() is only accurate to 16383us. -// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds -const uint16_t kMaxAccurateUsecDelay = 16383; -// Usecs to wait between messages we don't know the proper gap time. -const uint32_t kDefaultMessageGap = 100000; -/// Placeholder for missing sensor temp value -/// @note Not using "-1" as it may be a valid external temp -const float kNoTempValue = -100.0; - -/// Enumerators and Structures for the Common A/C API. -namespace stdAc { -/// Common A/C settings for A/C operating modes. -enum class opmode_t { - kOff = -1, - kAuto = 0, - kCool = 1, - kHeat = 2, - kDry = 3, - kFan = 4, - // Add new entries before this one, and update it to point to the last entry - kLastOpmodeEnum = kFan, -}; - -/// Common A/C settings for Fan Speeds. -enum class fanspeed_t { - kAuto = 0, - kMin = 1, - kLow = 2, - kMedium = 3, - kHigh = 4, - kMax = 5, - kMediumHigh = 6, - // Add new entries before this one, and update it to point to the last entry - kLastFanspeedEnum = kMediumHigh, -}; - -/// Common A/C settings for Vertical Swing. -enum class swingv_t { - kOff = -1, - kAuto = 0, - kHighest = 1, - kHigh = 2, - kMiddle = 3, - kLow = 4, - kLowest = 5, - kUpperMiddle = 6, - // Add new entries before this one, and update it to point to the last entry - kLastSwingvEnum = kUpperMiddle, -}; - -/// @brief Tyoe of A/C command (if the remote uses different codes for each) -/// @note Most remotes support only a single command or aggregate multiple -/// into one (e.g. control+timer). Use @c kControlCommand in such case -enum class ac_command_t { - kControlCommand = 0, - kSensorTempReport = 1, - kTimerCommand = 2, - kConfigCommand = 3, - // Add new entries before this one, and update it to point to the last entry - kLastAcCommandEnum = kConfigCommand, -}; - -/// Common A/C settings for Horizontal Swing. -enum class swingh_t { - kOff = -1, - kAuto = 0, // a.k.a. On. - kLeftMax = 1, - kLeft = 2, - kMiddle = 3, - kRight = 4, - kRightMax = 5, - kWide = 6, // a.k.a. left & right at the same time. - // Add new entries before this one, and update it to point to the last entry - kLastSwinghEnum = kWide, -}; - -/// Structure to hold a common A/C state. -struct state_t { - decode_type_t protocol = decode_type_t::UNKNOWN; - int16_t model = -1; // `-1` means unused. - bool power = false; - stdAc::opmode_t mode = stdAc::opmode_t::kOff; - float degrees = 25; - bool celsius = true; - stdAc::fanspeed_t fanspeed = stdAc::fanspeed_t::kAuto; - stdAc::swingv_t swingv = stdAc::swingv_t::kOff; - stdAc::swingh_t swingh = stdAc::swingh_t::kOff; - bool quiet = false; - bool turbo = false; - bool econo = false; - bool light = false; - bool filter = false; - bool clean = false; - bool beep = false; - int16_t sleep = -1; // `-1` means off. - int16_t clock = -1; // `-1` means not set. - stdAc::ac_command_t command = stdAc::ac_command_t::kControlCommand; - bool iFeel = false; - float sensorTemperature = kNoTempValue; // `kNoTempValue` means not set. -}; -}; // namespace stdAc - -/// Fujitsu A/C model numbers -enum fujitsu_ac_remote_model_t { - ARRAH2E = 1, ///< (1) AR-RAH2E, AR-RAC1E, AR-RAE1E, AR-RCE1E, AR-RAH2U, - ///< AR-REG1U (Default) - ///< Warning: Use on incorrect models can cause the A/C to lock - ///< up, requring the A/C to be physically powered off to fix. - ///< e.g. AR-RAH1U may lock up with a Swing command. - ARDB1, ///< (2) AR-DB1, AR-DL10 (AR-DL10 swing doesn't work) - ARREB1E, ///< (3) AR-REB1E, AR-RAH1U (Similar to ARRAH2E but no horiz - ///< control) - ARJW2, ///< (4) AR-JW2 (Same as ARDB1 but with horiz control) - ARRY4, ///< (5) AR-RY4 (Same as AR-RAH2E but with clean & filter) - ARREW4E, ///< (6) Similar to ARRAH2E, but with different temp config. -}; - -/// Gree A/C model numbers -enum gree_ac_remote_model_t { - YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default) - YBOFB, // (2) Green, YBOFB2, YAPOF3 - YX1FSF, // (3) Soleus Air window unit (Similar to YAW1F, but with an - // Operation mode of Energy Saver (Econo)) -}; - -/// HAIER_AC176 A/C model numbers -enum haier_ac176_remote_model_t { - V9014557_A = 1, // (1) V9014557 Remote in "A" setting. (Default) - V9014557_B, // (2) V9014557 Remote in "B" setting. -}; - -/// HITACHI_AC1 A/C model numbers -enum hitachi_ac1_remote_model_t { - R_LT0541_HTA_A = 1, // (1) R-LT0541-HTA Remote in "A" setting. (Default) - R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting. -}; - -/// MIRAGE A/C model numbers -enum mirage_ac_remote_model_t { - KKG9AC1 = 1, // (1) KKG9A-C1 Remote. (Default) - KKG29AC1, // (2) KKG29A-C1 Remote. -}; - -/// Panasonic A/C model numbers -enum panasonic_ac_remote_model_t { - kPanasonicUnknown = 0, - kPanasonicLke = 1, - kPanasonicNke = 2, - kPanasonicDke = 3, // PKR too. - kPanasonicJke = 4, - kPanasonicCkp = 5, - kPanasonicRkr = 6, -}; - -/// Sharp A/C model numbers -enum sharp_ac_remote_model_t { - A907 = 1, - A705 = 2, - A903 = 3, // 820 too -}; - -/// TCL (& Teknopoint) A/C model numbers -enum tcl_ac_remote_model_t { - TAC09CHSD = 1, - GZ055BE1 = 2, // Also Teknopoint GZ01-BEJ0-000 -}; - -/// Voltas A/C model numbers -enum voltas_ac_remote_model_t { - kVoltasUnknown = 0, // Full Function - kVoltas122LZF = 1, // (1) 122LZF (No SwingH support) (Default) -}; - -/// Whirlpool A/C model numbers -enum whirlpool_ac_remote_model_t { - DG11J13A = 1, // DG11J1-04 too - DG11J191, -}; - -/// LG A/C model numbers -enum lg_ac_remote_model_t { - GE6711AR2853M = 1, // (1) LG 28-bit Protocol (default) - AKB75215403, // (2) LG2 28-bit Protocol - AKB74955603, // (3) LG2 28-bit Protocol variant - AKB73757604, // (4) LG2 Variant of AKB74955603 - LG6711A20083V, // (5) Same as GE6711AR2853M, but only SwingV toggle. -}; - -/// Argo A/C model numbers -enum argo_ac_remote_model_t { - SAC_WREM2 = 1, // (1) ARGO WREM2 remote (default) - SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd -}; - -// Classes - -/// Class for sending all basic IR protocols. -/// @note Originally from https://github.com/shirriff/Arduino-IRremote/ -/// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for -/// sending IR code on ESP8266 -class IRsend { - public: - explicit IRsend(uint16_t IRsendPin, bool inverted = false, - bool use_modulation = true); - void begin(); - void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault); - VIRTUAL void _delayMicroseconds(uint32_t usec); - VIRTUAL uint16_t mark(uint16_t usec); - VIRTUAL void space(uint32_t usec); - int8_t calibrate(uint16_t hz = 38000U); - void sendRaw(const uint16_t buf[], const uint16_t len, const uint16_t hz); - void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, - uint32_t zerospace, uint64_t data, uint16_t nbits, - bool MSBfirst = true); - void sendManchesterData(const uint16_t half_period, const uint64_t data, - const uint16_t nbits, const bool MSBfirst = true, - const bool GEThomas = true); - void sendManchester(const uint16_t headermark, const uint32_t headerspace, - const uint16_t half_period, const uint16_t footermark, - const uint32_t gap, const uint64_t data, - const uint16_t nbits, const uint16_t frequency = 38, - const bool MSBfirst = true, - const uint16_t repeat = kNoRepeat, - const uint8_t dutycycle = kDutyDefault, - const bool GEThomas = true); - void sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint64_t data, const uint16_t nbits, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle); - void sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint32_t mesgtime, const uint64_t data, - const uint16_t nbits, const uint16_t frequency, - const bool MSBfirst, const uint16_t repeat, - const uint8_t dutycycle); - void sendGeneric(const uint16_t headermark, const uint32_t headerspace, - const uint16_t onemark, const uint32_t onespace, - const uint16_t zeromark, const uint32_t zerospace, - const uint16_t footermark, const uint32_t gap, - const uint8_t *dataptr, const uint16_t nbytes, - const uint16_t frequency, const bool MSBfirst, - const uint16_t repeat, const uint8_t dutycycle); - static uint16_t minRepeats(const decode_type_t protocol); - static uint16_t defaultBits(const decode_type_t protocol); - bool send(const decode_type_t type, const uint64_t data, - const uint16_t nbits, const uint16_t repeat = kNoRepeat); - bool send(const decode_type_t type, const uint8_t *state, - const uint16_t nbytes); -#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ - SEND_MIDEA24) - void sendNEC(uint64_t data, uint16_t nbits = kNECBits, - uint16_t repeat = kNoRepeat); - uint32_t encodeNEC(uint16_t address, uint16_t command); -#endif -#if SEND_SONY - // sendSony() should typically be called with repeat=2 as Sony devices - // expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes) - // Legacy use of this procedure was to only send a single code so call it with - // repeat=0 for backward compatibility. As of v2.0 it defaults to sending - // a Sony command that will be accepted be a device. - void sendSony(const uint64_t data, const uint16_t nbits = kSony20Bits, - const uint16_t repeat = kSonyMinRepeat); - void sendSony38(const uint64_t data, const uint16_t nbits = kSony20Bits, - const uint16_t repeat = kSonyMinRepeat + 1); - uint32_t encodeSony(const uint16_t nbits, const uint16_t command, - const uint16_t address, const uint16_t extended = 0); -#endif // SEND_SONY -#if SEND_SHERWOOD - void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits, - uint16_t repeat = kSherwoodMinRepeat); -#endif - // `sendSAMSUNG()` is required by `sendLG()` -#if (SEND_SAMSUNG || SEND_LG) - void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits, - const uint16_t repeat = kNoRepeat); - uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command); -#endif // (SEND_SAMSUNG || SEND_LG) -#if SEND_SAMSUNG36 - void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_SAMSUNG_AC - void sendSamsungAC(const unsigned char data[], - const uint16_t nbytes = kSamsungAcStateLength, - const uint16_t repeat = kSamsungAcDefaultRepeat); -#endif -#if SEND_LG - void sendLG(uint64_t data, uint16_t nbits = kLgBits, - uint16_t repeat = kNoRepeat); - void sendLG2(uint64_t data, uint16_t nbits = kLgBits, - uint16_t repeat = kNoRepeat); - uint32_t encodeLG(uint16_t address, uint16_t command); -#endif -#if (SEND_SHARP || SEND_DENON) - uint32_t encodeSharp(const uint16_t address, const uint16_t command, - const uint16_t expansion = 1, const uint16_t check = 0, - const bool MSBfirst = false); - void sendSharp(const uint16_t address, const uint16_t command, - const uint16_t nbits = kSharpBits, - const uint16_t repeat = kNoRepeat); - void sendSharpRaw(const uint64_t data, const uint16_t nbits = kSharpBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_SHARP_AC - void sendSharpAc(const unsigned char data[], - const uint16_t nbytes = kSharpAcStateLength, - const uint16_t repeat = kSharpAcDefaultRepeat); -#endif // SEND_SHARP_AC -#if SEND_JVC - void sendJVC(uint64_t data, uint16_t nbits = kJvcBits, - uint16_t repeat = kNoRepeat); - uint16_t encodeJVC(uint8_t address, uint8_t command); -#endif -#if SEND_DENON - void sendDenon(uint64_t data, uint16_t nbits = kDenonBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_SANYO - uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command); - void sendSanyoLC7461(const uint64_t data, - const uint16_t nbits = kSanyoLC7461Bits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_SANYO_AC - void sendSanyoAc(const uint8_t *data, - const uint16_t nbytes = kSanyoAcStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_SANYO_AC -#if SEND_SANYO_AC88 - void sendSanyoAc88(const uint8_t *data, - const uint16_t nbytes = kSanyoAc88StateLength, - const uint16_t repeat = kSanyoAc88MinRepeat); -#endif // SEND_SANYO_AC88 -#if SEND_SANYO_AC152 - void sendSanyoAc152(const uint8_t *data, - const uint16_t nbytes = kSanyoAc152StateLength, - const uint16_t repeat = kSanyoAc152MinRepeat); -#endif // SEND_SANYO_AC152 -#if SEND_DISH - // sendDISH() should typically be called with repeat=3 as DISH devices - // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) - // Legacy use of this procedure was only to send a single code - // so use repeat=0 for backward compatibility. - void sendDISH(uint64_t data, uint16_t nbits = kDishBits, - uint16_t repeat = kDishMinRepeat); -#endif -#if (SEND_PANASONIC || SEND_DENON) - void sendPanasonic64(const uint64_t data, - const uint16_t nbits = kPanasonicBits, - const uint16_t repeat = kNoRepeat); - void sendPanasonic(const uint16_t address, const uint32_t data, - const uint16_t nbits = kPanasonicBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodePanasonic(const uint16_t manufacturer, const uint8_t device, - const uint8_t subdevice, const uint8_t function); -#endif -#if SEND_RC5 - void sendRC5(const uint64_t data, uint16_t nbits = kRC5XBits, - const uint16_t repeat = kNoRepeat); - uint16_t encodeRC5(const uint8_t address, const uint8_t command, - const bool key_released = false); - uint16_t encodeRC5X(const uint8_t address, const uint8_t command, - const bool key_released = false); - uint64_t toggleRC5(const uint64_t data); -#endif -#if SEND_RC6 - void sendRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits, - const uint16_t repeat = kNoRepeat); - uint64_t encodeRC6(const uint32_t address, const uint8_t command, - const uint16_t mode = kRC6Mode0Bits); - uint64_t toggleRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits); -#endif -#if SEND_RCMM - void sendRCMM(uint64_t data, uint16_t nbits = kRCMMBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_COOLIX - void sendCOOLIX(const uint64_t data, const uint16_t nbits = kCoolixBits, - const uint16_t repeat = kCoolixDefaultRepeat); -#endif // SEND_COOLIX -#if SEND_COOLIX48 - void sendCoolix48(const uint64_t data, const uint16_t nbits = kCoolix48Bits, - const uint16_t repeat = kCoolixDefaultRepeat); -#endif // SEND_COOLIX48 -#if SEND_WHYNTER - void sendWhynter(const uint64_t data, const uint16_t nbits = kWhynterBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_MIRAGE - void sendMirage(const unsigned char data[], - const uint16_t nbytes = kMirageStateLength, - const uint16_t repeat = kMirageMinRepeat); -#endif // SEND_MIRAGE -#if SEND_MITSUBISHI - void sendMitsubishi(uint64_t data, uint16_t nbits = kMitsubishiBits, - uint16_t repeat = kMitsubishiMinRepeat); -#endif -#if SEND_MITSUBISHI136 - void sendMitsubishi136(const unsigned char data[], - const uint16_t nbytes = kMitsubishi136StateLength, - const uint16_t repeat = kMitsubishi136MinRepeat); -#endif -#if SEND_MITSUBISHI112 - void sendMitsubishi112(const unsigned char data[], - const uint16_t nbytes = kMitsubishi112StateLength, - const uint16_t repeat = kMitsubishi112MinRepeat); -#endif -#if SEND_MITSUBISHI2 - void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits, - uint16_t repeat = kMitsubishiMinRepeat); -#endif -#if SEND_MITSUBISHI_AC - void sendMitsubishiAC(const unsigned char data[], - const uint16_t nbytes = kMitsubishiACStateLength, - const uint16_t repeat = kMitsubishiACMinRepeat); -#endif -#if SEND_MITSUBISHIHEAVY - void sendMitsubishiHeavy88( - const unsigned char data[], - const uint16_t nbytes = kMitsubishiHeavy88StateLength, - const uint16_t repeat = kMitsubishiHeavy88MinRepeat); - void sendMitsubishiHeavy152( - const unsigned char data[], - const uint16_t nbytes = kMitsubishiHeavy152StateLength, - const uint16_t repeat = kMitsubishiHeavy152MinRepeat); -#endif -#if SEND_FUJITSU_AC - void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat = kFujitsuAcMinRepeat); -#endif -#if SEND_FUJITSU_AC264 - void sendFujitsuAC264(const unsigned char data[], - const uint16_t nbytes = kFujitsuAc264StateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_FUJITSU_AC264 -#if SEND_INAX - void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits, - const uint16_t repeat = kInaxMinRepeat); -#endif // SEND_INAX -#if SEND_GLOBALCACHE - void sendGC(uint16_t buf[], uint16_t len); -#endif -#if SEND_KELVINATOR - void sendKelvinator(const unsigned char data[], - const uint16_t nbytes = kKelvinatorStateLength, - const uint16_t repeat = kKelvinatorDefaultRepeat); -#endif -#if SEND_DAIKIN - void sendDaikin(const unsigned char data[], - const uint16_t nbytes = kDaikinStateLength, - const uint16_t repeat = kDaikinDefaultRepeat); -#endif -#if SEND_DAIKIN64 - void sendDaikin64(const uint64_t data, const uint16_t nbits = kDaikin64Bits, - const uint16_t repeat = kDaikin64DefaultRepeat); -#endif // SEND_DAIKIN64 -#if SEND_DAIKIN128 - void sendDaikin128(const unsigned char data[], - const uint16_t nbytes = kDaikin128StateLength, - const uint16_t repeat = kDaikin128DefaultRepeat); -#endif // SEND_DAIKIN128 -#if SEND_DAIKIN152 - void sendDaikin152(const unsigned char data[], - const uint16_t nbytes = kDaikin152StateLength, - const uint16_t repeat = kDaikin152DefaultRepeat); -#endif // SEND_DAIKIN152 -#if SEND_DAIKIN160 - void sendDaikin160(const unsigned char data[], - const uint16_t nbytes = kDaikin160StateLength, - const uint16_t repeat = kDaikin160DefaultRepeat); -#endif // SEND_DAIKIN160 -#if SEND_DAIKIN176 - void sendDaikin176(const unsigned char data[], - const uint16_t nbytes = kDaikin176StateLength, - const uint16_t repeat = kDaikin176DefaultRepeat); -#endif // SEND_DAIKIN176 -#if SEND_DAIKIN2 - void sendDaikin2(const unsigned char data[], - const uint16_t nbytes = kDaikin2StateLength, - const uint16_t repeat = kDaikin2DefaultRepeat); -#endif -#if SEND_DAIKIN200 - void sendDaikin200(const unsigned char data[], - const uint16_t nbytes = kDaikin200StateLength, - const uint16_t repeat = kDaikin200DefaultRepeat); -#endif // SEND_DAIKIN200 -#if SEND_DAIKIN216 - void sendDaikin216(const unsigned char data[], - const uint16_t nbytes = kDaikin216StateLength, - const uint16_t repeat = kDaikin216DefaultRepeat); -#endif // SEND_DAIKIN216 -#if SEND_DAIKIN312 - void sendDaikin312(const unsigned char data[], - const uint16_t nbytes = kDaikin312StateLength, - const uint16_t repeat = kDaikin312DefaultRepeat); -#endif // SEND_DAIKIN312 -#if SEND_AIWA_RC_T501 - void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, - uint16_t repeat = kAiwaRcT501MinRepeats); -#endif -#if SEND_GREE - void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits, - const uint16_t repeat = kGreeDefaultRepeat); - void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength, - const uint16_t repeat = kGreeDefaultRepeat); -#endif -#if SEND_GOODWEATHER - void sendGoodweather(const uint64_t data, - const uint16_t nbits = kGoodweatherBits, - const uint16_t repeat = kGoodweatherMinRepeat); -#endif // SEND_GOODWEATHER -#if SEND_GORENJE - void sendGorenje(const uint64_t data, const uint16_t nbits = kGorenjeBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_GORENJE -#if SEND_PRONTO - void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); -#endif -#if SEND_ARGO - void sendArgo(const unsigned char data[], - const uint16_t nbytes = kArgoStateLength, - const uint16_t repeat = kArgoDefaultRepeat, - bool sendFooter = false); - void sendArgoWREM3(const unsigned char data[], - const uint16_t nbytes = kArgoStateLength, - const uint16_t repeat = kArgoDefaultRepeat); -#endif // SEND_ARGO -#if SEND_TROTEC - void sendTrotec(const unsigned char data[], - const uint16_t nbytes = kTrotecStateLength, - const uint16_t repeat = kTrotecDefaultRepeat); -#endif // SEND_TROTEC -#if SEND_TROTEC_3550 - void sendTrotec3550(const unsigned char data[], - const uint16_t nbytes = kTrotecStateLength, - const uint16_t repeat = kTrotecDefaultRepeat); -#endif // SEND_TROTEC_3550 -#if SEND_NIKAI - void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_TOSHIBA_AC - void sendToshibaAC(const uint8_t data[], - const uint16_t nbytes = kToshibaACStateLength, - const uint16_t repeat = kToshibaACMinRepeat); -#endif -#if SEND_MIDEA - void sendMidea(uint64_t data, uint16_t nbits = kMideaBits, - uint16_t repeat = kMideaMinRepeat); -#endif // SEND_MIDEA -#if SEND_MIDEA24 - void sendMidea24(const uint64_t data, const uint16_t nbits = kMidea24Bits, - const uint16_t repeat = kMidea24MinRepeat); -#endif // SEND_MIDEA24 -#if SEND_MAGIQUEST - void sendMagiQuest(const uint64_t data, const uint16_t nbits = kMagiquestBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodeMagiQuest(const uint32_t wand_id, const uint16_t magnitude); -#endif -#if SEND_LASERTAG - void sendLasertag(uint64_t data, uint16_t nbits = kLasertagBits, - uint16_t repeat = kLasertagMinRepeat); -#endif -#if SEND_CARRIER_AC - void sendCarrierAC(uint64_t data, uint16_t nbits = kCarrierAcBits, - uint16_t repeat = kCarrierAcMinRepeat); -#endif -#if SEND_CARRIER_AC40 - void sendCarrierAC40(uint64_t data, uint16_t nbits = kCarrierAc40Bits, - uint16_t repeat = kCarrierAc40MinRepeat); -#endif -#if SEND_CARRIER_AC64 - void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits, - uint16_t repeat = kCarrierAc64MinRepeat); -#endif -#if SEND_CARRIER_AC84 - void sendCarrierAC84(const uint8_t data[], - const uint16_t nbytes = kCarrierAc84StateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_CARRIER_AC84 -#if SEND_CARRIER_AC128 - void sendCarrierAC128(const uint8_t data[], - uint16_t nbytes = kCarrierAc128StateLength, - uint16_t repeat = kCarrierAc128MinRepeat); -#endif // SEND_CARRIER_AC128 -#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) - void sendHaierAC(const unsigned char data[], - const uint16_t nbytes = kHaierACStateLength, - const uint16_t repeat = kHaierAcDefaultRepeat); -#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) -#if SEND_HAIER_AC_YRW02 - void sendHaierACYRW02(const unsigned char data[], - const uint16_t nbytes = kHaierACYRW02StateLength, - const uint16_t repeat = kHaierAcYrw02DefaultRepeat); -#endif // SEND_HAIER_AC_YRW02 -#if SEND_HAIER_AC160 - void sendHaierAC160(const unsigned char data[], - const uint16_t nbytes = kHaierAC160StateLength, - const uint16_t repeat = kHaierAc160DefaultRepeat); -#endif // SEND_HAIER_AC160 -#if SEND_HAIER_AC176 - void sendHaierAC176(const unsigned char data[], - const uint16_t nbytes = kHaierAC176StateLength, - const uint16_t repeat = kHaierAc176DefaultRepeat); -#endif // SEND_HAIER_AC176 -#if SEND_HITACHI_AC - void sendHitachiAC(const unsigned char data[], - const uint16_t nbytes = kHitachiAcStateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif -#if SEND_HITACHI_AC1 - void sendHitachiAC1(const unsigned char data[], - const uint16_t nbytes = kHitachiAc1StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif -#if SEND_HITACHI_AC2 - void sendHitachiAC2(const unsigned char data[], - const uint16_t nbytes = kHitachiAc2StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif -#if SEND_HITACHI_AC3 - void sendHitachiAc3(const unsigned char data[], - const uint16_t nbytes, // No default as there as so many - // different sizes - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC3 -#if SEND_HITACHI_AC264 - void sendHitachiAc264(const unsigned char data[], - const uint16_t nbytes = kHitachiAc264StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC264 -#if SEND_HITACHI_AC296 - void sendHitachiAc296(const unsigned char data[], - const uint16_t nbytes = kHitachiAc296StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC296 -#if SEND_HITACHI_AC344 - void sendHitachiAc344(const unsigned char data[], - const uint16_t nbytes = kHitachiAc344StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC344 -#if SEND_HITACHI_AC424 - void sendHitachiAc424(const unsigned char data[], - const uint16_t nbytes = kHitachiAc424StateLength, - const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC424 -#if SEND_GICABLE - void sendGICable(uint64_t data, uint16_t nbits = kGicableBits, - uint16_t repeat = kGicableMinRepeat); -#endif -#if SEND_WHIRLPOOL_AC - void sendWhirlpoolAC(const unsigned char data[], - const uint16_t nbytes = kWhirlpoolAcStateLength, - const uint16_t repeat = kWhirlpoolAcDefaultRepeat); -#endif -#if SEND_LUTRON - void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, - uint16_t repeat = kNoRepeat); -#endif -#if SEND_ELECTRA_AC - void sendElectraAC(const unsigned char data[], - const uint16_t nbytes = kElectraAcStateLength, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_PANASONIC_AC - void sendPanasonicAC(const unsigned char data[], - const uint16_t nbytes = kPanasonicAcStateLength, - const uint16_t repeat = kPanasonicAcDefaultRepeat); -#endif // SEND_PANASONIC_AC -#if SEND_PANASONIC_AC32 - void sendPanasonicAC32(const uint64_t data, - const uint16_t nbits = kPanasonicAc32Bits, - const uint16_t repeat = kPanasonicAcDefaultRepeat); -#endif // SEND_PANASONIC_AC32 -#if SEND_PIONEER - void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodePioneer(uint16_t address, uint16_t command); -#endif -#if SEND_MWM - void sendMWM(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_VESTEL_AC - void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_TCL96AC - void sendTcl96Ac(const unsigned char data[], - const uint16_t nbytes = kTcl96AcStateLength, - const uint16_t repeat = kTcl96AcDefaultRepeat); -#endif // SEND_TCL96AC -#if SEND_TCL112AC - void sendTcl112Ac(const unsigned char data[], - const uint16_t nbytes = kTcl112AcStateLength, - const uint16_t repeat = kTcl112AcDefaultRepeat); -#endif // SEND_TCL112AC -#if SEND_TECO - void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits, - const uint16_t repeat = kNoRepeat); -#endif -#if SEND_LEGOPF - void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, - const uint16_t repeat = kLegoPfMinRepeat); -#endif -#if SEND_NEOCLIMA - void sendNeoclima(const unsigned char data[], - const uint16_t nbytes = kNeoclimaStateLength, - const uint16_t repeat = kNeoclimaMinRepeat); -#endif // SEND_NEOCLIMA -#if SEND_AMCOR - void sendAmcor(const unsigned char data[], - const uint16_t nbytes = kAmcorStateLength, - const uint16_t repeat = kAmcorDefaultRepeat); -#endif // SEND_AMCOR -#if SEND_EPSON - void sendEpson(uint64_t data, uint16_t nbits = kEpsonBits, - uint16_t repeat = kEpsonMinRepeat); -#endif -#if SEND_SYMPHONY - void sendSymphony(uint64_t data, uint16_t nbits = kSymphonyBits, - uint16_t repeat = kSymphonyDefaultRepeat); -#endif -#if SEND_AIRWELL - void sendAirwell(uint64_t data, uint16_t nbits = kAirwellBits, - uint16_t repeat = kAirwellMinRepeats); -#endif -#if SEND_DELONGHI_AC - void sendDelonghiAc(uint64_t data, uint16_t nbits = kDelonghiAcBits, - uint16_t repeat = kDelonghiAcDefaultRepeat); -#endif -#if SEND_DOSHISHA - void sendDoshisha(const uint64_t data, uint16_t nbits = kDoshishaBits, - const uint16_t repeat = kNoRepeat); - uint64_t encodeDoshisha(const uint8_t command, const uint8_t channel = 0); -#endif // SEND_DOSHISHA -#if SEND_MULTIBRACKETS - void sendMultibrackets(const uint64_t data, - const uint16_t nbits = kMultibracketsBits, - const uint16_t repeat = kMultibracketsDefaultRepeat); -#endif -#if SEND_TECHNIBEL_AC - void sendTechnibelAc(uint64_t data, uint16_t nbits = kTechnibelAcBits, - uint16_t repeat = kTechnibelAcDefaultRepeat); -#endif -#if SEND_CORONA_AC - void sendCoronaAc(const uint8_t data[], - const uint16_t nbytes = kCoronaAcStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_CORONA_AC -#if SEND_ZEPEAL - void sendZepeal(const uint64_t data, - const uint16_t nbits = kZepealBits, - const uint16_t repeat = kZepealMinRepeat); -#endif // SEND_ZEPEAL -#if SEND_VOLTAS - void sendVoltas(const unsigned char data[], - const uint16_t nbytes = kVoltasStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_VOLTAS -#if SEND_METZ - void sendMetz(const uint64_t data, - const uint16_t nbits = kMetzBits, - const uint16_t repeat = kMetzMinRepeat); - static uint32_t encodeMetz(const uint8_t address, const uint8_t command, - const bool toggle = false); -#endif // SEND_METZ -#if SEND_TRANSCOLD - void sendTranscold(const uint64_t data, const uint16_t nbits = kTranscoldBits, - const uint16_t repeat = kTranscoldDefaultRepeat); -#endif // SEND_TRANSCOLD -#if SEND_ELITESCREENS - void sendElitescreens(const uint64_t data, - const uint16_t nbits = kEliteScreensBits, - const uint16_t repeat = kEliteScreensDefaultRepeat); -#endif // SEND_ELITESCREENS -#if SEND_MILESTAG2 - // Since There 2 types of transmissions - // (14bits for Shooting by default, you can set 24 bit for msg delivery) - void sendMilestag2(const uint64_t data, - const uint16_t nbits = kMilesTag2ShotBits, - const uint16_t repeat = kMilesMinRepeat); -#endif // SEND_MILESTAG2 -#if SEND_ECOCLIM - void sendEcoclim(const uint64_t data, const uint16_t nbits = kEcoclimBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_ECOCLIM -#if SEND_XMP - void sendXmp(const uint64_t data, const uint16_t nbits = kXmpBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_XMP -#if SEND_TRUMA - void sendTruma(const uint64_t data, const uint16_t nbits = kTrumaBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_TRUMA -#if SEND_TEKNOPOINT - void sendTeknopoint(const unsigned char data[], - const uint16_t nbytes = kTeknopointStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_TEKNOPOINT -#if SEND_KELON - void sendKelon(const uint64_t data, const uint16_t nbits = kKelonBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_KELON -#if SEND_KELON168 - void sendKelon168(const unsigned char data[], - const uint16_t nbytes = kKelon168StateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_KELON168 -#if SEND_BOSE - void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_BOSE -#if SEND_ARRIS - void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits, - const uint16_t repeat = kNoRepeat); - static uint32_t toggleArrisRelease(const uint32_t data); - static uint32_t encodeArris(const uint32_t command, const bool release); -#endif // SEND_ARRIS -#if SEND_RHOSS - void sendRhoss(const unsigned char data[], - const uint16_t nbytes = kRhossStateLength, - const uint16_t repeat = kRhossDefaultRepeat); -#endif // SEND_RHOSS -#if SEND_AIRTON - void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits, - const uint16_t repeat = kAirtonDefaultRepeat); -#endif // SEND_AIRTON -#if SEND_TOTO - void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits, - const uint16_t repeat = kTotoDefaultRepeat); -#endif // SEND_TOTO -#if SEND_CLIMABUTLER - void sendClimaButler(const uint64_t data, - const uint16_t nbits = kClimaButlerBits, - const uint16_t repeat = kNoRepeat); -#endif // SEND_CLIMABUTLER -#if SEND_BOSCH144 - void sendBosch144(const unsigned char data[], - const uint16_t nbytes = kBosch144StateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_BOSCH144 -#if SEND_WOWWEE - void sendWowwee(const uint64_t data, const uint16_t nbits = kWowweeBits, - const uint16_t repeat = kWowweeDefaultRepeat); -#endif // SEND_WOWWEE -#if SEND_YORK - void sendYork(const unsigned char data[], - const uint16_t nbytes = kYorkStateLength, - const uint16_t repeat = kNoRepeat); -#endif // SEND_YORK - - protected: -#ifdef UNIT_TEST -#ifndef HIGH -#define HIGH 0x1 -#endif -#ifndef LOW -#define LOW 0x0 -#endif -#endif // UNIT_TEST - uint8_t outputOn; - uint8_t outputOff; - VIRTUAL void ledOff(); - VIRTUAL void ledOn(); -#ifndef UNIT_TEST - - private: -#else - uint32_t _freq_unittest; -#endif // UNIT_TEST - uint16_t onTimePeriod; - uint16_t offTimePeriod; - uint16_t IRpin; - int8_t periodOffset; - uint8_t _dutycycle; - bool modulation; - uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true); -#if SEND_SONY - void _sendSony(const uint64_t data, const uint16_t nbits, - const uint16_t repeat, const uint16_t freq); -#endif // SEND_SONY -}; - -#endif // IRSEND_H_ +// Copyright 2009 Ken Shirriff +// Copyright 2015 Mark Szabo +// Copyright 2017 David Conran +#ifndef IRSEND_H_ +#define IRSEND_H_ + +#define __STDC_LIMIT_MACROS +#include +#include "IRremoteESP8266.h" + +// Originally from https://github.com/shirriff/Arduino-IRremote/ +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for +// sending IR code on ESP8266 + +#if TEST || UNIT_TEST +#define VIRTUAL virtual +#else +#define VIRTUAL +#endif + +// Constants +// Offset (in microseconds) to use in Period time calculations to account for +// code excution time in producing the software PWM signal. +#if defined(ESP32) +// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz +const int8_t kPeriodOffset = -2; +#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens) +// Calculated on an ESP8266 NodeMCU v2 board using: +// v2.6.0 with v2.5.2 ESP core @ 160MHz +const int8_t kPeriodOffset = -2; +#else // (defined(ESP8266) && F_CPU == 160000000L) +// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz +const int8_t kPeriodOffset = -5; +#endif // (defined(ESP8266) && F_CPU == 160000000L) +const uint8_t kDutyDefault = 50; // Percentage +const uint8_t kDutyMax = 100; // Percentage +// delayMicroseconds() is only accurate to 16383us. +// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds +const uint16_t kMaxAccurateUsecDelay = 16383; +// Usecs to wait between messages we don't know the proper gap time. +const uint32_t kDefaultMessageGap = 100000; +/// Placeholder for missing sensor temp value +/// @note Not using "-1" as it may be a valid external temp +const float kNoTempValue = -100.0; + +/// Enumerators and Structures for the Common A/C API. +namespace stdAc { +/// Common A/C settings for A/C operating modes. +enum class opmode_t { + kOff = -1, + kAuto = 0, + kCool = 1, + kHeat = 2, + kDry = 3, + kFan = 4, + // Add new entries before this one, and update it to point to the last entry + kLastOpmodeEnum = kFan, +}; + +/// Common A/C settings for Fan Speeds. +enum class fanspeed_t { + kAuto = 0, + kMin = 1, + kLow = 2, + kMedium = 3, + kHigh = 4, + kMax = 5, + kMediumHigh = 6, + // Add new entries before this one, and update it to point to the last entry + kLastFanspeedEnum = kMediumHigh, +}; + +/// Common A/C settings for Vertical Swing. +enum class swingv_t { + kOff = -1, + kAuto = 0, + kHighest = 1, + kHigh = 2, + kMiddle = 3, + kLow = 4, + kLowest = 5, + kUpperMiddle = 6, + // Add new entries before this one, and update it to point to the last entry + kLastSwingvEnum = kUpperMiddle, +}; + +/// @brief Tyoe of A/C command (if the remote uses different codes for each) +/// @note Most remotes support only a single command or aggregate multiple +/// into one (e.g. control+timer). Use @c kControlCommand in such case +enum class ac_command_t { + kControlCommand = 0, + kSensorTempReport = 1, + kTimerCommand = 2, + kConfigCommand = 3, + // Add new entries before this one, and update it to point to the last entry + kLastAcCommandEnum = kConfigCommand, +}; + +/// Common A/C settings for Horizontal Swing. +enum class swingh_t { + kOff = -1, + kAuto = 0, // a.k.a. On. + kLeftMax = 1, + kLeft = 2, + kMiddle = 3, + kRight = 4, + kRightMax = 5, + kWide = 6, // a.k.a. left & right at the same time. + // Add new entries before this one, and update it to point to the last entry + kLastSwinghEnum = kWide, +}; + +/// Structure to hold a common A/C state. +struct state_t { + decode_type_t protocol = decode_type_t::UNKNOWN; + int16_t model = -1; // `-1` means unused. + bool power = false; + stdAc::opmode_t mode = stdAc::opmode_t::kOff; + float degrees = 25; + bool celsius = true; + stdAc::fanspeed_t fanspeed = stdAc::fanspeed_t::kAuto; + stdAc::swingv_t swingv = stdAc::swingv_t::kOff; + stdAc::swingh_t swingh = stdAc::swingh_t::kOff; + bool quiet = false; + bool turbo = false; + bool econo = false; + bool light = false; + bool filter = false; + bool clean = false; + bool beep = false; + int16_t sleep = -1; // `-1` means off. + int16_t clock = -1; // `-1` means not set. + stdAc::ac_command_t command = stdAc::ac_command_t::kControlCommand; + bool iFeel = false; + float sensorTemperature = kNoTempValue; // `kNoTempValue` means not set. +}; +}; // namespace stdAc + +/// Fujitsu A/C model numbers +enum fujitsu_ac_remote_model_t { + ARRAH2E = 1, ///< (1) AR-RAH2E, AR-RAC1E, AR-RAE1E, AR-RCE1E, AR-RAH2U, + ///< AR-REG1U (Default) + ///< Warning: Use on incorrect models can cause the A/C to lock + ///< up, requring the A/C to be physically powered off to fix. + ///< e.g. AR-RAH1U may lock up with a Swing command. + ARDB1, ///< (2) AR-DB1, AR-DL10 (AR-DL10 swing doesn't work) + ARREB1E, ///< (3) AR-REB1E, AR-RAH1U (Similar to ARRAH2E but no horiz + ///< control) + ARJW2, ///< (4) AR-JW2 (Same as ARDB1 but with horiz control) + ARRY4, ///< (5) AR-RY4 (Same as AR-RAH2E but with clean & filter) + ARREW4E, ///< (6) Similar to ARRAH2E, but with different temp config. +}; + +/// Gree A/C model numbers +enum gree_ac_remote_model_t { + YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default) + YBOFB, // (2) Green, YBOFB2, YAPOF3 + YX1FSF, // (3) Soleus Air window unit (Similar to YAW1F, but with an + // Operation mode of Energy Saver (Econo)) +}; + +/// HAIER_AC176 A/C model numbers +enum haier_ac176_remote_model_t { + V9014557_A = 1, // (1) V9014557 Remote in "A" setting. (Default) + V9014557_B, // (2) V9014557 Remote in "B" setting. +}; + +/// HITACHI_AC1 A/C model numbers +enum hitachi_ac1_remote_model_t { + R_LT0541_HTA_A = 1, // (1) R-LT0541-HTA Remote in "A" setting. (Default) + R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting. +}; + +/// MIRAGE A/C model numbers +enum mirage_ac_remote_model_t { + KKG9AC1 = 1, // (1) KKG9A-C1 Remote. (Default) + KKG29AC1, // (2) KKG29A-C1 Remote. +}; + +/// Panasonic A/C model numbers +enum panasonic_ac_remote_model_t { + kPanasonicUnknown = 0, + kPanasonicLke = 1, + kPanasonicNke = 2, + kPanasonicDke = 3, // PKR too. + kPanasonicJke = 4, + kPanasonicCkp = 5, + kPanasonicRkr = 6, +}; + +/// Sharp A/C model numbers +enum sharp_ac_remote_model_t { + A907 = 1, + A705 = 2, + A903 = 3, // 820 too +}; + +/// TCL (& Teknopoint) A/C model numbers +enum tcl_ac_remote_model_t { + TAC09CHSD = 1, + GZ055BE1 = 2, // Also Teknopoint GZ01-BEJ0-000 +}; + +/// Voltas A/C model numbers +enum voltas_ac_remote_model_t { + kVoltasUnknown = 0, // Full Function + kVoltas122LZF = 1, // (1) 122LZF (No SwingH support) (Default) +}; + +/// Whirlpool A/C model numbers +enum whirlpool_ac_remote_model_t { + DG11J13A = 1, // DG11J1-04 too + DG11J191, +}; + +/// LG A/C model numbers +enum lg_ac_remote_model_t { + GE6711AR2853M = 1, // (1) LG 28-bit Protocol (default) + AKB75215403, // (2) LG2 28-bit Protocol + AKB74955603, // (3) LG2 28-bit Protocol variant + AKB73757604, // (4) LG2 Variant of AKB74955603 + LG6711A20083V, // (5) Same as GE6711AR2853M, but only SwingV toggle. +}; + +/// Argo A/C model numbers +enum argo_ac_remote_model_t { + SAC_WREM2 = 1, // (1) ARGO WREM2 remote (default) + SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd +}; + +// Classes + +/// Class for sending all basic IR protocols. +/// @note Originally from https://github.com/shirriff/Arduino-IRremote/ +/// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for +/// sending IR code on ESP8266 +class IRsend { + public: + explicit IRsend(uint16_t IRsendPin, bool inverted = false, + bool use_modulation = true); + void begin(); + void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault); + VIRTUAL void _delayMicroseconds(uint32_t usec); + VIRTUAL uint16_t mark(uint16_t usec); + VIRTUAL void space(uint32_t usec); + int8_t calibrate(uint16_t hz = 38000U); + void sendRaw(const uint16_t buf[], const uint16_t len, const uint16_t hz); + void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark, + uint32_t zerospace, uint64_t data, uint16_t nbits, + bool MSBfirst = true); + void sendManchesterData(const uint16_t half_period, const uint64_t data, + const uint16_t nbits, const bool MSBfirst = true, + const bool GEThomas = true); + void sendManchester(const uint16_t headermark, const uint32_t headerspace, + const uint16_t half_period, const uint16_t footermark, + const uint32_t gap, const uint64_t data, + const uint16_t nbits, const uint16_t frequency = 38, + const bool MSBfirst = true, + const uint16_t repeat = kNoRepeat, + const uint8_t dutycycle = kDutyDefault, + const bool GEThomas = true); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint64_t data, const uint16_t nbits, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint32_t mesgtime, const uint64_t data, + const uint16_t nbits, const uint16_t frequency, + const bool MSBfirst, const uint16_t repeat, + const uint8_t dutycycle); + void sendGeneric(const uint16_t headermark, const uint32_t headerspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t gap, + const uint8_t *dataptr, const uint16_t nbytes, + const uint16_t frequency, const bool MSBfirst, + const uint16_t repeat, const uint8_t dutycycle); + static uint16_t minRepeats(const decode_type_t protocol); + static uint16_t defaultBits(const decode_type_t protocol); + bool send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat = kNoRepeat); + bool send(const decode_type_t type, const uint8_t *state, + const uint16_t nbytes); +#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ + SEND_MIDEA24) + void sendNEC(uint64_t data, uint16_t nbits = kNECBits, + uint16_t repeat = kNoRepeat); + uint32_t encodeNEC(uint16_t address, uint16_t command); +#endif +#if SEND_SONY + // sendSony() should typically be called with repeat=2 as Sony devices + // expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes) + // Legacy use of this procedure was to only send a single code so call it with + // repeat=0 for backward compatibility. As of v2.0 it defaults to sending + // a Sony command that will be accepted be a device. + void sendSony(const uint64_t data, const uint16_t nbits = kSony20Bits, + const uint16_t repeat = kSonyMinRepeat); + void sendSony38(const uint64_t data, const uint16_t nbits = kSony20Bits, + const uint16_t repeat = kSonyMinRepeat + 1); + uint32_t encodeSony(const uint16_t nbits, const uint16_t command, + const uint16_t address, const uint16_t extended = 0); +#endif // SEND_SONY +#if SEND_SHERWOOD + void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits, + uint16_t repeat = kSherwoodMinRepeat); +#endif + // `sendSAMSUNG()` is required by `sendLG()` +#if (SEND_SAMSUNG || SEND_LG) + void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits, + const uint16_t repeat = kNoRepeat); + uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command); +#endif // (SEND_SAMSUNG || SEND_LG) +#if SEND_SAMSUNG36 + void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_SAMSUNG_AC + void sendSamsungAC(const unsigned char data[], + const uint16_t nbytes = kSamsungAcStateLength, + const uint16_t repeat = kSamsungAcDefaultRepeat); +#endif +#if SEND_LG + void sendLG(uint64_t data, uint16_t nbits = kLgBits, + uint16_t repeat = kNoRepeat); + void sendLG2(uint64_t data, uint16_t nbits = kLgBits, + uint16_t repeat = kNoRepeat); + uint32_t encodeLG(uint16_t address, uint16_t command); +#endif +#if (SEND_SHARP || SEND_DENON) + uint32_t encodeSharp(const uint16_t address, const uint16_t command, + const uint16_t expansion = 1, const uint16_t check = 0, + const bool MSBfirst = false); + void sendSharp(const uint16_t address, const uint16_t command, + const uint16_t nbits = kSharpBits, + const uint16_t repeat = kNoRepeat); + void sendSharpRaw(const uint64_t data, const uint16_t nbits = kSharpBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_SHARP_AC + void sendSharpAc(const unsigned char data[], + const uint16_t nbytes = kSharpAcStateLength, + const uint16_t repeat = kSharpAcDefaultRepeat); +#endif // SEND_SHARP_AC +#if SEND_JVC + void sendJVC(uint64_t data, uint16_t nbits = kJvcBits, + uint16_t repeat = kNoRepeat); + uint16_t encodeJVC(uint8_t address, uint8_t command); +#endif +#if SEND_DENON + void sendDenon(uint64_t data, uint16_t nbits = kDenonBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_SANYO + uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command); + void sendSanyoLC7461(const uint64_t data, + const uint16_t nbits = kSanyoLC7461Bits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_SANYO_AC + void sendSanyoAc(const uint8_t *data, + const uint16_t nbytes = kSanyoAcStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_SANYO_AC +#if SEND_SANYO_AC88 + void sendSanyoAc88(const uint8_t *data, + const uint16_t nbytes = kSanyoAc88StateLength, + const uint16_t repeat = kSanyoAc88MinRepeat); +#endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + void sendSanyoAc152(const uint8_t *data, + const uint16_t nbytes = kSanyoAc152StateLength, + const uint16_t repeat = kSanyoAc152MinRepeat); +#endif // SEND_SANYO_AC152 +#if SEND_DISH + // sendDISH() should typically be called with repeat=3 as DISH devices + // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) + // Legacy use of this procedure was only to send a single code + // so use repeat=0 for backward compatibility. + void sendDISH(uint64_t data, uint16_t nbits = kDishBits, + uint16_t repeat = kDishMinRepeat); +#endif +#if (SEND_PANASONIC || SEND_DENON) + void sendPanasonic64(const uint64_t data, + const uint16_t nbits = kPanasonicBits, + const uint16_t repeat = kNoRepeat); + void sendPanasonic(const uint16_t address, const uint32_t data, + const uint16_t nbits = kPanasonicBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodePanasonic(const uint16_t manufacturer, const uint8_t device, + const uint8_t subdevice, const uint8_t function); +#endif +#if SEND_RC5 + void sendRC5(const uint64_t data, uint16_t nbits = kRC5XBits, + const uint16_t repeat = kNoRepeat); + uint16_t encodeRC5(const uint8_t address, const uint8_t command, + const bool key_released = false); + uint16_t encodeRC5X(const uint8_t address, const uint8_t command, + const bool key_released = false); + uint64_t toggleRC5(const uint64_t data); +#endif +#if SEND_RC6 + void sendRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits, + const uint16_t repeat = kNoRepeat); + uint64_t encodeRC6(const uint32_t address, const uint8_t command, + const uint16_t mode = kRC6Mode0Bits); + uint64_t toggleRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits); +#endif +#if SEND_RCMM + void sendRCMM(uint64_t data, uint16_t nbits = kRCMMBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_COOLIX + void sendCOOLIX(const uint64_t data, const uint16_t nbits = kCoolixBits, + const uint16_t repeat = kCoolixDefaultRepeat); +#endif // SEND_COOLIX +#if SEND_COOLIX48 + void sendCoolix48(const uint64_t data, const uint16_t nbits = kCoolix48Bits, + const uint16_t repeat = kCoolixDefaultRepeat); +#endif // SEND_COOLIX48 +#if SEND_WHYNTER + void sendWhynter(const uint64_t data, const uint16_t nbits = kWhynterBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_MIRAGE + void sendMirage(const unsigned char data[], + const uint16_t nbytes = kMirageStateLength, + const uint16_t repeat = kMirageMinRepeat); +#endif // SEND_MIRAGE +#if SEND_MITSUBISHI + void sendMitsubishi(uint64_t data, uint16_t nbits = kMitsubishiBits, + uint16_t repeat = kMitsubishiMinRepeat); +#endif +#if SEND_MITSUBISHI136 + void sendMitsubishi136(const unsigned char data[], + const uint16_t nbytes = kMitsubishi136StateLength, + const uint16_t repeat = kMitsubishi136MinRepeat); +#endif +#if SEND_MITSUBISHI112 + void sendMitsubishi112(const unsigned char data[], + const uint16_t nbytes = kMitsubishi112StateLength, + const uint16_t repeat = kMitsubishi112MinRepeat); +#endif +#if SEND_MITSUBISHI2 + void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits, + uint16_t repeat = kMitsubishiMinRepeat); +#endif +#if SEND_MITSUBISHI_AC + void sendMitsubishiAC(const unsigned char data[], + const uint16_t nbytes = kMitsubishiACStateLength, + const uint16_t repeat = kMitsubishiACMinRepeat); +#endif +#if SEND_MITSUBISHIHEAVY + void sendMitsubishiHeavy88( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy88StateLength, + const uint16_t repeat = kMitsubishiHeavy88MinRepeat); + void sendMitsubishiHeavy152( + const unsigned char data[], + const uint16_t nbytes = kMitsubishiHeavy152StateLength, + const uint16_t repeat = kMitsubishiHeavy152MinRepeat); +#endif +#if SEND_FUJITSU_AC + void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kFujitsuAcMinRepeat); +#endif +#if SEND_FUJITSU_AC264 + void sendFujitsuAC264(const unsigned char data[], + const uint16_t nbytes = kFujitsuAc264StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_FUJITSU_AC264 +#if SEND_INAX + void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits, + const uint16_t repeat = kInaxMinRepeat); +#endif // SEND_INAX +#if SEND_GLOBALCACHE + void sendGC(uint16_t buf[], uint16_t len); +#endif +#if SEND_KELVINATOR + void sendKelvinator(const unsigned char data[], + const uint16_t nbytes = kKelvinatorStateLength, + const uint16_t repeat = kKelvinatorDefaultRepeat); +#endif +#if SEND_DAIKIN + void sendDaikin(const unsigned char data[], + const uint16_t nbytes = kDaikinStateLength, + const uint16_t repeat = kDaikinDefaultRepeat); +#endif +#if SEND_DAIKIN64 + void sendDaikin64(const uint64_t data, const uint16_t nbits = kDaikin64Bits, + const uint16_t repeat = kDaikin64DefaultRepeat); +#endif // SEND_DAIKIN64 +#if SEND_DAIKIN128 + void sendDaikin128(const unsigned char data[], + const uint16_t nbytes = kDaikin128StateLength, + const uint16_t repeat = kDaikin128DefaultRepeat); +#endif // SEND_DAIKIN128 +#if SEND_DAIKIN152 + void sendDaikin152(const unsigned char data[], + const uint16_t nbytes = kDaikin152StateLength, + const uint16_t repeat = kDaikin152DefaultRepeat); +#endif // SEND_DAIKIN152 +#if SEND_DAIKIN160 + void sendDaikin160(const unsigned char data[], + const uint16_t nbytes = kDaikin160StateLength, + const uint16_t repeat = kDaikin160DefaultRepeat); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + void sendDaikin176(const unsigned char data[], + const uint16_t nbytes = kDaikin176StateLength, + const uint16_t repeat = kDaikin176DefaultRepeat); +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + void sendDaikin2(const unsigned char data[], + const uint16_t nbytes = kDaikin2StateLength, + const uint16_t repeat = kDaikin2DefaultRepeat); +#endif +#if SEND_DAIKIN200 + void sendDaikin200(const unsigned char data[], + const uint16_t nbytes = kDaikin200StateLength, + const uint16_t repeat = kDaikin200DefaultRepeat); +#endif // SEND_DAIKIN200 +#if SEND_DAIKIN216 + void sendDaikin216(const unsigned char data[], + const uint16_t nbytes = kDaikin216StateLength, + const uint16_t repeat = kDaikin216DefaultRepeat); +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN312 + void sendDaikin312(const unsigned char data[], + const uint16_t nbytes = kDaikin312StateLength, + const uint16_t repeat = kDaikin312DefaultRepeat); +#endif // SEND_DAIKIN312 +#if SEND_AIWA_RC_T501 + void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, + uint16_t repeat = kAiwaRcT501MinRepeats); +#endif +#if SEND_GREE + void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits, + const uint16_t repeat = kGreeDefaultRepeat); + void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength, + const uint16_t repeat = kGreeDefaultRepeat); +#endif +#if SEND_GOODWEATHER + void sendGoodweather(const uint64_t data, + const uint16_t nbits = kGoodweatherBits, + const uint16_t repeat = kGoodweatherMinRepeat); +#endif // SEND_GOODWEATHER +#if SEND_GORENJE + void sendGorenje(const uint64_t data, const uint16_t nbits = kGorenjeBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_GORENJE +#if SEND_PRONTO + void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); +#endif +#if SEND_ARGO + void sendArgo(const unsigned char data[], + const uint16_t nbytes = kArgoStateLength, + const uint16_t repeat = kArgoDefaultRepeat, + bool sendFooter = false); + void sendArgoWREM3(const unsigned char data[], + const uint16_t nbytes = kArgoStateLength, + const uint16_t repeat = kArgoDefaultRepeat); +#endif // SEND_ARGO +#if SEND_TROTEC + void sendTrotec(const unsigned char data[], + const uint16_t nbytes = kTrotecStateLength, + const uint16_t repeat = kTrotecDefaultRepeat); +#endif // SEND_TROTEC +#if SEND_TROTEC_3550 + void sendTrotec3550(const unsigned char data[], + const uint16_t nbytes = kTrotecStateLength, + const uint16_t repeat = kTrotecDefaultRepeat); +#endif // SEND_TROTEC_3550 +#if SEND_NIKAI + void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_TOSHIBA_AC + void sendToshibaAC(const uint8_t data[], + const uint16_t nbytes = kToshibaACStateLength, + const uint16_t repeat = kToshibaACMinRepeat); +#endif +#if SEND_MIDEA + void sendMidea(uint64_t data, uint16_t nbits = kMideaBits, + uint16_t repeat = kMideaMinRepeat); +#endif // SEND_MIDEA +#if SEND_MIDEA24 + void sendMidea24(const uint64_t data, const uint16_t nbits = kMidea24Bits, + const uint16_t repeat = kMidea24MinRepeat); +#endif // SEND_MIDEA24 +#if SEND_MAGIQUEST + void sendMagiQuest(const uint64_t data, const uint16_t nbits = kMagiquestBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodeMagiQuest(const uint32_t wand_id, const uint16_t magnitude); +#endif +#if SEND_LASERTAG + void sendLasertag(uint64_t data, uint16_t nbits = kLasertagBits, + uint16_t repeat = kLasertagMinRepeat); +#endif +#if SEND_CARRIER_AC + void sendCarrierAC(uint64_t data, uint16_t nbits = kCarrierAcBits, + uint16_t repeat = kCarrierAcMinRepeat); +#endif +#if SEND_CARRIER_AC40 + void sendCarrierAC40(uint64_t data, uint16_t nbits = kCarrierAc40Bits, + uint16_t repeat = kCarrierAc40MinRepeat); +#endif +#if SEND_CARRIER_AC64 + void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits, + uint16_t repeat = kCarrierAc64MinRepeat); +#endif +#if SEND_CARRIER_AC84 + void sendCarrierAC84(const uint8_t data[], + const uint16_t nbytes = kCarrierAc84StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CARRIER_AC84 +#if SEND_CARRIER_AC128 + void sendCarrierAC128(const uint8_t data[], + uint16_t nbytes = kCarrierAc128StateLength, + uint16_t repeat = kCarrierAc128MinRepeat); +#endif // SEND_CARRIER_AC128 +#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) + void sendHaierAC(const unsigned char data[], + const uint16_t nbytes = kHaierACStateLength, + const uint16_t repeat = kHaierAcDefaultRepeat); +#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) +#if SEND_HAIER_AC_YRW02 + void sendHaierACYRW02(const unsigned char data[], + const uint16_t nbytes = kHaierACYRW02StateLength, + const uint16_t repeat = kHaierAcYrw02DefaultRepeat); +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HAIER_AC160 + void sendHaierAC160(const unsigned char data[], + const uint16_t nbytes = kHaierAC160StateLength, + const uint16_t repeat = kHaierAc160DefaultRepeat); +#endif // SEND_HAIER_AC160 +#if SEND_HAIER_AC176 + void sendHaierAC176(const unsigned char data[], + const uint16_t nbytes = kHaierAC176StateLength, + const uint16_t repeat = kHaierAc176DefaultRepeat); +#endif // SEND_HAIER_AC176 +#if SEND_HITACHI_AC + void sendHitachiAC(const unsigned char data[], + const uint16_t nbytes = kHitachiAcStateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif +#if SEND_HITACHI_AC1 + void sendHitachiAC1(const unsigned char data[], + const uint16_t nbytes = kHitachiAc1StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif +#if SEND_HITACHI_AC2 + void sendHitachiAC2(const unsigned char data[], + const uint16_t nbytes = kHitachiAc2StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif +#if SEND_HITACHI_AC3 + void sendHitachiAc3(const unsigned char data[], + const uint16_t nbytes, // No default as there as so many + // different sizes + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC3 +#if SEND_HITACHI_AC264 + void sendHitachiAc264(const unsigned char data[], + const uint16_t nbytes = kHitachiAc264StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void sendHitachiAc296(const unsigned char data[], + const uint16_t nbytes = kHitachiAc296StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC296 +#if SEND_HITACHI_AC344 + void sendHitachiAc344(const unsigned char data[], + const uint16_t nbytes = kHitachiAc344StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC344 +#if SEND_HITACHI_AC424 + void sendHitachiAc424(const unsigned char data[], + const uint16_t nbytes = kHitachiAc424StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC424 +#if SEND_GICABLE + void sendGICable(uint64_t data, uint16_t nbits = kGicableBits, + uint16_t repeat = kGicableMinRepeat); +#endif +#if SEND_WHIRLPOOL_AC + void sendWhirlpoolAC(const unsigned char data[], + const uint16_t nbytes = kWhirlpoolAcStateLength, + const uint16_t repeat = kWhirlpoolAcDefaultRepeat); +#endif +#if SEND_LUTRON + void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, + uint16_t repeat = kNoRepeat); +#endif +#if SEND_ELECTRA_AC + void sendElectraAC(const unsigned char data[], + const uint16_t nbytes = kElectraAcStateLength, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_PANASONIC_AC + void sendPanasonicAC(const unsigned char data[], + const uint16_t nbytes = kPanasonicAcStateLength, + const uint16_t repeat = kPanasonicAcDefaultRepeat); +#endif // SEND_PANASONIC_AC +#if SEND_PANASONIC_AC32 + void sendPanasonicAC32(const uint64_t data, + const uint16_t nbits = kPanasonicAc32Bits, + const uint16_t repeat = kPanasonicAcDefaultRepeat); +#endif // SEND_PANASONIC_AC32 +#if SEND_PIONEER + void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodePioneer(uint16_t address, uint16_t command); +#endif +#if SEND_MWM + void sendMWM(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_VESTEL_AC + void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_TCL96AC + void sendTcl96Ac(const unsigned char data[], + const uint16_t nbytes = kTcl96AcStateLength, + const uint16_t repeat = kTcl96AcDefaultRepeat); +#endif // SEND_TCL96AC +#if SEND_TCL112AC + void sendTcl112Ac(const unsigned char data[], + const uint16_t nbytes = kTcl112AcStateLength, + const uint16_t repeat = kTcl112AcDefaultRepeat); +#endif // SEND_TCL112AC +#if SEND_TECO + void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits, + const uint16_t repeat = kNoRepeat); +#endif +#if SEND_LEGOPF + void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, + const uint16_t repeat = kLegoPfMinRepeat); +#endif +#if SEND_NEOCLIMA + void sendNeoclima(const unsigned char data[], + const uint16_t nbytes = kNeoclimaStateLength, + const uint16_t repeat = kNeoclimaMinRepeat); +#endif // SEND_NEOCLIMA +#if SEND_AMCOR + void sendAmcor(const unsigned char data[], + const uint16_t nbytes = kAmcorStateLength, + const uint16_t repeat = kAmcorDefaultRepeat); +#endif // SEND_AMCOR +#if SEND_EPSON + void sendEpson(uint64_t data, uint16_t nbits = kEpsonBits, + uint16_t repeat = kEpsonMinRepeat); +#endif +#if SEND_SYMPHONY + void sendSymphony(uint64_t data, uint16_t nbits = kSymphonyBits, + uint16_t repeat = kSymphonyDefaultRepeat); +#endif +#if SEND_AIRWELL + void sendAirwell(uint64_t data, uint16_t nbits = kAirwellBits, + uint16_t repeat = kAirwellMinRepeats); +#endif +#if SEND_DELONGHI_AC + void sendDelonghiAc(uint64_t data, uint16_t nbits = kDelonghiAcBits, + uint16_t repeat = kDelonghiAcDefaultRepeat); +#endif +#if SEND_DOSHISHA + void sendDoshisha(const uint64_t data, uint16_t nbits = kDoshishaBits, + const uint16_t repeat = kNoRepeat); + uint64_t encodeDoshisha(const uint8_t command, const uint8_t channel = 0); +#endif // SEND_DOSHISHA +#if SEND_MULTIBRACKETS + void sendMultibrackets(const uint64_t data, + const uint16_t nbits = kMultibracketsBits, + const uint16_t repeat = kMultibracketsDefaultRepeat); +#endif +#if SEND_TECHNIBEL_AC + void sendTechnibelAc(uint64_t data, uint16_t nbits = kTechnibelAcBits, + uint16_t repeat = kTechnibelAcDefaultRepeat); +#endif +#if SEND_CORONA_AC + void sendCoronaAc(const uint8_t data[], + const uint16_t nbytes = kCoronaAcStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CORONA_AC +#if SEND_ZEPEAL + void sendZepeal(const uint64_t data, + const uint16_t nbits = kZepealBits, + const uint16_t repeat = kZepealMinRepeat); +#endif // SEND_ZEPEAL +#if SEND_VOLTAS + void sendVoltas(const unsigned char data[], + const uint16_t nbytes = kVoltasStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_VOLTAS +#if SEND_METZ + void sendMetz(const uint64_t data, + const uint16_t nbits = kMetzBits, + const uint16_t repeat = kMetzMinRepeat); + static uint32_t encodeMetz(const uint8_t address, const uint8_t command, + const bool toggle = false); +#endif // SEND_METZ +#if SEND_TRANSCOLD + void sendTranscold(const uint64_t data, const uint16_t nbits = kTranscoldBits, + const uint16_t repeat = kTranscoldDefaultRepeat); +#endif // SEND_TRANSCOLD +#if SEND_ELITESCREENS + void sendElitescreens(const uint64_t data, + const uint16_t nbits = kEliteScreensBits, + const uint16_t repeat = kEliteScreensDefaultRepeat); +#endif // SEND_ELITESCREENS +#if SEND_MILESTAG2 + // Since There 2 types of transmissions + // (14bits for Shooting by default, you can set 24 bit for msg delivery) + void sendMilestag2(const uint64_t data, + const uint16_t nbits = kMilesTag2ShotBits, + const uint16_t repeat = kMilesMinRepeat); +#endif // SEND_MILESTAG2 +#if SEND_ECOCLIM + void sendEcoclim(const uint64_t data, const uint16_t nbits = kEcoclimBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_ECOCLIM +#if SEND_XMP + void sendXmp(const uint64_t data, const uint16_t nbits = kXmpBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_XMP +#if SEND_TRUMA + void sendTruma(const uint64_t data, const uint16_t nbits = kTrumaBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_TRUMA +#if SEND_TEKNOPOINT + void sendTeknopoint(const unsigned char data[], + const uint16_t nbytes = kTeknopointStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_TEKNOPOINT +#if SEND_KELON + void sendKelon(const uint64_t data, const uint16_t nbits = kKelonBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_KELON +#if SEND_KELON168 + void sendKelon168(const unsigned char data[], + const uint16_t nbytes = kKelon168StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_KELON168 +#if SEND_BOSE + void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_BOSE +#if SEND_ARRIS + void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits, + const uint16_t repeat = kNoRepeat); + static uint32_t toggleArrisRelease(const uint32_t data); + static uint32_t encodeArris(const uint32_t command, const bool release); +#endif // SEND_ARRIS +#if SEND_RHOSS + void sendRhoss(const unsigned char data[], + const uint16_t nbytes = kRhossStateLength, + const uint16_t repeat = kRhossDefaultRepeat); +#endif // SEND_RHOSS +#if SEND_AIRTON + void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits, + const uint16_t repeat = kAirtonDefaultRepeat); +#endif // SEND_AIRTON +#if SEND_TOTO + void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits, + const uint16_t repeat = kTotoDefaultRepeat); +#endif // SEND_TOTO +#if SEND_CLIMABUTLER + void sendClimaButler(const uint64_t data, + const uint16_t nbits = kClimaButlerBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CLIMABUTLER +#if SEND_BOSCH144 + void sendBosch144(const unsigned char data[], + const uint16_t nbytes = kBosch144StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_BOSCH144 +#if SEND_WOWWEE + void sendWowwee(const uint64_t data, const uint16_t nbits = kWowweeBits, + const uint16_t repeat = kWowweeDefaultRepeat); +#endif // SEND_WOWWEE +#if SEND_YORK + void sendYork(const unsigned char data[], + const uint16_t nbytes = kYorkStateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_YORK + + protected: +#ifdef UNIT_TEST +#ifndef HIGH +#define HIGH 0x1 +#endif +#ifndef LOW +#define LOW 0x0 +#endif +#endif // UNIT_TEST + uint8_t outputOn; + uint8_t outputOff; + VIRTUAL void ledOff(); + VIRTUAL void ledOn(); +#ifndef UNIT_TEST + + private: +#else + uint32_t _freq_unittest; +#endif // UNIT_TEST + uint16_t onTimePeriod; + uint16_t offTimePeriod; + uint16_t IRpin; + int8_t periodOffset; + uint8_t _dutycycle; + bool modulation; + uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true); +#if SEND_SONY + void _sendSony(const uint64_t data, const uint16_t nbits, + const uint16_t repeat, const uint16_t freq); +#endif // SEND_SONY +}; + +#endif // IRSEND_H_ diff --git a/src/IRtext.cpp b/src/IRtext.cpp index 8441475b7..0dab569de 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -1,563 +1,563 @@ -// Copyright 2019-2021 - David Conran (@crankyoldgit) - -/// @file IRtext.cpp -/// @warning If you add or remove an entry in this file, you should run: -/// '../tools/generate_irtext_h.sh' to rebuild the `IRtext.h` file. - -#include "IRtext.h" -#ifndef UNIT_TEST -#include -#endif // UNIT_TEST -#include "IRremoteESP8266.h" -#include "i18n.h" - -#include "IRmacros.h" - -#ifndef PROGMEM -#define PROGMEM // Pretend we have the PROGMEM macro even if we really don't. -#endif - -#ifndef FPSTR -#define FPSTR(X) X // Also pretend we have flash-string helper class cast. -#endif - -#define IRTEXT_CONST_BLOB_NAME(NAME)\ - NAME ## Blob - -#define IRTEXT_CONST_BLOB_DECL(NAME)\ - const char IRTEXT_CONST_BLOB_NAME(NAME) [] PROGMEM - -#define IRTEXT_CONST_BLOB_PTR(NAME)\ - IRTEXT_CONST_PTR(NAME) {\ - IRTEXT_CONST_PTR_CAST(IRTEXT_CONST_BLOB_NAME(NAME)) } - -#define IRTEXT_CONST_STRING(NAME, VALUE)\ - static IRTEXT_CONST_BLOB_DECL(NAME) { VALUE };\ - IRTEXT_CONST_PTR(NAME) PROGMEM {\ - IRTEXT_CONST_PTR_CAST(&(IRTEXT_CONST_BLOB_NAME(NAME))[0]) } - -// Common -IRTEXT_CONST_STRING(kUnknownStr, D_STR_UNKNOWN); ///< "Unknown" -IRTEXT_CONST_STRING(kProtocolStr, D_STR_PROTOCOL); ///< "Protocol" -IRTEXT_CONST_STRING(kPowerStr, D_STR_POWER); ///< "Power" -IRTEXT_CONST_STRING(kOnStr, D_STR_ON); ///< "On" -IRTEXT_CONST_STRING(kOffStr, D_STR_OFF); ///< "Off" -IRTEXT_CONST_STRING(k1Str, D_STR_1); ///< "1" -IRTEXT_CONST_STRING(k0Str, D_STR_0); ///< "0" -IRTEXT_CONST_STRING(kModeStr, D_STR_MODE); ///< "Mode" -IRTEXT_CONST_STRING(kToggleStr, D_STR_TOGGLE); ///< "Toggle" -IRTEXT_CONST_STRING(kTurboStr, D_STR_TURBO); ///< "Turbo" -IRTEXT_CONST_STRING(kSuperStr, D_STR_SUPER); ///< "Super" -IRTEXT_CONST_STRING(kSleepStr, D_STR_SLEEP); ///< "Sleep" -IRTEXT_CONST_STRING(kLightStr, D_STR_LIGHT); ///< "Light" -IRTEXT_CONST_STRING(kPowerfulStr, D_STR_POWERFUL); ///< "Powerful" -IRTEXT_CONST_STRING(kQuietStr, D_STR_QUIET); ///< "Quiet" -IRTEXT_CONST_STRING(kEconoStr, D_STR_ECONO); ///< "Econo" -IRTEXT_CONST_STRING(kSwingStr, D_STR_SWING); ///< "Swing" -IRTEXT_CONST_STRING(kSwingHStr, D_STR_SWINGH); ///< "SwingH" -IRTEXT_CONST_STRING(kSwingVStr, D_STR_SWINGV); ///< "SwingV" -IRTEXT_CONST_STRING(kBeepStr, D_STR_BEEP); ///< "Beep" -IRTEXT_CONST_STRING(kZoneFollowStr, D_STR_ZONEFOLLOW); ///< "Zone Follow" -IRTEXT_CONST_STRING(kFixedStr, D_STR_FIXED); ///< "Fixed" -IRTEXT_CONST_STRING(kMouldStr, D_STR_MOULD); ///< "Mould" -IRTEXT_CONST_STRING(kCleanStr, D_STR_CLEAN); ///< "Clean" -IRTEXT_CONST_STRING(kPurifyStr, D_STR_PURIFY); ///< "Purify" -IRTEXT_CONST_STRING(kTimerStr, D_STR_TIMER); ///< "Timer" -IRTEXT_CONST_STRING(kOnTimerStr, D_STR_ONTIMER); ///< "On Timer" -IRTEXT_CONST_STRING(kOffTimerStr, D_STR_OFFTIMER); ///< "Off Timer" -IRTEXT_CONST_STRING(kTimerModeStr, D_STR_TIMERMODE); ///< "Timer Mode" -IRTEXT_CONST_STRING(kClockStr, D_STR_CLOCK); ///< "Clock" -IRTEXT_CONST_STRING(kCommandStr, D_STR_COMMAND); ///< "Command" -IRTEXT_CONST_STRING(kConfigCommandStr, D_STR_CONFIG); ///< "Config" -IRTEXT_CONST_STRING(kControlCommandStr, D_STR_CONTROL); ///< "Control" -IRTEXT_CONST_STRING(kXFanStr, D_STR_XFAN); ///< "XFan" -IRTEXT_CONST_STRING(kHealthStr, D_STR_HEALTH); ///< "Health" -IRTEXT_CONST_STRING(kModelStr, D_STR_MODEL); ///< "Model" -IRTEXT_CONST_STRING(kTempStr, D_STR_TEMP); ///< "Temp" -IRTEXT_CONST_STRING(kIFeelReportStr, D_STR_IFEELREPORT); ///< "IFeel Report" -IRTEXT_CONST_STRING(kIFeelStr, D_STR_IFEEL); ///< "IFeel" -IRTEXT_CONST_STRING(kHumidStr, D_STR_HUMID); ///< "Humid" -IRTEXT_CONST_STRING(kSaveStr, D_STR_SAVE); ///< "Save" -IRTEXT_CONST_STRING(kEyeStr, D_STR_EYE); ///< "Eye" -IRTEXT_CONST_STRING(kFollowStr, D_STR_FOLLOW); ///< "Follow" -IRTEXT_CONST_STRING(kIonStr, D_STR_ION); ///< "Ion" -IRTEXT_CONST_STRING(kFreshStr, D_STR_FRESH); ///< "Fresh" -IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold" -IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button" -IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat" -IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat" -IRTEXT_CONST_STRING(kISeeStr, D_STR_ISEE); ///< "ISee" -IRTEXT_CONST_STRING(kAbsenseDetectStr, D_STR_ABSENSEDETECT); - ///< "AbsenseDetect" -IRTEXT_CONST_STRING(kDirectIndirectModeStr, D_STR_DIRECTINDIRECTMODE); - ///< "Direct/Indirect mode" -IRTEXT_CONST_STRING(kDirectStr, D_STR_DIRECT); ///< "Direct" -IRTEXT_CONST_STRING(kIndirectStr, D_STR_INDIRECT); ///< "Indirect" - -IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night" -IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent" -IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter" -IRTEXT_CONST_STRING(k3DStr, D_STR_3D); ///< "3D" -IRTEXT_CONST_STRING(kCelsiusStr, D_STR_CELSIUS); ///< "Celsius" -IRTEXT_CONST_STRING(kCelsiusFahrenheitStr, D_STR_CELSIUS_FAHRENHEIT); ///< -///< "Celsius/Fahrenheit" -IRTEXT_CONST_STRING(kTempUpStr, D_STR_TEMPUP); ///< "Temp Up" -IRTEXT_CONST_STRING(kTempDownStr, D_STR_TEMPDOWN); ///< "Temp Down" -IRTEXT_CONST_STRING(kStartStr, D_STR_START); ///< "Start" -IRTEXT_CONST_STRING(kStopStr, D_STR_STOP); ///< "Stop" -IRTEXT_CONST_STRING(kMoveStr, D_STR_MOVE); ///< "Move" -IRTEXT_CONST_STRING(kSetStr, D_STR_SET); ///< "Set" -IRTEXT_CONST_STRING(kCancelStr, D_STR_CANCEL); ///< "Cancel" -IRTEXT_CONST_STRING(kUpStr, D_STR_UP); ///< "Up" -IRTEXT_CONST_STRING(kDownStr, D_STR_DOWN); ///< "Down" -IRTEXT_CONST_STRING(kChangeStr, D_STR_CHANGE); ///< "Change" -IRTEXT_CONST_STRING(kComfortStr, D_STR_COMFORT); ///< "Comfort" -IRTEXT_CONST_STRING(kSensorStr, D_STR_SENSOR); ///< "Sensor" -IRTEXT_CONST_STRING(kWeeklyTimerStr, D_STR_WEEKLYTIMER); ///< "WeeklyTimer" -IRTEXT_CONST_STRING(kWifiStr, D_STR_WIFI); ///< "Wifi" -IRTEXT_CONST_STRING(kLastStr, D_STR_LAST); ///< "Last" -IRTEXT_CONST_STRING(kFastStr, D_STR_FAST); ///< "Fast" -IRTEXT_CONST_STRING(kSlowStr, D_STR_SLOW); ///< "Slow" -IRTEXT_CONST_STRING(kAirFlowStr, D_STR_AIRFLOW); ///< "Air Flow" -IRTEXT_CONST_STRING(kStepStr, D_STR_STEP); ///< "Step" -IRTEXT_CONST_STRING(kNAStr, D_STR_NA); ///< "N/A" -IRTEXT_CONST_STRING(kInsideStr, D_STR_INSIDE); ///< "Inside" -IRTEXT_CONST_STRING(kOutsideStr, D_STR_OUTSIDE); ///< "Outside" -IRTEXT_CONST_STRING(kLoudStr, D_STR_LOUD); ///< "Loud" -IRTEXT_CONST_STRING(kLowerStr, D_STR_LOWER); ///< "Lower" -IRTEXT_CONST_STRING(kUpperStr, D_STR_UPPER); ///< "Upper" -IRTEXT_CONST_STRING(kUpperMiddleStr, D_STR_UPPER_MIDDLE); ///< "Upper-Middle" -IRTEXT_CONST_STRING(kBreezeStr, D_STR_BREEZE); ///< "Breeze" -IRTEXT_CONST_STRING(kCirculateStr, D_STR_CIRCULATE); ///< "Circulate" -IRTEXT_CONST_STRING(kCeilingStr, D_STR_CEILING); ///< "Ceiling" -IRTEXT_CONST_STRING(kWallStr, D_STR_WALL); ///< "Wall" -IRTEXT_CONST_STRING(kRoomStr, D_STR_ROOM); ///< "Room" -IRTEXT_CONST_STRING(k6thSenseStr, D_STR_6THSENSE); ///< "6th Sense" -IRTEXT_CONST_STRING(kTypeStr, D_STR_TYPE); ///< "Type" -IRTEXT_CONST_STRING(kSpecialStr, D_STR_SPECIAL); ///< "Special" -IRTEXT_CONST_STRING(kIdStr, D_STR_ID); ///< "Id" / Device Identifier -IRTEXT_CONST_STRING(kVaneStr, D_STR_VANE); ///< "Vane" -IRTEXT_CONST_STRING(kLockStr, D_STR_LOCK); ///< "Lock" - -IRTEXT_CONST_STRING(kAutoStr, D_STR_AUTO); ///< "Auto" -IRTEXT_CONST_STRING(kAutomaticStr, D_STR_AUTOMATIC); ///< "Automatic" -IRTEXT_CONST_STRING(kManualStr, D_STR_MANUAL); ///< "Manual" -IRTEXT_CONST_STRING(kCoolStr, D_STR_COOL); ///< "Cool" -IRTEXT_CONST_STRING(kCoolingStr, D_STR_COOLING); ///< "Cooling" -IRTEXT_CONST_STRING(kHeatStr, D_STR_HEAT); ///< "Heat" -IRTEXT_CONST_STRING(kHeatingStr, D_STR_HEATING); ///< "Heating" -IRTEXT_CONST_STRING(kDryStr, D_STR_DRY); ///< "Dry" -IRTEXT_CONST_STRING(kDryingStr, D_STR_DRYING); ///< "Drying" -IRTEXT_CONST_STRING(kDehumidifyStr, D_STR_DEHUMIDIFY); ///< "Dehumidify" -IRTEXT_CONST_STRING(kFanStr, D_STR_FAN); ///< "Fan" -// The following Fans strings with "only" are required to help with -// HomeAssistant & Google Home Climate integration. For compatibility only. -// Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes -IRTEXT_CONST_STRING(kFanOnlyStr, D_STR_FANONLY); ///< "fan-only" -IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (HA/legacy) -IRTEXT_CONST_STRING(kFanOnlyWithSpaceStr, D_STR_FANSPACEONLY); ///< "Fan Only" -IRTEXT_CONST_STRING(kFanOnlyNoSpaceStr, D_STR_FANONLYNOSPACE); ///< "FanOnly" - -IRTEXT_CONST_STRING(kRecycleStr, D_STR_RECYCLE); ///< "Recycle" - -IRTEXT_CONST_STRING(kMaxStr, D_STR_MAX); ///< "Max" -IRTEXT_CONST_STRING(kMaximumStr, D_STR_MAXIMUM); ///< "Maximum" -IRTEXT_CONST_STRING(kMinStr, D_STR_MIN); ///< "Min" -IRTEXT_CONST_STRING(kMinimumStr, D_STR_MINIMUM); ///< "Minimum" -IRTEXT_CONST_STRING(kMedHighStr, D_STR_MED_HIGH); ///< "Med-high" -IRTEXT_CONST_STRING(kMedStr, D_STR_MED); ///< "Med" -IRTEXT_CONST_STRING(kMediumStr, D_STR_MEDIUM); ///< "Medium" - -IRTEXT_CONST_STRING(kHighestStr, D_STR_HIGHEST); ///< "Highest" -IRTEXT_CONST_STRING(kHighStr, D_STR_HIGH); ///< "High" -IRTEXT_CONST_STRING(kHiStr, D_STR_HI); ///< "Hi" -IRTEXT_CONST_STRING(kMidStr, D_STR_MID); ///< "Mid" -IRTEXT_CONST_STRING(kMiddleStr, D_STR_MIDDLE); ///< "Middle" -IRTEXT_CONST_STRING(kLowStr, D_STR_LOW); ///< "Low" -IRTEXT_CONST_STRING(kLoStr, D_STR_LO); ///< "Lo" -IRTEXT_CONST_STRING(kLowestStr, D_STR_LOWEST); ///< "Lowest" -IRTEXT_CONST_STRING(kMaxRightStr, D_STR_MAXRIGHT); ///< "Max Right" -IRTEXT_CONST_STRING(kMaxRightNoSpaceStr, D_STR_MAXRIGHT_NOSPACE); ///< - ///< "MaxRight" -IRTEXT_CONST_STRING(kRightMaxStr, D_STR_RIGHTMAX); ///< "Right Max" -IRTEXT_CONST_STRING(kRightMaxNoSpaceStr, D_STR_RIGHTMAX_NOSPACE); ///< - ///< "RightMax" -IRTEXT_CONST_STRING(kRightStr, D_STR_RIGHT); ///< "Right" -IRTEXT_CONST_STRING(kLeftStr, D_STR_LEFT); ///< "Left" -IRTEXT_CONST_STRING(kMaxLeftStr, D_STR_MAXLEFT); ///< "Max Left" -IRTEXT_CONST_STRING(kMaxLeftNoSpaceStr, D_STR_MAXLEFT_NOSPACE); ///< "MaxLeft" -IRTEXT_CONST_STRING(kLeftMaxStr, D_STR_LEFTMAX); ///< "Left Max" -IRTEXT_CONST_STRING(kLeftMaxNoSpaceStr, D_STR_LEFTMAX_NOSPACE); ///< "LeftMax" -IRTEXT_CONST_STRING(kWideStr, D_STR_WIDE); ///< "Wide" -IRTEXT_CONST_STRING(kCentreStr, D_STR_CENTRE); ///< "Centre" -IRTEXT_CONST_STRING(kTopStr, D_STR_TOP); ///< "Top" -IRTEXT_CONST_STRING(kBottomStr, D_STR_BOTTOM); ///< "Bottom" - -// Compound words/phrases/descriptions from pre-defined words. -IRTEXT_CONST_STRING(kEconoToggleStr, D_STR_ECONOTOGGLE); ///< "Econo Toggle" -IRTEXT_CONST_STRING(kEyeAutoStr, D_STR_EYEAUTO); ///< "Eye Auto" -IRTEXT_CONST_STRING(kLightToggleStr, D_STR_LIGHTTOGGLE); ///< "Light Toggle" -///< "Outside Quiet" -IRTEXT_CONST_STRING(kOutsideQuietStr, D_STR_OUTSIDEQUIET); -IRTEXT_CONST_STRING(kPowerToggleStr, D_STR_POWERTOGGLE); ///< "Power Toggle" -IRTEXT_CONST_STRING(kPowerButtonStr, D_STR_POWERBUTTON); ///< "Power Button" -IRTEXT_CONST_STRING(kPreviousPowerStr, D_STR_PREVIOUSPOWER); ///< -///< "Previous Power" -IRTEXT_CONST_STRING(kDisplayTempStr, D_STR_DISPLAYTEMP); ///< "Display Temp" -IRTEXT_CONST_STRING(kSensorTempStr, D_STR_SENSORTEMP); ///< "Sensor Temp" -IRTEXT_CONST_STRING(kSleepTimerStr, D_STR_SLEEP_TIMER); ///< "Sleep Timer" -IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode" -IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///< -///< "Swing(V) Toggle" -IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle" -IRTEXT_CONST_STRING(kSetTimerCommandStr, D_STR_SET_TIMER); ///< "Set Timer" -IRTEXT_CONST_STRING(kScheduleStr, D_STR_SCHEDULE); ///< "Schedule" -IRTEXT_CONST_STRING(kChStr, D_STR_CH); ///< "CH#" -IRTEXT_CONST_STRING(kTimerActiveDaysStr, D_STR_TIMER_ACTIVE_DAYS); -///< "TimerActiveDays" -IRTEXT_CONST_STRING(kKeyStr, D_STR_KEY); ///< "Key" -IRTEXT_CONST_STRING(kValueStr, D_STR_VALUE); ///< "Value" - -// Separators & Punctuation -const char kTimeSep = D_CHR_TIME_SEP; ///< ':' -IRTEXT_CONST_STRING(kSpaceLBraceStr, D_STR_SPACELBRACE); ///< " (" -IRTEXT_CONST_STRING(kCommaSpaceStr, D_STR_COMMASPACE); ///< ", " -IRTEXT_CONST_STRING(kColonSpaceStr, D_STR_COLONSPACE); ///< ": " -IRTEXT_CONST_STRING(kDashStr, D_STR_DASH); ///< "-" - -// IRutils -// - Time -IRTEXT_CONST_STRING(kDayStr, D_STR_DAY); ///< "Day" -IRTEXT_CONST_STRING(kDaysStr, D_STR_DAYS); ///< "Days" -IRTEXT_CONST_STRING(kHourStr, D_STR_HOUR); ///< "Hour" -IRTEXT_CONST_STRING(kHoursStr, D_STR_HOURS); ///< "Hours" -IRTEXT_CONST_STRING(kMinuteStr, D_STR_MINUTE); ///< "Minute" -IRTEXT_CONST_STRING(kMinutesStr, D_STR_MINUTES); ///< "Minutes" -IRTEXT_CONST_STRING(kSecondStr, D_STR_SECOND); ///< "Second" -IRTEXT_CONST_STRING(kSecondsStr, D_STR_SECONDS); ///< "Seconds" -IRTEXT_CONST_STRING(kNowStr, D_STR_NOW); ///< "Now" -IRTEXT_CONST_STRING(kThreeLetterDayOfWeekStr, D_STR_THREELETTERDAYS); ///< -///< "SunMonTueWedThuFriSat" -IRTEXT_CONST_STRING(kYesStr, D_STR_YES); ///< "Yes" -IRTEXT_CONST_STRING(kNoStr, D_STR_NO); ///< "No" -IRTEXT_CONST_STRING(kTrueStr, D_STR_TRUE); ///< "True" -IRTEXT_CONST_STRING(kFalseStr, D_STR_FALSE); ///< "False" - -IRTEXT_CONST_STRING(kRepeatStr, D_STR_REPEAT); ///< "Repeat" -IRTEXT_CONST_STRING(kCodeStr, D_STR_CODE); ///< "Code" -IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits" - -// Model Names -IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F" -IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB" -IRTEXT_CONST_STRING(kYx1fsfStr, D_STR_YX1FSF); ///< "YX1FSF" -IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A" -IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B" -IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A" -IRTEXT_CONST_STRING(kRlt0541htabStr, D_STR_RLT0541HTA_B); ///< "R-LT0541-HTA-B" -IRTEXT_CONST_STRING(kArrah2eStr, D_STR_ARRAH2E); ///< "ARRAH2E" -IRTEXT_CONST_STRING(kArdb1Str, D_STR_ARDB1); ///< "ARDB1" -IRTEXT_CONST_STRING(kArreb1eStr, D_STR_ARREB1E); ///< "ARREB1E" -IRTEXT_CONST_STRING(kArjw2Str, D_STR_ARJW2); ///< "ARJW2" -IRTEXT_CONST_STRING(kArry4Str, D_STR_ARRY4); ///< "ARRY4" -IRTEXT_CONST_STRING(kArrew4eStr, D_STR_ARREW4E); ///< "ARREW4E" -IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///< - ///< "GE6711AR2853M" -IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403" -IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603" -IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604" -IRTEXT_CONST_STRING(kLg6711a20083vStr, D_STR_LG6711A20083V); ///< - ///< "LG6711A20083V" -IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1" -IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1" -IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE" -IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE" -IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE" -IRTEXT_CONST_STRING(kPkrStr, D_STR_PKR); ///< "PKR" -IRTEXT_CONST_STRING(kJkeStr, D_STR_JKE); ///< "JKE" -IRTEXT_CONST_STRING(kCkpStr, D_STR_CKP); ///< "CKP" -IRTEXT_CONST_STRING(kRkrStr, D_STR_RKR); ///< "RKR" -IRTEXT_CONST_STRING(kPanasonicLkeStr, D_STR_PANASONICLKE); ///< "PANASONICLKE" -IRTEXT_CONST_STRING(kPanasonicNkeStr, D_STR_PANASONICNKE); ///< "PANASONICNKE" -IRTEXT_CONST_STRING(kPanasonicDkeStr, D_STR_PANASONICDKE); ///< "PANASONICDKE" -IRTEXT_CONST_STRING(kPanasonicPkrStr, D_STR_PANASONICPKR); ///< "PANASONICPKR" -IRTEXT_CONST_STRING(kPanasonicJkeStr, D_STR_PANASONICJKE); ///< "PANASONICJKE" -IRTEXT_CONST_STRING(kPanasonicCkpStr, D_STR_PANASONICCKP); ///< "PANASONICCKP" -IRTEXT_CONST_STRING(kPanasonicRkrStr, D_STR_PANASONICRKR); ///< "PANASONICRKR" -IRTEXT_CONST_STRING(kA907Str, D_STR_A907); ///< "A907" -IRTEXT_CONST_STRING(kA705Str, D_STR_A705); ///< "A705" -IRTEXT_CONST_STRING(kA903Str, D_STR_A903); ///< "A903" -IRTEXT_CONST_STRING(kTac09chsdStr, D_STR_TAC09CHSD); ///< "TAC09CHSD" -IRTEXT_CONST_STRING(kGz055be1Str, D_STR_GZ055BE1); ///< "GZ055BE1" -IRTEXT_CONST_STRING(k122lzfStr, D_STR_122LZF); ///< "122LZF" -IRTEXT_CONST_STRING(kDg11j13aStr, D_STR_DG11J13A); ///< "DG11J13A" -IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104" -IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191" -IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3" -IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3" - -#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as - // a question mark, check for length > 1 - // to show only currently included protocols -// Protocol Names -// Needs to be in decode_type_t order. -IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { - D_STR_UNUSED "\x0" - COND(DECODE_RC5 || SEND_RC5, - D_STR_RC5, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RC6 || SEND_RC6, - D_STR_RC6, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_NEC || SEND_NEC, - D_STR_NEC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SONY || SEND_SONY, - D_STR_SONY, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PANASONIC || SEND_PANASONIC, - D_STR_PANASONIC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_JVC || SEND_JVC, - D_STR_JVC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SAMSUNG || SEND_SAMSUNG, - D_STR_SAMSUNG, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_WHYNTER || SEND_WHYNTER, - D_STR_WHYNTER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AIWA_RC_T501 || SEND_AIWA_RC_T501, - D_STR_AIWA_RC_T501, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LG || SEND_LG, - D_STR_LG, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO || SEND_SANYO, - D_STR_SANYO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI || SEND_MITSUBISHI, - D_STR_MITSUBISHI, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DISH || SEND_DISH, - D_STR_DISH, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SHARP || SEND_SHARP, - D_STR_SHARP, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_COOLIX || SEND_COOLIX, - D_STR_COOLIX, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN || SEND_DAIKIN, - D_STR_DAIKIN, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DENON || SEND_DENON, - D_STR_DENON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_KELVINATOR || SEND_KELVINATOR, - D_STR_KELVINATOR, D_STR_UNSUPPORTED) "\x0" - COND(SEND_SHERWOOD, - D_STR_SHERWOOD, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY - COND(DECODE_MITSUBISHI_AC || SEND_MITSUBISHI_AC, - D_STR_MITSUBISHI_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RCMM || SEND_RCMM, - D_STR_RCMM, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO || SEND_SANYO, - D_STR_SANYO_LC7461, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RC5 || SEND_RC5, - D_STR_RC5X, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GREE || SEND_GREE, - D_STR_GREE, D_STR_UNSUPPORTED) "\x0" - COND(SEND_PRONTO, - D_STR_PRONTO, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY - COND(DECODE_NEC || SEND_NEC, - D_STR_NEC_LIKE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ARGO || SEND_ARGO, - D_STR_ARGO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TROTEC || SEND_TROTEC, - D_STR_TROTEC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_NIKAI || SEND_NIKAI, - D_STR_NIKAI, D_STR_UNSUPPORTED) "\x0" - COND(SEND_RAW, - D_STR_RAW, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY - COND(SEND_GLOBALCACHE, - D_STR_GLOBALCACHE, D_STR_UNSUPPORTED) "\x0" // SEND - COND(DECODE_TOSHIBA_AC || SEND_TOSHIBA_AC, - D_STR_TOSHIBA_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_FUJITSU_AC || SEND_FUJITSU_AC, - D_STR_FUJITSU_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MIDEA || SEND_MIDEA, - D_STR_MIDEA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MAGIQUEST || SEND_MAGIQUEST, - D_STR_MAGIQUEST, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LASERTAG || SEND_LASERTAG, - D_STR_LASERTAG, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC || SEND_CARRIER_AC, - D_STR_CARRIER_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC || SEND_HAIER_AC, - D_STR_HAIER_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI2 || SEND_MITSUBISHI2, - D_STR_MITSUBISHI2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC || SEND_HITACHI_AC, - D_STR_HITACHI_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC1 || SEND_HITACHI_AC1, - D_STR_HITACHI_AC1, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC2 || SEND_HITACHI_AC2, - D_STR_HITACHI_AC2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GICABLE || SEND_GICABLE, - D_STR_GICABLE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC_YRW02 || SEND_HAIER_AC_YRW02, - D_STR_HAIER_AC_YRW02, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_WHIRLPOOL_AC || SEND_WHIRLPOOL_AC, - D_STR_WHIRLPOOL_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SAMSUNG_AC || SEND_SAMSUNG_AC, - D_STR_SAMSUNG_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LUTRON || SEND_LUTRON, - D_STR_LUTRON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ELECTRA_AC || SEND_ELECTRA_AC, - D_STR_ELECTRA_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PANASONIC_AC || SEND_PANASONIC_AC, - D_STR_PANASONIC_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PIONEER || SEND_PIONEER, - D_STR_PIONEER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LG || SEND_LG, - D_STR_LG2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MWM || SEND_MWM, - D_STR_MWM, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN2 || SEND_DAIKIN2, - D_STR_DAIKIN2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_VESTEL_AC || SEND_VESTEL_AC, - D_STR_VESTEL_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TECO || SEND_TECO, - D_STR_TECO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SAMSUNG36 || SEND_SAMSUNG36, - D_STR_SAMSUNG36, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TCL112AC || SEND_TCL112AC, - D_STR_TCL112AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_LEGOPF || SEND_LEGOPF, - D_STR_LEGOPF, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, - D_STR_MITSUBISHI_HEAVY_88, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, - D_STR_MITSUBISHI_HEAVY_152, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN216 || SEND_DAIKIN216, - D_STR_DAIKIN216, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SHARP_AC || SEND_SHARP_AC, - D_STR_SHARP_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GOODWEATHER || SEND_GOODWEATHER, - D_STR_GOODWEATHER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_INAX || SEND_INAX, - D_STR_INAX, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN160 || SEND_DAIKIN160, - D_STR_DAIKIN160, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_NEOCLIMA || SEND_NEOCLIMA, - D_STR_NEOCLIMA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN176 || SEND_DAIKIN176, - D_STR_DAIKIN176, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN128 || SEND_DAIKIN128, - D_STR_DAIKIN128, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AMCOR || SEND_AMCOR, - D_STR_AMCOR, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN152 || SEND_DAIKIN152, - D_STR_DAIKIN152, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI136 || SEND_MITSUBISHI136, - D_STR_MITSUBISHI136, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MITSUBISHI112 || SEND_MITSUBISHI112, - D_STR_MITSUBISHI112, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC424 || SEND_HITACHI_AC424, - D_STR_HITACHI_AC424, D_STR_UNSUPPORTED) "\x0" - COND(SEND_SONY, - D_STR_SONY_38K, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_EPSON || SEND_EPSON, - D_STR_EPSON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SYMPHONY || SEND_SYMPHONY, - D_STR_SYMPHONY, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC3 || SEND_HITACHI_AC3, - D_STR_HITACHI_AC3, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN64 || SEND_DAIKIN64, - D_STR_DAIKIN64, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AIRWELL || SEND_AIRWELL, - D_STR_AIRWELL, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DELONGHI_AC || SEND_DELONGHI_AC, - D_STR_DELONGHI_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DOSHISHA || SEND_DOSHISHA, - D_STR_DOSHISHA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MULTIBRACKETS || SEND_MULTIBRACKETS, - D_STR_MULTIBRACKETS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC40 || SEND_CARRIER_AC40, - D_STR_CARRIER_AC40, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC64 || SEND_CARRIER_AC64, - D_STR_CARRIER_AC64, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC344 || SEND_HITACHI_AC344, - D_STR_HITACHI_AC344, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CORONA_AC || SEND_CORONA_AC, - D_STR_CORONA_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MIDEA24 || SEND_MIDEA24, - D_STR_MIDEA24, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ZEPEAL || SEND_ZEPEAL, - D_STR_ZEPEAL, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO_AC || SEND_SANYO_AC, - D_STR_SANYO_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_VOLTAS || SEND_VOLTAS, - D_STR_VOLTAS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_METZ || SEND_METZ, - D_STR_METZ, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TRANSCOLD || SEND_TRANSCOLD, - D_STR_TRANSCOLD, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TECHNIBEL_AC || SEND_TECHNIBEL_AC, - D_STR_TECHNIBEL_AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MIRAGE || SEND_MIRAGE, - D_STR_MIRAGE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ELITESCREENS || SEND_ELITESCREENS, - D_STR_ELITESCREENS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_PANASONIC_AC32 || SEND_PANASONIC_AC32, - D_STR_PANASONIC_AC32, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_MILESTAG2 || SEND_MILESTAG2, - D_STR_MILESTAG2, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ECOCLIM || SEND_ECOCLIM, - D_STR_ECOCLIM, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_XMP || SEND_XMP, - D_STR_XMP, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TRUMA || SEND_TRUMA, - D_STR_TRUMA, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC176 || SEND_HAIER_AC176, - D_STR_HAIER_AC176, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TEKNOPOINT || SEND_TEKNOPOINT, - D_STR_TEKNOPOINT, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_KELON || SEND_KELON, - D_STR_KELON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TROTEC_3550 || SEND_TROTEC_3550, - D_STR_TROTEC_3550, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO_AC88 || SEND_SANYO_AC88, - D_STR_SANYO_AC88, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_BOSE || SEND_BOSE, - D_STR_BOSE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_ARRIS || SEND_ARRIS, - D_STR_ARRIS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_RHOSS || SEND_RHOSS, - D_STR_RHOSS, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_AIRTON || SEND_AIRTON, - D_STR_AIRTON, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_COOLIX48 || SEND_COOLIX48, - D_STR_COOLIX48, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC264 || SEND_HITACHI_AC264, - D_STR_HITACHI_AC264, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_KELON168 || SEND_KELON168, - D_STR_KELON168, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HITACHI_AC296 || SEND_HITACHI_AC296, - D_STR_HITACHI_AC296, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN200 || SEND_DAIKIN200, - D_STR_DAIKIN200, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_HAIER_AC160 || SEND_HAIER_AC160, - D_STR_HAIER_AC160, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC128 || SEND_CARRIER_AC128, - D_STR_CARRIER_AC128, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TOTO || SEND_TOTO, - D_STR_TOTO, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CLIMABUTLER || SEND_CLIMABUTLER, - D_STR_CLIMABUTLER, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_TCL96AC || SEND_TCL96AC, - D_STR_TCL96AC, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_BOSCH144 || SEND_BOSCH144, - D_STR_BOSCH144, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_SANYO_AC152 || SEND_SANYO_AC152, - D_STR_SANYO_AC152, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_DAIKIN312 || SEND_DAIKIN312, - D_STR_DAIKIN312, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_GORENJE || SEND_GORENJE, - D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_WOWWEE || SEND_WOWWEE, - D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_CARRIER_AC84 || SEND_CARRIER_AC84, - D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_YORK || SEND_YORK, - D_STR_YORK, D_STR_UNSUPPORTED) "\x0" - COND(DECODE_FUJITSU_AC264 || SEND_FUJITSU_AC264, - D_STR_FUJITSU_AC264, D_STR_UNSUPPORTED) "\x0" - ///< New protocol (macro) strings should be added just above this line. - "\x0" ///< This string requires double null termination. -}; -IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr); +// Copyright 2019-2021 - David Conran (@crankyoldgit) + +/// @file IRtext.cpp +/// @warning If you add or remove an entry in this file, you should run: +/// '../tools/generate_irtext_h.sh' to rebuild the `IRtext.h` file. + +#include "IRtext.h" +#ifndef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "i18n.h" + +#include "IRmacros.h" + +#ifndef PROGMEM +#define PROGMEM // Pretend we have the PROGMEM macro even if we really don't. +#endif + +#ifndef FPSTR +#define FPSTR(X) X // Also pretend we have flash-string helper class cast. +#endif + +#define IRTEXT_CONST_BLOB_NAME(NAME)\ + NAME ## Blob + +#define IRTEXT_CONST_BLOB_DECL(NAME)\ + const char IRTEXT_CONST_BLOB_NAME(NAME) [] PROGMEM + +#define IRTEXT_CONST_BLOB_PTR(NAME)\ + IRTEXT_CONST_PTR(NAME) {\ + IRTEXT_CONST_PTR_CAST(IRTEXT_CONST_BLOB_NAME(NAME)) } + +#define IRTEXT_CONST_STRING(NAME, VALUE)\ + static IRTEXT_CONST_BLOB_DECL(NAME) { VALUE };\ + IRTEXT_CONST_PTR(NAME) PROGMEM {\ + IRTEXT_CONST_PTR_CAST(&(IRTEXT_CONST_BLOB_NAME(NAME))[0]) } + +// Common +IRTEXT_CONST_STRING(kUnknownStr, D_STR_UNKNOWN); ///< "Unknown" +IRTEXT_CONST_STRING(kProtocolStr, D_STR_PROTOCOL); ///< "Protocol" +IRTEXT_CONST_STRING(kPowerStr, D_STR_POWER); ///< "Power" +IRTEXT_CONST_STRING(kOnStr, D_STR_ON); ///< "On" +IRTEXT_CONST_STRING(kOffStr, D_STR_OFF); ///< "Off" +IRTEXT_CONST_STRING(k1Str, D_STR_1); ///< "1" +IRTEXT_CONST_STRING(k0Str, D_STR_0); ///< "0" +IRTEXT_CONST_STRING(kModeStr, D_STR_MODE); ///< "Mode" +IRTEXT_CONST_STRING(kToggleStr, D_STR_TOGGLE); ///< "Toggle" +IRTEXT_CONST_STRING(kTurboStr, D_STR_TURBO); ///< "Turbo" +IRTEXT_CONST_STRING(kSuperStr, D_STR_SUPER); ///< "Super" +IRTEXT_CONST_STRING(kSleepStr, D_STR_SLEEP); ///< "Sleep" +IRTEXT_CONST_STRING(kLightStr, D_STR_LIGHT); ///< "Light" +IRTEXT_CONST_STRING(kPowerfulStr, D_STR_POWERFUL); ///< "Powerful" +IRTEXT_CONST_STRING(kQuietStr, D_STR_QUIET); ///< "Quiet" +IRTEXT_CONST_STRING(kEconoStr, D_STR_ECONO); ///< "Econo" +IRTEXT_CONST_STRING(kSwingStr, D_STR_SWING); ///< "Swing" +IRTEXT_CONST_STRING(kSwingHStr, D_STR_SWINGH); ///< "SwingH" +IRTEXT_CONST_STRING(kSwingVStr, D_STR_SWINGV); ///< "SwingV" +IRTEXT_CONST_STRING(kBeepStr, D_STR_BEEP); ///< "Beep" +IRTEXT_CONST_STRING(kZoneFollowStr, D_STR_ZONEFOLLOW); ///< "Zone Follow" +IRTEXT_CONST_STRING(kFixedStr, D_STR_FIXED); ///< "Fixed" +IRTEXT_CONST_STRING(kMouldStr, D_STR_MOULD); ///< "Mould" +IRTEXT_CONST_STRING(kCleanStr, D_STR_CLEAN); ///< "Clean" +IRTEXT_CONST_STRING(kPurifyStr, D_STR_PURIFY); ///< "Purify" +IRTEXT_CONST_STRING(kTimerStr, D_STR_TIMER); ///< "Timer" +IRTEXT_CONST_STRING(kOnTimerStr, D_STR_ONTIMER); ///< "On Timer" +IRTEXT_CONST_STRING(kOffTimerStr, D_STR_OFFTIMER); ///< "Off Timer" +IRTEXT_CONST_STRING(kTimerModeStr, D_STR_TIMERMODE); ///< "Timer Mode" +IRTEXT_CONST_STRING(kClockStr, D_STR_CLOCK); ///< "Clock" +IRTEXT_CONST_STRING(kCommandStr, D_STR_COMMAND); ///< "Command" +IRTEXT_CONST_STRING(kConfigCommandStr, D_STR_CONFIG); ///< "Config" +IRTEXT_CONST_STRING(kControlCommandStr, D_STR_CONTROL); ///< "Control" +IRTEXT_CONST_STRING(kXFanStr, D_STR_XFAN); ///< "XFan" +IRTEXT_CONST_STRING(kHealthStr, D_STR_HEALTH); ///< "Health" +IRTEXT_CONST_STRING(kModelStr, D_STR_MODEL); ///< "Model" +IRTEXT_CONST_STRING(kTempStr, D_STR_TEMP); ///< "Temp" +IRTEXT_CONST_STRING(kIFeelReportStr, D_STR_IFEELREPORT); ///< "IFeel Report" +IRTEXT_CONST_STRING(kIFeelStr, D_STR_IFEEL); ///< "IFeel" +IRTEXT_CONST_STRING(kHumidStr, D_STR_HUMID); ///< "Humid" +IRTEXT_CONST_STRING(kSaveStr, D_STR_SAVE); ///< "Save" +IRTEXT_CONST_STRING(kEyeStr, D_STR_EYE); ///< "Eye" +IRTEXT_CONST_STRING(kFollowStr, D_STR_FOLLOW); ///< "Follow" +IRTEXT_CONST_STRING(kIonStr, D_STR_ION); ///< "Ion" +IRTEXT_CONST_STRING(kFreshStr, D_STR_FRESH); ///< "Fresh" +IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold" +IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button" +IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat" +IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat" +IRTEXT_CONST_STRING(kISeeStr, D_STR_ISEE); ///< "ISee" +IRTEXT_CONST_STRING(kAbsenseDetectStr, D_STR_ABSENSEDETECT); + ///< "AbsenseDetect" +IRTEXT_CONST_STRING(kDirectIndirectModeStr, D_STR_DIRECTINDIRECTMODE); + ///< "Direct/Indirect mode" +IRTEXT_CONST_STRING(kDirectStr, D_STR_DIRECT); ///< "Direct" +IRTEXT_CONST_STRING(kIndirectStr, D_STR_INDIRECT); ///< "Indirect" + +IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night" +IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent" +IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter" +IRTEXT_CONST_STRING(k3DStr, D_STR_3D); ///< "3D" +IRTEXT_CONST_STRING(kCelsiusStr, D_STR_CELSIUS); ///< "Celsius" +IRTEXT_CONST_STRING(kCelsiusFahrenheitStr, D_STR_CELSIUS_FAHRENHEIT); ///< +///< "Celsius/Fahrenheit" +IRTEXT_CONST_STRING(kTempUpStr, D_STR_TEMPUP); ///< "Temp Up" +IRTEXT_CONST_STRING(kTempDownStr, D_STR_TEMPDOWN); ///< "Temp Down" +IRTEXT_CONST_STRING(kStartStr, D_STR_START); ///< "Start" +IRTEXT_CONST_STRING(kStopStr, D_STR_STOP); ///< "Stop" +IRTEXT_CONST_STRING(kMoveStr, D_STR_MOVE); ///< "Move" +IRTEXT_CONST_STRING(kSetStr, D_STR_SET); ///< "Set" +IRTEXT_CONST_STRING(kCancelStr, D_STR_CANCEL); ///< "Cancel" +IRTEXT_CONST_STRING(kUpStr, D_STR_UP); ///< "Up" +IRTEXT_CONST_STRING(kDownStr, D_STR_DOWN); ///< "Down" +IRTEXT_CONST_STRING(kChangeStr, D_STR_CHANGE); ///< "Change" +IRTEXT_CONST_STRING(kComfortStr, D_STR_COMFORT); ///< "Comfort" +IRTEXT_CONST_STRING(kSensorStr, D_STR_SENSOR); ///< "Sensor" +IRTEXT_CONST_STRING(kWeeklyTimerStr, D_STR_WEEKLYTIMER); ///< "WeeklyTimer" +IRTEXT_CONST_STRING(kWifiStr, D_STR_WIFI); ///< "Wifi" +IRTEXT_CONST_STRING(kLastStr, D_STR_LAST); ///< "Last" +IRTEXT_CONST_STRING(kFastStr, D_STR_FAST); ///< "Fast" +IRTEXT_CONST_STRING(kSlowStr, D_STR_SLOW); ///< "Slow" +IRTEXT_CONST_STRING(kAirFlowStr, D_STR_AIRFLOW); ///< "Air Flow" +IRTEXT_CONST_STRING(kStepStr, D_STR_STEP); ///< "Step" +IRTEXT_CONST_STRING(kNAStr, D_STR_NA); ///< "N/A" +IRTEXT_CONST_STRING(kInsideStr, D_STR_INSIDE); ///< "Inside" +IRTEXT_CONST_STRING(kOutsideStr, D_STR_OUTSIDE); ///< "Outside" +IRTEXT_CONST_STRING(kLoudStr, D_STR_LOUD); ///< "Loud" +IRTEXT_CONST_STRING(kLowerStr, D_STR_LOWER); ///< "Lower" +IRTEXT_CONST_STRING(kUpperStr, D_STR_UPPER); ///< "Upper" +IRTEXT_CONST_STRING(kUpperMiddleStr, D_STR_UPPER_MIDDLE); ///< "Upper-Middle" +IRTEXT_CONST_STRING(kBreezeStr, D_STR_BREEZE); ///< "Breeze" +IRTEXT_CONST_STRING(kCirculateStr, D_STR_CIRCULATE); ///< "Circulate" +IRTEXT_CONST_STRING(kCeilingStr, D_STR_CEILING); ///< "Ceiling" +IRTEXT_CONST_STRING(kWallStr, D_STR_WALL); ///< "Wall" +IRTEXT_CONST_STRING(kRoomStr, D_STR_ROOM); ///< "Room" +IRTEXT_CONST_STRING(k6thSenseStr, D_STR_6THSENSE); ///< "6th Sense" +IRTEXT_CONST_STRING(kTypeStr, D_STR_TYPE); ///< "Type" +IRTEXT_CONST_STRING(kSpecialStr, D_STR_SPECIAL); ///< "Special" +IRTEXT_CONST_STRING(kIdStr, D_STR_ID); ///< "Id" / Device Identifier +IRTEXT_CONST_STRING(kVaneStr, D_STR_VANE); ///< "Vane" +IRTEXT_CONST_STRING(kLockStr, D_STR_LOCK); ///< "Lock" + +IRTEXT_CONST_STRING(kAutoStr, D_STR_AUTO); ///< "Auto" +IRTEXT_CONST_STRING(kAutomaticStr, D_STR_AUTOMATIC); ///< "Automatic" +IRTEXT_CONST_STRING(kManualStr, D_STR_MANUAL); ///< "Manual" +IRTEXT_CONST_STRING(kCoolStr, D_STR_COOL); ///< "Cool" +IRTEXT_CONST_STRING(kCoolingStr, D_STR_COOLING); ///< "Cooling" +IRTEXT_CONST_STRING(kHeatStr, D_STR_HEAT); ///< "Heat" +IRTEXT_CONST_STRING(kHeatingStr, D_STR_HEATING); ///< "Heating" +IRTEXT_CONST_STRING(kDryStr, D_STR_DRY); ///< "Dry" +IRTEXT_CONST_STRING(kDryingStr, D_STR_DRYING); ///< "Drying" +IRTEXT_CONST_STRING(kDehumidifyStr, D_STR_DEHUMIDIFY); ///< "Dehumidify" +IRTEXT_CONST_STRING(kFanStr, D_STR_FAN); ///< "Fan" +// The following Fans strings with "only" are required to help with +// HomeAssistant & Google Home Climate integration. For compatibility only. +// Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes +IRTEXT_CONST_STRING(kFanOnlyStr, D_STR_FANONLY); ///< "fan-only" +IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (HA/legacy) +IRTEXT_CONST_STRING(kFanOnlyWithSpaceStr, D_STR_FANSPACEONLY); ///< "Fan Only" +IRTEXT_CONST_STRING(kFanOnlyNoSpaceStr, D_STR_FANONLYNOSPACE); ///< "FanOnly" + +IRTEXT_CONST_STRING(kRecycleStr, D_STR_RECYCLE); ///< "Recycle" + +IRTEXT_CONST_STRING(kMaxStr, D_STR_MAX); ///< "Max" +IRTEXT_CONST_STRING(kMaximumStr, D_STR_MAXIMUM); ///< "Maximum" +IRTEXT_CONST_STRING(kMinStr, D_STR_MIN); ///< "Min" +IRTEXT_CONST_STRING(kMinimumStr, D_STR_MINIMUM); ///< "Minimum" +IRTEXT_CONST_STRING(kMedHighStr, D_STR_MED_HIGH); ///< "Med-high" +IRTEXT_CONST_STRING(kMedStr, D_STR_MED); ///< "Med" +IRTEXT_CONST_STRING(kMediumStr, D_STR_MEDIUM); ///< "Medium" + +IRTEXT_CONST_STRING(kHighestStr, D_STR_HIGHEST); ///< "Highest" +IRTEXT_CONST_STRING(kHighStr, D_STR_HIGH); ///< "High" +IRTEXT_CONST_STRING(kHiStr, D_STR_HI); ///< "Hi" +IRTEXT_CONST_STRING(kMidStr, D_STR_MID); ///< "Mid" +IRTEXT_CONST_STRING(kMiddleStr, D_STR_MIDDLE); ///< "Middle" +IRTEXT_CONST_STRING(kLowStr, D_STR_LOW); ///< "Low" +IRTEXT_CONST_STRING(kLoStr, D_STR_LO); ///< "Lo" +IRTEXT_CONST_STRING(kLowestStr, D_STR_LOWEST); ///< "Lowest" +IRTEXT_CONST_STRING(kMaxRightStr, D_STR_MAXRIGHT); ///< "Max Right" +IRTEXT_CONST_STRING(kMaxRightNoSpaceStr, D_STR_MAXRIGHT_NOSPACE); ///< + ///< "MaxRight" +IRTEXT_CONST_STRING(kRightMaxStr, D_STR_RIGHTMAX); ///< "Right Max" +IRTEXT_CONST_STRING(kRightMaxNoSpaceStr, D_STR_RIGHTMAX_NOSPACE); ///< + ///< "RightMax" +IRTEXT_CONST_STRING(kRightStr, D_STR_RIGHT); ///< "Right" +IRTEXT_CONST_STRING(kLeftStr, D_STR_LEFT); ///< "Left" +IRTEXT_CONST_STRING(kMaxLeftStr, D_STR_MAXLEFT); ///< "Max Left" +IRTEXT_CONST_STRING(kMaxLeftNoSpaceStr, D_STR_MAXLEFT_NOSPACE); ///< "MaxLeft" +IRTEXT_CONST_STRING(kLeftMaxStr, D_STR_LEFTMAX); ///< "Left Max" +IRTEXT_CONST_STRING(kLeftMaxNoSpaceStr, D_STR_LEFTMAX_NOSPACE); ///< "LeftMax" +IRTEXT_CONST_STRING(kWideStr, D_STR_WIDE); ///< "Wide" +IRTEXT_CONST_STRING(kCentreStr, D_STR_CENTRE); ///< "Centre" +IRTEXT_CONST_STRING(kTopStr, D_STR_TOP); ///< "Top" +IRTEXT_CONST_STRING(kBottomStr, D_STR_BOTTOM); ///< "Bottom" + +// Compound words/phrases/descriptions from pre-defined words. +IRTEXT_CONST_STRING(kEconoToggleStr, D_STR_ECONOTOGGLE); ///< "Econo Toggle" +IRTEXT_CONST_STRING(kEyeAutoStr, D_STR_EYEAUTO); ///< "Eye Auto" +IRTEXT_CONST_STRING(kLightToggleStr, D_STR_LIGHTTOGGLE); ///< "Light Toggle" +///< "Outside Quiet" +IRTEXT_CONST_STRING(kOutsideQuietStr, D_STR_OUTSIDEQUIET); +IRTEXT_CONST_STRING(kPowerToggleStr, D_STR_POWERTOGGLE); ///< "Power Toggle" +IRTEXT_CONST_STRING(kPowerButtonStr, D_STR_POWERBUTTON); ///< "Power Button" +IRTEXT_CONST_STRING(kPreviousPowerStr, D_STR_PREVIOUSPOWER); ///< +///< "Previous Power" +IRTEXT_CONST_STRING(kDisplayTempStr, D_STR_DISPLAYTEMP); ///< "Display Temp" +IRTEXT_CONST_STRING(kSensorTempStr, D_STR_SENSORTEMP); ///< "Sensor Temp" +IRTEXT_CONST_STRING(kSleepTimerStr, D_STR_SLEEP_TIMER); ///< "Sleep Timer" +IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode" +IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///< +///< "Swing(V) Toggle" +IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle" +IRTEXT_CONST_STRING(kSetTimerCommandStr, D_STR_SET_TIMER); ///< "Set Timer" +IRTEXT_CONST_STRING(kScheduleStr, D_STR_SCHEDULE); ///< "Schedule" +IRTEXT_CONST_STRING(kChStr, D_STR_CH); ///< "CH#" +IRTEXT_CONST_STRING(kTimerActiveDaysStr, D_STR_TIMER_ACTIVE_DAYS); +///< "TimerActiveDays" +IRTEXT_CONST_STRING(kKeyStr, D_STR_KEY); ///< "Key" +IRTEXT_CONST_STRING(kValueStr, D_STR_VALUE); ///< "Value" + +// Separators & Punctuation +const char kTimeSep = D_CHR_TIME_SEP; ///< ':' +IRTEXT_CONST_STRING(kSpaceLBraceStr, D_STR_SPACELBRACE); ///< " (" +IRTEXT_CONST_STRING(kCommaSpaceStr, D_STR_COMMASPACE); ///< ", " +IRTEXT_CONST_STRING(kColonSpaceStr, D_STR_COLONSPACE); ///< ": " +IRTEXT_CONST_STRING(kDashStr, D_STR_DASH); ///< "-" + +// IRutils +// - Time +IRTEXT_CONST_STRING(kDayStr, D_STR_DAY); ///< "Day" +IRTEXT_CONST_STRING(kDaysStr, D_STR_DAYS); ///< "Days" +IRTEXT_CONST_STRING(kHourStr, D_STR_HOUR); ///< "Hour" +IRTEXT_CONST_STRING(kHoursStr, D_STR_HOURS); ///< "Hours" +IRTEXT_CONST_STRING(kMinuteStr, D_STR_MINUTE); ///< "Minute" +IRTEXT_CONST_STRING(kMinutesStr, D_STR_MINUTES); ///< "Minutes" +IRTEXT_CONST_STRING(kSecondStr, D_STR_SECOND); ///< "Second" +IRTEXT_CONST_STRING(kSecondsStr, D_STR_SECONDS); ///< "Seconds" +IRTEXT_CONST_STRING(kNowStr, D_STR_NOW); ///< "Now" +IRTEXT_CONST_STRING(kThreeLetterDayOfWeekStr, D_STR_THREELETTERDAYS); ///< +///< "SunMonTueWedThuFriSat" +IRTEXT_CONST_STRING(kYesStr, D_STR_YES); ///< "Yes" +IRTEXT_CONST_STRING(kNoStr, D_STR_NO); ///< "No" +IRTEXT_CONST_STRING(kTrueStr, D_STR_TRUE); ///< "True" +IRTEXT_CONST_STRING(kFalseStr, D_STR_FALSE); ///< "False" + +IRTEXT_CONST_STRING(kRepeatStr, D_STR_REPEAT); ///< "Repeat" +IRTEXT_CONST_STRING(kCodeStr, D_STR_CODE); ///< "Code" +IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits" + +// Model Names +IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F" +IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB" +IRTEXT_CONST_STRING(kYx1fsfStr, D_STR_YX1FSF); ///< "YX1FSF" +IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A" +IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B" +IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A" +IRTEXT_CONST_STRING(kRlt0541htabStr, D_STR_RLT0541HTA_B); ///< "R-LT0541-HTA-B" +IRTEXT_CONST_STRING(kArrah2eStr, D_STR_ARRAH2E); ///< "ARRAH2E" +IRTEXT_CONST_STRING(kArdb1Str, D_STR_ARDB1); ///< "ARDB1" +IRTEXT_CONST_STRING(kArreb1eStr, D_STR_ARREB1E); ///< "ARREB1E" +IRTEXT_CONST_STRING(kArjw2Str, D_STR_ARJW2); ///< "ARJW2" +IRTEXT_CONST_STRING(kArry4Str, D_STR_ARRY4); ///< "ARRY4" +IRTEXT_CONST_STRING(kArrew4eStr, D_STR_ARREW4E); ///< "ARREW4E" +IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///< + ///< "GE6711AR2853M" +IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403" +IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603" +IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604" +IRTEXT_CONST_STRING(kLg6711a20083vStr, D_STR_LG6711A20083V); ///< + ///< "LG6711A20083V" +IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1" +IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1" +IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE" +IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE" +IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE" +IRTEXT_CONST_STRING(kPkrStr, D_STR_PKR); ///< "PKR" +IRTEXT_CONST_STRING(kJkeStr, D_STR_JKE); ///< "JKE" +IRTEXT_CONST_STRING(kCkpStr, D_STR_CKP); ///< "CKP" +IRTEXT_CONST_STRING(kRkrStr, D_STR_RKR); ///< "RKR" +IRTEXT_CONST_STRING(kPanasonicLkeStr, D_STR_PANASONICLKE); ///< "PANASONICLKE" +IRTEXT_CONST_STRING(kPanasonicNkeStr, D_STR_PANASONICNKE); ///< "PANASONICNKE" +IRTEXT_CONST_STRING(kPanasonicDkeStr, D_STR_PANASONICDKE); ///< "PANASONICDKE" +IRTEXT_CONST_STRING(kPanasonicPkrStr, D_STR_PANASONICPKR); ///< "PANASONICPKR" +IRTEXT_CONST_STRING(kPanasonicJkeStr, D_STR_PANASONICJKE); ///< "PANASONICJKE" +IRTEXT_CONST_STRING(kPanasonicCkpStr, D_STR_PANASONICCKP); ///< "PANASONICCKP" +IRTEXT_CONST_STRING(kPanasonicRkrStr, D_STR_PANASONICRKR); ///< "PANASONICRKR" +IRTEXT_CONST_STRING(kA907Str, D_STR_A907); ///< "A907" +IRTEXT_CONST_STRING(kA705Str, D_STR_A705); ///< "A705" +IRTEXT_CONST_STRING(kA903Str, D_STR_A903); ///< "A903" +IRTEXT_CONST_STRING(kTac09chsdStr, D_STR_TAC09CHSD); ///< "TAC09CHSD" +IRTEXT_CONST_STRING(kGz055be1Str, D_STR_GZ055BE1); ///< "GZ055BE1" +IRTEXT_CONST_STRING(k122lzfStr, D_STR_122LZF); ///< "122LZF" +IRTEXT_CONST_STRING(kDg11j13aStr, D_STR_DG11J13A); ///< "DG11J13A" +IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104" +IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191" +IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3" +IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3" + +#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as + // a question mark, check for length > 1 + // to show only currently included protocols +// Protocol Names +// Needs to be in decode_type_t order. +IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { + D_STR_UNUSED "\x0" + COND(DECODE_RC5 || SEND_RC5, + D_STR_RC5, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RC6 || SEND_RC6, + D_STR_RC6, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_NEC || SEND_NEC, + D_STR_NEC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SONY || SEND_SONY, + D_STR_SONY, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PANASONIC || SEND_PANASONIC, + D_STR_PANASONIC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_JVC || SEND_JVC, + D_STR_JVC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SAMSUNG || SEND_SAMSUNG, + D_STR_SAMSUNG, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_WHYNTER || SEND_WHYNTER, + D_STR_WHYNTER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AIWA_RC_T501 || SEND_AIWA_RC_T501, + D_STR_AIWA_RC_T501, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LG || SEND_LG, + D_STR_LG, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO || SEND_SANYO, + D_STR_SANYO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI || SEND_MITSUBISHI, + D_STR_MITSUBISHI, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DISH || SEND_DISH, + D_STR_DISH, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SHARP || SEND_SHARP, + D_STR_SHARP, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_COOLIX || SEND_COOLIX, + D_STR_COOLIX, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN || SEND_DAIKIN, + D_STR_DAIKIN, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DENON || SEND_DENON, + D_STR_DENON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_KELVINATOR || SEND_KELVINATOR, + D_STR_KELVINATOR, D_STR_UNSUPPORTED) "\x0" + COND(SEND_SHERWOOD, + D_STR_SHERWOOD, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY + COND(DECODE_MITSUBISHI_AC || SEND_MITSUBISHI_AC, + D_STR_MITSUBISHI_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RCMM || SEND_RCMM, + D_STR_RCMM, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO || SEND_SANYO, + D_STR_SANYO_LC7461, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RC5 || SEND_RC5, + D_STR_RC5X, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GREE || SEND_GREE, + D_STR_GREE, D_STR_UNSUPPORTED) "\x0" + COND(SEND_PRONTO, + D_STR_PRONTO, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY + COND(DECODE_NEC || SEND_NEC, + D_STR_NEC_LIKE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ARGO || SEND_ARGO, + D_STR_ARGO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TROTEC || SEND_TROTEC, + D_STR_TROTEC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_NIKAI || SEND_NIKAI, + D_STR_NIKAI, D_STR_UNSUPPORTED) "\x0" + COND(SEND_RAW, + D_STR_RAW, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY + COND(SEND_GLOBALCACHE, + D_STR_GLOBALCACHE, D_STR_UNSUPPORTED) "\x0" // SEND + COND(DECODE_TOSHIBA_AC || SEND_TOSHIBA_AC, + D_STR_TOSHIBA_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_FUJITSU_AC || SEND_FUJITSU_AC, + D_STR_FUJITSU_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MIDEA || SEND_MIDEA, + D_STR_MIDEA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MAGIQUEST || SEND_MAGIQUEST, + D_STR_MAGIQUEST, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LASERTAG || SEND_LASERTAG, + D_STR_LASERTAG, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC || SEND_CARRIER_AC, + D_STR_CARRIER_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC || SEND_HAIER_AC, + D_STR_HAIER_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI2 || SEND_MITSUBISHI2, + D_STR_MITSUBISHI2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC || SEND_HITACHI_AC, + D_STR_HITACHI_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC1 || SEND_HITACHI_AC1, + D_STR_HITACHI_AC1, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC2 || SEND_HITACHI_AC2, + D_STR_HITACHI_AC2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GICABLE || SEND_GICABLE, + D_STR_GICABLE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC_YRW02 || SEND_HAIER_AC_YRW02, + D_STR_HAIER_AC_YRW02, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_WHIRLPOOL_AC || SEND_WHIRLPOOL_AC, + D_STR_WHIRLPOOL_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SAMSUNG_AC || SEND_SAMSUNG_AC, + D_STR_SAMSUNG_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LUTRON || SEND_LUTRON, + D_STR_LUTRON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ELECTRA_AC || SEND_ELECTRA_AC, + D_STR_ELECTRA_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PANASONIC_AC || SEND_PANASONIC_AC, + D_STR_PANASONIC_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PIONEER || SEND_PIONEER, + D_STR_PIONEER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LG || SEND_LG, + D_STR_LG2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MWM || SEND_MWM, + D_STR_MWM, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN2 || SEND_DAIKIN2, + D_STR_DAIKIN2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_VESTEL_AC || SEND_VESTEL_AC, + D_STR_VESTEL_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TECO || SEND_TECO, + D_STR_TECO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SAMSUNG36 || SEND_SAMSUNG36, + D_STR_SAMSUNG36, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TCL112AC || SEND_TCL112AC, + D_STR_TCL112AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_LEGOPF || SEND_LEGOPF, + D_STR_LEGOPF, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, + D_STR_MITSUBISHI_HEAVY_88, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY, + D_STR_MITSUBISHI_HEAVY_152, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN216 || SEND_DAIKIN216, + D_STR_DAIKIN216, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SHARP_AC || SEND_SHARP_AC, + D_STR_SHARP_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GOODWEATHER || SEND_GOODWEATHER, + D_STR_GOODWEATHER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_INAX || SEND_INAX, + D_STR_INAX, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN160 || SEND_DAIKIN160, + D_STR_DAIKIN160, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_NEOCLIMA || SEND_NEOCLIMA, + D_STR_NEOCLIMA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN176 || SEND_DAIKIN176, + D_STR_DAIKIN176, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN128 || SEND_DAIKIN128, + D_STR_DAIKIN128, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AMCOR || SEND_AMCOR, + D_STR_AMCOR, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN152 || SEND_DAIKIN152, + D_STR_DAIKIN152, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI136 || SEND_MITSUBISHI136, + D_STR_MITSUBISHI136, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MITSUBISHI112 || SEND_MITSUBISHI112, + D_STR_MITSUBISHI112, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC424 || SEND_HITACHI_AC424, + D_STR_HITACHI_AC424, D_STR_UNSUPPORTED) "\x0" + COND(SEND_SONY, + D_STR_SONY_38K, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_EPSON || SEND_EPSON, + D_STR_EPSON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SYMPHONY || SEND_SYMPHONY, + D_STR_SYMPHONY, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC3 || SEND_HITACHI_AC3, + D_STR_HITACHI_AC3, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN64 || SEND_DAIKIN64, + D_STR_DAIKIN64, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AIRWELL || SEND_AIRWELL, + D_STR_AIRWELL, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DELONGHI_AC || SEND_DELONGHI_AC, + D_STR_DELONGHI_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DOSHISHA || SEND_DOSHISHA, + D_STR_DOSHISHA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MULTIBRACKETS || SEND_MULTIBRACKETS, + D_STR_MULTIBRACKETS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC40 || SEND_CARRIER_AC40, + D_STR_CARRIER_AC40, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC64 || SEND_CARRIER_AC64, + D_STR_CARRIER_AC64, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC344 || SEND_HITACHI_AC344, + D_STR_HITACHI_AC344, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CORONA_AC || SEND_CORONA_AC, + D_STR_CORONA_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MIDEA24 || SEND_MIDEA24, + D_STR_MIDEA24, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ZEPEAL || SEND_ZEPEAL, + D_STR_ZEPEAL, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO_AC || SEND_SANYO_AC, + D_STR_SANYO_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_VOLTAS || SEND_VOLTAS, + D_STR_VOLTAS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_METZ || SEND_METZ, + D_STR_METZ, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TRANSCOLD || SEND_TRANSCOLD, + D_STR_TRANSCOLD, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TECHNIBEL_AC || SEND_TECHNIBEL_AC, + D_STR_TECHNIBEL_AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MIRAGE || SEND_MIRAGE, + D_STR_MIRAGE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ELITESCREENS || SEND_ELITESCREENS, + D_STR_ELITESCREENS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_PANASONIC_AC32 || SEND_PANASONIC_AC32, + D_STR_PANASONIC_AC32, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_MILESTAG2 || SEND_MILESTAG2, + D_STR_MILESTAG2, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ECOCLIM || SEND_ECOCLIM, + D_STR_ECOCLIM, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_XMP || SEND_XMP, + D_STR_XMP, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TRUMA || SEND_TRUMA, + D_STR_TRUMA, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC176 || SEND_HAIER_AC176, + D_STR_HAIER_AC176, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TEKNOPOINT || SEND_TEKNOPOINT, + D_STR_TEKNOPOINT, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_KELON || SEND_KELON, + D_STR_KELON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TROTEC_3550 || SEND_TROTEC_3550, + D_STR_TROTEC_3550, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO_AC88 || SEND_SANYO_AC88, + D_STR_SANYO_AC88, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_BOSE || SEND_BOSE, + D_STR_BOSE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_ARRIS || SEND_ARRIS, + D_STR_ARRIS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_RHOSS || SEND_RHOSS, + D_STR_RHOSS, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_AIRTON || SEND_AIRTON, + D_STR_AIRTON, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_COOLIX48 || SEND_COOLIX48, + D_STR_COOLIX48, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC264 || SEND_HITACHI_AC264, + D_STR_HITACHI_AC264, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_KELON168 || SEND_KELON168, + D_STR_KELON168, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HITACHI_AC296 || SEND_HITACHI_AC296, + D_STR_HITACHI_AC296, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN200 || SEND_DAIKIN200, + D_STR_DAIKIN200, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_HAIER_AC160 || SEND_HAIER_AC160, + D_STR_HAIER_AC160, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC128 || SEND_CARRIER_AC128, + D_STR_CARRIER_AC128, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TOTO || SEND_TOTO, + D_STR_TOTO, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CLIMABUTLER || SEND_CLIMABUTLER, + D_STR_CLIMABUTLER, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_TCL96AC || SEND_TCL96AC, + D_STR_TCL96AC, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_BOSCH144 || SEND_BOSCH144, + D_STR_BOSCH144, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_SANYO_AC152 || SEND_SANYO_AC152, + D_STR_SANYO_AC152, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_DAIKIN312 || SEND_DAIKIN312, + D_STR_DAIKIN312, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_GORENJE || SEND_GORENJE, + D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_WOWWEE || SEND_WOWWEE, + D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC84 || SEND_CARRIER_AC84, + D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_YORK || SEND_YORK, + D_STR_YORK, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_FUJITSU_AC264 || SEND_FUJITSU_AC264, + D_STR_FUJITSU_AC264, D_STR_UNSUPPORTED) "\x0" + ///< New protocol (macro) strings should be added just above this line. + "\x0" ///< This string requires double null termination. +}; +IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr); diff --git a/src/IRutils.cpp b/src/IRutils.cpp index d69580c2e..4e950be46 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -1,1439 +1,1439 @@ -// Copyright 2017-2021 David Conran - -#include "IRutils.h" -#ifndef UNIT_TEST -#include -#endif - -#define __STDC_LIMIT_MACROS -#include -#include -#include -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRtext.h" - -// On the ESP8266 platform we need to use a set of ..._P functions -// to handle the strings stored in the flash address space. -#ifndef STRCASECMP -#if defined(ESP8266) -#define STRCASECMP(LHS, RHS) \ - strcasecmp_P(LHS, reinterpret_cast(RHS)) -#else // ESP8266 -#define STRCASECMP strcasecmp -#endif // ESP8266 -#endif // STRCASECMP -#ifndef STRLEN -#if defined(ESP8266) -#define STRLEN(PTR) strlen_P(PTR) -#else // ESP8266 -#define STRLEN(PTR) strlen(PTR) -#endif // ESP8266 -#endif // STRLEN -#ifndef FPSTR -#define FPSTR(X) X -#endif // FPSTR - -/// Reverse the order of the requested least significant nr. of bits. -/// @param[in] input Bit pattern/integer to reverse. -/// @param[in] nbits Nr. of bits to reverse. (LSB -> MSB) -/// @return The reversed bit pattern. -uint64_t reverseBits(uint64_t input, uint16_t nbits) { - if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. - // Cap the nr. of bits to rotate to the max nr. of bits in the input. - nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); - uint64_t output = 0; - for (uint16_t i = 0; i < nbits; i++) { - output <<= 1; - output |= (input & 1); - input >>= 1; - } - // Merge any remaining unreversed bits back to the top of the reversed bits. - return (input << nbits) | output; -} - -/// Convert a uint64_t (unsigned long long) to a string. -/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. -/// @param[in] input The value to print -/// @param[in] base The output base. -/// @returns A String representation of the integer. -/// @note Based on Arduino's Print::printNumber() -String uint64ToString(uint64_t input, uint8_t base) { - String result = ""; - // prevent issues if called with base <= 1 - if (base < 2) base = 10; - // Check we have a base that we can actually print. - // i.e. [0-9A-Z] == 36 - if (base > 36) base = 10; - - // Reserve some string space to reduce fragmentation. - // 16 bytes should store a uint64 in hex text which is the likely worst case. - // 64 bytes would be the worst case (base 2). - result.reserve(16); - - do { - char c = input % base; - input /= base; - - if (c < 10) - c += '0'; - else - c += 'A' - 10; - result = c + result; - } while (input); - return result; -} - -/// Convert a int64_t (signed long long) to a string. -/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. -/// @param[in] input The value to print -/// @param[in] base The output base. -/// @returns A String representation of the integer. -String int64ToString(int64_t input, uint8_t base) { - if (input < 0) { - // Using String(kDashStr) to keep compatible with old arduino - // frameworks. Not needed with 3.0.2. - ///> @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1639#issuecomment-944906016 - return String(kDashStr) + uint64ToString(-input, base); - } - return uint64ToString(input, base); -} - -#ifdef ARDUINO -/// Print a uint64_t/unsigned long long to the Serial port -/// Serial.print() can't handle printing long longs. (uint64_t) -/// @param[in] input The value to print -/// @param[in] base The output base. -void serialPrintUint64(uint64_t input, uint8_t base) { - Serial.print(uint64ToString(input, base)); -} -#endif - -/// Convert a C-style string to a decode_type_t. -/// @param[in] str A C-style string containing a protocol name or number. -/// @return A decode_type_t enum. (decode_type_t::UNKNOWN if no match.) -decode_type_t strToDecodeType(const char * const str) { - auto *ptr = reinterpret_cast(kAllProtocolNamesStr); - uint16_t length = STRLEN(ptr); - for (uint16_t i = 0; length; i++) { - if (!STRCASECMP(str, ptr)) return (decode_type_t)i; - ptr += length + 1; - length = STRLEN(ptr); - } - // Handle integer values of the type by converting to a string and back again. - decode_type_t result = strToDecodeType( - typeToString((decode_type_t)atoi(str)).c_str()); - if (result > 0) - return result; - - return decode_type_t::UNKNOWN; -} - -/// Convert a protocol type (enum etc) to a human readable string. -/// @param[in] protocol Nr. (enum) of the protocol. -/// @param[in] isRepeat A flag indicating if it is a repeat message. -/// @return A String containing the protocol name. kUnknownStr if no match. -String typeToString(const decode_type_t protocol, const bool isRepeat) { - String result = ""; - result.reserve(30); // Size of longest protocol name + " (Repeat)" - if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { - result = kUnknownStr; - } else { - auto *ptr = reinterpret_cast(kAllProtocolNamesStr); - for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) { - if (i == protocol) { - result = FPSTR(ptr); - break; - } - ptr += STRLEN(ptr) + 1; - } - } - if (isRepeat) { - result += kSpaceLBraceStr; - result += kRepeatStr; - result += ')'; - } - return result; -} - -/// Does the given protocol use a complex state as part of the decode? -/// @param[in] protocol The decode_type_t protocol we are enquiring about. -/// @return True if the protocol uses a state array. False if just an integer. -bool hasACState(const decode_type_t protocol) { - switch (protocol) { - // This is kept sorted by name - case AMCOR: - case ARGO: - case BOSCH144: - case CARRIER_AC84: - case CARRIER_AC128: - case CORONA_AC: - case DAIKIN: - case DAIKIN128: - case DAIKIN152: - case DAIKIN160: - case DAIKIN176: - case DAIKIN2: - case DAIKIN200: - case DAIKIN216: - case DAIKIN312: - case ELECTRA_AC: - case FUJITSU_AC: - case FUJITSU_AC264: - case GREE: - case HAIER_AC: - case HAIER_AC_YRW02: - case HAIER_AC160: - case HAIER_AC176: - case HITACHI_AC: - case HITACHI_AC1: - case HITACHI_AC2: - case HITACHI_AC3: - case HITACHI_AC264: - case HITACHI_AC296: - case HITACHI_AC344: - case HITACHI_AC424: - case KELON168: - case KELVINATOR: - case MIRAGE: - case MITSUBISHI136: - case MITSUBISHI112: - case MITSUBISHI_AC: - case MITSUBISHI_HEAVY_88: - case MITSUBISHI_HEAVY_152: - case MWM: - case NEOCLIMA: - case PANASONIC_AC: - case RHOSS: - case SAMSUNG_AC: - case SANYO_AC: - case SANYO_AC88: - case SANYO_AC152: - case SHARP_AC: - case TCL96AC: - case TCL112AC: - case TEKNOPOINT: - case TOSHIBA_AC: - case TROTEC: - case TROTEC_3550: - case VOLTAS: - case WHIRLPOOL_AC: - case YORK: - return true; - default: - return false; - } -} - -/// Return the corrected length of a 'raw' format array structure -/// after over-large values are converted into multiple entries. -/// @param[in] results A ptr to a decode_results structure. -/// @return The corrected length. -uint16_t getCorrectedRawLength(const decode_results * const results) { - uint16_t extended_length = results->rawlen - 1; - for (uint16_t i = 0; i < results->rawlen - 1; i++) { - uint32_t usecs = results->rawbuf[i] * kRawTick; - // Add two extra entries for multiple larger than UINT16_MAX it is. - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - return extended_length; -} - -/// Return a String containing the key values of a decode_results structure -/// in a C/C++ code style format. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the code-ified result. -String resultToSourceCode(const decode_results * const results) { - String output = ""; - const uint16_t length = getCorrectedRawLength(results); - const bool hasState = hasACState(results->decode_type); - // Reserve some space for the string to reduce heap fragmentation. - // "uint16_t rawData[9999] = {}; // LONGEST_PROTOCOL\n" = ~55 chars. - // "NNNN, " = ~7 chars on average per raw entry - // Protocols with a `state`: - // "uint8_t state[NN] = {};\n" = ~25 chars - // "0xNN, " = 6 chars per byte. - // Protocols without a `state`: - // " DEADBEEFDEADBEEF\n" - // "uint32_t address = 0xDEADBEEF;\n" - // "uint32_t command = 0xDEADBEEF;\n" - // "uint64_t data = 0xDEADBEEFDEADBEEF;" = ~116 chars max. - output.reserve(55 + (length * 7) + hasState ? 25 + (results->bits / 8) * 6 - : 116); - // Start declaration - output += F("uint16_t "); // variable type - output += F("rawData["); // array name - output += uint64ToString(length, 10); - // array size - output += F("] = {"); // Start declaration - - // Dump data - for (uint16_t i = 1; i < results->rawlen; i++) { - uint32_t usecs; - for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; - usecs -= UINT16_MAX) { - output += uint64ToString(UINT16_MAX); - if (i % 2) - output += F(", 0, "); - else - output += F(", 0, "); - } - output += uint64ToString(usecs, 10); - if (i < results->rawlen - 1) - output += kCommaSpaceStr; // ',' not needed on the last one - if (i % 2 == 0) output += ' '; // Extra if it was even. - } - - // End declaration - output += F("};"); - - // Comment - output += F(" // "); - output += typeToString(results->decode_type, results->repeat); - // Only display the value if the decode type doesn't have an A/C state. - if (!hasState) - output += ' ' + uint64ToString(results->value, 16); - output += F("\n"); - - // Now dump "known" codes - if (results->decode_type != UNKNOWN) { - if (hasState) { -#if DECODE_AC - uint16_t nbytes = ceil(static_cast(results->bits) / 8.0); - output += F("uint8_t state["); - output += uint64ToString(nbytes); - output += F("] = {"); - for (uint16_t i = 0; i < nbytes; i++) { - output += F("0x"); - if (results->state[i] < 0x10) output += '0'; - output += uint64ToString(results->state[i], 16); - if (i < nbytes - 1) output += kCommaSpaceStr; - } - output += F("};\n"); -#endif // DECODE_AC - } else { - // Simple protocols - // Some protocols have an address &/or command. - // NOTE: It will ignore the atypical case when a message has been - // decoded but the address & the command are both 0. - if (results->address > 0 || results->command > 0) { - output += F("uint32_t address = 0x"); - output += uint64ToString(results->address, 16); - output += F(";\n"); - output += F("uint32_t command = 0x"); - output += uint64ToString(results->command, 16); - output += F(";\n"); - } - // Most protocols have data - output += F("uint64_t data = 0x"); - output += uint64ToString(results->value, 16); - output += F(";\n"); - } - } - return output; -} - -/// Dump out the decode_results structure. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the legacy information format. -/// @deprecated This is only for those that want this legacy format. -String resultToTimingInfo(const decode_results * const results) { - String output = ""; - String value = ""; - // Reserve some space for the string to reduce heap fragmentation. - // "Raw Timing[NNNN]:\n\n" = 19 chars - // " +123456, " / "-123456, " = ~12 chars on avg per raw entry. - output.reserve(19 + 12 * results->rawlen); // Should be less than this. - value.reserve(6); // Max value should be 2^17 = 131072 - output += F("Raw Timing["); - output += uint64ToString(results->rawlen - 1, 10); - output += F("]:\n"); - - for (uint16_t i = 1; i < results->rawlen; i++) { - if (i % 2 == 0) - output += kDashStr; // even - else - output += F(" +"); // odd - value = uint64ToString(results->rawbuf[i] * kRawTick); - // Space pad the value till it is at least 6 chars long. - while (value.length() < 6) value = ' ' + value; - output += value; - if (i < results->rawlen - 1) - output += kCommaSpaceStr; // ',' not needed for last one - if (!(i % 8)) output += '\n'; // Newline every 8 entries. - } - output += '\n'; - return output; -} - -/// Convert the decode_results structure's value/state to simple hexadecimal. -/// @param[in] result A ptr to a decode_results structure. -/// @return A String containing the output. -String resultToHexidecimal(const decode_results * const result) { - String output = F("0x"); - // Reserve some space for the string to reduce heap fragmentation. - output.reserve(2 * kStateSizeMax + 2); // Should cover worst cases. - if (hasACState(result->decode_type)) { -#if DECODE_AC - for (uint16_t i = 0; result->bits > i * 8; i++) { - if (result->state[i] < 0x10) output += '0'; // Zero pad - output += uint64ToString(result->state[i], 16); - } -#endif // DECODE_AC - } else { - output += uint64ToString(result->value, 16); - } - return output; -} - -/// Dump out the decode_results structure into a human readable format. -/// @param[in] results A ptr to a decode_results structure. -/// @return A String containing the output. -String resultToHumanReadableBasic(const decode_results * const results) { - String output = ""; - // Reserve some space for the string to reduce heap fragmentation. - // "Protocol : LONGEST_PROTOCOL_NAME (Repeat)\n" - // "Code : 0x (NNNN Bits)\n" = 70 chars - output.reserve(2 * kStateSizeMax + 70); // Should cover most cases. - // Show Encoding standard - output += kProtocolStr; - output += F(" : "); - output += typeToString(results->decode_type, results->repeat); - output += '\n'; - - // Show Code & length - output += kCodeStr; - output += F(" : "); - output += resultToHexidecimal(results); - output += kSpaceLBraceStr; - output += uint64ToString(results->bits); - output += ' '; - output += kBitsStr; - output += F(")\n"); - return output; -} - -/// Convert a decode_results into an array suitable for `sendRaw()`. -/// @param[in] decode A ptr to a decode_results structure that contains a mesg. -/// @return A PTR to a dynamically allocated uint16_t sendRaw compatible array. -/// @note The returned array needs to be delete[]'ed/free()'ed (deallocated) -/// after use by caller. -uint16_t* resultToRawArray(const decode_results * const decode) { - uint16_t *result = new uint16_t[getCorrectedRawLength(decode)]; - if (result != NULL) { // The memory was allocated successfully. - // Convert the decode data. - uint16_t pos = 0; - for (uint16_t i = 1; i < decode->rawlen; i++) { - uint32_t usecs = decode->rawbuf[i] * kRawTick; - while (usecs > UINT16_MAX) { // Keep truncating till it fits. - result[pos++] = UINT16_MAX; - result[pos++] = 0; // A 0 in a sendRaw() array basically means skip. - usecs -= UINT16_MAX; - } - result[pos++] = usecs; - } - } - return result; -} - -/// Sum all the bytes of an array and return the least significant 8-bits of -/// the result. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The 8-bit calculated result of all the bytes and init value. -uint8_t sumBytes(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t checksum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; - return checksum; -} - -/// Calculate a rolling XOR of all the bytes of an array. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The 8-bit calculated result of all the bytes and init value. -uint8_t xorBytes(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t checksum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; - return checksum; -} - -/// Count the number of bits of a certain type in an array. -/// @param[in] start A ptr to the start of the byte array to calculate over. -/// @param[in] length How many bytes to use in the calculation. -/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The nr. of bits found of the given type found in the array. -uint16_t countBits(const uint8_t * const start, const uint16_t length, - const bool ones, const uint16_t init) { - uint16_t count = init; - for (uint16_t offset = 0; offset < length; offset++) - for (uint8_t currentbyte = *(start + offset); - currentbyte; - currentbyte >>= 1) - if (currentbyte & 1) count++; - if (ones || length == 0) - return count; - else - return (length * 8) - count; -} - -/// Count the number of bits of a certain type in an Integer. -/// @param[in] data The value you want bits counted for. Starting from the LSB. -/// @param[in] length How many bits to use in the calculation? Starts at the LSB -/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. -/// @param[in] init Starting value of the calculation to use. (Default is 0) -/// @return The nr. of bits found of the given type found in the Integer. -uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones, - const uint16_t init) { - uint16_t count = init; - uint8_t bitsSoFar = length; - for (uint64_t remainder = data; remainder && bitsSoFar; - remainder >>= 1, bitsSoFar--) - if (remainder & 1) count++; - if (ones || length == 0) - return count; - else - return length - count; -} - -/// Invert/Flip the bits in an Integer. -/// @param[in] data The Integer that will be inverted. -/// @param[in] nbits How many bits are to be inverted. Starting from the LSB. -/// @return An Integer with the appropriate bits inverted/flipped. -uint64_t invertBits(const uint64_t data, const uint16_t nbits) { - // No change if we are asked to invert no bits. - if (nbits == 0) return data; - uint64_t result = ~data; - // If we are asked to invert all the bits or more than we have, it's simple. - if (nbits >= sizeof(data) * 8) return result; - // Mask off any unwanted bits and return the result. - return (result & ((1ULL << nbits) - 1)); -} - -/// Convert degrees Celsius to degrees Fahrenheit. -float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } - -/// Convert degrees Fahrenheit to degrees Celsius. -float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } - -namespace irutils { - /// Create a String with a colon separated "label: value" pair suitable for - /// Humans. - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addLabeledString(const String value, const String label, - const bool precomma) { - String result = ""; - // ", " + ": " = 4 chars - result.reserve(4 + value.length() + label.length()); - if (precomma) result += kCommaSpaceStr; - result += label; - result += kColonSpaceStr; - return result + value; - } - - /// Create a String with a colon separated flag suitable for Humans. - /// e.g. "Power: On" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addBoolToString(const bool value, const String label, - const bool precomma) { - return addLabeledString(value ? kOnStr : kOffStr, label, precomma); - } - - /// Create a String with a colon separated toggle flag suitable for Humans. - /// e.g. "Light: Toggle", "Light: -" - /// @param[in] toggle The value of the toggle to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addToggleToString(const bool toggle, const String label, - const bool precomma) { - return addLabeledString(toggle ? kToggleStr : kDashStr, label, precomma); - } - - /// Create a String with a colon separated labeled Integer suitable for - /// Humans. - /// e.g. "Foo: 23" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addIntToString(const uint16_t value, const String label, - const bool precomma) { - return addLabeledString(uint64ToString(value), label, precomma); - } - - /// Create a String with a colon separated labeled Integer suitable for - /// Humans. - /// e.g. "Foo: 23" - /// @param[in] value The value to come after the label. - /// @param[in] label The label to precede the value. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addSignedIntToString(const int16_t value, const String label, - const bool precomma) { - return addLabeledString(int64ToString(value), label, precomma); - } - - - /// Generate the model string for a given Protocol/Model pair. - /// @param[in] protocol The IR protocol. - /// @param[in] model The model number for that protocol. - /// @return The resulting String. - /// @note After adding a new model you should update IRac::strToModel() too. - String modelToStr(const decode_type_t protocol, const int16_t model) { - switch (protocol) { - case decode_type_t::FUJITSU_AC: - switch (model) { - case fujitsu_ac_remote_model_t::ARRAH2E: return kArrah2eStr; - case fujitsu_ac_remote_model_t::ARDB1: return kArdb1Str; - case fujitsu_ac_remote_model_t::ARREB1E: return kArreb1eStr; - case fujitsu_ac_remote_model_t::ARJW2: return kArjw2Str; - case fujitsu_ac_remote_model_t::ARRY4: return kArry4Str; - case fujitsu_ac_remote_model_t::ARREW4E: return kArrew4eStr; - default: return kUnknownStr; - } - break; - case decode_type_t::GREE: - switch (model) { - case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; - case gree_ac_remote_model_t::YBOFB: return kYbofbStr; - case gree_ac_remote_model_t::YX1FSF: return kYx1fsfStr; - default: return kUnknownStr; - } - break; - case decode_type_t::HAIER_AC176: - switch (model) { - case haier_ac176_remote_model_t::V9014557_A: - return kV9014557AStr; - case haier_ac176_remote_model_t::V9014557_B: - return kV9014557BStr; - default: - return kUnknownStr; - } - break; - case decode_type_t::HITACHI_AC1: - switch (model) { - case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: - return kRlt0541htaaStr; - case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: - return kRlt0541htabStr; - default: - return kUnknownStr; - } - break; - case decode_type_t::LG: - case decode_type_t::LG2: - switch (model) { - case lg_ac_remote_model_t::GE6711AR2853M: return kGe6711ar2853mStr; - case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str; - case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str; - case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str; - case lg_ac_remote_model_t::LG6711A20083V: return kLg6711a20083vStr; - default: return kUnknownStr; - } - break; - case decode_type_t::MIRAGE: - switch (model) { - case mirage_ac_remote_model_t::KKG9AC1: return kKkg9ac1Str; - case mirage_ac_remote_model_t::KKG29AC1: return kKkg29ac1Str; - default: return kUnknownStr; - } - break; - case decode_type_t::PANASONIC_AC: - switch (model) { - case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr; - case panasonic_ac_remote_model_t::kPanasonicNke: return kNkeStr; - case panasonic_ac_remote_model_t::kPanasonicDke: return kDkeStr; - case panasonic_ac_remote_model_t::kPanasonicJke: return kJkeStr; - case panasonic_ac_remote_model_t::kPanasonicCkp: return kCkpStr; - case panasonic_ac_remote_model_t::kPanasonicRkr: return kRkrStr; - default: return kUnknownStr; - } - break; - case decode_type_t::SHARP_AC: - switch (model) { - case sharp_ac_remote_model_t::A907: return kA907Str; - case sharp_ac_remote_model_t::A705: return kA705Str; - case sharp_ac_remote_model_t::A903: return kA903Str; - default: return kUnknownStr; - } - break; - case decode_type_t::TCL112AC: - switch (model) { - case tcl_ac_remote_model_t::TAC09CHSD: return kTac09chsdStr; - case tcl_ac_remote_model_t::GZ055BE1: return kGz055be1Str; - default: return kUnknownStr; - } - break; - case decode_type_t::VOLTAS: - switch (model) { - case voltas_ac_remote_model_t::kVoltas122LZF: return k122lzfStr; - default: return kUnknownStr; - } - break; - case decode_type_t::WHIRLPOOL_AC: - switch (model) { - case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr; - case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str; - default: return kUnknownStr; - } - break; - case decode_type_t::ARGO: - switch (model) { - case argo_ac_remote_model_t::SAC_WREM2: return kArgoWrem2Str; - case argo_ac_remote_model_t::SAC_WREM3: return kArgoWrem3Str; - default: return kUnknownStr; - } - break; - default: return kUnknownStr; - } - } - - /// Create a String of human output for a given protocol model number. - /// e.g. "Model: JKE" - /// @param[in] protocol The IR protocol. - /// @param[in] model The model number for that protocol. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addModelToString(const decode_type_t protocol, const int16_t model, - const bool precomma) { - String result = ""; - // ", Model: NNN (BlahBlahEtc)" = ~40 chars for longest model name. - result.reserve(40); - result += addIntToString(model, kModelStr, precomma); - result += kSpaceLBraceStr; - result += modelToStr(protocol, model); - return result + ')'; - } - - /// Create a String of human output for a given temperature. - /// e.g. "Temp: 25C" - /// @param[in] degrees The temperature in degrees. - /// @param[in] celsius Is the temp Celsius or Fahrenheit. - /// true is C, false is F - /// @param[in] precomma Should the output string start with ", " or not? - /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? - /// @return The resulting String. - String addTempToString(const uint16_t degrees, const bool celsius, - const bool precomma, const bool isSensorTemp) { - String result = addIntToString(degrees, (isSensorTemp)? - kSensorTempStr : kTempStr, precomma); - result += celsius ? 'C' : 'F'; - return result; - } - - /// Create a String of human output for a given temperature. - /// e.g. "Temp: 25.5C" - /// @param[in] degrees The temperature in degrees. - /// @param[in] celsius Is the temp Celsius or Fahrenheit. - /// true is C, false is F - /// @param[in] precomma Should the output string start with ", " or not? - /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? - /// @return The resulting String. - String addTempFloatToString(const float degrees, const bool celsius, - const bool precomma, const bool isSensorTemp) { - String result = ""; - result.reserve(21); // Assuming ", Sensor Temp: XXX.5F" is the largest. - result += addIntToString(degrees, (isSensorTemp)? - kSensorTempStr : kTempStr, precomma); - // Is it a half degree? - if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); - result += celsius ? 'C' : 'F'; - return result; - } - - /// Create a String of human output for the given operating mode. - /// e.g. "Mode: 1 (Cool)" - /// @param[in] mode The operating mode to display. - /// @param[in] automatic The numeric value for Auto mode. - /// @param[in] cool The numeric value for Cool mode. - /// @param[in] heat The numeric value for Heat mode. - /// @param[in] dry The numeric value for Dry mode. - /// @param[in] fan The numeric value for Fan mode. - /// @return The resulting String. - String addModeToString(const uint8_t mode, const uint8_t automatic, - const uint8_t cool, const uint8_t heat, - const uint8_t dry, const uint8_t fan) { - String result = ""; - result.reserve(22); // ", Mode: NNN (UNKNOWN)" - result += addIntToString(mode, kModeStr); - result += kSpaceLBraceStr; - if (mode == automatic) result += kAutoStr; - else if (mode == cool) result += kCoolStr; - else if (mode == heat) result += kHeatStr; - else if (mode == dry) result += kDryStr; - else if (mode == fan) result += kFanStr; - else - result += kUnknownStr; - return result + ')'; - } - - /// Create a String of the 3-letter day of the week from a numerical day of - /// the week. e.g. "Day: 1 (Mon)" - /// @param[in] day_of_week A numerical version of the sequential day of the - /// week. e.g. Saturday = 7 etc. - /// @param[in] offset Days to offset by. - /// e.g. For different day starting the week. - /// @param[in] precomma Should the output string start with ", " or not? - /// @return The resulting String. - String addDayToString(const uint8_t day_of_week, const int8_t offset, - const bool precomma) { - String result = ""; - result.reserve(19); // ", Day: N (UNKNOWN)" - result += addIntToString(day_of_week, kDayStr, precomma); - result += kSpaceLBraceStr; - result += dayToString(day_of_week, offset); - return result + ')'; - } - - /// Create a String of the 3-letter day of the week from a numerical day of - /// the week. e.g. "Mon" - /// @param[in] day_of_week A numerical version of the sequential day of the - /// week. e.g. Sunday = 1, Monday = 2, ..., Saturday = 7 - /// @param[in] offset Days to offset by. - /// e.g. For different day starting the week. - /// @return The resulting String. - String dayToString(const uint8_t day_of_week, const int8_t offset) { - if ((uint8_t)(day_of_week + offset) < 7) -#if UNIT_TEST - return String(kThreeLetterDayOfWeekStr).substr( - (day_of_week + offset) * 3, 3); -#else // UNIT_TEST - return String(kThreeLetterDayOfWeekStr).substring( - (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3); -#endif // UNIT_TEST - else - return kUnknownStr; - } - - /// Create a String of human output for the given fan speed. - /// e.g. "Fan: 0 (Auto)" - /// @param[in] speed The numeric speed of the fan to display. - /// @param[in] high The numeric value for High speed. (second highest) - /// @param[in] low The numeric value for Low speed. - /// @param[in] automatic The numeric value for Auto speed. - /// @param[in] quiet The numeric value for Quiet speed. - /// @param[in] medium The numeric value for Medium speed. - /// @param[in] maximum The numeric value for Highest speed. (if > high) - /// @param[in] medium_high The numeric value for third-highest speed. - /// (if > medium) - /// @return The resulting String. - String addFanToString(const uint8_t speed, const uint8_t high, - const uint8_t low, const uint8_t automatic, - const uint8_t quiet, const uint8_t medium, - const uint8_t maximum, const uint8_t medium_high) { - String result = ""; - result.reserve(21); // ", Fan: NNN (UNKNOWN)" - result += addIntToString(speed, kFanStr); - result += kSpaceLBraceStr; - if (speed == high) result += kHighStr; - else if (speed == low) result += kLowStr; - else if (speed == automatic) result += kAutoStr; - else if (speed == quiet) result += kQuietStr; - else if (speed == medium) result += kMediumStr; - else if (speed == maximum) result += kMaximumStr; - else if (speed == medium_high) result += kMedHighStr; - else - result += kUnknownStr; - return result + ')'; - } - - /// Create a String of human output for the given horizontal swing setting. - /// e.g. "Swing(H): 0 (Auto)" - /// @param[in] position The numeric position of the swing to display. - /// @param[in] automatic The numeric value for Auto position. - /// @param[in] maxleft The numeric value for most left position. - /// @param[in] left The numeric value for Left position. - /// @param[in] middle The numeric value for Middle position. - /// @param[in] right The numeric value for Right position. - /// @param[in] maxright The numeric value for most right position. - /// @param[in] off The numeric value for Off position. - /// @param[in] leftright The numeric value for "left right" position. - /// @param[in] rightleft The numeric value for "right left" position. - /// @param[in] threed The numeric value for 3D setting. - /// @param[in] wide The numeric value for Wide position. - /// @return The resulting String. - String addSwingHToString(const uint8_t position, const uint8_t automatic, - const uint8_t maxleft, const uint8_t left, - const uint8_t middle, - const uint8_t right, const uint8_t maxright, - const uint8_t off, - const uint8_t leftright, const uint8_t rightleft, - const uint8_t threed, const uint8_t wide) { - String result = ""; - result.reserve(30); // ", Swing(H): NNN (Left Right)" - result += addIntToString(position, kSwingHStr); - result += kSpaceLBraceStr; - if (position == automatic) { - result += kAutoStr; - } else if (position == left) { - result += kLeftStr; - } else if (position == middle) { - result += kMiddleStr; - } else if (position == right) { - result += kRightStr; - } else if (position == maxleft) { - result += kMaxLeftStr; - } else if (position == maxright) { - result += kMaxRightStr; - } else if (position == off) { - result += kOffStr; - } else if (position == leftright) { - result += kLeftStr; - result += ' '; - result += kRightStr; - } else if (position == rightleft) { - result += kRightStr; - result += ' '; - result += kLeftStr; - } else if (position == threed) { - result += k3DStr; - } else if (position == wide) { - result += kWideStr; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// Create a String of human output for the given vertical swing setting. - /// e.g. "Swing(V): 0 (Auto)" - /// @param[in] position The numeric position of the swing to display. - /// @param[in] automatic The numeric value for Auto position. - /// @param[in] highest The numeric value for Highest position. - /// @param[in] high The numeric value for High position. - /// @param[in] uppermiddle The numeric value for Upper Middle position. - /// @param[in] middle The numeric value for Middle position. - /// @param[in] lowermiddle The numeric value for Lower Middle position. - /// @param[in] low The numeric value for Low position. - /// @param[in] lowest The numeric value for Low position. - /// @param[in] off The numeric value for Off position. - /// @param[in] swing The numeric value for Swing setting. - /// @param[in] breeze The numeric value for Breeze setting. - /// @param[in] circulate The numeric value for Circulate setting. - /// @return The resulting String. - String addSwingVToString(const uint8_t position, const uint8_t automatic, - const uint8_t highest, const uint8_t high, - const uint8_t uppermiddle, - const uint8_t middle, - const uint8_t lowermiddle, - const uint8_t low, const uint8_t lowest, - const uint8_t off, const uint8_t swing, - const uint8_t breeze, const uint8_t circulate) { - String result = ""; - result.reserve(31); // ", Swing(V): NNN (Upper Middle)" - result += addIntToString(position, kSwingVStr); - result += kSpaceLBraceStr; - if (position == automatic) { - result += kAutoStr; - } else if (position == highest) { - result += kHighestStr; - } else if (position == high) { - result += kHighStr; - } else if (position == middle) { - result += kMiddleStr; - } else if (position == low) { - result += kLowStr; - } else if (position == lowest) { - result += kLowestStr; - } else if (position == off) { - result += kOffStr; - } else if (position == uppermiddle) { - result += kUpperStr; - result += ' '; - result += kMiddleStr; - } else if (position == lowermiddle) { - result += kLowerStr; - result += ' '; - result += kMiddleStr; - } else if (position == swing) { - result += kSwingStr; - } else if (position == breeze) { - result += kBreezeStr; - } else if (position == circulate) { - result += kCirculateStr; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// @brief Create a String of human output for the given timer setting. - /// e.g. "Timer Mode: 2 (Schedule 1)" - /// @param[in] timerMode The numeric value of the timer mode to display. - /// @param[in] noTimer The numeric value for no timer (off) - /// @param[in] delayTimer The numeric value for delay (sleep) timer - /// @param[in] schedule1 The numeric value for schedule timer #1 - /// @param[in] schedule2 The numeric value for schedule timer #2 - /// @param[in] schedule3 The numeric value for schedule timer #3 - /// @param[in] precomma Should the output string start with ", " or not? - /// @return String representation - String addTimerModeToString(const uint8_t timerMode, const uint8_t noTimer, - const uint8_t delayTimer, const uint8_t schedule1, - const uint8_t schedule2, const uint8_t schedule3, - const bool precomma) { - String result = ""; - result.reserve(28); // ", Timer Mode: 2 (Schedule 1)" - result += addIntToString(timerMode, kTimerModeStr, precomma); - result += kSpaceLBraceStr; - if (timerMode == noTimer) { - result += kOffStr; - } else if (timerMode == delayTimer) { - result += kSleepTimerStr; - } else if (timerMode == schedule1) { - result += kScheduleStr; - result += '1'; - } else if (timerMode == schedule2) { - result += kScheduleStr; - result += '2'; - } else if (timerMode == schedule3) { - result += kScheduleStr; - result += '3'; - } else { - result += kUnknownStr; - } - return result + ')'; - } - - /// @brief Create a String of human output for the given channel - /// e.g. "[CH#0]" - /// @param channel The numeric value of the channel to display. - /// @return String representation - String channelToString(const uint8_t channel) { - String result = ""; - result.reserve(6); // "[CH#4]" - result += "["; - result += kChStr; - result += uint64ToString(channel); - result += "]"; - return result; - } - - /// @brief Create a String of human output for the given command type - /// e.g. "IFeel Report" - /// @param irCommandType The numeric value of the command type to display. - /// @param acControlCmd The numeric value of the "control" (default) command - /// @param iFeelReportCmd The numeric value of the sensor temperature command - /// @param timerCmd The numeric value of the timer config IR command - /// @param configCmd The numeric value of the config param set IR command - /// @return String representation - String irCommandTypeToString(uint8_t irCommandType, uint8_t acControlCmd, - uint8_t iFeelReportCmd, uint8_t timerCmd, - uint8_t configCmd) { - String result = ""; - result.reserve(12); // "IFeel Report" - if (irCommandType == acControlCmd) { - result += kCommandStr; - } else if (irCommandType == iFeelReportCmd) { - result += kIFeelReportStr; - } else if (irCommandType == timerCmd) { - result += kTimerStr; - } else if (irCommandType == configCmd) { - result += kConfigCommandStr; - } else { - result += kUnknownStr; - } - return result; - } - - /// @brief Create a String of the 3-letter day of the week bitmap - // e.g. 0b0000101 is "Sun | Tue" - /// @param[in] daysBitmap The bitmap representing days of week to represent - /// e.g bit[0]=Sunday, bit[1]=Monday, ... - /// @param[in] offset Days to offset by. - /// e.g. For different day starting the week. - /// @return String representation. - String daysBitmaskToString(uint8_t daysBitmap, uint8_t offset) { - String result = ""; - result.reserve(27); // Sun|Mon|Tue|Wed|Thu|Fri|Sat - - for (uint8_t i = 0; i < 7; ++i) { - if (((daysBitmap >> i) & 0b1) == 0b1) { - if (result.length() > 0) { - result += "|"; - } - result += irutils::dayToString(i, offset); - } - } - return result; - } - - /// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. - /// @param[in] unescaped A String containing text to make HTML safe. - /// @return A string that is HTML safe. - String htmlEscape(const String unescaped) { - String result = ""; - uint16_t ulen = unescaped.length(); - result.reserve(ulen); // The result will be at least the size of input. - for (size_t i = 0; i < ulen; i++) { - char c = unescaped[i]; - switch (c) { - // ';!-"<>=&#{}() are all unsafe. - case '\'': result += F("'"); break; - case ';': result += F(";"); break; - case '!': result += F("!"); break; - case '-': result += F("‐"); break; - case '\"': result += F("""); break; - case '<': result += F("<"); break; - case '>': result += F(">"); break; - case '=': result += F("&#equals;"); break; - case '&': result += F("&"); break; - case '#': result += F("#"); break; - case '{': result += F("{"); break; - case '}': result += F("}"); break; - case '(': result += F("("); break; - case ')': result += F(")"); break; - default: result += c; - } - } - return result; - } - - /// Convert a nr. of milliSeconds into a Human-readable string. - /// e.g. "1 Day 6 Hours 34 Minutes 17 Seconds" - /// @param[in] msecs Nr. of milliSeconds (ms). - /// @return A human readable string. - String msToString(uint32_t const msecs) { - uint32_t totalseconds = msecs / 1000; - if (totalseconds == 0) return kNowStr; - - // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. - uint8_t days = totalseconds / (60 * 60 * 24); - uint8_t hours = (totalseconds / (60 * 60)) % 24; - uint8_t minutes = (totalseconds / 60) % 60; - uint8_t seconds = totalseconds % 60; - - String result = ""; - result.reserve(42); // "99 Days, 23 Hours, 59 Minutes, 59 Seconds" - if (days) - result += uint64ToString(days) + ' ' + String((days > 1) ? kDaysStr - : kDayStr); - if (hours) { - if (result.length()) result += ' '; - result += uint64ToString(hours) + ' ' + String((hours > 1) ? kHoursStr - : kHourStr); - } - if (minutes) { - if (result.length()) result += ' '; - result += uint64ToString(minutes) + ' ' + String( - (minutes > 1) ? kMinutesStr : kMinuteStr); - } - if (seconds) { - if (result.length()) result += ' '; - result += uint64ToString(seconds) + ' ' + String( - (seconds > 1) ? kSecondsStr : kSecondStr); - } - return result; - } - - /// Convert a nr. of minutes into a 24h clock format Human-readable string. - /// e.g. "23:59" - /// @param[in] mins Nr. of Minutes. - /// @return A human readable string. - String minsToString(const uint16_t mins) { - String result = ""; - result.reserve(5); // 23:59 is the typical worst case. - if (mins / 60 < 10) result += '0'; // Zero pad the hours - result += uint64ToString(mins / 60) + kTimeSep; - if (mins % 60 < 10) result += '0'; // Zero pad the minutes. - result += uint64ToString(mins % 60); - return result; - } - - /// Sum all the nibbles together in a series of bytes. - /// @param[in] start A ptr to the start of the byte array to calculate over. - /// @param[in] length How many bytes to use in the calculation. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @return The 8-bit calculated result of all the bytes and init value. - uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, - const uint8_t init) { - uint8_t sum = init; - const uint8_t *ptr; - for (ptr = start; ptr - start < length; ptr++) - sum += (*ptr >> 4) + (*ptr & 0xF); - return sum; - } - - /// Sum all the nibbles together in an integer. - /// @param[in] data The integer to be summed. - /// @param[in] count The number of nibbles to sum. Starts from LSB. Max of 16. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @param[in] nibbleonly true, the result is 4 bits. false, it's 8 bits. - /// @return The 4/8-bit calculated result of all the nibbles and init value. - uint8_t sumNibbles(const uint64_t data, const uint8_t count, - const uint8_t init, const bool nibbleonly) { - uint8_t sum = init; - uint64_t copy = data; - const uint8_t nrofnibbles = (count < 16) ? count : (64 / 4); - for (uint8_t i = 0; i < nrofnibbles; i++, copy >>= 4) sum += copy & 0xF; - return nibbleonly ? sum & 0xF : sum; - } - - /// Sum all the bytes together in an integer. - /// @param[in] data The integer to be summed. - /// @param[in] count The number of bytes to sum. Starts from LSB. Max of 8. - /// @param[in] init Starting value of the calculation to use. (Default is 0) - /// @param[in] byteonly true, the result is 8 bits. false, it's 16 bits. - /// @return The 8/16-bit calculated result of all the bytes and init value. - uint16_t sumBytes(const uint64_t data, const uint8_t count, - const uint8_t init, const bool byteonly) { - uint16_t sum = init; - uint64_t copy = data; - const uint8_t nrofbytes = (count < 8) ? count : (64 / 8); - for (uint8_t i = 0; i < nrofbytes; i++, copy >>= 8) sum += (copy & 0xFF); - return byteonly ? sum & 0xFF : sum; - } - - /// Convert a byte of Binary Coded Decimal(BCD) into an Integer. - /// @param[in] bcd The BCD value. - /// @return A normal Integer value. - uint8_t bcdToUint8(const uint8_t bcd) { - if (bcd > 0x99) return 255; // Too big. - return (bcd >> 4) * 10 + (bcd & 0xF); - } - - /// Convert an Integer into a byte of Binary Coded Decimal(BCD). - /// @param[in] integer The number to convert. - /// @return An 8-bit BCD value. - uint8_t uint8ToBcd(const uint8_t integer) { - if (integer > 99) return 255; // Too big. - return ((integer / 10) << 4) + (integer % 10); - } - - /// Return the value of `position`th bit of an Integer. - /// @param[in] data Value to be examined. - /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. - /// @param[in] size Nr. of bits in data. - /// @return The bit's value. - bool getBit(const uint64_t data, const uint8_t position, const uint8_t size) { - if (position >= size) return false; // Outside of range. - return data & (1ULL << position); - } - - /// Return the value of `position`th bit of an Integer. - /// @param[in] data Value to be examined. - /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. - /// @return The bit's value. - bool getBit(const uint8_t data, const uint8_t position) { - if (position >= 8) return false; // Outside of range. - return data & (1 << position); - } - - /// Return the value of an Integer with the `position`th bit changed. - /// @param[in] data Value to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - /// @param[in] size Nr. of bits in data. - /// @return A suitably modified integer. - uint64_t setBit(const uint64_t data, const uint8_t position, const bool on, - const uint8_t size) { - if (position >= size) return data; // Outside of range. - uint64_t mask = 1ULL << position; - if (on) - return data | mask; - else - return data & ~mask; - } - - /// Return the value of an Integer with the `position`th bit changed. - /// @param[in] data Value to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - /// @return A suitably modified integer. - uint8_t setBit(const uint8_t data, const uint8_t position, const bool on) { - if (position >= 8) return data; // Outside of range. - uint8_t mask = 1 << position; - if (on) - return data | mask; - else - return data & ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 8-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint8_t * const data, const uint8_t position, const bool on) { - uint8_t mask = 1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 32-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint32_t * const data, const uint8_t position, const bool on) { - uint32_t mask = (uint32_t)1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter the value of an Integer with the `position`th bit changed. - /// @param[in,out] data A pointer to the 64-bit integer to be changed. - /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. - /// @param[in] on Value to set the position'th bit to. - void setBit(uint64_t * const data, const uint8_t position, const bool on) { - uint64_t mask = (uint64_t)1 << position; - if (on) - *data |= mask; - else - *data &= ~mask; - } - - /// Alter an uint8_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint8_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint8_t data) { - if (offset >= 8 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(uint8_t)(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Alter an uint32_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint32_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint32_t data) { - if (offset >= 32 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint32_t mask = UINT32_MAX >> (32 - ((nbits > 32) ? 32 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Alter an uint64_t value by overwriting an arbitrary given number of bits. - /// @param[in,out] dst A pointer to the value to be changed. - /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored - /// @param[in] nbits Nr of bits of data to be placed into the destination. - /// @param[in] data The value to be placed. - void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits, - const uint64_t data) { - if (offset >= 64 || !nbits) return; // Short circuit as it won't change. - // Calculate the mask for the supplied value. - uint64_t mask = UINT64_MAX >> (64 - ((nbits > 64) ? 64 : nbits)); - // Calculate the mask & clear the space for the data. - // Clear the destination bits. - *dst &= ~(mask << offset); - // Merge in the data. - *dst |= ((data & mask) << offset); - } - - /// Create byte pairs where the second byte of the pair is a bit - /// inverted/flipped copy of the first/previous byte of the pair. - /// @param[in,out] ptr A pointer to the start of array to modify. - /// @param[in] length The byte size of the array. - /// @note A length of `<= 1` will do nothing. - /// @return A ptr to the modified array. - uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) { - for (uint16_t i = 1; i < length; i += 2) { - // Code done this way to avoid a compiler warning bug. - uint8_t inv = ~*(ptr + i - 1); - *(ptr + i) = inv; - } - return ptr; - } - - /// Check an array to see if every second byte of a pair is a bit - /// inverted/flipped copy of the first/previous byte of the pair. - /// @param[in] ptr A pointer to the start of array to check. - /// @param[in] length The byte size of the array. - /// @note A length of `<= 1` will always return true. - /// @return true, if every second byte is inverted. Otherwise false. - bool checkInvertedBytePairs(const uint8_t * const ptr, - const uint16_t length) { - for (uint16_t i = 1; i < length; i += 2) { - // Code done this way to avoid a compiler warning bug. - uint8_t inv = ~*(ptr + i - 1); - if (*(ptr + i) != inv) return false; - } - return true; - } - - /// Perform a low level bit manipulation sanity check for the given cpu - /// architecture and the compiler operation. Calls to this should return - /// 0 if everything is as expected, anything else means the library won't work - /// as expected. - /// @return A bit mask value of potential issues. - /// 0: (e.g. 0b00000000) Everything appears okay. - /// 0th bit set: (0b1) Unexpected bit field/packing encountered. - /// Try a different compiler. - /// 1st bit set: (0b10) Unexpected Endianness. Try a different compiler flag - /// or use a CPU different architecture. - /// e.g. A result of 3 (0b11) would mean both a bit field and an Endianness - /// issue has been found. - uint8_t lowLevelSanityCheck(void) { - const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL; - volatile uint32_t EndianTest = 0x12345678; - const uint8_t kBitFieldError = 0b01; - const uint8_t kEndiannessError = 0b10; - uint8_t result = 0; - union bitpackdata { - struct { - uint64_t lowestbit:1; // 0th bit - uint64_t next7bits:7; // 1-7th bits - uint64_t _unused_1:20; // 8-27th bits - // Cross the 32 bit boundary. - uint64_t crossbits:16; // 28-43rd bits - uint64_t _usused_2:18; // 44-61st bits - uint64_t highest2bits:2; // 62-63rd bits - }; - uint64_t all; - }; - - bitpackdata data; - data.lowestbit = true; - data.next7bits = 0b0011100; // 0x1C - data._unused_1 = 0; - data.crossbits = 0x1234; - data._usused_2 = 0; - data.highest2bits = 0b10; // 2 - - if (data.all != kExpectedBitFieldResult) result |= kBitFieldError; - // Check that we are using Little Endian for integers -#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) - if (BYTE_ORDER != LITTLE_ENDIAN) result |= kEndiannessError; -#endif -#if defined(__IEEE_BIG_ENDIAN) || defined(__IEEE_BYTES_BIG_ENDIAN) - result |= kEndiannessError; -#endif - // Brute force check for little endian. - if (*((uint8_t*)(&EndianTest)) != 0x78) // NOLINT(readability/casting) - result |= kEndiannessError; - return result; - } -} // namespace irutils +// Copyright 2017-2021 David Conran + +#include "IRutils.h" +#ifndef UNIT_TEST +#include +#endif + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRtext.h" + +// On the ESP8266 platform we need to use a set of ..._P functions +// to handle the strings stored in the flash address space. +#ifndef STRCASECMP +#if defined(ESP8266) +#define STRCASECMP(LHS, RHS) \ + strcasecmp_P(LHS, reinterpret_cast(RHS)) +#else // ESP8266 +#define STRCASECMP strcasecmp +#endif // ESP8266 +#endif // STRCASECMP +#ifndef STRLEN +#if defined(ESP8266) +#define STRLEN(PTR) strlen_P(PTR) +#else // ESP8266 +#define STRLEN(PTR) strlen(PTR) +#endif // ESP8266 +#endif // STRLEN +#ifndef FPSTR +#define FPSTR(X) X +#endif // FPSTR + +/// Reverse the order of the requested least significant nr. of bits. +/// @param[in] input Bit pattern/integer to reverse. +/// @param[in] nbits Nr. of bits to reverse. (LSB -> MSB) +/// @return The reversed bit pattern. +uint64_t reverseBits(uint64_t input, uint16_t nbits) { + if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all. + // Cap the nr. of bits to rotate to the max nr. of bits in the input. + nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); + uint64_t output = 0; + for (uint16_t i = 0; i < nbits; i++) { + output <<= 1; + output |= (input & 1); + input >>= 1; + } + // Merge any remaining unreversed bits back to the top of the reversed bits. + return (input << nbits) | output; +} + +/// Convert a uint64_t (unsigned long long) to a string. +/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. +/// @param[in] input The value to print +/// @param[in] base The output base. +/// @returns A String representation of the integer. +/// @note Based on Arduino's Print::printNumber() +String uint64ToString(uint64_t input, uint8_t base) { + String result = ""; + // prevent issues if called with base <= 1 + if (base < 2) base = 10; + // Check we have a base that we can actually print. + // i.e. [0-9A-Z] == 36 + if (base > 36) base = 10; + + // Reserve some string space to reduce fragmentation. + // 16 bytes should store a uint64 in hex text which is the likely worst case. + // 64 bytes would be the worst case (base 2). + result.reserve(16); + + do { + char c = input % base; + input /= base; + + if (c < 10) + c += '0'; + else + c += 'A' - 10; + result = c + result; + } while (input); + return result; +} + +/// Convert a int64_t (signed long long) to a string. +/// Arduino String/toInt/Serial.print() can't handle printing 64 bit values. +/// @param[in] input The value to print +/// @param[in] base The output base. +/// @returns A String representation of the integer. +String int64ToString(int64_t input, uint8_t base) { + if (input < 0) { + // Using String(kDashStr) to keep compatible with old arduino + // frameworks. Not needed with 3.0.2. + ///> @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1639#issuecomment-944906016 + return String(kDashStr) + uint64ToString(-input, base); + } + return uint64ToString(input, base); +} + +#ifdef ARDUINO +/// Print a uint64_t/unsigned long long to the Serial port +/// Serial.print() can't handle printing long longs. (uint64_t) +/// @param[in] input The value to print +/// @param[in] base The output base. +void serialPrintUint64(uint64_t input, uint8_t base) { + Serial.print(uint64ToString(input, base)); +} +#endif + +/// Convert a C-style string to a decode_type_t. +/// @param[in] str A C-style string containing a protocol name or number. +/// @return A decode_type_t enum. (decode_type_t::UNKNOWN if no match.) +decode_type_t strToDecodeType(const char * const str) { + auto *ptr = reinterpret_cast(kAllProtocolNamesStr); + uint16_t length = STRLEN(ptr); + for (uint16_t i = 0; length; i++) { + if (!STRCASECMP(str, ptr)) return (decode_type_t)i; + ptr += length + 1; + length = STRLEN(ptr); + } + // Handle integer values of the type by converting to a string and back again. + decode_type_t result = strToDecodeType( + typeToString((decode_type_t)atoi(str)).c_str()); + if (result > 0) + return result; + + return decode_type_t::UNKNOWN; +} + +/// Convert a protocol type (enum etc) to a human readable string. +/// @param[in] protocol Nr. (enum) of the protocol. +/// @param[in] isRepeat A flag indicating if it is a repeat message. +/// @return A String containing the protocol name. kUnknownStr if no match. +String typeToString(const decode_type_t protocol, const bool isRepeat) { + String result = ""; + result.reserve(30); // Size of longest protocol name + " (Repeat)" + if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) { + result = kUnknownStr; + } else { + auto *ptr = reinterpret_cast(kAllProtocolNamesStr); + for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) { + if (i == protocol) { + result = FPSTR(ptr); + break; + } + ptr += STRLEN(ptr) + 1; + } + } + if (isRepeat) { + result += kSpaceLBraceStr; + result += kRepeatStr; + result += ')'; + } + return result; +} + +/// Does the given protocol use a complex state as part of the decode? +/// @param[in] protocol The decode_type_t protocol we are enquiring about. +/// @return True if the protocol uses a state array. False if just an integer. +bool hasACState(const decode_type_t protocol) { + switch (protocol) { + // This is kept sorted by name + case AMCOR: + case ARGO: + case BOSCH144: + case CARRIER_AC84: + case CARRIER_AC128: + case CORONA_AC: + case DAIKIN: + case DAIKIN128: + case DAIKIN152: + case DAIKIN160: + case DAIKIN176: + case DAIKIN2: + case DAIKIN200: + case DAIKIN216: + case DAIKIN312: + case ELECTRA_AC: + case FUJITSU_AC: + case FUJITSU_AC264: + case GREE: + case HAIER_AC: + case HAIER_AC_YRW02: + case HAIER_AC160: + case HAIER_AC176: + case HITACHI_AC: + case HITACHI_AC1: + case HITACHI_AC2: + case HITACHI_AC3: + case HITACHI_AC264: + case HITACHI_AC296: + case HITACHI_AC344: + case HITACHI_AC424: + case KELON168: + case KELVINATOR: + case MIRAGE: + case MITSUBISHI136: + case MITSUBISHI112: + case MITSUBISHI_AC: + case MITSUBISHI_HEAVY_88: + case MITSUBISHI_HEAVY_152: + case MWM: + case NEOCLIMA: + case PANASONIC_AC: + case RHOSS: + case SAMSUNG_AC: + case SANYO_AC: + case SANYO_AC88: + case SANYO_AC152: + case SHARP_AC: + case TCL96AC: + case TCL112AC: + case TEKNOPOINT: + case TOSHIBA_AC: + case TROTEC: + case TROTEC_3550: + case VOLTAS: + case WHIRLPOOL_AC: + case YORK: + return true; + default: + return false; + } +} + +/// Return the corrected length of a 'raw' format array structure +/// after over-large values are converted into multiple entries. +/// @param[in] results A ptr to a decode_results structure. +/// @return The corrected length. +uint16_t getCorrectedRawLength(const decode_results * const results) { + uint16_t extended_length = results->rawlen - 1; + for (uint16_t i = 0; i < results->rawlen - 1; i++) { + uint32_t usecs = results->rawbuf[i] * kRawTick; + // Add two extra entries for multiple larger than UINT16_MAX it is. + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + return extended_length; +} + +/// Return a String containing the key values of a decode_results structure +/// in a C/C++ code style format. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the code-ified result. +String resultToSourceCode(const decode_results * const results) { + String output = ""; + const uint16_t length = getCorrectedRawLength(results); + const bool hasState = hasACState(results->decode_type); + // Reserve some space for the string to reduce heap fragmentation. + // "uint16_t rawData[9999] = {}; // LONGEST_PROTOCOL\n" = ~55 chars. + // "NNNN, " = ~7 chars on average per raw entry + // Protocols with a `state`: + // "uint8_t state[NN] = {};\n" = ~25 chars + // "0xNN, " = 6 chars per byte. + // Protocols without a `state`: + // " DEADBEEFDEADBEEF\n" + // "uint32_t address = 0xDEADBEEF;\n" + // "uint32_t command = 0xDEADBEEF;\n" + // "uint64_t data = 0xDEADBEEFDEADBEEF;" = ~116 chars max. + output.reserve(55 + (length * 7) + hasState ? 25 + (results->bits / 8) * 6 + : 116); + // Start declaration + output += F("uint16_t "); // variable type + output += F("rawData["); // array name + output += uint64ToString(length, 10); + // array size + output += F("] = {"); // Start declaration + + // Dump data + for (uint16_t i = 1; i < results->rawlen; i++) { + uint32_t usecs; + for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX; + usecs -= UINT16_MAX) { + output += uint64ToString(UINT16_MAX); + if (i % 2) + output += F(", 0, "); + else + output += F(", 0, "); + } + output += uint64ToString(usecs, 10); + if (i < results->rawlen - 1) + output += kCommaSpaceStr; // ',' not needed on the last one + if (i % 2 == 0) output += ' '; // Extra if it was even. + } + + // End declaration + output += F("};"); + + // Comment + output += F(" // "); + output += typeToString(results->decode_type, results->repeat); + // Only display the value if the decode type doesn't have an A/C state. + if (!hasState) + output += ' ' + uint64ToString(results->value, 16); + output += F("\n"); + + // Now dump "known" codes + if (results->decode_type != UNKNOWN) { + if (hasState) { +#if DECODE_AC + uint16_t nbytes = ceil(static_cast(results->bits) / 8.0); + output += F("uint8_t state["); + output += uint64ToString(nbytes); + output += F("] = {"); + for (uint16_t i = 0; i < nbytes; i++) { + output += F("0x"); + if (results->state[i] < 0x10) output += '0'; + output += uint64ToString(results->state[i], 16); + if (i < nbytes - 1) output += kCommaSpaceStr; + } + output += F("};\n"); +#endif // DECODE_AC + } else { + // Simple protocols + // Some protocols have an address &/or command. + // NOTE: It will ignore the atypical case when a message has been + // decoded but the address & the command are both 0. + if (results->address > 0 || results->command > 0) { + output += F("uint32_t address = 0x"); + output += uint64ToString(results->address, 16); + output += F(";\n"); + output += F("uint32_t command = 0x"); + output += uint64ToString(results->command, 16); + output += F(";\n"); + } + // Most protocols have data + output += F("uint64_t data = 0x"); + output += uint64ToString(results->value, 16); + output += F(";\n"); + } + } + return output; +} + +/// Dump out the decode_results structure. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the legacy information format. +/// @deprecated This is only for those that want this legacy format. +String resultToTimingInfo(const decode_results * const results) { + String output = ""; + String value = ""; + // Reserve some space for the string to reduce heap fragmentation. + // "Raw Timing[NNNN]:\n\n" = 19 chars + // " +123456, " / "-123456, " = ~12 chars on avg per raw entry. + output.reserve(19 + 12 * results->rawlen); // Should be less than this. + value.reserve(6); // Max value should be 2^17 = 131072 + output += F("Raw Timing["); + output += uint64ToString(results->rawlen - 1, 10); + output += F("]:\n"); + + for (uint16_t i = 1; i < results->rawlen; i++) { + if (i % 2 == 0) + output += kDashStr; // even + else + output += F(" +"); // odd + value = uint64ToString(results->rawbuf[i] * kRawTick); + // Space pad the value till it is at least 6 chars long. + while (value.length() < 6) value = ' ' + value; + output += value; + if (i < results->rawlen - 1) + output += kCommaSpaceStr; // ',' not needed for last one + if (!(i % 8)) output += '\n'; // Newline every 8 entries. + } + output += '\n'; + return output; +} + +/// Convert the decode_results structure's value/state to simple hexadecimal. +/// @param[in] result A ptr to a decode_results structure. +/// @return A String containing the output. +String resultToHexidecimal(const decode_results * const result) { + String output = F("0x"); + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax + 2); // Should cover worst cases. + if (hasACState(result->decode_type)) { +#if DECODE_AC + for (uint16_t i = 0; result->bits > i * 8; i++) { + if (result->state[i] < 0x10) output += '0'; // Zero pad + output += uint64ToString(result->state[i], 16); + } +#endif // DECODE_AC + } else { + output += uint64ToString(result->value, 16); + } + return output; +} + +/// Dump out the decode_results structure into a human readable format. +/// @param[in] results A ptr to a decode_results structure. +/// @return A String containing the output. +String resultToHumanReadableBasic(const decode_results * const results) { + String output = ""; + // Reserve some space for the string to reduce heap fragmentation. + // "Protocol : LONGEST_PROTOCOL_NAME (Repeat)\n" + // "Code : 0x (NNNN Bits)\n" = 70 chars + output.reserve(2 * kStateSizeMax + 70); // Should cover most cases. + // Show Encoding standard + output += kProtocolStr; + output += F(" : "); + output += typeToString(results->decode_type, results->repeat); + output += '\n'; + + // Show Code & length + output += kCodeStr; + output += F(" : "); + output += resultToHexidecimal(results); + output += kSpaceLBraceStr; + output += uint64ToString(results->bits); + output += ' '; + output += kBitsStr; + output += F(")\n"); + return output; +} + +/// Convert a decode_results into an array suitable for `sendRaw()`. +/// @param[in] decode A ptr to a decode_results structure that contains a mesg. +/// @return A PTR to a dynamically allocated uint16_t sendRaw compatible array. +/// @note The returned array needs to be delete[]'ed/free()'ed (deallocated) +/// after use by caller. +uint16_t* resultToRawArray(const decode_results * const decode) { + uint16_t *result = new uint16_t[getCorrectedRawLength(decode)]; + if (result != NULL) { // The memory was allocated successfully. + // Convert the decode data. + uint16_t pos = 0; + for (uint16_t i = 1; i < decode->rawlen; i++) { + uint32_t usecs = decode->rawbuf[i] * kRawTick; + while (usecs > UINT16_MAX) { // Keep truncating till it fits. + result[pos++] = UINT16_MAX; + result[pos++] = 0; // A 0 in a sendRaw() array basically means skip. + usecs -= UINT16_MAX; + } + result[pos++] = usecs; + } + } + return result; +} + +/// Sum all the bytes of an array and return the least significant 8-bits of +/// the result. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The 8-bit calculated result of all the bytes and init value. +uint8_t sumBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t checksum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; + return checksum; +} + +/// Calculate a rolling XOR of all the bytes of an array. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The 8-bit calculated result of all the bytes and init value. +uint8_t xorBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t checksum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; + return checksum; +} + +/// Count the number of bits of a certain type in an array. +/// @param[in] start A ptr to the start of the byte array to calculate over. +/// @param[in] length How many bytes to use in the calculation. +/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The nr. of bits found of the given type found in the array. +uint16_t countBits(const uint8_t * const start, const uint16_t length, + const bool ones, const uint16_t init) { + uint16_t count = init; + for (uint16_t offset = 0; offset < length; offset++) + for (uint8_t currentbyte = *(start + offset); + currentbyte; + currentbyte >>= 1) + if (currentbyte & 1) count++; + if (ones || length == 0) + return count; + else + return (length * 8) - count; +} + +/// Count the number of bits of a certain type in an Integer. +/// @param[in] data The value you want bits counted for. Starting from the LSB. +/// @param[in] length How many bits to use in the calculation? Starts at the LSB +/// @param[in] ones Count the binary nr of `1` bits. False is count the `0`s. +/// @param[in] init Starting value of the calculation to use. (Default is 0) +/// @return The nr. of bits found of the given type found in the Integer. +uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones, + const uint16_t init) { + uint16_t count = init; + uint8_t bitsSoFar = length; + for (uint64_t remainder = data; remainder && bitsSoFar; + remainder >>= 1, bitsSoFar--) + if (remainder & 1) count++; + if (ones || length == 0) + return count; + else + return length - count; +} + +/// Invert/Flip the bits in an Integer. +/// @param[in] data The Integer that will be inverted. +/// @param[in] nbits How many bits are to be inverted. Starting from the LSB. +/// @return An Integer with the appropriate bits inverted/flipped. +uint64_t invertBits(const uint64_t data, const uint16_t nbits) { + // No change if we are asked to invert no bits. + if (nbits == 0) return data; + uint64_t result = ~data; + // If we are asked to invert all the bits or more than we have, it's simple. + if (nbits >= sizeof(data) * 8) return result; + // Mask off any unwanted bits and return the result. + return (result & ((1ULL << nbits) - 1)); +} + +/// Convert degrees Celsius to degrees Fahrenheit. +float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } + +/// Convert degrees Fahrenheit to degrees Celsius. +float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } + +namespace irutils { + /// Create a String with a colon separated "label: value" pair suitable for + /// Humans. + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addLabeledString(const String value, const String label, + const bool precomma) { + String result = ""; + // ", " + ": " = 4 chars + result.reserve(4 + value.length() + label.length()); + if (precomma) result += kCommaSpaceStr; + result += label; + result += kColonSpaceStr; + return result + value; + } + + /// Create a String with a colon separated flag suitable for Humans. + /// e.g. "Power: On" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addBoolToString(const bool value, const String label, + const bool precomma) { + return addLabeledString(value ? kOnStr : kOffStr, label, precomma); + } + + /// Create a String with a colon separated toggle flag suitable for Humans. + /// e.g. "Light: Toggle", "Light: -" + /// @param[in] toggle The value of the toggle to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addToggleToString(const bool toggle, const String label, + const bool precomma) { + return addLabeledString(toggle ? kToggleStr : kDashStr, label, precomma); + } + + /// Create a String with a colon separated labeled Integer suitable for + /// Humans. + /// e.g. "Foo: 23" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addIntToString(const uint16_t value, const String label, + const bool precomma) { + return addLabeledString(uint64ToString(value), label, precomma); + } + + /// Create a String with a colon separated labeled Integer suitable for + /// Humans. + /// e.g. "Foo: 23" + /// @param[in] value The value to come after the label. + /// @param[in] label The label to precede the value. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addSignedIntToString(const int16_t value, const String label, + const bool precomma) { + return addLabeledString(int64ToString(value), label, precomma); + } + + + /// Generate the model string for a given Protocol/Model pair. + /// @param[in] protocol The IR protocol. + /// @param[in] model The model number for that protocol. + /// @return The resulting String. + /// @note After adding a new model you should update IRac::strToModel() too. + String modelToStr(const decode_type_t protocol, const int16_t model) { + switch (protocol) { + case decode_type_t::FUJITSU_AC: + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: return kArrah2eStr; + case fujitsu_ac_remote_model_t::ARDB1: return kArdb1Str; + case fujitsu_ac_remote_model_t::ARREB1E: return kArreb1eStr; + case fujitsu_ac_remote_model_t::ARJW2: return kArjw2Str; + case fujitsu_ac_remote_model_t::ARRY4: return kArry4Str; + case fujitsu_ac_remote_model_t::ARREW4E: return kArrew4eStr; + default: return kUnknownStr; + } + break; + case decode_type_t::GREE: + switch (model) { + case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; + case gree_ac_remote_model_t::YBOFB: return kYbofbStr; + case gree_ac_remote_model_t::YX1FSF: return kYx1fsfStr; + default: return kUnknownStr; + } + break; + case decode_type_t::HAIER_AC176: + switch (model) { + case haier_ac176_remote_model_t::V9014557_A: + return kV9014557AStr; + case haier_ac176_remote_model_t::V9014557_B: + return kV9014557BStr; + default: + return kUnknownStr; + } + break; + case decode_type_t::HITACHI_AC1: + switch (model) { + case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: + return kRlt0541htaaStr; + case hitachi_ac1_remote_model_t::R_LT0541_HTA_B: + return kRlt0541htabStr; + default: + return kUnknownStr; + } + break; + case decode_type_t::LG: + case decode_type_t::LG2: + switch (model) { + case lg_ac_remote_model_t::GE6711AR2853M: return kGe6711ar2853mStr; + case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str; + case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str; + case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str; + case lg_ac_remote_model_t::LG6711A20083V: return kLg6711a20083vStr; + default: return kUnknownStr; + } + break; + case decode_type_t::MIRAGE: + switch (model) { + case mirage_ac_remote_model_t::KKG9AC1: return kKkg9ac1Str; + case mirage_ac_remote_model_t::KKG29AC1: return kKkg29ac1Str; + default: return kUnknownStr; + } + break; + case decode_type_t::PANASONIC_AC: + switch (model) { + case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr; + case panasonic_ac_remote_model_t::kPanasonicNke: return kNkeStr; + case panasonic_ac_remote_model_t::kPanasonicDke: return kDkeStr; + case panasonic_ac_remote_model_t::kPanasonicJke: return kJkeStr; + case panasonic_ac_remote_model_t::kPanasonicCkp: return kCkpStr; + case panasonic_ac_remote_model_t::kPanasonicRkr: return kRkrStr; + default: return kUnknownStr; + } + break; + case decode_type_t::SHARP_AC: + switch (model) { + case sharp_ac_remote_model_t::A907: return kA907Str; + case sharp_ac_remote_model_t::A705: return kA705Str; + case sharp_ac_remote_model_t::A903: return kA903Str; + default: return kUnknownStr; + } + break; + case decode_type_t::TCL112AC: + switch (model) { + case tcl_ac_remote_model_t::TAC09CHSD: return kTac09chsdStr; + case tcl_ac_remote_model_t::GZ055BE1: return kGz055be1Str; + default: return kUnknownStr; + } + break; + case decode_type_t::VOLTAS: + switch (model) { + case voltas_ac_remote_model_t::kVoltas122LZF: return k122lzfStr; + default: return kUnknownStr; + } + break; + case decode_type_t::WHIRLPOOL_AC: + switch (model) { + case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr; + case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str; + default: return kUnknownStr; + } + break; + case decode_type_t::ARGO: + switch (model) { + case argo_ac_remote_model_t::SAC_WREM2: return kArgoWrem2Str; + case argo_ac_remote_model_t::SAC_WREM3: return kArgoWrem3Str; + default: return kUnknownStr; + } + break; + default: return kUnknownStr; + } + } + + /// Create a String of human output for a given protocol model number. + /// e.g. "Model: JKE" + /// @param[in] protocol The IR protocol. + /// @param[in] model The model number for that protocol. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addModelToString(const decode_type_t protocol, const int16_t model, + const bool precomma) { + String result = ""; + // ", Model: NNN (BlahBlahEtc)" = ~40 chars for longest model name. + result.reserve(40); + result += addIntToString(model, kModelStr, precomma); + result += kSpaceLBraceStr; + result += modelToStr(protocol, model); + return result + ')'; + } + + /// Create a String of human output for a given temperature. + /// e.g. "Temp: 25C" + /// @param[in] degrees The temperature in degrees. + /// @param[in] celsius Is the temp Celsius or Fahrenheit. + /// true is C, false is F + /// @param[in] precomma Should the output string start with ", " or not? + /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? + /// @return The resulting String. + String addTempToString(const uint16_t degrees, const bool celsius, + const bool precomma, const bool isSensorTemp) { + String result = addIntToString(degrees, (isSensorTemp)? + kSensorTempStr : kTempStr, precomma); + result += celsius ? 'C' : 'F'; + return result; + } + + /// Create a String of human output for a given temperature. + /// e.g. "Temp: 25.5C" + /// @param[in] degrees The temperature in degrees. + /// @param[in] celsius Is the temp Celsius or Fahrenheit. + /// true is C, false is F + /// @param[in] precomma Should the output string start with ", " or not? + /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target? + /// @return The resulting String. + String addTempFloatToString(const float degrees, const bool celsius, + const bool precomma, const bool isSensorTemp) { + String result = ""; + result.reserve(21); // Assuming ", Sensor Temp: XXX.5F" is the largest. + result += addIntToString(degrees, (isSensorTemp)? + kSensorTempStr : kTempStr, precomma); + // Is it a half degree? + if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); + result += celsius ? 'C' : 'F'; + return result; + } + + /// Create a String of human output for the given operating mode. + /// e.g. "Mode: 1 (Cool)" + /// @param[in] mode The operating mode to display. + /// @param[in] automatic The numeric value for Auto mode. + /// @param[in] cool The numeric value for Cool mode. + /// @param[in] heat The numeric value for Heat mode. + /// @param[in] dry The numeric value for Dry mode. + /// @param[in] fan The numeric value for Fan mode. + /// @return The resulting String. + String addModeToString(const uint8_t mode, const uint8_t automatic, + const uint8_t cool, const uint8_t heat, + const uint8_t dry, const uint8_t fan) { + String result = ""; + result.reserve(22); // ", Mode: NNN (UNKNOWN)" + result += addIntToString(mode, kModeStr); + result += kSpaceLBraceStr; + if (mode == automatic) result += kAutoStr; + else if (mode == cool) result += kCoolStr; + else if (mode == heat) result += kHeatStr; + else if (mode == dry) result += kDryStr; + else if (mode == fan) result += kFanStr; + else + result += kUnknownStr; + return result + ')'; + } + + /// Create a String of the 3-letter day of the week from a numerical day of + /// the week. e.g. "Day: 1 (Mon)" + /// @param[in] day_of_week A numerical version of the sequential day of the + /// week. e.g. Saturday = 7 etc. + /// @param[in] offset Days to offset by. + /// e.g. For different day starting the week. + /// @param[in] precomma Should the output string start with ", " or not? + /// @return The resulting String. + String addDayToString(const uint8_t day_of_week, const int8_t offset, + const bool precomma) { + String result = ""; + result.reserve(19); // ", Day: N (UNKNOWN)" + result += addIntToString(day_of_week, kDayStr, precomma); + result += kSpaceLBraceStr; + result += dayToString(day_of_week, offset); + return result + ')'; + } + + /// Create a String of the 3-letter day of the week from a numerical day of + /// the week. e.g. "Mon" + /// @param[in] day_of_week A numerical version of the sequential day of the + /// week. e.g. Sunday = 1, Monday = 2, ..., Saturday = 7 + /// @param[in] offset Days to offset by. + /// e.g. For different day starting the week. + /// @return The resulting String. + String dayToString(const uint8_t day_of_week, const int8_t offset) { + if ((uint8_t)(day_of_week + offset) < 7) +#if UNIT_TEST + return String(kThreeLetterDayOfWeekStr).substr( + (day_of_week + offset) * 3, 3); +#else // UNIT_TEST + return String(kThreeLetterDayOfWeekStr).substring( + (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3); +#endif // UNIT_TEST + else + return kUnknownStr; + } + + /// Create a String of human output for the given fan speed. + /// e.g. "Fan: 0 (Auto)" + /// @param[in] speed The numeric speed of the fan to display. + /// @param[in] high The numeric value for High speed. (second highest) + /// @param[in] low The numeric value for Low speed. + /// @param[in] automatic The numeric value for Auto speed. + /// @param[in] quiet The numeric value for Quiet speed. + /// @param[in] medium The numeric value for Medium speed. + /// @param[in] maximum The numeric value for Highest speed. (if > high) + /// @param[in] medium_high The numeric value for third-highest speed. + /// (if > medium) + /// @return The resulting String. + String addFanToString(const uint8_t speed, const uint8_t high, + const uint8_t low, const uint8_t automatic, + const uint8_t quiet, const uint8_t medium, + const uint8_t maximum, const uint8_t medium_high) { + String result = ""; + result.reserve(21); // ", Fan: NNN (UNKNOWN)" + result += addIntToString(speed, kFanStr); + result += kSpaceLBraceStr; + if (speed == high) result += kHighStr; + else if (speed == low) result += kLowStr; + else if (speed == automatic) result += kAutoStr; + else if (speed == quiet) result += kQuietStr; + else if (speed == medium) result += kMediumStr; + else if (speed == maximum) result += kMaximumStr; + else if (speed == medium_high) result += kMedHighStr; + else + result += kUnknownStr; + return result + ')'; + } + + /// Create a String of human output for the given horizontal swing setting. + /// e.g. "Swing(H): 0 (Auto)" + /// @param[in] position The numeric position of the swing to display. + /// @param[in] automatic The numeric value for Auto position. + /// @param[in] maxleft The numeric value for most left position. + /// @param[in] left The numeric value for Left position. + /// @param[in] middle The numeric value for Middle position. + /// @param[in] right The numeric value for Right position. + /// @param[in] maxright The numeric value for most right position. + /// @param[in] off The numeric value for Off position. + /// @param[in] leftright The numeric value for "left right" position. + /// @param[in] rightleft The numeric value for "right left" position. + /// @param[in] threed The numeric value for 3D setting. + /// @param[in] wide The numeric value for Wide position. + /// @return The resulting String. + String addSwingHToString(const uint8_t position, const uint8_t automatic, + const uint8_t maxleft, const uint8_t left, + const uint8_t middle, + const uint8_t right, const uint8_t maxright, + const uint8_t off, + const uint8_t leftright, const uint8_t rightleft, + const uint8_t threed, const uint8_t wide) { + String result = ""; + result.reserve(30); // ", Swing(H): NNN (Left Right)" + result += addIntToString(position, kSwingHStr); + result += kSpaceLBraceStr; + if (position == automatic) { + result += kAutoStr; + } else if (position == left) { + result += kLeftStr; + } else if (position == middle) { + result += kMiddleStr; + } else if (position == right) { + result += kRightStr; + } else if (position == maxleft) { + result += kMaxLeftStr; + } else if (position == maxright) { + result += kMaxRightStr; + } else if (position == off) { + result += kOffStr; + } else if (position == leftright) { + result += kLeftStr; + result += ' '; + result += kRightStr; + } else if (position == rightleft) { + result += kRightStr; + result += ' '; + result += kLeftStr; + } else if (position == threed) { + result += k3DStr; + } else if (position == wide) { + result += kWideStr; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// Create a String of human output for the given vertical swing setting. + /// e.g. "Swing(V): 0 (Auto)" + /// @param[in] position The numeric position of the swing to display. + /// @param[in] automatic The numeric value for Auto position. + /// @param[in] highest The numeric value for Highest position. + /// @param[in] high The numeric value for High position. + /// @param[in] uppermiddle The numeric value for Upper Middle position. + /// @param[in] middle The numeric value for Middle position. + /// @param[in] lowermiddle The numeric value for Lower Middle position. + /// @param[in] low The numeric value for Low position. + /// @param[in] lowest The numeric value for Low position. + /// @param[in] off The numeric value for Off position. + /// @param[in] swing The numeric value for Swing setting. + /// @param[in] breeze The numeric value for Breeze setting. + /// @param[in] circulate The numeric value for Circulate setting. + /// @return The resulting String. + String addSwingVToString(const uint8_t position, const uint8_t automatic, + const uint8_t highest, const uint8_t high, + const uint8_t uppermiddle, + const uint8_t middle, + const uint8_t lowermiddle, + const uint8_t low, const uint8_t lowest, + const uint8_t off, const uint8_t swing, + const uint8_t breeze, const uint8_t circulate) { + String result = ""; + result.reserve(31); // ", Swing(V): NNN (Upper Middle)" + result += addIntToString(position, kSwingVStr); + result += kSpaceLBraceStr; + if (position == automatic) { + result += kAutoStr; + } else if (position == highest) { + result += kHighestStr; + } else if (position == high) { + result += kHighStr; + } else if (position == middle) { + result += kMiddleStr; + } else if (position == low) { + result += kLowStr; + } else if (position == lowest) { + result += kLowestStr; + } else if (position == off) { + result += kOffStr; + } else if (position == uppermiddle) { + result += kUpperStr; + result += ' '; + result += kMiddleStr; + } else if (position == lowermiddle) { + result += kLowerStr; + result += ' '; + result += kMiddleStr; + } else if (position == swing) { + result += kSwingStr; + } else if (position == breeze) { + result += kBreezeStr; + } else if (position == circulate) { + result += kCirculateStr; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// @brief Create a String of human output for the given timer setting. + /// e.g. "Timer Mode: 2 (Schedule 1)" + /// @param[in] timerMode The numeric value of the timer mode to display. + /// @param[in] noTimer The numeric value for no timer (off) + /// @param[in] delayTimer The numeric value for delay (sleep) timer + /// @param[in] schedule1 The numeric value for schedule timer #1 + /// @param[in] schedule2 The numeric value for schedule timer #2 + /// @param[in] schedule3 The numeric value for schedule timer #3 + /// @param[in] precomma Should the output string start with ", " or not? + /// @return String representation + String addTimerModeToString(const uint8_t timerMode, const uint8_t noTimer, + const uint8_t delayTimer, const uint8_t schedule1, + const uint8_t schedule2, const uint8_t schedule3, + const bool precomma) { + String result = ""; + result.reserve(28); // ", Timer Mode: 2 (Schedule 1)" + result += addIntToString(timerMode, kTimerModeStr, precomma); + result += kSpaceLBraceStr; + if (timerMode == noTimer) { + result += kOffStr; + } else if (timerMode == delayTimer) { + result += kSleepTimerStr; + } else if (timerMode == schedule1) { + result += kScheduleStr; + result += '1'; + } else if (timerMode == schedule2) { + result += kScheduleStr; + result += '2'; + } else if (timerMode == schedule3) { + result += kScheduleStr; + result += '3'; + } else { + result += kUnknownStr; + } + return result + ')'; + } + + /// @brief Create a String of human output for the given channel + /// e.g. "[CH#0]" + /// @param channel The numeric value of the channel to display. + /// @return String representation + String channelToString(const uint8_t channel) { + String result = ""; + result.reserve(6); // "[CH#4]" + result += "["; + result += kChStr; + result += uint64ToString(channel); + result += "]"; + return result; + } + + /// @brief Create a String of human output for the given command type + /// e.g. "IFeel Report" + /// @param irCommandType The numeric value of the command type to display. + /// @param acControlCmd The numeric value of the "control" (default) command + /// @param iFeelReportCmd The numeric value of the sensor temperature command + /// @param timerCmd The numeric value of the timer config IR command + /// @param configCmd The numeric value of the config param set IR command + /// @return String representation + String irCommandTypeToString(uint8_t irCommandType, uint8_t acControlCmd, + uint8_t iFeelReportCmd, uint8_t timerCmd, + uint8_t configCmd) { + String result = ""; + result.reserve(12); // "IFeel Report" + if (irCommandType == acControlCmd) { + result += kCommandStr; + } else if (irCommandType == iFeelReportCmd) { + result += kIFeelReportStr; + } else if (irCommandType == timerCmd) { + result += kTimerStr; + } else if (irCommandType == configCmd) { + result += kConfigCommandStr; + } else { + result += kUnknownStr; + } + return result; + } + + /// @brief Create a String of the 3-letter day of the week bitmap + // e.g. 0b0000101 is "Sun | Tue" + /// @param[in] daysBitmap The bitmap representing days of week to represent + /// e.g bit[0]=Sunday, bit[1]=Monday, ... + /// @param[in] offset Days to offset by. + /// e.g. For different day starting the week. + /// @return String representation. + String daysBitmaskToString(uint8_t daysBitmap, uint8_t offset) { + String result = ""; + result.reserve(27); // Sun|Mon|Tue|Wed|Thu|Fri|Sat + + for (uint8_t i = 0; i < 7; ++i) { + if (((daysBitmap >> i) & 0b1) == 0b1) { + if (result.length() > 0) { + result += "|"; + } + result += irutils::dayToString(i, offset); + } + } + return result; + } + + /// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. + /// @param[in] unescaped A String containing text to make HTML safe. + /// @return A string that is HTML safe. + String htmlEscape(const String unescaped) { + String result = ""; + uint16_t ulen = unescaped.length(); + result.reserve(ulen); // The result will be at least the size of input. + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + switch (c) { + // ';!-"<>=&#{}() are all unsafe. + case '\'': result += F("'"); break; + case ';': result += F(";"); break; + case '!': result += F("!"); break; + case '-': result += F("‐"); break; + case '\"': result += F("""); break; + case '<': result += F("<"); break; + case '>': result += F(">"); break; + case '=': result += F("&#equals;"); break; + case '&': result += F("&"); break; + case '#': result += F("#"); break; + case '{': result += F("{"); break; + case '}': result += F("}"); break; + case '(': result += F("("); break; + case ')': result += F(")"); break; + default: result += c; + } + } + return result; + } + + /// Convert a nr. of milliSeconds into a Human-readable string. + /// e.g. "1 Day 6 Hours 34 Minutes 17 Seconds" + /// @param[in] msecs Nr. of milliSeconds (ms). + /// @return A human readable string. + String msToString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return kNowStr; + + // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. + uint8_t days = totalseconds / (60 * 60 * 24); + uint8_t hours = (totalseconds / (60 * 60)) % 24; + uint8_t minutes = (totalseconds / 60) % 60; + uint8_t seconds = totalseconds % 60; + + String result = ""; + result.reserve(42); // "99 Days, 23 Hours, 59 Minutes, 59 Seconds" + if (days) + result += uint64ToString(days) + ' ' + String((days > 1) ? kDaysStr + : kDayStr); + if (hours) { + if (result.length()) result += ' '; + result += uint64ToString(hours) + ' ' + String((hours > 1) ? kHoursStr + : kHourStr); + } + if (minutes) { + if (result.length()) result += ' '; + result += uint64ToString(minutes) + ' ' + String( + (minutes > 1) ? kMinutesStr : kMinuteStr); + } + if (seconds) { + if (result.length()) result += ' '; + result += uint64ToString(seconds) + ' ' + String( + (seconds > 1) ? kSecondsStr : kSecondStr); + } + return result; + } + + /// Convert a nr. of minutes into a 24h clock format Human-readable string. + /// e.g. "23:59" + /// @param[in] mins Nr. of Minutes. + /// @return A human readable string. + String minsToString(const uint16_t mins) { + String result = ""; + result.reserve(5); // 23:59 is the typical worst case. + if (mins / 60 < 10) result += '0'; // Zero pad the hours + result += uint64ToString(mins / 60) + kTimeSep; + if (mins % 60 < 10) result += '0'; // Zero pad the minutes. + result += uint64ToString(mins % 60); + return result; + } + + /// Sum all the nibbles together in a series of bytes. + /// @param[in] start A ptr to the start of the byte array to calculate over. + /// @param[in] length How many bytes to use in the calculation. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @return The 8-bit calculated result of all the bytes and init value. + uint8_t sumNibbles(const uint8_t * const start, const uint16_t length, + const uint8_t init) { + uint8_t sum = init; + const uint8_t *ptr; + for (ptr = start; ptr - start < length; ptr++) + sum += (*ptr >> 4) + (*ptr & 0xF); + return sum; + } + + /// Sum all the nibbles together in an integer. + /// @param[in] data The integer to be summed. + /// @param[in] count The number of nibbles to sum. Starts from LSB. Max of 16. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @param[in] nibbleonly true, the result is 4 bits. false, it's 8 bits. + /// @return The 4/8-bit calculated result of all the nibbles and init value. + uint8_t sumNibbles(const uint64_t data, const uint8_t count, + const uint8_t init, const bool nibbleonly) { + uint8_t sum = init; + uint64_t copy = data; + const uint8_t nrofnibbles = (count < 16) ? count : (64 / 4); + for (uint8_t i = 0; i < nrofnibbles; i++, copy >>= 4) sum += copy & 0xF; + return nibbleonly ? sum & 0xF : sum; + } + + /// Sum all the bytes together in an integer. + /// @param[in] data The integer to be summed. + /// @param[in] count The number of bytes to sum. Starts from LSB. Max of 8. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @param[in] byteonly true, the result is 8 bits. false, it's 16 bits. + /// @return The 8/16-bit calculated result of all the bytes and init value. + uint16_t sumBytes(const uint64_t data, const uint8_t count, + const uint8_t init, const bool byteonly) { + uint16_t sum = init; + uint64_t copy = data; + const uint8_t nrofbytes = (count < 8) ? count : (64 / 8); + for (uint8_t i = 0; i < nrofbytes; i++, copy >>= 8) sum += (copy & 0xFF); + return byteonly ? sum & 0xFF : sum; + } + + /// Convert a byte of Binary Coded Decimal(BCD) into an Integer. + /// @param[in] bcd The BCD value. + /// @return A normal Integer value. + uint8_t bcdToUint8(const uint8_t bcd) { + if (bcd > 0x99) return 255; // Too big. + return (bcd >> 4) * 10 + (bcd & 0xF); + } + + /// Convert an Integer into a byte of Binary Coded Decimal(BCD). + /// @param[in] integer The number to convert. + /// @return An 8-bit BCD value. + uint8_t uint8ToBcd(const uint8_t integer) { + if (integer > 99) return 255; // Too big. + return ((integer / 10) << 4) + (integer % 10); + } + + /// Return the value of `position`th bit of an Integer. + /// @param[in] data Value to be examined. + /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. + /// @param[in] size Nr. of bits in data. + /// @return The bit's value. + bool getBit(const uint64_t data, const uint8_t position, const uint8_t size) { + if (position >= size) return false; // Outside of range. + return data & (1ULL << position); + } + + /// Return the value of `position`th bit of an Integer. + /// @param[in] data Value to be examined. + /// @param[in] position Nr. of the Nth bit to be examined. `0` is the LSB. + /// @return The bit's value. + bool getBit(const uint8_t data, const uint8_t position) { + if (position >= 8) return false; // Outside of range. + return data & (1 << position); + } + + /// Return the value of an Integer with the `position`th bit changed. + /// @param[in] data Value to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + /// @param[in] size Nr. of bits in data. + /// @return A suitably modified integer. + uint64_t setBit(const uint64_t data, const uint8_t position, const bool on, + const uint8_t size) { + if (position >= size) return data; // Outside of range. + uint64_t mask = 1ULL << position; + if (on) + return data | mask; + else + return data & ~mask; + } + + /// Return the value of an Integer with the `position`th bit changed. + /// @param[in] data Value to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + /// @return A suitably modified integer. + uint8_t setBit(const uint8_t data, const uint8_t position, const bool on) { + if (position >= 8) return data; // Outside of range. + uint8_t mask = 1 << position; + if (on) + return data | mask; + else + return data & ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 8-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint8_t * const data, const uint8_t position, const bool on) { + uint8_t mask = 1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 32-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint32_t * const data, const uint8_t position, const bool on) { + uint32_t mask = (uint32_t)1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter the value of an Integer with the `position`th bit changed. + /// @param[in,out] data A pointer to the 64-bit integer to be changed. + /// @param[in] position Nr. of the bit to be changed. `0` is the LSB. + /// @param[in] on Value to set the position'th bit to. + void setBit(uint64_t * const data, const uint8_t position, const bool on) { + uint64_t mask = (uint64_t)1 << position; + if (on) + *data |= mask; + else + *data &= ~mask; + } + + /// Alter an uint8_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint8_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint8_t data) { + if (offset >= 8 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint8_t mask = UINT8_MAX >> (8 - ((nbits > 8) ? 8 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(uint8_t)(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Alter an uint32_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint32_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint32_t data) { + if (offset >= 32 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint32_t mask = UINT32_MAX >> (32 - ((nbits > 32) ? 32 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Alter an uint64_t value by overwriting an arbitrary given number of bits. + /// @param[in,out] dst A pointer to the value to be changed. + /// @param[in] offset Nr. of bits from the Least Significant Bit to be ignored + /// @param[in] nbits Nr of bits of data to be placed into the destination. + /// @param[in] data The value to be placed. + void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits, + const uint64_t data) { + if (offset >= 64 || !nbits) return; // Short circuit as it won't change. + // Calculate the mask for the supplied value. + uint64_t mask = UINT64_MAX >> (64 - ((nbits > 64) ? 64 : nbits)); + // Calculate the mask & clear the space for the data. + // Clear the destination bits. + *dst &= ~(mask << offset); + // Merge in the data. + *dst |= ((data & mask) << offset); + } + + /// Create byte pairs where the second byte of the pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in,out] ptr A pointer to the start of array to modify. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will do nothing. + /// @return A ptr to the modified array. + uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + *(ptr + i) = inv; + } + return ptr; + } + + /// Check an array to see if every second byte of a pair is a bit + /// inverted/flipped copy of the first/previous byte of the pair. + /// @param[in] ptr A pointer to the start of array to check. + /// @param[in] length The byte size of the array. + /// @note A length of `<= 1` will always return true. + /// @return true, if every second byte is inverted. Otherwise false. + bool checkInvertedBytePairs(const uint8_t * const ptr, + const uint16_t length) { + for (uint16_t i = 1; i < length; i += 2) { + // Code done this way to avoid a compiler warning bug. + uint8_t inv = ~*(ptr + i - 1); + if (*(ptr + i) != inv) return false; + } + return true; + } + + /// Perform a low level bit manipulation sanity check for the given cpu + /// architecture and the compiler operation. Calls to this should return + /// 0 if everything is as expected, anything else means the library won't work + /// as expected. + /// @return A bit mask value of potential issues. + /// 0: (e.g. 0b00000000) Everything appears okay. + /// 0th bit set: (0b1) Unexpected bit field/packing encountered. + /// Try a different compiler. + /// 1st bit set: (0b10) Unexpected Endianness. Try a different compiler flag + /// or use a CPU different architecture. + /// e.g. A result of 3 (0b11) would mean both a bit field and an Endianness + /// issue has been found. + uint8_t lowLevelSanityCheck(void) { + const uint64_t kExpectedBitFieldResult = 0x8000012340000039ULL; + volatile uint32_t EndianTest = 0x12345678; + const uint8_t kBitFieldError = 0b01; + const uint8_t kEndiannessError = 0b10; + uint8_t result = 0; + union bitpackdata { + struct { + uint64_t lowestbit:1; // 0th bit + uint64_t next7bits:7; // 1-7th bits + uint64_t _unused_1:20; // 8-27th bits + // Cross the 32 bit boundary. + uint64_t crossbits:16; // 28-43rd bits + uint64_t _usused_2:18; // 44-61st bits + uint64_t highest2bits:2; // 62-63rd bits + }; + uint64_t all; + }; + + bitpackdata data; + data.lowestbit = true; + data.next7bits = 0b0011100; // 0x1C + data._unused_1 = 0; + data.crossbits = 0x1234; + data._usused_2 = 0; + data.highest2bits = 0b10; // 2 + + if (data.all != kExpectedBitFieldResult) result |= kBitFieldError; + // Check that we are using Little Endian for integers +#if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) + if (BYTE_ORDER != LITTLE_ENDIAN) result |= kEndiannessError; +#endif +#if defined(__IEEE_BIG_ENDIAN) || defined(__IEEE_BYTES_BIG_ENDIAN) + result |= kEndiannessError; +#endif + // Brute force check for little endian. + if (*((uint8_t*)(&EndianTest)) != 0x78) // NOLINT(readability/casting) + result |= kEndiannessError; + return result; + } +} // namespace irutils diff --git a/src/ir_Fujitsu.cpp b/src/ir_Fujitsu.cpp index 0e4b56f76..bc1340cec 100644 --- a/src/ir_Fujitsu.cpp +++ b/src/ir_Fujitsu.cpp @@ -1,2092 +1,2092 @@ -// Copyright 2017 Jonny Graham -// Copyright 2017-2022 David Conran -// Copyright 2021 siriuslzx -// Copyright 2023 Takeshi Shimizu - -/// @file -/// @brief Support for Fujitsu A/C protocols. -/// Fujitsu A/C support added by Jonny Graham & David Conran -/// @warning Use of incorrect model may cause the A/C unit to lock up. -/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical -/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. -/// The correct model for it is ARREB1E. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 - -#include "ir_Fujitsu.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRtext.h" -#include "IRutils.h" - -// Ref: -// These values are based on averages of measurements -const uint16_t kFujitsuAcHdrMark = 3324; -const uint16_t kFujitsuAcHdrSpace = 1574; -const uint16_t kFujitsuAcBitMark = 448; -const uint16_t kFujitsuAcOneSpace = 1182; -const uint16_t kFujitsuAcZeroSpace = 390; -const uint16_t kFujitsuAcMinGap = 8100; -const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. - -using irutils::addBoolToString; -using irutils::addIntToString; -using irutils::addLabeledString; -using irutils::addModeToString; -using irutils::addModelToString; -using irutils::addFanToString; -using irutils::addTempFloatToString; -using irutils::minsToString; -using irutils::addSignedIntToString; - -#if SEND_FUJITSU_AC -/// Send a Fujitsu A/C formatted message. -/// Status: STABLE / Known Good. -/// @param[in] data The message to be sent. -/// @param[in] nbytes The number of bytes of message to be sent. -/// Typically one of: -/// kFujitsuAcStateLength, -/// kFujitsuAcStateLength - 1, -/// kFujitsuAcStateLengthShort, -/// kFujitsuAcStateLengthShort - 1 -/// @param[in] repeat The number of times the command is to be repeated. -void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC - -// Code to emulate Fujitsu A/C IR remote control unit. - -/// Class Constructor -/// @param[in] pin GPIO to be used when sending. -/// @param[in] model The enum for the model of A/C to be emulated. -/// @param[in] inverted Is the output signal to be inverted? -/// @param[in] use_modulation Is frequency modulation to be used? -IRFujitsuAC::IRFujitsuAC(const uint16_t pin, - const fujitsu_ac_remote_model_t model, - const bool inverted, const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { - setModel(model); - stateReset(); -} - -/// Set the currently emulated model of the A/C. -/// @param[in] model An enum representing the model to support/emulate. -void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { - _model = model; - switch (model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _state_length = kFujitsuAcStateLength - 1; - _state_length_short = kFujitsuAcStateLengthShort - 1; - break; - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - default: - _state_length = kFujitsuAcStateLength; - _state_length_short = kFujitsuAcStateLengthShort; - } -} - -/// Get the currently emulated/detected model of the A/C. -/// @return The enum representing the model of A/C. -fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } - -/// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset(void) { - for (size_t i = 0; i < kFujitsuAcStateLength; i++) { - _.longcode[i] = 0; - } - setTemp(24); - _.Fan = kFujitsuAcFanHigh; - _.Mode = kFujitsuAcModeCool; - _.Swing = kFujitsuAcSwingBoth; - _cmd = kFujitsuAcCmdTurnOn; - _.Filter = false; - _.Clean = false; - _.TimerType = kFujitsuAcStopTimers; - _.OnTimer = 0; - _.OffTimer = 0; - _.longcode[0] = 0x14; - _.longcode[1] = 0x63; - _.longcode[3] = 0x10; - _.longcode[4] = 0x10; - _rawstatemodified = true; -} - -/// Set up hardware to be able to send a message. -void IRFujitsuAC::begin(void) { _irsend.begin(); } - -#if SEND_FUJITSU_AC -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRFujitsuAC::send(const uint16_t repeat) { - _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); -} -#endif // SEND_FUJITSU_AC - -/// Update the length (size) of the state code for the current configuration. -/// @return true, if use long codes; false, use short codes. -bool IRFujitsuAC::updateUseLongOrShort(void) { - bool fullCmd = false; - switch (_cmd) { - case kFujitsuAcCmdTurnOff: // 0x02 - case kFujitsuAcCmdEcono: // 0x09 - case kFujitsuAcCmdPowerful: // 0x39 - case kFujitsuAcCmdStepVert: // 0x6C - case kFujitsuAcCmdToggleSwingVert: // 0x6D - case kFujitsuAcCmdStepHoriz: // 0x79 - case kFujitsuAcCmdToggleSwingHoriz: // 0x7A - _.Cmd = _cmd; - _rawstatemodified = true; - break; - default: - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - _.Cmd = 0xFE; - _rawstatemodified = true; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Cmd = 0xFC; - _rawstatemodified = true; - break; - } - fullCmd = true; - break; - } - return fullCmd; -} - -/// Calculate and set the checksum values for the internal state. -void IRFujitsuAC::checkSum(void) { - _rawstatemodified = true; - if (updateUseLongOrShort()) { // Is it going to be a long code? - // Nr. of bytes in the message after this byte. - _.RestLength = _state_length - 7; - _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; - _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); - - // These values depend on model - if (_model != fujitsu_ac_remote_model_t::ARREB1E && - _model != fujitsu_ac_remote_model_t::ARREW4E) { - _.OutsideQuiet = 0; - if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { - _.TimerType = kFujitsuAcStopTimers; - } - } - if (_model != fujitsu_ac_remote_model_t::ARRY4) { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - break; - default: - _.Clean = false; - } - _.Filter = false; - } - // Set the On/Off/Sleep timer Nr of mins. - _.OffTimer = getOffSleepTimer(); - _.OnTimer = getOnTimer(); - // Enable bit for the Off/Sleep timer - _.OffTimerEnable = _.OffTimer > 0; - // Enable bit for the On timer - _.OnTimerEnable = _.OnTimer > 0; - - uint8_t checksum = 0; - uint8_t checksum_complement = 0; - switch (_model) { - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - _.Swing = kFujitsuAcSwingOff; - checksum = sumBytes(_.longcode, _state_length - 1); - checksum_complement = 0x9B; - break; - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - _.unknown = 1; - // FALL THRU - default: - checksum = sumBytes(_.longcode + _state_length_short, - _state_length - _state_length_short - 1); - } - // and negate the checksum and store it in the last byte. - _.longcode[_state_length - 1] = checksum_complement - checksum; - } else { // short codes - for (size_t i = 0; i < _state_length_short; i++) { - _.shortcode[i] = _.longcode[i]; - } - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - // The last byte is the inverse of penultimate byte - _.shortcode[_state_length_short - 1] = - ~_.shortcode[_state_length_short - 2]; - break; - default: - {}; // We don't need to do anything for the others. - } - } -} - -/// Get the length (size) of the state code for the current configuration. -/// @return The length of the state array required for this config. -uint8_t IRFujitsuAC::getStateLength(void) { - return updateUseLongOrShort() ? _state_length : _state_length_short; -} - -/// Is the current binary state representation a long or a short code? -/// @return true, if long; false, if short. -bool IRFujitsuAC::isLongCode(void) const { - return _.Cmd == 0xFE || _.Cmd == 0xFC; -} - -/// 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* IRFujitsuAC::getRaw(void) { - checkSum(); - return isLongCode() ? _.longcode : _.shortcode; -} - -/// Build the internal state/config from the current (raw) A/C message. -/// @param[in] length Size of the current/used (raw) A/C message array. -void IRFujitsuAC::buildFromState(const uint16_t length) { - switch (length) { - case kFujitsuAcStateLength - 1: - case kFujitsuAcStateLengthShort - 1: - setModel(fujitsu_ac_remote_model_t::ARDB1); - // ARJW2 has horizontal swing. - if (_.Swing > kFujitsuAcSwingVert) - setModel(fujitsu_ac_remote_model_t::ARJW2); - break; - default: - switch (_.Cmd) { - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setModel(fujitsu_ac_remote_model_t::ARREB1E); - break; - default: - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - } - } - switch (_.RestLength) { - case 8: - if (_model != fujitsu_ac_remote_model_t::ARJW2) - setModel(fujitsu_ac_remote_model_t::ARDB1); - break; - case 9: - if (_model != fujitsu_ac_remote_model_t::ARREB1E) - setModel(fujitsu_ac_remote_model_t::ARRAH2E); - break; - } - if (_.Power) - setCmd(kFujitsuAcCmdTurnOn); - else - setCmd(kFujitsuAcCmdStayOn); - // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if - // either the raw Filter or Clean setting is on. - if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean) && - !get10CHeat()) - setModel(fujitsu_ac_remote_model_t::ARRY4); - if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) - setModel(fujitsu_ac_remote_model_t::ARREB1E); - switch (_.Cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - setCmd(_.Cmd); - break; - } - if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -/// @param[in] length Size of the newState array. -/// @return true, if successful; Otherwise false. (i.e. size check) -bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAcStateLength) return false; - for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { - if (i < length) - _.longcode[i] = newState[i]; - else - _.longcode[i] = 0; - } - buildFromState(length); - _rawstatemodified = false; - return true; -} - -/// Request the A/C to step the Horizontal Swing. -void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } - -/// Request the A/C to toggle the Horizontal Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingHoriz(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingHoriz); -} - -/// Request the A/C to step the Vertical Swing. -void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } - -/// Request the A/C to toggle the Vertical Swing mode. -/// @param[in] update Do we need to update the general swing config? -void IRFujitsuAC::toggleSwingVert(const bool update) { - // Toggle the current setting. - if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); - // and set the appropriate special command. - setCmd(kFujitsuAcCmdToggleSwingVert); -} - -/// Set the requested (special) command part for the A/C message. -/// @param[in] cmd The special command code. -void IRFujitsuAC::setCmd(const uint8_t cmd) { - switch (cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdTurnOn: - case kFujitsuAcCmdStayOn: - case kFujitsuAcCmdStepVert: - case kFujitsuAcCmdToggleSwingVert: - _cmd = cmd; - break; - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdToggleSwingHoriz: - switch (_model) { - // Only these remotes have horizontal. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - case kFujitsuAcCmdEcono: - case kFujitsuAcCmdPowerful: - switch (_model) { - // Only these remotes have these commands. - case ARREB1E: - case ARREW4E: - _cmd = cmd; - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } - break; - default: - _cmd = kFujitsuAcCmdStayOn; - } -} - -/// Set the requested (special) command part for the A/C message. -/// @return The special command code. -uint8_t IRFujitsuAC::getCmd(void) const { - return _cmd; -} - -/// Change the power setting. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setPower(const bool on) { - setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); -} - -/// Set the requested power state of the A/C to off. -void IRFujitsuAC::off(void) { setPower(false); } - -/// Set the requested power state of the A/C to on. -void IRFujitsuAC::on(void) { setPower(true); } - -/// Get the value of the current power setting. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } - -/// Set the Outside Quiet mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setOutsideQuiet(const bool on) { - _.OutsideQuiet = on; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Outside Quiet mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getOutsideQuiet(void) const { - switch (_model) { - // Only ARREB1E & ARREW4E seems to have this mode. - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - return _.OutsideQuiet; - default: return false; - } -} - -/// Set the temperature. -/// @param[in] temp The temperature in degrees. -/// @param[in] useCelsius Use Celsius or Fahrenheit? -void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { - float mintemp; - float maxtemp; - uint8_t offset; - bool _useCelsius; - float _temp; - - switch (_model) { - // These models have native Fahrenheit & Celsius upport. - case fujitsu_ac_remote_model_t::ARREW4E: - _useCelsius = useCelsius; - _temp = temp; - break; - // Make sure everything else uses Celsius. - default: - _useCelsius = true; - _temp = useCelsius ? temp : fahrenheitToCelsius(temp); - } - setCelsius(_useCelsius); - if (_useCelsius) { - mintemp = kFujitsuAcMinTemp; - maxtemp = kFujitsuAcMaxTemp; - offset = kFujitsuAcTempOffsetC; - } else { - mintemp = kFujitsuAcMinTempF; - maxtemp = kFujitsuAcMaxTempF; - offset = kFujitsuAcTempOffsetF; - } - _temp = std::max(mintemp, _temp); - _temp = std::min(maxtemp, _temp); - if (_useCelsius) { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) - _.Temp = (_temp - (offset / 2)) * 2; - else - _.Temp = (_temp - offset) * 4; - } else { - _.Temp = _temp - offset; - } - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current temperature setting. -/// @return The current setting for temp. in degrees of the currently set units. -float IRFujitsuAC::getTemp(void) const { - if (_model == fujitsu_ac_remote_model_t::ARREW4E) { - if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. - return _.Temp + kFujitsuAcTempOffsetF; - else - return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); - } else { - return _.Temp / 4 + kFujitsuAcMinTemp; - } -} - -/// Set the speed of the fan. -/// @param[in] fanSpeed The desired setting. -void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { - if (fanSpeed > kFujitsuAcFanQuiet) - _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - else - _.Fan = fanSpeed; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -void IRFujitsuAC::setMode(const uint8_t mode) { - if (mode > kFujitsuAcModeHeat) - _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - else - _.Mode = mode; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } - -/// Set the requested swing operation mode of the A/C unit. -/// @param[in] swingMode The swingMode code for the A/C. -/// Vertical, Horizon, or Both. See constants for details. -/// @note Not all models support all possible swing modes. -void IRFujitsuAC::setSwing(const uint8_t swingMode) { - _.Swing = swingMode; - _rawstatemodified = true; - switch (_model) { - // No Horizontal support. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; - break; - // Has Horizontal support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARJW2: - default: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; - } - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the requested swing operation mode of the A/C unit. -/// @return The contents of the swing state/mode. -uint8_t IRFujitsuAC::getSwing(void) const { return _.Swing; } - -/// Set the Clean mode of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setClean(const bool on) { - _.Clean = on; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getClean(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; - default: return false; - } -} - -/// Set the Filter mode status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::setFilter(const bool on) { - _.Filter = on; - _rawstatemodified = true; - setCmd(kFujitsuAcCmdStayOn); // No special command involved. -} - -/// Get the Filter mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getFilter(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; - default: return false; - } -} - -/// Set the 10C heat status of the A/C. -/// @param[in] on true, the setting is on. false, the setting is off. -void IRFujitsuAC::set10CHeat(const bool on) { - switch (_model) { - // Only selected models support this. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - setClean(on); // 10C Heat uses the same bit as Clean - if (on) { - _.Mode = kFujitsuAcModeFan; - _.Power = true; - _.Fan = kFujitsuAcFanAuto; - _.Swing = kFujitsuAcSwingOff; - _rawstatemodified = true; - } - default: - break; - } -} - -/// Get the 10C heat status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::get10CHeat(void) const { - switch (_model) { - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && - _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); - default: return false; - } -} - -/// Get the Timer type of the A/C message. -/// @return The current timer type in numeric form. -uint8_t IRFujitsuAC::getTimerType(void) const { - switch (_model) { - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: return _.TimerType; - default: return kFujitsuAcStopTimers; - } -} - -/// Set the Timer type of the A/C message. -/// @param[in] timertype The kind of timer to use for the message. -void IRFujitsuAC::setTimerType(const uint8_t timertype) { - switch (timertype) { - case kFujitsuAcSleepTimer: - case kFujitsuAcOnTimer: - case kFujitsuAcOffTimer: - case kFujitsuAcStopTimers: - _.TimerType = timertype; - break; - default: _.TimerType = kFujitsuAcStopTimers; - } - _rawstatemodified = true; -} - -/// Get the On Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOnTimer(void) const { - if (getTimerType() == kFujitsuAcOnTimer) - return _.OnTimer; - return 0; -} - -/// Set the On Timer setting of the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { - _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. - _rawstatemodified = true; - if (_.OnTimer) { - _.TimerType = kFujitsuAcOnTimer; - } else if (getTimerType() == kFujitsuAcOnTimer) { - _.TimerType = kFujitsuAcStopTimers; - } -} - -/// Get the Off/Sleep Timer setting of the A/C. -/// @return nr of minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC::getOffSleepTimer(void) const { - switch (getTimerType()) { - case kFujitsuAcOffTimer: - case kFujitsuAcSleepTimer: return _.OffTimer; - default: return 0; - } -} - -/// Set the Off/Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { - _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. - _rawstatemodified = true; -} - -/// Set the Off Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. - if (nr_mins) - _.TimerType = kFujitsuAcOffTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// Set the Sleep Timer time for the A/C. -/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. -void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. - if (nr_mins) - _.TimerType = kFujitsuAcSleepTimer; - else if (getTimerType() != kFujitsuAcOnTimer) - _.TimerType = kFujitsuAcStopTimers; -} - -/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = state[length - 1]; - switch (length) { - case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 - return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 - sum = sumBytes(state, length - 1); - sum_complement = 0x9B; - break; - case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E - sum = sumBytes(state + kFujitsuAcStateLengthShort, - length - 1 - kFujitsuAcStateLengthShort); - break; - default: // Includes ARDB1 & ARJW2 short. - return true; // Assume the checksum is valid for other lengths. - } - return checksum == (uint8_t)(sum_complement - sum); // Does it match? -} - -/// Set the device's remote ID number. -/// @param[in] num The ID for the remote. Valid number range is 0 to 3. -void IRFujitsuAC::setId(const uint8_t num) { - _.Id = num; - _rawstatemodified = true; -} - -/// Get the current device's remote ID number. -/// @return The current device's remote ID number. -uint8_t IRFujitsuAC::getId(void) const { return _.Id; } - -/// Set the Temperature units for the A/C. -/// @param[in] on true, use Celsius. false, use Fahrenheit. -void IRFujitsuAC::setCelsius(const bool on) { - _.Fahrenheit = !on; - _rawstatemodified = true; -} - -/// Get the Clean mode status of the A/C. -/// @return true, the setting is on. false, the setting is off. -bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } - -/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; - case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; - case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; - case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; - default: return kFujitsuAcModeAuto; - } -} - -/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; - case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; - case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; - default: return kFujitsuAcFanAuto; - } -} - -/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { - switch (mode) { - case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; - case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; - case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; - case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; - case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; - case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; - case kFujitsuAcFanQuiet: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to a previous state. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRFujitsuAC::toCommon(const stdAc::state_t *prev) { - stdAc::state_t result{}; - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::FUJITSU_AC; - checkSum(); - result.model = _model; - result.power = getPower(); - // Only update these settings if it is a long message, or we have no previous - // state info for those settings. - if (isLongCode() || prev == NULL) { - result.mode = toCommonMode(_.Mode); - result.celsius = getCelsius(); - { - const float minHeat = result.celsius ? kFujitsuAcMinHeat - : kFujitsuAcMinHeatF; - result.degrees = get10CHeat() ? minHeat : getTemp(); - } - result.fanspeed = toCommonFanSpeed(_.Fan); - uint8_t swing = _.Swing; - switch (result.model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - result.clean = _.Clean; - result.filter = _.Filter; - result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto - : stdAc::swingv_t::kOff; - result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto - : stdAc::swingh_t::kOff; - break; - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - default: - result.swingv = stdAc::swingv_t::kOff; - result.swingh = stdAc::swingh_t::kOff; - } - } - result.quiet = _.Fan == kFujitsuAcFanQuiet; - result.turbo = _cmd == kFujitsuAcCmdPowerful; - result.econo = _cmd == kFujitsuAcCmdEcono; - // Not supported. - result.light = false; - result.filter = 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 IRFujitsuAC::toString(void) const { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - fujitsu_ac_remote_model_t model = _model; - result += addModelToString(decode_type_t::FUJITSU_AC, model, false); - result += addIntToString(_.Id, kIdStr); - result += addBoolToString(getPower(), kPowerStr); - if (_rawstatemodified || isLongCode()) { - result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, - kFujitsuAcModeHeat, kFujitsuAcModeDry, - kFujitsuAcModeFan); - { - const bool isCelsius = getCelsius(); - const float minHeat = isCelsius ? kFujitsuAcMinHeat : kFujitsuAcMinHeatF; - result += addTempFloatToString(get10CHeat() ? minHeat : getTemp(), - isCelsius); - } - result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, - kFujitsuAcFanAuto, kFujitsuAcFanQuiet, - kFujitsuAcFanMed); - switch (model) { - // These models have no internal swing, clean. or filter state. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - break; - // These models have Clean & Filter, plus Swing (via fall thru) - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - result += addBoolToString(getClean(), kCleanStr); - result += addBoolToString(getFilter(), kFilterStr); - // FALL THRU - default: // e.g. ARREW4E - switch (model) { - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(get10CHeat(), k10CHeatStr); - break; - default: - break; - } - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kFujitsuAcSwingOff: - result += kOffStr; - break; - case kFujitsuAcSwingVert: - result += kSwingVStr; - break; - case kFujitsuAcSwingHoriz: - result += kSwingHStr; - break; - case kFujitsuAcSwingBoth: - result += kSwingVStr; - result += '+'; - result += kSwingHStr; - break; - default: - result += kUnknownStr; - } - result += ')'; - } - } - result += kCommaSpaceStr; - result += kCommandStr; - result += kColonSpaceStr; - switch (_cmd) { - case kFujitsuAcCmdStepHoriz: - result += kStepStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdStepVert: - result += kStepStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdToggleSwingHoriz: - result += kToggleStr; - result += ' '; - result += kSwingHStr; - break; - case kFujitsuAcCmdToggleSwingVert: - result += kToggleStr; - result += ' '; - result += kSwingVStr; - break; - case kFujitsuAcCmdEcono: - result += kEconoStr; - break; - case kFujitsuAcCmdPowerful: - result += kPowerfulStr; - break; - default: - result += kNAStr; - } - if (_rawstatemodified || isLongCode()) { - uint16_t mins = 0; - String type_str = kTimerStr; - switch (model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); - // FALL THRU - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - switch (getTimerType()) { - case kFujitsuAcOnTimer: - type_str = kOnTimerStr; - mins = getOnTimer(); - break; - case kFujitsuAcOffTimer: - type_str = kOffTimerStr; - mins = getOffSleepTimer(); - break; - case kFujitsuAcSleepTimer: - type_str = kSleepTimerStr; - mins = getOffSleepTimer(); - break; - } - result += addLabeledString(mins ? minsToString(mins) : kOffStr, - type_str); - break; - default: - break; - } - } - return result; -} - -#if DECODE_FUJITSU_AC -/// Decode the supplied Fujitsu AC IR message if possible. -/// Status: STABLE / Working. -/// @param[in,out] results Ptr to the data to decode & where to store the decode -/// 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 A boolean. True if it can decode it, false if it can't. -bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + - offset) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAcBits: - case kFujitsuAcBits - 8: - case kFujitsuAcMinBits: - case kFujitsuAcMinBits + 8: break; - default: return false; // Must be called with the correct nr. of bits. - } - } - - // Header / Some of the Data - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kFujitsuAcMinBits - 8, - kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header - kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - 0, 0, // No Footer (yet) - false, _tolerance + kFujitsuAcExtraTolerance, 0, - false); // LSBF - if (!used) return false; - offset += used; - // Check we have the typical data header. - if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; - dataBitsSoFar += kFujitsuAcMinBits - 8; - - // Keep reading bytes until we either run out of message or state to fill. - match_result_t data_result; - for (uint16_t i = 5; - offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - _tolerance + kFujitsuAcExtraTolerance, 0, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Footer - if (offset > results->rawlen || - !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) - return false; - // The space is optional if we are out of capture. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - results->decode_type = FUJITSU_AC; - results->bits = dataBitsSoFar; - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAcMinBits: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFC) return false; - return true; // Success - case kFujitsuAcMinBits + 8: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFE) return false; - // The last byte needs to be the inverse of the penultimate byte. - if (results->state[5] != (uint8_t)~results->state[6]) return false; - return true; // Success - case kFujitsuAcBits - 8: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFC) return false; - break; - case kFujitsuAcBits: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - return true; // All good. -} -#endif // DECODE_FUJITSU_AC - -#if SEND_FUJITSU_AC264 -/// Send a Fujitsu 264 bit A/C formatted message. -/// Status: STABLE / Known Good. -/// @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::sendFujitsuAC264(const unsigned char data[], const uint16_t nbytes, - const uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC264 - -// Code to emulate Fujitsu 264 bit A/C IR remote control unit. - -/// 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? -IRFujitsuAC264::IRFujitsuAC264(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 IRFujitsuAC264::begin(void) { _irsend.begin(); } - -/// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC264::stateReset(void) { - for (size_t i = 0; i < kFujitsuAc264StateLength; i++) { - _.raw[i] = 0; - } - _ispoweredon = false; - _isecofan = false; - _isoutsidequiet = false; - _settemp = 0; - setTemp(24); - _cmd = _.Cmd = kFujitsuAc264CmdCool; - _.TempAuto = 0; - _.Mode = kFujitsuAc264ModeCool; - _.FanSpeed = kFujitsuAc264FanSpeedHigh; - _.FanAngle = kFujitsuAc264FanAngleStay; - _.Swing = false; - _.Economy = false; - _.Clean = false; - _.ClockHours = 0; - _.ClockMins = 0; - _.SleepTimerEnable = false; - _.SleepTimer = 0; - _.TimerEnable = kFujitsuAc264OnOffTimerDisable; - _.OnTimer = 0; - _.OffTimer = 0; - _.raw[0] = 0x14; - _.raw[1] = 0x63; - _.raw[2] = 0x00; - _.raw[3] = 0x10; - _.raw[4] = 0x10; -} - -#if SEND_FUJITSU_AC264 -/// Send the current internal state as an IR message. -/// @param[in] repeat Nr. of times the message will be repeated. -void IRFujitsuAC264::send(const uint16_t repeat) { - _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); - _settemp = _.Temp; // Preserve the sent setting - _ispoweredon = (_cmd < kFujitsuAc264SpCmdTurnOff); - if (_cmd == kFujitsuAc264SpCmdEcoFanOn) - _isecofan = true; - if (_cmd == kFujitsuAc264SpCmdEcoFanOff) - _isecofan = false; - if (_cmd == kFujitsuAc264SpCmdOutsideQuietOn) - _isoutsidequiet = true; - if (_cmd == kFujitsuAc264SpCmdOutsideQuietOff) - _isoutsidequiet = false; -} -#endif // SEND_FUJITSU_AC264 - -/// 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* IRFujitsuAC264::getRaw(void) { - checkSum(); - return _.raw; -} - -/// Set the internal state from a valid code for this protocol. -/// @param[in] newState A valid code for this protocol. -/// @param[in] length Size of the newState array. -/// @return True, if successful; Otherwise false. (i.e. size check) -bool IRFujitsuAC264::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAc264StateLength) return false; - for (uint16_t i = 0; i < kFujitsuAc264StateLength; i++) { - if (i < length) - _.raw[i] = newState[i]; - else - _.raw[i] = 0; - } - switch (length) { - case kFujitsuAc264StateLengthShort: - if (std::memcmp(_.raw, kFujitsuAc264StatesTurnOff, - kFujitsuAc264StateLengthShort) == 0) - _cmd = kFujitsuAc264SpCmdTurnOff; - if (std::memcmp(_.raw, kFujitsuAc264StatesTogglePowerful, - kFujitsuAc264StateLengthShort) == 0) - _cmd = kFujitsuAc264SpCmdTogglePowerful; - if (std::memcmp(_.raw, kFujitsuAc264StatesEcoFanOff, - kFujitsuAc264StateLengthShort) == 0) - _cmd = kFujitsuAc264SpCmdEcoFanOff; - if (std::memcmp(_.raw, kFujitsuAc264StatesEcoFanOn, - kFujitsuAc264StateLengthShort) == 0) - _cmd = kFujitsuAc264SpCmdEcoFanOn; - break; - case kFujitsuAc264StateLengthMiddle: - if (std::memcmp(_.raw, kFujitsuAc264StatesOutsideQuietOff, - kFujitsuAc264StateLengthMiddle) == 0) - _cmd = kFujitsuAc264SpCmdOutsideQuietOff; - if (std::memcmp(_.raw, kFujitsuAc264StatesOutsideQuietOn, - kFujitsuAc264StateLengthMiddle) == 0) - _cmd = kFujitsuAc264SpCmdOutsideQuietOn; - if (std::memcmp(_.raw, kFujitsuAc264StatesToggleSterilization, - kFujitsuAc264StateLengthMiddle) == 0) - _cmd = kFujitsuAc264SpCmdToggleSterilization; - break; - case kFujitsuAc264StateLength: - setPower(true); - _cmd = _.Cmd; - break; - default: - return false; - } - return true; -} - -/// 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 IRFujitsuAC264::validChecksum(uint8_t state[], const uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = 0; - - if (length == kFujitsuAc264StateLengthShort) { - checksum = state[kFujitsuAc264StateLengthShort - 1]; - sum = state[kFujitsuAc264StateLengthShort - 2]; - sum_complement = 0xFF; - } else if (length == kFujitsuAc264StateLengthMiddle) { - checksum = state[kFujitsuAc264StateLengthMiddle - 1]; - sum = sumBytes(state, kFujitsuAc264StateLengthMiddle - 1); - sum_complement = 0x9E; - // The current command is normal - } else if (length == kFujitsuAc264StateLength) { - checksum = state[kFujitsuAc264StateLength - 1]; - sum = sumBytes(state, kFujitsuAc264StateLength - 1); - sum_complement = 0xAF; - } else { - return false; - } - return checksum == (uint8_t) (sum_complement - sum); // Does it match? -} - -/// Calculate and set the checksum values for the internal state. -void IRFujitsuAC264::checkSum(void) { - if (!isSpecialCommand()) { // The current command is not special - _.raw[5] = 0xFE; - _.RestLength = 0x1A; - _.Protocol = 0x40; - _.raw[13] = 0x00; - _.raw[15] |= 0x12; - _.raw[16] |= 0x06; - _.raw[17] = 0x00; - _.raw[21] |= 0x40; - _.raw[22] |= 0x10; - _.raw[25] = 0x00; - _.raw[26] = 0x00; - _.raw[27] = 0x00; - _.raw[28] |= 0xF0; - _.raw[29] = 0xFF; - _.raw[30] = 0xFF; - - uint8_t checksum = 0; - uint8_t checksum_complement = 0; - checksum = sumBytes(_.raw, kFujitsuAc264StateLength - 1); - checksum_complement = 0xAF; - _.raw[kFujitsuAc264StateLength - 1] = checksum_complement - checksum; - } -} - -/// Is the current command a special command? -/// @return True, if special command (kFujitsuAc264SpCmd*); -/// false, if normal command (kFujitsuAc264Cmd*). -bool IRFujitsuAC264::isSpecialCommand(void) const { - return (_cmd & 0xF0) == 0xF0; -} - -/// Get the length (size) of the state code for the current configuration. -/// @return The length of the state array required for this config. -uint8_t IRFujitsuAC264::getStateLength(void) { - uint8_t stateLength = 0; - - switch (_cmd) { - case kFujitsuAc264SpCmdTurnOff: - case kFujitsuAc264SpCmdTogglePowerful: - case kFujitsuAc264SpCmdEcoFanOff: - case kFujitsuAc264SpCmdEcoFanOn: - stateLength = kFujitsuAc264StateLengthShort; - break; - case kFujitsuAc264SpCmdOutsideQuietOff: - case kFujitsuAc264SpCmdOutsideQuietOn: - case kFujitsuAc264SpCmdToggleSterilization: - stateLength = kFujitsuAc264StateLengthMiddle; - break; - default: - stateLength = kFujitsuAc264StateLength; - break; - } - return stateLength; -} - -/// Set the requested power state of the A/C to on. -/// @note Mode should be set after this function. -void IRFujitsuAC264::on(void) { setPower(true); } - -/// Set the requested power state of the A/C to off. -void IRFujitsuAC264::off(void) { setPower(false); } - -/// Change the power setting. -/// @param[in] on True, the setting is on. false, the setting is off. -/// @note If on = true, a mode should be set after calling this function. -void IRFujitsuAC264::setPower(const bool on) { - if (on) { - _cmd = kFujitsuAc264CmdCool; - } else { - _cmd = kFujitsuAc264SpCmdTurnOff; - std::memcpy(_.raw, kFujitsuAc264StatesTurnOff, - kFujitsuAc264StateLengthShort); - } -} - -/// Get the value of the current power setting. -/// @return True, the setting is on. false, the setting is off. -bool IRFujitsuAC264::getPower(void) const { return _ispoweredon; } - -/// Check if the temperature setting is changed. -/// @return True if the temperature is not changed. -bool IRFujitsuAC264::isTempStayed(void) const { return _settemp == _.Temp; } - -/// Set the temperature. -/// @param[in] temp The temperature in degrees Celcius. -/// @note The fractional part which is truncated to multiple of 0.5. -void IRFujitsuAC264::setTemp(const float temp) { - float _temp; - - if (temp > kFujitsuAc264MaxTemp) - _temp = kFujitsuAc264MaxTemp; - else if ((temp < kFujitsuAc264MinTemp) && (_.Mode != kFujitsuAc264ModeHeat)) - _temp = kFujitsuAc264MinTemp; - else if (temp < kFujitsuAc264MinHeat) - _temp = kFujitsuAc264MinHeat; - else - _temp = temp; - - _.Temp = (_temp - (kFujitsuAc264TempOffsetC / 2)) * 2; - _cmd = _.Cmd = kFujitsuAc264CmdTemp; - _.SubCmd = isTempStayed(); -} - -/// Get the current temperature setting. -/// @return The current setting for temperature in degrees Celcius. -float IRFujitsuAC264::getTemp(void) const { - return static_cast(_.Temp / 2.0) + (kFujitsuAc264TempOffsetC / 2); -} - -/// Set the temperature in auto mode. -/// @param[in] temp The temperature in auto mode in degrees Celcius. -/// @note The fractional part which is truncated to multiple of 0.5. -void IRFujitsuAC264::setTempAuto(const float temp) { - int8_t _tempx10; - - _tempx10 = (int8_t) (temp * 10); - _tempx10 -= _tempx10 % 5; - if (temp > kFujitsuAc264MaxTempAuto) - _tempx10 = kFujitsuAc264MaxTempAuto * 10; - else if (temp < kFujitsuAc264MinTempAuto) - _tempx10 = kFujitsuAc264MinTempAuto * 10; - - _.TempAuto = _tempx10; - _cmd = _.Cmd = kFujitsuAc264CmdTemp; -} - -/// Get the current temperature in auto mode setting. -/// @return The current setting for temp in auto mode in degrees Celcius. -float IRFujitsuAC264::getTempAuto(void) const { - return static_cast(static_cast(_.TempAuto) / 10.0); -} - -/// Set the operating mode of the A/C. -/// @param[in] mode The desired operating mode. -/// @param[in] weakdry True if dry mode is in weak. -void IRFujitsuAC264::setMode(const uint8_t mode, const bool weakdry) { - switch (mode) { - case kFujitsuAc264ModeAuto: - _.Mode = kFujitsuAc264ModeAuto; - _cmd = _.Cmd = kFujitsuAc264CmdAuto; - break; - case kFujitsuAc264ModeCool: - _.Mode = kFujitsuAc264ModeCool; - _cmd = _.Cmd = kFujitsuAc264CmdCool; - break; - case kFujitsuAc264ModeFan: - _.Mode = kFujitsuAc264ModeFan; - _cmd = _.Cmd = kFujitsuAc264CmdFan; - break; - case kFujitsuAc264ModeHeat: - _.Mode = kFujitsuAc264ModeHeat; - _cmd = _.Cmd = kFujitsuAc264CmdHeat; - break; - case kFujitsuAc264ModeDry: - _.Mode = kFujitsuAc264ModeDry; - _.WeakDry = weakdry; - _cmd = _.Cmd = kFujitsuAc264CmdDry; - break; - default: - _.Mode = kFujitsuAc264ModeAuto; - _cmd = _.Cmd = kFujitsuAc264CmdAuto; - break; - } - _.SubCmd = 1; -} - -/// Get the operating mode setting of the A/C. -/// @return The current operating mode setting. -uint8_t IRFujitsuAC264::getMode(void) const { return _.Mode; } - -/// Get the weak dry mode setting of the A/C. -/// @return The weak dry mode setting. -bool IRFujitsuAC264::isWeakDry(void) const { return _.WeakDry; } - -/// Set the speed of the fan. -/// @param[in] fanSpeed The desired setting. -void IRFujitsuAC264::setFanSpeed(const uint8_t fanSpeed) { - // Set the fan to auto if out of range. - if ((fanSpeed == kFujitsuAc264FanSpeedQuiet) || - (fanSpeed == kFujitsuAc264FanSpeedLow) || - (fanSpeed == kFujitsuAc264FanSpeedMed) || - (fanSpeed == kFujitsuAc264FanSpeedHigh)) - _.FanSpeed = fanSpeed; - else - _.FanSpeed = kFujitsuAc264FanSpeedAuto; - _cmd = _.Cmd = kFujitsuAc264CmdFanSpeed; - _.SubCmd = 0; -} - -/// Get the current fan speed setting. -/// @return The current fan speed. -uint8_t IRFujitsuAC264::getFanSpeed(void) const { return _.FanSpeed; } - -/// Set the angle of the fan. -/// @param[in] fanAngle The desired setting. -void IRFujitsuAC264::setFanAngle(const uint8_t fanAngle) { - // Set the fan to stay if out of range. - if ((fanAngle > kFujitsuAc264FanAngle7) || - (fanAngle < kFujitsuAc264FanAngle1)) { - _.FanAngle = kFujitsuAc264FanAngleStay; - } else { - _.FanAngle = fanAngle; - } - _cmd = _.Cmd = kFujitsuAc264CmdFanAngle; - _.SubCmd = 0; -} - -/// Get the current fan angle setting. -/// @return The current fan angle. -uint8_t IRFujitsuAC264::getFanAngle(void) const { return _.FanAngle; } - -/// Set weather the swing of fan is enabled or not. -/// @param[in] on True if swing is enabled, false if disabled. -void IRFujitsuAC264::setSwing(const bool on) { - _.Swing = on; - _.FanAngle = kFujitsuAc264FanAngleStay; // Set the fan to stay. - _cmd = _.Cmd = kFujitsuAc264CmdSwing; - _.SubCmd = 0; -} - -/// Get the requested swing operation mode of the A/C unit. -/// @return True if swing is enabled, false if disabled. -bool IRFujitsuAC264::getSwing(void) const { return _.Swing; } - -/// Set weather economy mode is enabled or not. -/// @param[in] on True if economy mode is enabled, false if disabled. -void IRFujitsuAC264::setEconomy(const bool on) { - _.Economy = on; - _cmd = _.Cmd = kFujitsuAc264CmdEconomy; - _.SubCmd = 0; -} - -/// Get the requested economy mode of the A/C unit. -/// @return True if economy mode is enabled, false if disabled. -bool IRFujitsuAC264::getEconomy(void) const { return _.Economy; } - -/// Set weather clean mode is enabled or not. -/// @param[in] on True if swing is enabled, false if disabled. -void IRFujitsuAC264::setClean(const bool on) { - _.Clean = on; - _cmd = _.Cmd = kFujitsuAc264CmdClean; - _.SubCmd = 0; -} - -/// Get the requested clean mode of the A/C unit. -/// @return True if clean is enabled, false if disabled. -bool IRFujitsuAC264::getClean(void) const { return _.Clean; } - -/// Toggle the sterilization. -/// @note This command is valid only when AC's power is off. -void IRFujitsuAC264::toggleSterilization(void) { - if (getPower()) - return; - _cmd = kFujitsuAc264SpCmdToggleSterilization; - std::memcpy(_.raw, kFujitsuAc264StatesToggleSterilization, - kFujitsuAc264StateLengthMiddle); -} - -/// Set weather outside quiet mode is enabled or not. -/// @param[in] on True if outside quiet is enabled, false if disabled. -/// @note This command is valid only when AC's power is off. -void IRFujitsuAC264::setOutsideQuiet(const bool on) { - if (getPower()) - return; - if (on) { - _cmd = kFujitsuAc264SpCmdOutsideQuietOn; - std::memcpy(_.raw, kFujitsuAc264StatesOutsideQuietOn, - kFujitsuAc264StateLengthMiddle); - } else { - _cmd = kFujitsuAc264SpCmdOutsideQuietOff; - std::memcpy(_.raw, kFujitsuAc264StatesOutsideQuietOff, - kFujitsuAc264StateLengthMiddle); - } -} - -/// Get the requested outside quiet mode of the A/C unit. -/// @return True if outside quiet is enabled, false if disabled. -bool IRFujitsuAC264::getOutsideQuiet(void) const { return _isoutsidequiet; } - -/// Set weather economy fan mode is enabled or not. -/// @param[in] on True if economy fan mode is enabled, false if disabled. -/// @note This command is valid only when AC's power is off. -void IRFujitsuAC264::setEcoFan(const bool on) { - if (getPower()) - return; - if (on) { - _cmd = kFujitsuAc264SpCmdEcoFanOn; - std::memcpy(_.raw, kFujitsuAc264StatesEcoFanOn, - kFujitsuAc264StateLengthShort); - } else { - _cmd = kFujitsuAc264SpCmdEcoFanOff; - std::memcpy(_.raw, kFujitsuAc264StatesEcoFanOff, - kFujitsuAc264StateLengthShort); - } -} - -/// Get the requested economy fan mode of the A/C unit. -/// @return True if economy fan mode is enabled, false if disabled. -bool IRFujitsuAC264::getEcoFan(void) const { return _isecofan; } - -/// Toggle the powerful mode. -/// @note This command is valid only when AC's power is on. -void IRFujitsuAC264::togglePowerful(void) { - if (!getPower()) - return; - _cmd = kFujitsuAc264SpCmdTogglePowerful; - std::memcpy(_.raw, kFujitsuAc264StatesTogglePowerful, - kFujitsuAc264StateLengthShort); -} - -/// Set the clock on the A/C unit. -/// @param[in] mins_since_midnight Nr. of minutes past midnight. -void IRFujitsuAC264::setClock(const uint16_t mins_since_midnight) { - uint16_t mins = mins_since_midnight; - if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. - // Hours. - _.ClockHours = mins / 60; - // Minutes. - _.ClockMins = mins % 60; -} - -/// Get the clock time to be sent to the A/C unit. -/// @return The number of minutes past midnight. -uint16_t IRFujitsuAC264::getClock(void) const { - return (_.ClockHours * 60 + _.ClockMins); -} - -/// Set the sleep timer setting of the A/C. -/// @param[in] mins Minutes to set the timer to. 0 means disabled. -void IRFujitsuAC264::setSleepTimer(const uint16_t mins) { - if (mins == 0) { - _.SleepTimerEnable = false; - _.SleepTimer = 0; - _cmd = _.Cmd = kFujitsuAc264CmdCancelSleepTimer; - _.SubCmd = 0; - } else if (mins <= kFujitsuAc264SleepTimerMax) { - _.SleepTimerEnable = true; - _.SleepTimer = 0x800 + mins; - _cmd = _.Cmd = kFujitsuAc264CmdSleepTime; - _.SubCmd = 1; - } -} - -/// Get the sleep timer setting of the A/C. -/// @return Minutes left on the timer. 0 means disabled/not supported. -uint16_t IRFujitsuAC264::getSleepTimer(void) const { - if (_.SleepTimerEnable) - return (_.SleepTimer - 0x800); - return 0; -} - -/// Set the Timer enable of the A/C message. -/// @param[in] timer_enable The kind of timer to enable for the message. -void IRFujitsuAC264::setTimerEnable(const uint8_t timer_enable) { - switch (timer_enable) { - case kFujitsuAc264OnTimerEnable: - _.TimerEnable = timer_enable; - _cmd = _.Cmd = kFujitsuAc264CmdOnTimer; - _.SubCmd = 0; - break; - case kFujitsuAc264OffTimerEnable: - case kFujitsuAc264OnOffTimerEnable: - _.TimerEnable = timer_enable; - _cmd = _.Cmd = kFujitsuAc264CmdOffTimer; - _.SubCmd = 0; - break; - case kFujitsuAc264OnOffTimerDisable: - _.TimerEnable = timer_enable; - _cmd = _.Cmd = kFujitsuAc264CmdCancelOnOffTimer; - _.SubCmd = 0; - break; - default: - _.TimerEnable = kFujitsuAc264OnOffTimerDisable; - _cmd = _.Cmd = kFujitsuAc264CmdCancelOnOffTimer; - _.SubCmd = 0; - break; - } -} - -/// Get the Timer enable of the A/C message. -/// @return The current timer enable in numeric form. -uint8_t IRFujitsuAC264::getTimerEnable(void) const { return _.TimerEnable; } - -/// Set the on timer setting of the A/C. -/// @param[in] mins10 Time in 10 minutes unit, when the A/C will turn on. -/// 0 means 0:00 AM, 1 means 0:10 AM. -void IRFujitsuAC264::setOnTimer(const uint8_t mins10) { - if (mins10 <= kFujitsuAc26OnOffTimerMax) - _.OnTimer = mins10; -} - -/// Get the on timer setting of the A/C. -/// @return Time in 10 minutes unit, when the A/C will turn on. -/// 0 means 0:00 AM, 1 means 0:10 AM. -uint8_t IRFujitsuAC264::getOnTimer(void) const { return _.OnTimer; } - -/// Set the off timer setting of the A/C. -/// @param[in] mins10 Time in 10 minutes unit, when the A/C will turn off. -/// 0 means 0:00 AM, 1 means 0:10 AM. -void IRFujitsuAC264::setOffTimer(const uint8_t mins10) { - if (mins10 <= kFujitsuAc26OnOffTimerMax) - _.OffTimer = mins10; -} - -/// Get the off timer setting of the A/C. -/// @return Time in 10 minutes unit, when the A/C will turn off. -/// 0 means 0:00 AM, 1 means 0:10 AM. -uint8_t IRFujitsuAC264::getOffTimer(void) const { return _.OffTimer; } - -/// Set the requested (normal) command part for the A/C message. -/// @param[in] cmd Command to be set. -/// @note Only normal commands (=!isSpecialCommand()) can be set -/// with this function. -void IRFujitsuAC264::setCmd(const uint8_t cmd) { - switch (cmd) { - case kFujitsuAc264CmdCool: - case kFujitsuAc264CmdHeat: - case kFujitsuAc264CmdDry: - case kFujitsuAc264CmdAuto: - case kFujitsuAc264CmdFan: - case kFujitsuAc264CmdSleepTime: - _cmd = _.Cmd = cmd; - _.SubCmd = 1; - break; - case kFujitsuAc264CmdTemp: - case kFujitsuAc264CmdSwing: - case kFujitsuAc264CmdEconomy: - case kFujitsuAc264CmdClean: - case kFujitsuAc264CmdFanSpeed: - case kFujitsuAc264CmdFanAngle: - case kFujitsuAc264CmdCancelSleepTimer: - case kFujitsuAc264CmdOnTimer: - case kFujitsuAc264CmdOffTimer: - case kFujitsuAc264CmdCancelOnOffTimer: - _cmd = _.Cmd = cmd; - _.SubCmd = 0; - break; - default: - break; - } -} - -/// Get the requested command part for the A/C message. -/// @return The command code. -uint8_t IRFujitsuAC264::getCmd(void) const { - return _cmd; -} - -/// 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 IRFujitsuAC264::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kFujitsuAc264ModeCool; - case stdAc::opmode_t::kHeat: return kFujitsuAc264ModeHeat; - case stdAc::opmode_t::kDry: return kFujitsuAc264ModeDry; - case stdAc::opmode_t::kFan: return kFujitsuAc264ModeFan; - default: return kFujitsuAc264ModeAuto; - } -} - -/// 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 IRFujitsuAC264::convertFanSpeed(stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: return kFujitsuAc264FanSpeedQuiet; - case stdAc::fanspeed_t::kLow: return kFujitsuAc264FanSpeedLow; - case stdAc::fanspeed_t::kMedium: return kFujitsuAc264FanSpeedMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: return kFujitsuAc264FanSpeedHigh; - default: return kFujitsuAc264FanSpeedAuto; - } -} - -/// 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 IRFujitsuAC264::toCommonMode(const uint8_t mode) { - switch (mode) { - case kFujitsuAc264ModeCool: return stdAc::opmode_t::kCool; - case kFujitsuAc264ModeHeat: return stdAc::opmode_t::kHeat; - case kFujitsuAc264ModeDry: return stdAc::opmode_t::kDry; - case kFujitsuAc264ModeFan: 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 IRFujitsuAC264::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kFujitsuAc264FanSpeedHigh: return stdAc::fanspeed_t::kMax; - case kFujitsuAc264FanSpeedMed: return stdAc::fanspeed_t::kMedium; - case kFujitsuAc264FanSpeedLow: return stdAc::fanspeed_t::kLow; - case kFujitsuAc264FanSpeedQuiet: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; - } -} - -/// Convert the current internal state into its stdAc::state_t equivalent. -/// @param[in] prev Ptr to a previous state. -/// @return The stdAc equivalent of the native settings. -stdAc::state_t IRFujitsuAC264::toCommon(const stdAc::state_t *prev) { - stdAc::state_t result{}; - if (prev != NULL) result = *prev; - result.protocol = decode_type_t::FUJITSU_AC264; - checkSum(); - result.power = _cmd != kFujitsuAc264SpCmdTurnOff; - // Only update these settings if it is not a special command message, - // or we have no previous state info for those settings. - if (!isSpecialCommand() || prev == NULL) { - result.mode = toCommonMode(_.Mode); - result.celsius = true; - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.FanSpeed); - result.clean = getClean(); - result.swingv = getSwing()? stdAc::swingv_t::kAuto : - stdAc::swingv_t::kOff; - result.econo = getEconomy(); - result.clock = getClock(); - uint16_t sleep_time = getSleepTimer(); - result.sleep = sleep_time? sleep_time: -1; - } - result.quiet = getEcoFan(); - // Not supported. - result.turbo = false; - result.swingh = stdAc::swingh_t::kOff; - result.light = false; - result.filter = false; - result.beep = false; - return result; -} - -/// Convert the current internal state into a human readable string. -/// @return A human readable string. -String IRFujitsuAC264::toString(void) const { - String result = ""; - result.reserve(180); // Reserve some heap for the string to reduce fragging. - if (isSpecialCommand()) { // Special commands - result += kCommandStr; - result += kColonSpaceStr; - switch (_cmd) { - case kFujitsuAc264SpCmdTurnOff: - result += "Power Off"; - break; - case kFujitsuAc264SpCmdTogglePowerful: - result += kPowerfulStr; - break; - case kFujitsuAc264SpCmdEcoFanOff: - result += "Eco Fan "; - result += kOffStr; - break; - case kFujitsuAc264SpCmdEcoFanOn: - result += "Eco Fan "; - result += kOnStr; - break; - case kFujitsuAc264SpCmdOutsideQuietOff: - result += "Outside Quiet "; - result += kOffStr; - break; - case kFujitsuAc264SpCmdOutsideQuietOn: - result += "Outside Quiet "; - result += kOnStr; - break; - case kFujitsuAc264SpCmdToggleSterilization: - result += "Sterilization"; - break; - default: - result += kNAStr; - } - } else { // Normal commands - result += addBoolToString(true, kPowerStr, false); - // Mode - result += addModeToString(_.Mode, kFujitsuAc264ModeAuto, - kFujitsuAc264ModeCool, kFujitsuAc264ModeHeat, - kFujitsuAc264ModeDry, kFujitsuAc264ModeFan); - // Temp - float degrees = getTemp(); - result += addTempFloatToString(getTemp()); - // Temp in Auto - degrees = getTempAuto(); - String degrees_str = (degrees >= 0)? uint64ToString(degrees): - String(kDashStr) + uint64ToString(-degrees); - result += addLabeledString(degrees_str, "Temp (Auto)"); - if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); - result += 'C'; - // Fan Speed - result += addFanToString(_.FanSpeed, kFujitsuAc264FanSpeedHigh, - kFujitsuAc264FanSpeedLow, kFujitsuAc264FanSpeedAuto, - kFujitsuAc264FanSpeedQuiet, kFujitsuAc264FanSpeedMed); - // Fan Angle - result += addIntToString(_.FanAngle, "Fan Angle"); - result += kSpaceLBraceStr; - switch (_.FanAngle) { - case kFujitsuAc264FanAngle1: - result += kHighestStr; - break; - case kFujitsuAc264FanAngle2: - result += kHighStr; - break; - case kFujitsuAc264FanAngle4: - result += kMiddleStr; - break; - case kFujitsuAc264FanAngle6: - result += kLowStr; - break; - case kFujitsuAc264FanAngle7: - result += kLowestStr; - break; - case kFujitsuAc264FanAngle3: - result += kUpperStr; - result += ' '; - result += kMiddleStr; - break; - case kFujitsuAc264FanAngle5: - result += kLowerStr; - result += ' '; - result += kMiddleStr; - break; - case kFujitsuAc264FanAngleStay: - result += "Stay"; - break; - default: - result += kUnknownStr; - } - result += ')'; - // Swing - result += addBoolToString(getSwing(), kSwingStr); - // Low Power - result += addBoolToString(getEconomy(), "Economy"); - // Clean - result += addBoolToString(getClean(), kCleanStr); - // Cmd - result += kCommaSpaceStr; - result += kCommandStr; - result += kColonSpaceStr; - switch (_.Cmd) { - case kFujitsuAc264CmdCool: - result += kCoolStr; - break; - case kFujitsuAc264CmdHeat: - result += kHeatStr; - break; - case kFujitsuAc264CmdDry: - if (_.WeakDry) - result += "Weak "; - result += kDryStr; - break; - case kFujitsuAc264CmdAuto: - result += kAutoStr; - break; - case kFujitsuAc264CmdFan: - result += kFanStr; - break; - case kFujitsuAc264CmdTemp: - result += kTempStr; - break; - case kFujitsuAc264CmdSwing: - result += kSwingStr; - break; - case kFujitsuAc264CmdSleepTime: - result += kSleepTimerStr; - break; - case kFujitsuAc264CmdEconomy: - result += "Economy"; - break; - case kFujitsuAc264CmdClean: - result += kCleanStr; - break; - case kFujitsuAc264CmdFanSpeed: - result += "Fan Speed"; - break; - case kFujitsuAc264CmdFanAngle: - result += "Fan Angle"; - break; - case kFujitsuAc264CmdCancelSleepTimer: - result += "Cancel Sleep Timer"; - break; - case kFujitsuAc264CmdOffTimer: - result += kOffTimerStr; - break; - case kFujitsuAc264CmdOnTimer: - result += kOnTimerStr; - break; - case kFujitsuAc264CmdCancelOnOffTimer: - result += "Cancel On/Off Timer"; - break; - default: - result += kNAStr; - } - // Clock - uint16_t mins = 0; - mins = getClock(); - result += addLabeledString(mins ? minsToString(mins) : kNAStr, - "Current Time"); - // Sleep Timer - mins = getSleepTimer(); - result += addLabeledString(mins ? minsToString(mins) : kOffStr, - kSleepTimerStr); - // On/Off Timer - uint8_t timer_enable = getTimerEnable(); - mins = getOnTimer(); - result += addLabeledString((timer_enable & kFujitsuAc264OnTimerEnable)? - minsToString(mins * 10): kOffStr, kOnTimerStr); - mins = getOffTimer(); - result += addLabeledString((timer_enable & kFujitsuAc264OffTimerEnable)? - minsToString(mins * 10): kOffStr, kOffTimerStr); - } - return result; -} - -#if DECODE_FUJITSU_AC264 -/// Decode the supplied Fujitsu 264 bit AC IR message if possible. -/// Status: STABLE / Working. -/// @param[in,out] results Ptr to the data to decode & where to store the decode -/// 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 A boolean. True if it can decode it, false if it can't. -bool IRrecv::decodeFujitsuAC264(decode_results* results, uint16_t offset, - const uint16_t nbits, - const bool strict) { - uint16_t dataBitsSoFar = 0; - uint8_t restLength = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAc264BitsShort) + kHeader + kFooter - 1 + - offset) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAc264Bits: - case kFujitsuAc264BitsMiddle: - case kFujitsuAc264BitsShort: break; - default: return false; // Must be called with the correct nr. of bits. - } - } - - // Header / Some of the Data - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, kFujitsuAc264BitsShort, - kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header - kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - 0, 0, // No Footer (yet) - false, _tolerance + kFujitsuAcExtraTolerance, 0, - false); // LSBF - offset += used; - dataBitsSoFar += kFujitsuAc264BitsShort; - - // Check if it has the Fujitsu AC264 protocol header - if (results->state[0] != 0x14 || results->state[1] != 0x63 || - results->state[2] != 0x00 || results->state[3] != 0x10 || - results->state[4] != 0x10) - return false; - - // Identify which command it is - switch (results->state[5]) { - case 0xFE: // Command length is normal or middle - restLength = results->state[6]; - // check the rest length - if ((restLength != 0x1A) && (restLength != 0x09)) - return false; - break; - case 0x51: // Command length is short - case 0x50: // Command length is short - case 0x39: // Command length is short - case 0x02: // Command length is short - if (results->state[6] == (uint8_t)~results->state[5]) { // checksum - results->decode_type = FUJITSU_AC264; - results->bits = dataBitsSoFar; - return true; - } else { - return false; - } - default: - return false; - } - - // Keep reading bytes until we either run out of message or state to fill. - match_result_t data_result; - for (uint16_t i = kFujitsuAc264StateLengthShort; - offset <= results->rawlen - 16 && i < kFujitsuAc264StateLengthShort + - restLength; i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, - _tolerance + kFujitsuAcExtraTolerance, 0, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAc264BitsMiddle: - case kFujitsuAc264Bits: - // Check if the state[5] is matched with the protocol. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - - if (!IRFujitsuAC264::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - results->decode_type = FUJITSU_AC264; - results->bits = dataBitsSoFar; - return true; // All good. -} -#endif // DECODE_FUJITSU_AC264 +// Copyright 2017 Jonny Graham +// Copyright 2017-2022 David Conran +// Copyright 2021 siriuslzx +// Copyright 2023 Takeshi Shimizu + +/// @file +/// @brief Support for Fujitsu A/C protocols. +/// Fujitsu A/C support added by Jonny Graham & David Conran +/// @warning Use of incorrect model may cause the A/C unit to lock up. +/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical +/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. +/// The correct model for it is ARREB1E. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 + +#include "ir_Fujitsu.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Ref: +// These values are based on averages of measurements +const uint16_t kFujitsuAcHdrMark = 3324; +const uint16_t kFujitsuAcHdrSpace = 1574; +const uint16_t kFujitsuAcBitMark = 448; +const uint16_t kFujitsuAcOneSpace = 1182; +const uint16_t kFujitsuAcZeroSpace = 390; +const uint16_t kFujitsuAcMinGap = 8100; +const uint8_t kFujitsuAcExtraTolerance = 5; // Extra tolerance percentage. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addFanToString; +using irutils::addTempFloatToString; +using irutils::minsToString; +using irutils::addSignedIntToString; + +#if SEND_FUJITSU_AC +/// Send a Fujitsu A/C formatted message. +/// Status: STABLE / Known Good. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// Typically one of: +/// kFujitsuAcStateLength, +/// kFujitsuAcStateLength - 1, +/// kFujitsuAcStateLengthShort, +/// kFujitsuAcStateLengthShort - 1 +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC + +// Code to emulate Fujitsu A/C IR remote control unit. + +/// Class Constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] model The enum for the model of A/C to be emulated. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRFujitsuAC::IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model, + const bool inverted, const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + setModel(model); + stateReset(); +} + +/// Set the currently emulated model of the A/C. +/// @param[in] model An enum representing the model to support/emulate. +void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { + _model = model; + switch (model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _state_length = kFujitsuAcStateLength - 1; + _state_length_short = kFujitsuAcStateLengthShort - 1; + break; + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + default: + _state_length = kFujitsuAcStateLength; + _state_length_short = kFujitsuAcStateLengthShort; + } +} + +/// Get the currently emulated/detected model of the A/C. +/// @return The enum representing the model of A/C. +fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) const { return _model; } + +/// Reset the state of the remote to a known good state/sequence. +void IRFujitsuAC::stateReset(void) { + for (size_t i = 0; i < kFujitsuAcStateLength; i++) { + _.longcode[i] = 0; + } + setTemp(24); + _.Fan = kFujitsuAcFanHigh; + _.Mode = kFujitsuAcModeCool; + _.Swing = kFujitsuAcSwingBoth; + _cmd = kFujitsuAcCmdTurnOn; + _.Filter = false; + _.Clean = false; + _.TimerType = kFujitsuAcStopTimers; + _.OnTimer = 0; + _.OffTimer = 0; + _.longcode[0] = 0x14; + _.longcode[1] = 0x63; + _.longcode[3] = 0x10; + _.longcode[4] = 0x10; + _rawstatemodified = true; +} + +/// Set up hardware to be able to send a message. +void IRFujitsuAC::begin(void) { _irsend.begin(); } + +#if SEND_FUJITSU_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRFujitsuAC::send(const uint16_t repeat) { + _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); +} +#endif // SEND_FUJITSU_AC + +/// Update the length (size) of the state code for the current configuration. +/// @return true, if use long codes; false, use short codes. +bool IRFujitsuAC::updateUseLongOrShort(void) { + bool fullCmd = false; + switch (_cmd) { + case kFujitsuAcCmdTurnOff: // 0x02 + case kFujitsuAcCmdEcono: // 0x09 + case kFujitsuAcCmdPowerful: // 0x39 + case kFujitsuAcCmdStepVert: // 0x6C + case kFujitsuAcCmdToggleSwingVert: // 0x6D + case kFujitsuAcCmdStepHoriz: // 0x79 + case kFujitsuAcCmdToggleSwingHoriz: // 0x7A + _.Cmd = _cmd; + _rawstatemodified = true; + break; + default: + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + _.Cmd = 0xFE; + _rawstatemodified = true; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Cmd = 0xFC; + _rawstatemodified = true; + break; + } + fullCmd = true; + break; + } + return fullCmd; +} + +/// Calculate and set the checksum values for the internal state. +void IRFujitsuAC::checkSum(void) { + _rawstatemodified = true; + if (updateUseLongOrShort()) { // Is it going to be a long code? + // Nr. of bytes in the message after this byte. + _.RestLength = _state_length - 7; + _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; + _.Power = (_cmd == kFujitsuAcCmdTurnOn) || get10CHeat(); + + // These values depend on model + if (_model != fujitsu_ac_remote_model_t::ARREB1E && + _model != fujitsu_ac_remote_model_t::ARREW4E) { + _.OutsideQuiet = 0; + if (_model != fujitsu_ac_remote_model_t::ARRAH2E) { + _.TimerType = kFujitsuAcStopTimers; + } + } + if (_model != fujitsu_ac_remote_model_t::ARRY4) { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + break; + default: + _.Clean = false; + } + _.Filter = false; + } + // Set the On/Off/Sleep timer Nr of mins. + _.OffTimer = getOffSleepTimer(); + _.OnTimer = getOnTimer(); + // Enable bit for the Off/Sleep timer + _.OffTimerEnable = _.OffTimer > 0; + // Enable bit for the On timer + _.OnTimerEnable = _.OnTimer > 0; + + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + switch (_model) { + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + _.Swing = kFujitsuAcSwingOff; + checksum = sumBytes(_.longcode, _state_length - 1); + checksum_complement = 0x9B; + break; + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + _.unknown = 1; + // FALL THRU + default: + checksum = sumBytes(_.longcode + _state_length_short, + _state_length - _state_length_short - 1); + } + // and negate the checksum and store it in the last byte. + _.longcode[_state_length - 1] = checksum_complement - checksum; + } else { // short codes + for (size_t i = 0; i < _state_length_short; i++) { + _.shortcode[i] = _.longcode[i]; + } + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + // The last byte is the inverse of penultimate byte + _.shortcode[_state_length_short - 1] = + ~_.shortcode[_state_length_short - 2]; + break; + default: + {}; // We don't need to do anything for the others. + } + } +} + +/// Get the length (size) of the state code for the current configuration. +/// @return The length of the state array required for this config. +uint8_t IRFujitsuAC::getStateLength(void) { + return updateUseLongOrShort() ? _state_length : _state_length_short; +} + +/// Is the current binary state representation a long or a short code? +/// @return true, if long; false, if short. +bool IRFujitsuAC::isLongCode(void) const { + return _.Cmd == 0xFE || _.Cmd == 0xFC; +} + +/// 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* IRFujitsuAC::getRaw(void) { + checkSum(); + return isLongCode() ? _.longcode : _.shortcode; +} + +/// Build the internal state/config from the current (raw) A/C message. +/// @param[in] length Size of the current/used (raw) A/C message array. +void IRFujitsuAC::buildFromState(const uint16_t length) { + switch (length) { + case kFujitsuAcStateLength - 1: + case kFujitsuAcStateLengthShort - 1: + setModel(fujitsu_ac_remote_model_t::ARDB1); + // ARJW2 has horizontal swing. + if (_.Swing > kFujitsuAcSwingVert) + setModel(fujitsu_ac_remote_model_t::ARJW2); + break; + default: + switch (_.Cmd) { + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setModel(fujitsu_ac_remote_model_t::ARREB1E); + break; + default: + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + } + } + switch (_.RestLength) { + case 8: + if (_model != fujitsu_ac_remote_model_t::ARJW2) + setModel(fujitsu_ac_remote_model_t::ARDB1); + break; + case 9: + if (_model != fujitsu_ac_remote_model_t::ARREB1E) + setModel(fujitsu_ac_remote_model_t::ARRAH2E); + break; + } + if (_.Power) + setCmd(kFujitsuAcCmdTurnOn); + else + setCmd(kFujitsuAcCmdStayOn); + // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if + // either the raw Filter or Clean setting is on. + if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean) && + !get10CHeat()) + setModel(fujitsu_ac_remote_model_t::ARRY4); + if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) + setModel(fujitsu_ac_remote_model_t::ARREB1E); + switch (_.Cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + setCmd(_.Cmd); + break; + } + if (_.Protocol == 0x31) setModel(fujitsu_ac_remote_model_t::ARREW4E); +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +/// @param[in] length Size of the newState array. +/// @return true, if successful; Otherwise false. (i.e. size check) +bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > kFujitsuAcStateLength) return false; + for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { + if (i < length) + _.longcode[i] = newState[i]; + else + _.longcode[i] = 0; + } + buildFromState(length); + _rawstatemodified = false; + return true; +} + +/// Request the A/C to step the Horizontal Swing. +void IRFujitsuAC::stepHoriz(void) { setCmd(kFujitsuAcCmdStepHoriz); } + +/// Request the A/C to toggle the Horizontal Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingHoriz(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingHoriz); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingHoriz); +} + +/// Request the A/C to step the Vertical Swing. +void IRFujitsuAC::stepVert(void) { setCmd(kFujitsuAcCmdStepVert); } + +/// Request the A/C to toggle the Vertical Swing mode. +/// @param[in] update Do we need to update the general swing config? +void IRFujitsuAC::toggleSwingVert(const bool update) { + // Toggle the current setting. + if (update) setSwing(getSwing() ^ kFujitsuAcSwingVert); + // and set the appropriate special command. + setCmd(kFujitsuAcCmdToggleSwingVert); +} + +/// Set the requested (special) command part for the A/C message. +/// @param[in] cmd The special command code. +void IRFujitsuAC::setCmd(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdTurnOn: + case kFujitsuAcCmdStayOn: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + _cmd = cmd; + break; + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + switch (_model) { + // Only these remotes have horizontal. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + switch (_model) { + // Only these remotes have these commands. + case ARREB1E: + case ARREW4E: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } +} + +/// Set the requested (special) command part for the A/C message. +/// @return The special command code. +uint8_t IRFujitsuAC::getCmd(void) const { + return _cmd; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setPower(const bool on) { + setCmd(on ? kFujitsuAcCmdTurnOn : kFujitsuAcCmdTurnOff); +} + +/// Set the requested power state of the A/C to off. +void IRFujitsuAC::off(void) { setPower(false); } + +/// Set the requested power state of the A/C to on. +void IRFujitsuAC::on(void) { setPower(true); } + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } + +/// Set the Outside Quiet mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setOutsideQuiet(const bool on) { + _.OutsideQuiet = on; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Outside Quiet mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getOutsideQuiet(void) const { + switch (_model) { + // Only ARREB1E & ARREW4E seems to have this mode. + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + return _.OutsideQuiet; + default: return false; + } +} + +/// Set the temperature. +/// @param[in] temp The temperature in degrees. +/// @param[in] useCelsius Use Celsius or Fahrenheit? +void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { + float mintemp; + float maxtemp; + uint8_t offset; + bool _useCelsius; + float _temp; + + switch (_model) { + // These models have native Fahrenheit & Celsius upport. + case fujitsu_ac_remote_model_t::ARREW4E: + _useCelsius = useCelsius; + _temp = temp; + break; + // Make sure everything else uses Celsius. + default: + _useCelsius = true; + _temp = useCelsius ? temp : fahrenheitToCelsius(temp); + } + setCelsius(_useCelsius); + if (_useCelsius) { + mintemp = kFujitsuAcMinTemp; + maxtemp = kFujitsuAcMaxTemp; + offset = kFujitsuAcTempOffsetC; + } else { + mintemp = kFujitsuAcMinTempF; + maxtemp = kFujitsuAcMaxTempF; + offset = kFujitsuAcTempOffsetF; + } + _temp = std::max(mintemp, _temp); + _temp = std::min(maxtemp, _temp); + if (_useCelsius) { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) + _.Temp = (_temp - (offset / 2)) * 2; + else + _.Temp = (_temp - offset) * 4; + } else { + _.Temp = _temp - offset; + } + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees of the currently set units. +float IRFujitsuAC::getTemp(void) const { + if (_model == fujitsu_ac_remote_model_t::ARREW4E) { + if (_.Fahrenheit) // Currently only ARREW4E supports native Fahrenheit. + return _.Temp + kFujitsuAcTempOffsetF; + else + return (_.Temp / 2.0) + (kFujitsuAcMinTemp / 2); + } else { + return _.Temp / 4 + kFujitsuAcMinTemp; + } +} + +/// Set the speed of the fan. +/// @param[in] fanSpeed The desired setting. +void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { + if (fanSpeed > kFujitsuAcFanQuiet) + _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. + else + _.Fan = fanSpeed; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRFujitsuAC::getFanSpeed(void) const { return _.Fan; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRFujitsuAC::setMode(const uint8_t mode) { + if (mode > kFujitsuAcModeHeat) + _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. + else + _.Mode = mode; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } + +/// Set the requested swing operation mode of the A/C unit. +/// @param[in] swingMode The swingMode code for the A/C. +/// Vertical, Horizon, or Both. See constants for details. +/// @note Not all models support all possible swing modes. +void IRFujitsuAC::setSwing(const uint8_t swingMode) { + _.Swing = swingMode; + _rawstatemodified = true; + switch (_model) { + // No Horizontal support. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingVert) _.Swing = kFujitsuAcSwingVert; + break; + // Has Horizontal support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARJW2: + default: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingBoth) _.Swing = kFujitsuAcSwingBoth; + } + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the requested swing operation mode of the A/C unit. +/// @return The contents of the swing state/mode. +uint8_t IRFujitsuAC::getSwing(void) const { return _.Swing; } + +/// Set the Clean mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setClean(const bool on) { + _.Clean = on; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getClean(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Clean; + default: return false; + } +} + +/// Set the Filter mode status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::setFilter(const bool on) { + _.Filter = on; + _rawstatemodified = true; + setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +/// Get the Filter mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getFilter(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; + default: return false; + } +} + +/// Set the 10C heat status of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRFujitsuAC::set10CHeat(const bool on) { + switch (_model) { + // Only selected models support this. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + setClean(on); // 10C Heat uses the same bit as Clean + if (on) { + _.Mode = kFujitsuAcModeFan; + _.Power = true; + _.Fan = kFujitsuAcFanAuto; + _.Swing = kFujitsuAcSwingOff; + _rawstatemodified = true; + } + default: + break; + } +} + +/// Get the 10C heat status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::get10CHeat(void) const { + switch (_model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && + _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); + default: return false; + } +} + +/// Get the Timer type of the A/C message. +/// @return The current timer type in numeric form. +uint8_t IRFujitsuAC::getTimerType(void) const { + switch (_model) { + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: return _.TimerType; + default: return kFujitsuAcStopTimers; + } +} + +/// Set the Timer type of the A/C message. +/// @param[in] timertype The kind of timer to use for the message. +void IRFujitsuAC::setTimerType(const uint8_t timertype) { + switch (timertype) { + case kFujitsuAcSleepTimer: + case kFujitsuAcOnTimer: + case kFujitsuAcOffTimer: + case kFujitsuAcStopTimers: + _.TimerType = timertype; + break; + default: _.TimerType = kFujitsuAcStopTimers; + } + _rawstatemodified = true; +} + +/// Get the On Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOnTimer(void) const { + if (getTimerType() == kFujitsuAcOnTimer) + return _.OnTimer; + return 0; +} + +/// Set the On Timer setting of the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { + _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; + if (_.OnTimer) { + _.TimerType = kFujitsuAcOnTimer; + } else if (getTimerType() == kFujitsuAcOnTimer) { + _.TimerType = kFujitsuAcStopTimers; + } +} + +/// Get the Off/Sleep Timer setting of the A/C. +/// @return nr of minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC::getOffSleepTimer(void) const { + switch (getTimerType()) { + case kFujitsuAcOffTimer: + case kFujitsuAcSleepTimer: return _.OffTimer; + default: return 0; + } +} + +/// Set the Off/Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { + _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; +} + +/// Set the Off Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. + if (nr_mins) + _.TimerType = kFujitsuAcOffTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// Set the Sleep Timer time for the A/C. +/// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. +void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. + if (nr_mins) + _.TimerType = kFujitsuAcSleepTimer; + else if (getTimerType() != kFujitsuAcOnTimer) + _.TimerType = kFujitsuAcStopTimers; +} + +/// 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 IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = state[length - 1]; + switch (length) { + case kFujitsuAcStateLengthShort: // ARRAH2E, ARREB1E, & ARRY4 + return state[length - 1] == (uint8_t)~state[length - 2]; + case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 + sum = sumBytes(state, length - 1); + sum_complement = 0x9B; + break; + case kFujitsuAcStateLength: // ARRAH2E, ARRY4, & ARREB1E + sum = sumBytes(state + kFujitsuAcStateLengthShort, + length - 1 - kFujitsuAcStateLengthShort); + break; + default: // Includes ARDB1 & ARJW2 short. + return true; // Assume the checksum is valid for other lengths. + } + return checksum == (uint8_t)(sum_complement - sum); // Does it match? +} + +/// Set the device's remote ID number. +/// @param[in] num The ID for the remote. Valid number range is 0 to 3. +void IRFujitsuAC::setId(const uint8_t num) { + _.Id = num; + _rawstatemodified = true; +} + +/// Get the current device's remote ID number. +/// @return The current device's remote ID number. +uint8_t IRFujitsuAC::getId(void) const { return _.Id; } + +/// Set the Temperature units for the A/C. +/// @param[in] on true, use Celsius. false, use Fahrenheit. +void IRFujitsuAC::setCelsius(const bool on) { + _.Fahrenheit = !on; + _rawstatemodified = true; +} + +/// Get the Clean mode status of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRFujitsuAC::getCelsius(void) const { return !_.Fahrenheit; } + +/// 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 IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kFujitsuAcModeCool; + case stdAc::opmode_t::kHeat: return kFujitsuAcModeHeat; + case stdAc::opmode_t::kDry: return kFujitsuAcModeDry; + case stdAc::opmode_t::kFan: return kFujitsuAcModeFan; + default: return kFujitsuAcModeAuto; + } +} + +/// 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 IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kFujitsuAcFanQuiet; + case stdAc::fanspeed_t::kLow: return kFujitsuAcFanLow; + case stdAc::fanspeed_t::kMedium: return kFujitsuAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kFujitsuAcFanHigh; + default: return kFujitsuAcFanAuto; + } +} + +/// 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 IRFujitsuAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAcModeFan: 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 IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAcFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRFujitsuAC::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result{}; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::FUJITSU_AC; + checkSum(); + result.model = _model; + result.power = getPower(); + // Only update these settings if it is a long message, or we have no previous + // state info for those settings. + if (isLongCode() || prev == NULL) { + result.mode = toCommonMode(_.Mode); + result.celsius = getCelsius(); + { + const float minHeat = result.celsius ? kFujitsuAcMinHeat + : kFujitsuAcMinHeatF; + result.degrees = get10CHeat() ? minHeat : getTemp(); + } + result.fanspeed = toCommonFanSpeed(_.Fan); + uint8_t swing = _.Swing; + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + result.clean = _.Clean; + result.filter = _.Filter; + result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + default: + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + } + } + result.quiet = _.Fan == kFujitsuAcFanQuiet; + result.turbo = _cmd == kFujitsuAcCmdPowerful; + result.econo = _cmd == kFujitsuAcCmdEcono; + // Not supported. + result.light = false; + result.filter = 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 IRFujitsuAC::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + fujitsu_ac_remote_model_t model = _model; + result += addModelToString(decode_type_t::FUJITSU_AC, model, false); + result += addIntToString(_.Id, kIdStr); + result += addBoolToString(getPower(), kPowerStr); + if (_rawstatemodified || isLongCode()) { + result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + { + const bool isCelsius = getCelsius(); + const float minHeat = isCelsius ? kFujitsuAcMinHeat : kFujitsuAcMinHeatF; + result += addTempFloatToString(get10CHeat() ? minHeat : getTemp(), + isCelsius); + } + result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + switch (model) { + // These models have no internal swing, clean. or filter state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + // These models have Clean & Filter, plus Swing (via fall thru) + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getFilter(), kFilterStr); + // FALL THRU + default: // e.g. ARREW4E + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(get10CHeat(), k10CHeatStr); + break; + default: + break; + } + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kFujitsuAcSwingOff: + result += kOffStr; + break; + case kFujitsuAcSwingVert: + result += kSwingVStr; + break; + case kFujitsuAcSwingHoriz: + result += kSwingHStr; + break; + case kFujitsuAcSwingBoth: + result += kSwingVStr; + result += '+'; + result += kSwingHStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + } + } + result += kCommaSpaceStr; + result += kCommandStr; + result += kColonSpaceStr; + switch (_cmd) { + case kFujitsuAcCmdStepHoriz: + result += kStepStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdStepVert: + result += kStepStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdToggleSwingHoriz: + result += kToggleStr; + result += ' '; + result += kSwingHStr; + break; + case kFujitsuAcCmdToggleSwingVert: + result += kToggleStr; + result += ' '; + result += kSwingVStr; + break; + case kFujitsuAcCmdEcono: + result += kEconoStr; + break; + case kFujitsuAcCmdPowerful: + result += kPowerfulStr; + break; + default: + result += kNAStr; + } + if (_rawstatemodified || isLongCode()) { + uint16_t mins = 0; + String type_str = kTimerStr; + switch (model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); + // FALL THRU + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + switch (getTimerType()) { + case kFujitsuAcOnTimer: + type_str = kOnTimerStr; + mins = getOnTimer(); + break; + case kFujitsuAcOffTimer: + type_str = kOffTimerStr; + mins = getOffSleepTimer(); + break; + case kFujitsuAcSleepTimer: + type_str = kSleepTimerStr; + mins = getOffSleepTimer(); + break; + } + result += addLabeledString(mins ? minsToString(mins) : kOffStr, + type_str); + break; + default: + break; + } + } + return result; +} + +#if DECODE_FUJITSU_AC +/// Decode the supplied Fujitsu AC IR message if possible. +/// Status: STABLE / Working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// 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 A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1 + + offset) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAcBits: + case kFujitsuAcBits - 8: + case kFujitsuAcMinBits: + case kFujitsuAcMinBits + 8: break; + default: return false; // Must be called with the correct nr. of bits. + } + } + + // Header / Some of the Data + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kFujitsuAcMinBits - 8, + kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header + kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + 0, 0, // No Footer (yet) + false, _tolerance + kFujitsuAcExtraTolerance, 0, + false); // LSBF + if (!used) return false; + offset += used; + // Check we have the typical data header. + if (results->state[0] != 0x14 || results->state[1] != 0x63) return false; + dataBitsSoFar += kFujitsuAcMinBits - 8; + + // Keep reading bytes until we either run out of message or state to fill. + match_result_t data_result; + for (uint16_t i = 5; + offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + _tolerance + kFujitsuAcExtraTolerance, 0, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Footer + if (offset > results->rawlen || + !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) + return false; + // The space is optional if we are out of capture. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) + return false; + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + results->decode_type = FUJITSU_AC; + results->bits = dataBitsSoFar; + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAcMinBits: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFC) return false; + return true; // Success + case kFujitsuAcMinBits + 8: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFE) return false; + // The last byte needs to be the inverse of the penultimate byte. + if (results->state[5] != (uint8_t)~results->state[6]) return false; + return true; // Success + case kFujitsuAcBits - 8: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFC) return false; + break; + case kFujitsuAcBits: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + return true; // All good. +} +#endif // DECODE_FUJITSU_AC + +#if SEND_FUJITSU_AC264 +/// Send a Fujitsu 264 bit A/C formatted message. +/// Status: STABLE / Known Good. +/// @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::sendFujitsuAC264(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC264 + +// Code to emulate Fujitsu 264 bit A/C IR remote control unit. + +/// 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? +IRFujitsuAC264::IRFujitsuAC264(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 IRFujitsuAC264::begin(void) { _irsend.begin(); } + +/// Reset the state of the remote to a known good state/sequence. +void IRFujitsuAC264::stateReset(void) { + for (size_t i = 0; i < kFujitsuAc264StateLength; i++) { + _.raw[i] = 0; + } + _ispoweredon = false; + _isecofan = false; + _isoutsidequiet = false; + _settemp = 0; + setTemp(24); + _cmd = _.Cmd = kFujitsuAc264CmdCool; + _.TempAuto = 0; + _.Mode = kFujitsuAc264ModeCool; + _.FanSpeed = kFujitsuAc264FanSpeedHigh; + _.FanAngle = kFujitsuAc264FanAngleStay; + _.Swing = false; + _.Economy = false; + _.Clean = false; + _.ClockHours = 0; + _.ClockMins = 0; + _.SleepTimerEnable = false; + _.SleepTimer = 0; + _.TimerEnable = kFujitsuAc264OnOffTimerDisable; + _.OnTimer = 0; + _.OffTimer = 0; + _.raw[0] = 0x14; + _.raw[1] = 0x63; + _.raw[2] = 0x00; + _.raw[3] = 0x10; + _.raw[4] = 0x10; +} + +#if SEND_FUJITSU_AC264 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRFujitsuAC264::send(const uint16_t repeat) { + _irsend.sendFujitsuAC(getRaw(), getStateLength(), repeat); + _settemp = _.Temp; // Preserve the sent setting + _ispoweredon = (_cmd < kFujitsuAc264SpCmdTurnOff); + if (_cmd == kFujitsuAc264SpCmdEcoFanOn) + _isecofan = true; + if (_cmd == kFujitsuAc264SpCmdEcoFanOff) + _isecofan = false; + if (_cmd == kFujitsuAc264SpCmdOutsideQuietOn) + _isoutsidequiet = true; + if (_cmd == kFujitsuAc264SpCmdOutsideQuietOff) + _isoutsidequiet = false; +} +#endif // SEND_FUJITSU_AC264 + +/// 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* IRFujitsuAC264::getRaw(void) { + checkSum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] newState A valid code for this protocol. +/// @param[in] length Size of the newState array. +/// @return True, if successful; Otherwise false. (i.e. size check) +bool IRFujitsuAC264::setRaw(const uint8_t newState[], const uint16_t length) { + if (length > kFujitsuAc264StateLength) return false; + for (uint16_t i = 0; i < kFujitsuAc264StateLength; i++) { + if (i < length) + _.raw[i] = newState[i]; + else + _.raw[i] = 0; + } + switch (length) { + case kFujitsuAc264StateLengthShort: + if (std::memcmp(_.raw, kFujitsuAc264StatesTurnOff, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdTurnOff; + if (std::memcmp(_.raw, kFujitsuAc264StatesTogglePowerful, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdTogglePowerful; + if (std::memcmp(_.raw, kFujitsuAc264StatesEcoFanOff, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdEcoFanOff; + if (std::memcmp(_.raw, kFujitsuAc264StatesEcoFanOn, + kFujitsuAc264StateLengthShort) == 0) + _cmd = kFujitsuAc264SpCmdEcoFanOn; + break; + case kFujitsuAc264StateLengthMiddle: + if (std::memcmp(_.raw, kFujitsuAc264StatesOutsideQuietOff, + kFujitsuAc264StateLengthMiddle) == 0) + _cmd = kFujitsuAc264SpCmdOutsideQuietOff; + if (std::memcmp(_.raw, kFujitsuAc264StatesOutsideQuietOn, + kFujitsuAc264StateLengthMiddle) == 0) + _cmd = kFujitsuAc264SpCmdOutsideQuietOn; + if (std::memcmp(_.raw, kFujitsuAc264StatesToggleSterilization, + kFujitsuAc264StateLengthMiddle) == 0) + _cmd = kFujitsuAc264SpCmdToggleSterilization; + break; + case kFujitsuAc264StateLength: + setPower(true); + _cmd = _.Cmd; + break; + default: + return false; + } + return true; +} + +/// 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 IRFujitsuAC264::validChecksum(uint8_t state[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = 0; + + if (length == kFujitsuAc264StateLengthShort) { + checksum = state[kFujitsuAc264StateLengthShort - 1]; + sum = state[kFujitsuAc264StateLengthShort - 2]; + sum_complement = 0xFF; + } else if (length == kFujitsuAc264StateLengthMiddle) { + checksum = state[kFujitsuAc264StateLengthMiddle - 1]; + sum = sumBytes(state, kFujitsuAc264StateLengthMiddle - 1); + sum_complement = 0x9E; + // The current command is normal + } else if (length == kFujitsuAc264StateLength) { + checksum = state[kFujitsuAc264StateLength - 1]; + sum = sumBytes(state, kFujitsuAc264StateLength - 1); + sum_complement = 0xAF; + } else { + return false; + } + return checksum == (uint8_t) (sum_complement - sum); // Does it match? +} + +/// Calculate and set the checksum values for the internal state. +void IRFujitsuAC264::checkSum(void) { + if (!isSpecialCommand()) { // The current command is not special + _.raw[5] = 0xFE; + _.RestLength = 0x1A; + _.Protocol = 0x40; + _.raw[13] = 0x00; + _.raw[15] |= 0x12; + _.raw[16] |= 0x06; + _.raw[17] = 0x00; + _.raw[21] |= 0x40; + _.raw[22] |= 0x10; + _.raw[25] = 0x00; + _.raw[26] = 0x00; + _.raw[27] = 0x00; + _.raw[28] |= 0xF0; + _.raw[29] = 0xFF; + _.raw[30] = 0xFF; + + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + checksum = sumBytes(_.raw, kFujitsuAc264StateLength - 1); + checksum_complement = 0xAF; + _.raw[kFujitsuAc264StateLength - 1] = checksum_complement - checksum; + } +} + +/// Is the current command a special command? +/// @return True, if special command (kFujitsuAc264SpCmd*); +/// false, if normal command (kFujitsuAc264Cmd*). +bool IRFujitsuAC264::isSpecialCommand(void) const { + return (_cmd & 0xF0) == 0xF0; +} + +/// Get the length (size) of the state code for the current configuration. +/// @return The length of the state array required for this config. +uint8_t IRFujitsuAC264::getStateLength(void) { + uint8_t stateLength = 0; + + switch (_cmd) { + case kFujitsuAc264SpCmdTurnOff: + case kFujitsuAc264SpCmdTogglePowerful: + case kFujitsuAc264SpCmdEcoFanOff: + case kFujitsuAc264SpCmdEcoFanOn: + stateLength = kFujitsuAc264StateLengthShort; + break; + case kFujitsuAc264SpCmdOutsideQuietOff: + case kFujitsuAc264SpCmdOutsideQuietOn: + case kFujitsuAc264SpCmdToggleSterilization: + stateLength = kFujitsuAc264StateLengthMiddle; + break; + default: + stateLength = kFujitsuAc264StateLength; + break; + } + return stateLength; +} + +/// Set the requested power state of the A/C to on. +/// @note Mode should be set after this function. +void IRFujitsuAC264::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRFujitsuAC264::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on True, the setting is on. false, the setting is off. +/// @note If on = true, a mode should be set after calling this function. +void IRFujitsuAC264::setPower(const bool on) { + if (on) { + _cmd = kFujitsuAc264CmdCool; + } else { + _cmd = kFujitsuAc264SpCmdTurnOff; + std::memcpy(_.raw, kFujitsuAc264StatesTurnOff, + kFujitsuAc264StateLengthShort); + } +} + +/// Get the value of the current power setting. +/// @return True, the setting is on. false, the setting is off. +bool IRFujitsuAC264::getPower(void) const { return _ispoweredon; } + +/// Check if the temperature setting is changed. +/// @return True if the temperature is not changed. +bool IRFujitsuAC264::isTempStayed(void) const { return _settemp == _.Temp; } + +/// Set the temperature. +/// @param[in] temp The temperature in degrees Celcius. +/// @note The fractional part which is truncated to multiple of 0.5. +void IRFujitsuAC264::setTemp(const float temp) { + float _temp; + + if (temp > kFujitsuAc264MaxTemp) + _temp = kFujitsuAc264MaxTemp; + else if ((temp < kFujitsuAc264MinTemp) && (_.Mode != kFujitsuAc264ModeHeat)) + _temp = kFujitsuAc264MinTemp; + else if (temp < kFujitsuAc264MinHeat) + _temp = kFujitsuAc264MinHeat; + else + _temp = temp; + + _.Temp = (_temp - (kFujitsuAc264TempOffsetC / 2)) * 2; + _cmd = _.Cmd = kFujitsuAc264CmdTemp; + _.SubCmd = isTempStayed(); +} + +/// Get the current temperature setting. +/// @return The current setting for temperature in degrees Celcius. +float IRFujitsuAC264::getTemp(void) const { + return static_cast(_.Temp / 2.0) + (kFujitsuAc264TempOffsetC / 2); +} + +/// Set the temperature in auto mode. +/// @param[in] temp The temperature in auto mode in degrees Celcius. +/// @note The fractional part which is truncated to multiple of 0.5. +void IRFujitsuAC264::setTempAuto(const float temp) { + int8_t _tempx10; + + _tempx10 = (int8_t) (temp * 10); + _tempx10 -= _tempx10 % 5; + if (temp > kFujitsuAc264MaxTempAuto) + _tempx10 = kFujitsuAc264MaxTempAuto * 10; + else if (temp < kFujitsuAc264MinTempAuto) + _tempx10 = kFujitsuAc264MinTempAuto * 10; + + _.TempAuto = _tempx10; + _cmd = _.Cmd = kFujitsuAc264CmdTemp; +} + +/// Get the current temperature in auto mode setting. +/// @return The current setting for temp in auto mode in degrees Celcius. +float IRFujitsuAC264::getTempAuto(void) const { + return static_cast(static_cast(_.TempAuto) / 10.0); +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +/// @param[in] weakdry True if dry mode is in weak. +void IRFujitsuAC264::setMode(const uint8_t mode, const bool weakdry) { + switch (mode) { + case kFujitsuAc264ModeAuto: + _.Mode = kFujitsuAc264ModeAuto; + _cmd = _.Cmd = kFujitsuAc264CmdAuto; + break; + case kFujitsuAc264ModeCool: + _.Mode = kFujitsuAc264ModeCool; + _cmd = _.Cmd = kFujitsuAc264CmdCool; + break; + case kFujitsuAc264ModeFan: + _.Mode = kFujitsuAc264ModeFan; + _cmd = _.Cmd = kFujitsuAc264CmdFan; + break; + case kFujitsuAc264ModeHeat: + _.Mode = kFujitsuAc264ModeHeat; + _cmd = _.Cmd = kFujitsuAc264CmdHeat; + break; + case kFujitsuAc264ModeDry: + _.Mode = kFujitsuAc264ModeDry; + _.WeakDry = weakdry; + _cmd = _.Cmd = kFujitsuAc264CmdDry; + break; + default: + _.Mode = kFujitsuAc264ModeAuto; + _cmd = _.Cmd = kFujitsuAc264CmdAuto; + break; + } + _.SubCmd = 1; +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRFujitsuAC264::getMode(void) const { return _.Mode; } + +/// Get the weak dry mode setting of the A/C. +/// @return The weak dry mode setting. +bool IRFujitsuAC264::isWeakDry(void) const { return _.WeakDry; } + +/// Set the speed of the fan. +/// @param[in] fanSpeed The desired setting. +void IRFujitsuAC264::setFanSpeed(const uint8_t fanSpeed) { + // Set the fan to auto if out of range. + if ((fanSpeed == kFujitsuAc264FanSpeedQuiet) || + (fanSpeed == kFujitsuAc264FanSpeedLow) || + (fanSpeed == kFujitsuAc264FanSpeedMed) || + (fanSpeed == kFujitsuAc264FanSpeedHigh)) + _.FanSpeed = fanSpeed; + else + _.FanSpeed = kFujitsuAc264FanSpeedAuto; + _cmd = _.Cmd = kFujitsuAc264CmdFanSpeed; + _.SubCmd = 0; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRFujitsuAC264::getFanSpeed(void) const { return _.FanSpeed; } + +/// Set the angle of the fan. +/// @param[in] fanAngle The desired setting. +void IRFujitsuAC264::setFanAngle(const uint8_t fanAngle) { + // Set the fan to stay if out of range. + if ((fanAngle > kFujitsuAc264FanAngle7) || + (fanAngle < kFujitsuAc264FanAngle1)) { + _.FanAngle = kFujitsuAc264FanAngleStay; + } else { + _.FanAngle = fanAngle; + } + _cmd = _.Cmd = kFujitsuAc264CmdFanAngle; + _.SubCmd = 0; +} + +/// Get the current fan angle setting. +/// @return The current fan angle. +uint8_t IRFujitsuAC264::getFanAngle(void) const { return _.FanAngle; } + +/// Set weather the swing of fan is enabled or not. +/// @param[in] on True if swing is enabled, false if disabled. +void IRFujitsuAC264::setSwing(const bool on) { + _.Swing = on; + _.FanAngle = kFujitsuAc264FanAngleStay; // Set the fan to stay. + _cmd = _.Cmd = kFujitsuAc264CmdSwing; + _.SubCmd = 0; +} + +/// Get the requested swing operation mode of the A/C unit. +/// @return True if swing is enabled, false if disabled. +bool IRFujitsuAC264::getSwing(void) const { return _.Swing; } + +/// Set weather economy mode is enabled or not. +/// @param[in] on True if economy mode is enabled, false if disabled. +void IRFujitsuAC264::setEconomy(const bool on) { + _.Economy = on; + _cmd = _.Cmd = kFujitsuAc264CmdEconomy; + _.SubCmd = 0; +} + +/// Get the requested economy mode of the A/C unit. +/// @return True if economy mode is enabled, false if disabled. +bool IRFujitsuAC264::getEconomy(void) const { return _.Economy; } + +/// Set weather clean mode is enabled or not. +/// @param[in] on True if swing is enabled, false if disabled. +void IRFujitsuAC264::setClean(const bool on) { + _.Clean = on; + _cmd = _.Cmd = kFujitsuAc264CmdClean; + _.SubCmd = 0; +} + +/// Get the requested clean mode of the A/C unit. +/// @return True if clean is enabled, false if disabled. +bool IRFujitsuAC264::getClean(void) const { return _.Clean; } + +/// Toggle the sterilization. +/// @note This command is valid only when AC's power is off. +void IRFujitsuAC264::toggleSterilization(void) { + if (getPower()) + return; + _cmd = kFujitsuAc264SpCmdToggleSterilization; + std::memcpy(_.raw, kFujitsuAc264StatesToggleSterilization, + kFujitsuAc264StateLengthMiddle); +} + +/// Set weather outside quiet mode is enabled or not. +/// @param[in] on True if outside quiet is enabled, false if disabled. +/// @note This command is valid only when AC's power is off. +void IRFujitsuAC264::setOutsideQuiet(const bool on) { + if (getPower()) + return; + if (on) { + _cmd = kFujitsuAc264SpCmdOutsideQuietOn; + std::memcpy(_.raw, kFujitsuAc264StatesOutsideQuietOn, + kFujitsuAc264StateLengthMiddle); + } else { + _cmd = kFujitsuAc264SpCmdOutsideQuietOff; + std::memcpy(_.raw, kFujitsuAc264StatesOutsideQuietOff, + kFujitsuAc264StateLengthMiddle); + } +} + +/// Get the requested outside quiet mode of the A/C unit. +/// @return True if outside quiet is enabled, false if disabled. +bool IRFujitsuAC264::getOutsideQuiet(void) const { return _isoutsidequiet; } + +/// Set weather economy fan mode is enabled or not. +/// @param[in] on True if economy fan mode is enabled, false if disabled. +/// @note This command is valid only when AC's power is off. +void IRFujitsuAC264::setEcoFan(const bool on) { + if (getPower()) + return; + if (on) { + _cmd = kFujitsuAc264SpCmdEcoFanOn; + std::memcpy(_.raw, kFujitsuAc264StatesEcoFanOn, + kFujitsuAc264StateLengthShort); + } else { + _cmd = kFujitsuAc264SpCmdEcoFanOff; + std::memcpy(_.raw, kFujitsuAc264StatesEcoFanOff, + kFujitsuAc264StateLengthShort); + } +} + +/// Get the requested economy fan mode of the A/C unit. +/// @return True if economy fan mode is enabled, false if disabled. +bool IRFujitsuAC264::getEcoFan(void) const { return _isecofan; } + +/// Toggle the powerful mode. +/// @note This command is valid only when AC's power is on. +void IRFujitsuAC264::togglePowerful(void) { + if (!getPower()) + return; + _cmd = kFujitsuAc264SpCmdTogglePowerful; + std::memcpy(_.raw, kFujitsuAc264StatesTogglePowerful, + kFujitsuAc264StateLengthShort); +} + +/// Set the clock on the A/C unit. +/// @param[in] mins_since_midnight Nr. of minutes past midnight. +void IRFujitsuAC264::setClock(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check. + // Hours. + _.ClockHours = mins / 60; + // Minutes. + _.ClockMins = mins % 60; +} + +/// Get the clock time to be sent to the A/C unit. +/// @return The number of minutes past midnight. +uint16_t IRFujitsuAC264::getClock(void) const { + return (_.ClockHours * 60 + _.ClockMins); +} + +/// Set the sleep timer setting of the A/C. +/// @param[in] mins Minutes to set the timer to. 0 means disabled. +void IRFujitsuAC264::setSleepTimer(const uint16_t mins) { + if (mins == 0) { + _.SleepTimerEnable = false; + _.SleepTimer = 0; + _cmd = _.Cmd = kFujitsuAc264CmdCancelSleepTimer; + _.SubCmd = 0; + } else if (mins <= kFujitsuAc264SleepTimerMax) { + _.SleepTimerEnable = true; + _.SleepTimer = 0x800 + mins; + _cmd = _.Cmd = kFujitsuAc264CmdSleepTime; + _.SubCmd = 1; + } +} + +/// Get the sleep timer setting of the A/C. +/// @return Minutes left on the timer. 0 means disabled/not supported. +uint16_t IRFujitsuAC264::getSleepTimer(void) const { + if (_.SleepTimerEnable) + return (_.SleepTimer - 0x800); + return 0; +} + +/// Set the Timer enable of the A/C message. +/// @param[in] timer_enable The kind of timer to enable for the message. +void IRFujitsuAC264::setTimerEnable(const uint8_t timer_enable) { + switch (timer_enable) { + case kFujitsuAc264OnTimerEnable: + _.TimerEnable = timer_enable; + _cmd = _.Cmd = kFujitsuAc264CmdOnTimer; + _.SubCmd = 0; + break; + case kFujitsuAc264OffTimerEnable: + case kFujitsuAc264OnOffTimerEnable: + _.TimerEnable = timer_enable; + _cmd = _.Cmd = kFujitsuAc264CmdOffTimer; + _.SubCmd = 0; + break; + case kFujitsuAc264OnOffTimerDisable: + _.TimerEnable = timer_enable; + _cmd = _.Cmd = kFujitsuAc264CmdCancelOnOffTimer; + _.SubCmd = 0; + break; + default: + _.TimerEnable = kFujitsuAc264OnOffTimerDisable; + _cmd = _.Cmd = kFujitsuAc264CmdCancelOnOffTimer; + _.SubCmd = 0; + break; + } +} + +/// Get the Timer enable of the A/C message. +/// @return The current timer enable in numeric form. +uint8_t IRFujitsuAC264::getTimerEnable(void) const { return _.TimerEnable; } + +/// Set the on timer setting of the A/C. +/// @param[in] mins10 Time in 10 minutes unit, when the A/C will turn on. +/// 0 means 0:00 AM, 1 means 0:10 AM. +void IRFujitsuAC264::setOnTimer(const uint8_t mins10) { + if (mins10 <= kFujitsuAc26OnOffTimerMax) + _.OnTimer = mins10; +} + +/// Get the on timer setting of the A/C. +/// @return Time in 10 minutes unit, when the A/C will turn on. +/// 0 means 0:00 AM, 1 means 0:10 AM. +uint8_t IRFujitsuAC264::getOnTimer(void) const { return _.OnTimer; } + +/// Set the off timer setting of the A/C. +/// @param[in] mins10 Time in 10 minutes unit, when the A/C will turn off. +/// 0 means 0:00 AM, 1 means 0:10 AM. +void IRFujitsuAC264::setOffTimer(const uint8_t mins10) { + if (mins10 <= kFujitsuAc26OnOffTimerMax) + _.OffTimer = mins10; +} + +/// Get the off timer setting of the A/C. +/// @return Time in 10 minutes unit, when the A/C will turn off. +/// 0 means 0:00 AM, 1 means 0:10 AM. +uint8_t IRFujitsuAC264::getOffTimer(void) const { return _.OffTimer; } + +/// Set the requested (normal) command part for the A/C message. +/// @param[in] cmd Command to be set. +/// @note Only normal commands (=!isSpecialCommand()) can be set +/// with this function. +void IRFujitsuAC264::setCmd(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAc264CmdCool: + case kFujitsuAc264CmdHeat: + case kFujitsuAc264CmdDry: + case kFujitsuAc264CmdAuto: + case kFujitsuAc264CmdFan: + case kFujitsuAc264CmdSleepTime: + _cmd = _.Cmd = cmd; + _.SubCmd = 1; + break; + case kFujitsuAc264CmdTemp: + case kFujitsuAc264CmdSwing: + case kFujitsuAc264CmdEconomy: + case kFujitsuAc264CmdClean: + case kFujitsuAc264CmdFanSpeed: + case kFujitsuAc264CmdFanAngle: + case kFujitsuAc264CmdCancelSleepTimer: + case kFujitsuAc264CmdOnTimer: + case kFujitsuAc264CmdOffTimer: + case kFujitsuAc264CmdCancelOnOffTimer: + _cmd = _.Cmd = cmd; + _.SubCmd = 0; + break; + default: + break; + } +} + +/// Get the requested command part for the A/C message. +/// @return The command code. +uint8_t IRFujitsuAC264::getCmd(void) const { + return _cmd; +} + +/// 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 IRFujitsuAC264::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kFujitsuAc264ModeCool; + case stdAc::opmode_t::kHeat: return kFujitsuAc264ModeHeat; + case stdAc::opmode_t::kDry: return kFujitsuAc264ModeDry; + case stdAc::opmode_t::kFan: return kFujitsuAc264ModeFan; + default: return kFujitsuAc264ModeAuto; + } +} + +/// 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 IRFujitsuAC264::convertFanSpeed(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kFujitsuAc264FanSpeedQuiet; + case stdAc::fanspeed_t::kLow: return kFujitsuAc264FanSpeedLow; + case stdAc::fanspeed_t::kMedium: return kFujitsuAc264FanSpeedMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kFujitsuAc264FanSpeedHigh; + default: return kFujitsuAc264FanSpeedAuto; + } +} + +/// 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 IRFujitsuAC264::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAc264ModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAc264ModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAc264ModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAc264ModeFan: 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 IRFujitsuAC264::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAc264FanSpeedHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAc264FanSpeedMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAc264FanSpeedLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAc264FanSpeedQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRFujitsuAC264::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result{}; + if (prev != NULL) result = *prev; + result.protocol = decode_type_t::FUJITSU_AC264; + checkSum(); + result.power = _cmd != kFujitsuAc264SpCmdTurnOff; + // Only update these settings if it is not a special command message, + // or we have no previous state info for those settings. + if (!isSpecialCommand() || prev == NULL) { + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(_.FanSpeed); + result.clean = getClean(); + result.swingv = getSwing()? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.econo = getEconomy(); + result.clock = getClock(); + uint16_t sleep_time = getSleepTimer(); + result.sleep = sleep_time? sleep_time: -1; + } + result.quiet = getEcoFan(); + // Not supported. + result.turbo = false; + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.filter = false; + result.beep = false; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRFujitsuAC264::toString(void) const { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + if (isSpecialCommand()) { // Special commands + result += kCommandStr; + result += kColonSpaceStr; + switch (_cmd) { + case kFujitsuAc264SpCmdTurnOff: + result += "Power Off"; + break; + case kFujitsuAc264SpCmdTogglePowerful: + result += kPowerfulStr; + break; + case kFujitsuAc264SpCmdEcoFanOff: + result += "Eco Fan "; + result += kOffStr; + break; + case kFujitsuAc264SpCmdEcoFanOn: + result += "Eco Fan "; + result += kOnStr; + break; + case kFujitsuAc264SpCmdOutsideQuietOff: + result += "Outside Quiet "; + result += kOffStr; + break; + case kFujitsuAc264SpCmdOutsideQuietOn: + result += "Outside Quiet "; + result += kOnStr; + break; + case kFujitsuAc264SpCmdToggleSterilization: + result += "Sterilization"; + break; + default: + result += kNAStr; + } + } else { // Normal commands + result += addBoolToString(true, kPowerStr, false); + // Mode + result += addModeToString(_.Mode, kFujitsuAc264ModeAuto, + kFujitsuAc264ModeCool, kFujitsuAc264ModeHeat, + kFujitsuAc264ModeDry, kFujitsuAc264ModeFan); + // Temp + float degrees = getTemp(); + result += addTempFloatToString(getTemp()); + // Temp in Auto + degrees = getTempAuto(); + String degrees_str = (degrees >= 0)? uint64ToString(degrees): + String(kDashStr) + uint64ToString(-degrees); + result += addLabeledString(degrees_str, "Temp (Auto)"); + if (((uint16_t)(2 * degrees)) & 1) result += F(".5"); + result += 'C'; + // Fan Speed + result += addFanToString(_.FanSpeed, kFujitsuAc264FanSpeedHigh, + kFujitsuAc264FanSpeedLow, kFujitsuAc264FanSpeedAuto, + kFujitsuAc264FanSpeedQuiet, kFujitsuAc264FanSpeedMed); + // Fan Angle + result += addIntToString(_.FanAngle, "Fan Angle"); + result += kSpaceLBraceStr; + switch (_.FanAngle) { + case kFujitsuAc264FanAngle1: + result += kHighestStr; + break; + case kFujitsuAc264FanAngle2: + result += kHighStr; + break; + case kFujitsuAc264FanAngle4: + result += kMiddleStr; + break; + case kFujitsuAc264FanAngle6: + result += kLowStr; + break; + case kFujitsuAc264FanAngle7: + result += kLowestStr; + break; + case kFujitsuAc264FanAngle3: + result += kUpperStr; + result += ' '; + result += kMiddleStr; + break; + case kFujitsuAc264FanAngle5: + result += kLowerStr; + result += ' '; + result += kMiddleStr; + break; + case kFujitsuAc264FanAngleStay: + result += "Stay"; + break; + default: + result += kUnknownStr; + } + result += ')'; + // Swing + result += addBoolToString(getSwing(), kSwingStr); + // Low Power + result += addBoolToString(getEconomy(), "Economy"); + // Clean + result += addBoolToString(getClean(), kCleanStr); + // Cmd + result += kCommaSpaceStr; + result += kCommandStr; + result += kColonSpaceStr; + switch (_.Cmd) { + case kFujitsuAc264CmdCool: + result += kCoolStr; + break; + case kFujitsuAc264CmdHeat: + result += kHeatStr; + break; + case kFujitsuAc264CmdDry: + if (_.WeakDry) + result += "Weak "; + result += kDryStr; + break; + case kFujitsuAc264CmdAuto: + result += kAutoStr; + break; + case kFujitsuAc264CmdFan: + result += kFanStr; + break; + case kFujitsuAc264CmdTemp: + result += kTempStr; + break; + case kFujitsuAc264CmdSwing: + result += kSwingStr; + break; + case kFujitsuAc264CmdSleepTime: + result += kSleepTimerStr; + break; + case kFujitsuAc264CmdEconomy: + result += "Economy"; + break; + case kFujitsuAc264CmdClean: + result += kCleanStr; + break; + case kFujitsuAc264CmdFanSpeed: + result += "Fan Speed"; + break; + case kFujitsuAc264CmdFanAngle: + result += "Fan Angle"; + break; + case kFujitsuAc264CmdCancelSleepTimer: + result += "Cancel Sleep Timer"; + break; + case kFujitsuAc264CmdOffTimer: + result += kOffTimerStr; + break; + case kFujitsuAc264CmdOnTimer: + result += kOnTimerStr; + break; + case kFujitsuAc264CmdCancelOnOffTimer: + result += "Cancel On/Off Timer"; + break; + default: + result += kNAStr; + } + // Clock + uint16_t mins = 0; + mins = getClock(); + result += addLabeledString(mins ? minsToString(mins) : kNAStr, + "Current Time"); + // Sleep Timer + mins = getSleepTimer(); + result += addLabeledString(mins ? minsToString(mins) : kOffStr, + kSleepTimerStr); + // On/Off Timer + uint8_t timer_enable = getTimerEnable(); + mins = getOnTimer(); + result += addLabeledString((timer_enable & kFujitsuAc264OnTimerEnable)? + minsToString(mins * 10): kOffStr, kOnTimerStr); + mins = getOffTimer(); + result += addLabeledString((timer_enable & kFujitsuAc264OffTimerEnable)? + minsToString(mins * 10): kOffStr, kOffTimerStr); + } + return result; +} + +#if DECODE_FUJITSU_AC264 +/// Decode the supplied Fujitsu 264 bit AC IR message if possible. +/// Status: STABLE / Working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// 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 A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeFujitsuAC264(decode_results* results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + uint16_t dataBitsSoFar = 0; + uint8_t restLength = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAc264BitsShort) + kHeader + kFooter - 1 + + offset) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAc264Bits: + case kFujitsuAc264BitsMiddle: + case kFujitsuAc264BitsShort: break; + default: return false; // Must be called with the correct nr. of bits. + } + } + + // Header / Some of the Data + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kFujitsuAc264BitsShort, + kFujitsuAcHdrMark, kFujitsuAcHdrSpace, // Header + kFujitsuAcBitMark, kFujitsuAcOneSpace, // Data + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + 0, 0, // No Footer (yet) + false, _tolerance + kFujitsuAcExtraTolerance, 0, + false); // LSBF + offset += used; + dataBitsSoFar += kFujitsuAc264BitsShort; + + // Check if it has the Fujitsu AC264 protocol header + if (results->state[0] != 0x14 || results->state[1] != 0x63 || + results->state[2] != 0x00 || results->state[3] != 0x10 || + results->state[4] != 0x10) + return false; + + // Identify which command it is + switch (results->state[5]) { + case 0xFE: // Command length is normal or middle + restLength = results->state[6]; + // check the rest length + if ((restLength != 0x1A) && (restLength != 0x09)) + return false; + break; + case 0x51: // Command length is short + case 0x50: // Command length is short + case 0x39: // Command length is short + case 0x02: // Command length is short + if (results->state[6] == (uint8_t)~results->state[5]) { // checksum + results->decode_type = FUJITSU_AC264; + results->bits = dataBitsSoFar; + return true; + } else { + return false; + } + default: + return false; + } + + // Keep reading bytes until we either run out of message or state to fill. + match_result_t data_result; + for (uint16_t i = kFujitsuAc264StateLengthShort; + offset <= results->rawlen - 16 && i < kFujitsuAc264StateLengthShort + + restLength; i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, + _tolerance + kFujitsuAcExtraTolerance, 0, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAc264BitsMiddle: + case kFujitsuAc264Bits: + // Check if the state[5] is matched with the protocol. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + + if (!IRFujitsuAC264::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + results->decode_type = FUJITSU_AC264; + results->bits = dataBitsSoFar; + return true; // All good. +} +#endif // DECODE_FUJITSU_AC264 diff --git a/src/ir_Fujitsu.h b/src/ir_Fujitsu.h index ef8f865bc..8f746c302 100644 --- a/src/ir_Fujitsu.h +++ b/src/ir_Fujitsu.h @@ -1,544 +1,544 @@ -// Copyright 2017 Jonny Graham -// Copyright 2018-2022 David Conran -// Copyright 2021 siriuslzx -// Copyright 2023 Takeshi Shimizu - -/// @file -/// @brief Support for Fujitsu A/C protocols. -/// Fujitsu A/C support added by Jonny Graham -/// @warning Use of incorrect model may cause the A/C unit to lock up. -/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical -/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. -/// The correct model for it is ARREB1E. -/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 - -// Supports: -// Brand: Fujitsu, Model: AR-RAH2E remote (ARRAH2E) -// Brand: Fujitsu, Model: ASYG30LFCA A/C (ARRAH2E) -// Brand: Fujitsu General, Model: AR-RCE1E remote (ARRAH2E) -// Brand: Fujitsu General, Model: ASHG09LLCA A/C (ARRAH2E) -// Brand: Fujitsu General, Model: AOHG09LLC A/C (ARRAH2E) -// Brand: Fujitsu, Model: AR-DB1 remote (ARDB1) -// Brand: Fujitsu, Model: AST9RSGCW A/C (ARDB1) -// Brand: Fujitsu, Model: AR-REB1E remote (ARREB1E) -// Brand: Fujitsu, Model: ASYG7LMCA A/C (ARREB1E) -// Brand: Fujitsu, Model: AR-RAE1E remote (ARRAH2E) -// Brand: Fujitsu, Model: AGTV14LAC A/C (ARRAH2E) -// Brand: Fujitsu, Model: AR-RAC1E remote (ARRAH2E) -// Brand: Fujitsu, Model: ASTB09LBC A/C (ARRY4) -// Brand: Fujitsu, Model: AR-RY4 remote (ARRY4) -// Brand: Fujitsu General, Model: AR-JW2 remote (ARJW2) -// Brand: Fujitsu, Model: AR-DL10 remote (ARDB1) -// Brand: Fujitsu, Model: ASU30C1 A/C (ARDB1) -// Brand: Fujitsu, Model: AR-RAH1U remote (ARREB1E) -// Brand: Fujitsu, Model: AR-RAH2U remote (ARRAH2E) -// Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E) -// Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E) -// Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E) -// Brand: Fujitsu, Model: AR-REB4E remote (ARREB1E) -// Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E) -// Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E) -// Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E) -// Brand: Fujitsu, Model: AR-REG1U remote (ARRAH2E) -// Brand: OGeneral, Model: AR-RCL1E remote (ARRAH2E) -// Brand: Fujitsu General, Model: AR-JW17 remote (ARDB1) -// Brand: Fujitsu, Model: AS-AH402M A/C (FUJITSU_AC264 AR-RLB2J) - -#ifndef IR_FUJITSU_H_ -#define IR_FUJITSU_H_ - -#define __STDC_LIMIT_MACROS -#include -#include -#ifdef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#ifdef UNIT_TEST -#include "IRsend_test.h" -#endif - -/// Native representation of a Fujitsu A/C message. -union FujitsuProtocol { - struct { - uint8_t longcode[kFujitsuAcStateLength]; ///< The state of the IR remote. - uint8_t shortcode[kFujitsuAcStateLengthShort]; - }; - struct { - // Byte 0~1 - uint64_t :16; // Fixed header - // Byte 2 - uint64_t :4; - uint64_t Id :2; // Device Number/Identifier - uint64_t :2; - // Byte 3-4 - uint64_t :16; - // Byte 5 - uint64_t Cmd :8; // short codes:cmd; long codes:fixed value - // Byte 6 - uint64_t RestLength :8; // Nr. of bytes in the message after this byte. - // Byte 7 - uint64_t Protocol :8; // Seems like a protocol version number. Not sure. - // Byte 8 - uint64_t Power :1; - uint64_t Fahrenheit :1; - uint64_t Temp :6; // Internal representation varies between models. - // Byte 9 - uint64_t Mode :3; - uint64_t Clean :1; // Also 10C Heat in ARREW4E. - uint64_t TimerType :2; - uint64_t :2; - // Byte 10 - uint64_t Fan :3; - uint64_t :1; - uint64_t Swing :2; - uint64_t :2; - // Byte 11~13 - uint64_t OffTimer :11; // Also is the sleep timer value - uint64_t OffTimerEnable :1; - uint64_t OnTimer :11; - uint64_t OnTimerEnable :1; - // Byte 14 - uint64_t :3; - uint64_t Filter :1; - uint64_t :1; - uint64_t unknown :1; - uint64_t :1; - uint64_t OutsideQuiet :1; - // Byte 15 - uint64_t :0; // Checksum - }; -}; - -// Constants -const uint8_t kFujitsuAcModeAuto = 0x0; // 0b000 -const uint8_t kFujitsuAcModeCool = 0x1; // 0b001 -const uint8_t kFujitsuAcModeDry = 0x2; // 0b010 -const uint8_t kFujitsuAcModeFan = 0x3; // 0b011 -const uint8_t kFujitsuAcModeHeat = 0x4; // 0b100 - -const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000 -const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001 -const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010 -const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001 -const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001 -const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100 -const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101 -const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001 -const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010 - -const uint8_t kFujitsuAcFanAuto = 0x00; -const uint8_t kFujitsuAcFanHigh = 0x01; -const uint8_t kFujitsuAcFanMed = 0x02; -const uint8_t kFujitsuAcFanLow = 0x03; -const uint8_t kFujitsuAcFanQuiet = 0x04; - -const float kFujitsuAcMinHeat = 10; // 10C -const float kFujitsuAcMinTemp = 16; // 16C -const float kFujitsuAcMaxTemp = 30; // 30C -const uint8_t kFujitsuAcTempOffsetC = kFujitsuAcMinTemp; -const float kFujitsuAcMinHeatF = 50; // 50F -const float kFujitsuAcMinTempF = 60; // 60F -const float kFujitsuAcMaxTempF = 88; // 88F -const uint8_t kFujitsuAcTempOffsetF = 44; - -const uint8_t kFujitsuAcSwingOff = 0x00; -const uint8_t kFujitsuAcSwingVert = 0x01; -const uint8_t kFujitsuAcSwingHoriz = 0x02; -const uint8_t kFujitsuAcSwingBoth = 0x03; - -const uint8_t kFujitsuAcStopTimers = 0b00; // 0 -const uint8_t kFujitsuAcSleepTimer = 0b01; // 1 -const uint8_t kFujitsuAcOffTimer = 0b10; // 2 -const uint8_t kFujitsuAcOnTimer = 0b11; // 3 -const uint16_t kFujitsuAcTimerMax = 12 * 60; ///< Minutes. - -/// Native representation of a Fujitsu 264 bit A/C message. -union Fujitsu264Protocol { - struct { - uint8_t raw[kFujitsuAc264StateLength]; ///< The state of the IR remote. - }; - struct { - uint8_t middlecode[kFujitsuAc264StateLengthMiddle]; - uint8_t shortcode[kFujitsuAc264StateLengthShort]; - }; - struct { - // Byte 0~1 - uint8_t :8; // Fixed header - uint8_t :8; // Fixed header - // Byte 2 - uint8_t :8; - // Byte 3-4 - uint8_t :8; - uint8_t :8; - // Byte 5 - uint8_t :8; - // Byte 6 - uint8_t RestLength :8; // Nr. of bytes in the message after this byte. - // Byte 7 - uint8_t Protocol :8; // Seems like a protocol version number. - // Byte 8 - uint64_t SubCmd :1; - uint64_t :1; - uint64_t Temp :6; // Temperature in modes except auto - // Byte 9 - uint64_t Mode :4; - uint64_t SleepTimerEnable :1; - uint64_t :3; - // Byte 10 - uint64_t FanSpeed :4; - uint64_t Swing :1; - uint64_t :3; - // Byte 11~12 - uint64_t SleepTimer :12; - uint64_t :4; - // Byte 13 - uint64_t :8; - // Byte 14 - uint64_t TempAuto :8; // Temperature in auto mode - // Byte 15 - uint64_t Economy :1; - uint64_t :7; - // Byte 16 - uint8_t Clean :1; - uint8_t :7; - // Byte 17 - uint8_t :8; - // Byte 18 - uint8_t Cmd :8; - // Byte 19 - uint8_t ClockHours :8; - // Byte 20 - uint8_t ClockMins :8; - // Byte 21 - uint8_t :5; - uint8_t WeakDry :1; // Valid only when mode = dry - uint8_t :2; - // Byte 22 - uint8_t TimerEnable :4; - uint8_t :4; - // Byte 23 - uint8_t OnTimer :8; - // Byte 24 - uint8_t OffTimer :8; - // Byte 25~27 - uint8_t :8; - uint8_t :8; - uint8_t :8; - // Byte 28 - uint8_t FanAngle :4; - uint8_t :4; - // Byte 29~31 - uint8_t :8; - uint8_t :8; - uint8_t :8; - // Byte 32 - uint8_t CheckSum :8; - }; -}; - -// Constants -const uint8_t kFujitsuAc264ModeAuto = 0x0; // 0b0000 -const uint8_t kFujitsuAc264ModeCool = 0x1; // 0b0001 -const uint8_t kFujitsuAc264ModeFan = 0x3; // 0b0011 -const uint8_t kFujitsuAc264ModeHeat = 0x4; // 0b0100 -const uint8_t kFujitsuAc264ModeDry = 0x5; // 0b0101 - -const uint8_t kFujitsuAc264CmdCool = 0x01; // 0b00000001 -const uint8_t kFujitsuAc264CmdHeat = 0x02; // 0b00000010 -const uint8_t kFujitsuAc264CmdDry = 0x03; // 0b00000011 -const uint8_t kFujitsuAc264CmdAuto = 0x04; // 0b00000100 -const uint8_t kFujitsuAc264CmdFan = 0x05; // 0b00000101 -const uint8_t kFujitsuAc264CmdTemp = 0x07; // 0b00000111 -const uint8_t kFujitsuAc264CmdSwing = 0x0B; // 0b00001011 -const uint8_t kFujitsuAc264CmdSleepTime = 0x0E; // 0b00001110 -const uint8_t kFujitsuAc264CmdEconomy = 0x14; // 0b00010100 -const uint8_t kFujitsuAc264CmdClean = 0x1B; // 0b00011011 -const uint8_t kFujitsuAc264CmdFanSpeed = 0x1E; // 0b00011110 -const uint8_t kFujitsuAc264CmdFanAngle = 0x22; // 0b00100010 -const uint8_t kFujitsuAc264CmdCancelSleepTimer = 0x30; // 0b00110000 -const uint8_t kFujitsuAc264CmdOnTimer = 0x39; // 0b00111001 -const uint8_t kFujitsuAc264CmdOffTimer = 0x3A; // 0b00111010 -const uint8_t kFujitsuAc264CmdCancelOnOffTimer = 0x3B; // 0b00111011 - -const uint8_t kFujitsuAc264SpCmdTogglePowerful = 0xF0; // 0b11110000 -const uint8_t kFujitsuAc264SpCmdTurnOff = 0xF1; // 0b11110001 -const uint8_t kFujitsuAc264SpCmdEcoFanOff = 0xF2; // 0b11110010 -const uint8_t kFujitsuAc264SpCmdEcoFanOn = 0xF3; // 0b11110011 -const uint8_t kFujitsuAc264SpCmdOutsideQuietOff = 0xF4; // 0b11110100 -const uint8_t kFujitsuAc264SpCmdOutsideQuietOn = 0xF5; // 0b11110101 -const uint8_t kFujitsuAc264SpCmdToggleSterilization = 0xF6; // 0b11110110 - -const uint8_t kFujitsuAc264FanSpeedAuto = 0x0; // 0b0000 -const uint8_t kFujitsuAc264FanSpeedQuiet = 0x1; // 0b0001 -const uint8_t kFujitsuAc264FanSpeedLow = 0x3; // 0b0011 -const uint8_t kFujitsuAc264FanSpeedMed = 0x6; // 0b0110 -const uint8_t kFujitsuAc264FanSpeedHigh = 0x8; // 0b1000 - -const uint8_t kFujitsuAc264FanAngle1 = 0x1; // 0b0001 -const uint8_t kFujitsuAc264FanAngle2 = 0x2; // 0b0010 -const uint8_t kFujitsuAc264FanAngle3 = 0x3; // 0b0011 -const uint8_t kFujitsuAc264FanAngle4 = 0x4; // 0b0100 -const uint8_t kFujitsuAc264FanAngle5 = 0x5; // 0b0101 -const uint8_t kFujitsuAc264FanAngle6 = 0x6; // 0b0110 -const uint8_t kFujitsuAc264FanAngle7 = 0x7; // 0b0111 -const uint8_t kFujitsuAc264FanAngleStay = 0xF; // 0b1111 - -const uint8_t kFujitsuAc264MinHeat = 16; // 16C -const uint8_t kFujitsuAc264MinTemp = 18; // 18C -const uint8_t kFujitsuAc264MaxTemp = 30; // 30C -const uint8_t kFujitsuAc264TempOffsetC = 16; -const int8_t kFujitsuAc264MinTempAuto = -2; // -2C -const int8_t kFujitsuAc264MaxTempAuto = 2; // 2C - -const uint16_t kFujitsuAc264SleepTimerMax = 12 * 60; ///< Minutes. -const uint8_t kFujitsuAc264OnOffTimerDisable = 0x0; // 0b0000 -const uint8_t kFujitsuAc264OnTimerEnable = 0x1; // 0b0001 -const uint8_t kFujitsuAc264OffTimerEnable = 0x2; // 0b0010 -const uint8_t kFujitsuAc264OnOffTimerEnable = 0x3; // 0b0011 -const uint16_t kFujitsuAc26OnOffTimerMax = 24 * 6 - 1; ///< 10 Minutes. - -/// Special command for Power Off -const uint8_t kFujitsuAc264StatesTurnOff - [kFujitsuAc264StateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD}; -/// Special command for Toggle Powerful -const uint8_t kFujitsuAc264StatesTogglePowerful - [kFujitsuAc264StateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; -/// Special command for Eco Fan Off -const uint8_t kFujitsuAc264StatesEcoFanOff - [kFujitsuAc264StateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x51, 0xAE}; -/// Special command for Eco Fan On -const uint8_t kFujitsuAc264StatesEcoFanOn - [kFujitsuAc264StateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x50, 0xAF}; -/// Special command for Outside Quiet Off -/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, -/// but has different meaning. -const uint8_t kFujitsuAc264StatesOutsideQuietOff - [kFujitsuAc264StateLengthMiddle] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, - 0x40, 0x01, 0x00, 0x00, 0xFE, 0xBF, 0x00, 0x41}; -/// Special command for Outside Quiet Off -/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, -/// but has different meaning. -const uint8_t kFujitsuAc264StatesOutsideQuietOn - [kFujitsuAc264StateLengthMiddle] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, - 0x40, 0x00, 0x00, 0x00, 0xFF, 0xBF, 0x00, 0x41}; -/// Special command for Toggle Sterilization -/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, -/// but has different meaning. -const uint8_t kFujitsuAc264StatesToggleSterilization - [kFujitsuAc264StateLengthMiddle] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, - 0x60, 0x03, 0x00, 0x00, 0xFC, 0x9F, 0x00, 0x41}; - -// Legacy defines. -#define FUJITSU_AC_MODE_AUTO kFujitsuAcModeAuto -#define FUJITSU_AC_MODE_COOL kFujitsuAcModeCool -#define FUJITSU_AC_MODE_DRY kFujitsuAcModeDry -#define FUJITSU_AC_MODE_FAN kFujitsuAcModeFan -#define FUJITSU_AC_MODE_HEAT kFujitsuAcModeHeat -#define FUJITSU_AC_CMD_STAY_ON kFujitsuAcCmdStayOn -#define FUJITSU_AC_CMD_TURN_ON kFujitsuAcCmdTurnOn -#define FUJITSU_AC_CMD_TURN_OFF kFujitsuAcCmdTurnOff -#define FUJITSU_AC_CMD_STEP_HORIZ kFujitsuAcCmdStepHoriz -#define FUJITSU_AC_CMD_STEP_VERT kFujitsuAcCmdStepVert -#define FUJITSU_AC_FAN_AUTO kFujitsuAcFanAuto -#define FUJITSU_AC_FAN_HIGH kFujitsuAcFanHigh -#define FUJITSU_AC_FAN_MED kFujitsuAcFanMed -#define FUJITSU_AC_FAN_LOW kFujitsuAcFanLow -#define FUJITSU_AC_FAN_QUIET kFujitsuAcFanQuiet -#define FUJITSU_AC_MIN_TEMP kFujitsuAcMinTemp -#define FUJITSU_AC_MAX_TEMP kFujitsuAcMaxTemp -#define FUJITSU_AC_SWING_OFF kFujitsuAcSwingOff -#define FUJITSU_AC_SWING_VERT kFujitsuAcSwingVert -#define FUJITSU_AC_SWING_HORIZ kFujitsuAcSwingHoriz -#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth - -/// Class for handling detailed Fujitsu A/C messages. -class IRFujitsuAC { - public: - explicit IRFujitsuAC(const uint16_t pin, - const fujitsu_ac_remote_model_t model = ARRAH2E, - const bool inverted = false, - const bool use_modulation = true); - void setModel(const fujitsu_ac_remote_model_t model); - fujitsu_ac_remote_model_t getModel(void) const; - void stateReset(void); -#if SEND_FUJITSU_AC - void send(const uint16_t repeat = kFujitsuAcMinRepeat); - /// 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_FUJITSU_AC - void begin(void); - void stepHoriz(void); - void toggleSwingHoriz(const bool update = true); - void stepVert(void); - void toggleSwingVert(const bool update = true); - void setCmd(const uint8_t cmd); - uint8_t getCmd(void) const; - void setTemp(const float temp, const bool useCelsius = true); - float getTemp(void) const; - void setFanSpeed(const uint8_t fan); - uint8_t getFanSpeed(void) const; - void setMode(const uint8_t mode); - uint8_t getMode(void) const; - void setSwing(const uint8_t mode); - uint8_t getSwing(void) const; - uint8_t* getRaw(void); - bool setRaw(const uint8_t newState[], const uint16_t length); - uint8_t getStateLength(void); - static bool validChecksum(uint8_t* state, const uint16_t length); - bool isLongCode(void) const; - void setPower(const bool on); - void off(void); - void on(void); - bool getPower(void) const; - void setClean(const bool on); - bool getClean(void) const; - void setFilter(const bool on); - bool getFilter(void) const; - void set10CHeat(const bool on); - bool get10CHeat(void) const; - void setOutsideQuiet(const bool on); - bool getOutsideQuiet(void) const; - uint8_t getTimerType(void) const; - void setTimerType(const uint8_t timertype); - uint16_t getOnTimer(void) const; - void setOnTimer(const uint16_t nr_mins); - uint16_t getOffSleepTimer(void) const; - void setOffTimer(const uint16_t nr_mins); - void setSleepTimer(const uint16_t nr_mins); - void setId(const uint8_t num); - uint8_t getId(void) const; - void setCelsius(const bool on); - bool getCelsius(void) const; - static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFan(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(const stdAc::state_t *prev = NULL); - String toString(void) const; -#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 - FujitsuProtocol _; - uint8_t _cmd; - fujitsu_ac_remote_model_t _model; - uint8_t _state_length; - uint8_t _state_length_short; - bool _rawstatemodified; - void checkSum(void); - bool updateUseLongOrShort(void); - void buildFromState(const uint16_t length); - void setOffSleepTimer(const uint16_t nr_mins); -}; - -/// Class for handling detailed Fujitsu 264 bit A/C messages. -class IRFujitsuAC264 { - public: - explicit IRFujitsuAC264(const uint16_t pin, - const bool inverted = false, - const bool use_modulation = true); -#if SEND_FUJITSU_AC264 - void send(const uint16_t repeat = kFujitsuAc264DefaultRepeat); - /// 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_FUJITSU_AC264 - void begin(void); - uint8_t* getRaw(void); - bool setRaw(const uint8_t newState[], const uint16_t length); - static bool validChecksum(uint8_t state[], - const uint16_t length = kFujitsuAc264StateLength); - - void on(void); - void off(void); - void setPower(const bool on); - bool getPower(void) const; - bool isTempStayed(void) const; - void setTemp(const float temp); - float getTemp(void) const; - void setTempAuto(const float temp); - float getTempAuto(void) const; - void setMode(const uint8_t mode, const bool weakdry = false); - uint8_t getMode(void) const; - bool isWeakDry(void) const; - void setFanSpeed(const uint8_t fanSpeed); - uint8_t getFanSpeed(void) const; - void setFanAngle(const uint8_t fanAngle); - uint8_t getFanAngle(void) const; - void setSwing(const bool on); - bool getSwing(void) const; - void setEconomy(const bool on); - bool getEconomy(void) const; - void setClean(const bool on); - bool getClean(void) const; - - void toggleSterilization(void); - void setOutsideQuiet(const bool on); - bool getOutsideQuiet(void) const; - void setEcoFan(const bool on); - bool getEcoFan(void) const; - void togglePowerful(void); - - void setClock(const uint16_t mins_since_midnight); - uint16_t getClock(void) const; - void setSleepTimer(const uint16_t mins); - uint16_t getSleepTimer(void) const; - void setTimerEnable(const uint8_t timer_enable); - uint8_t getTimerEnable(void) const; - void setOnTimer(const uint8_t mins10); - uint8_t getOnTimer(void) const; - void setOffTimer(const uint8_t mins10); - uint8_t getOffTimer(void) const; - - void setCmd(const uint8_t cmd); - uint8_t getCmd(void) const; - uint8_t getStateLength(void); - - static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFanSpeed(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(const stdAc::state_t *prev = NULL); - String toString(void) const; - -#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 - Fujitsu264Protocol _; - uint8_t _cmd; - bool _ispoweredon; - bool _isecofan; - bool _isoutsidequiet; - uint8_t _settemp; - void stateReset(void); - void checkSum(void); - bool isSpecialCommand(void) const; -}; - -#endif // IR_FUJITSU_H_ +// Copyright 2017 Jonny Graham +// Copyright 2018-2022 David Conran +// Copyright 2021 siriuslzx +// Copyright 2023 Takeshi Shimizu + +/// @file +/// @brief Support for Fujitsu A/C protocols. +/// Fujitsu A/C support added by Jonny Graham +/// @warning Use of incorrect model may cause the A/C unit to lock up. +/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical +/// power rest, if incorrect model (ARRAH2E) is used with a Swing command. +/// The correct model for it is ARREB1E. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376 + +// Supports: +// Brand: Fujitsu, Model: AR-RAH2E remote (ARRAH2E) +// Brand: Fujitsu, Model: ASYG30LFCA A/C (ARRAH2E) +// Brand: Fujitsu General, Model: AR-RCE1E remote (ARRAH2E) +// Brand: Fujitsu General, Model: ASHG09LLCA A/C (ARRAH2E) +// Brand: Fujitsu General, Model: AOHG09LLC A/C (ARRAH2E) +// Brand: Fujitsu, Model: AR-DB1 remote (ARDB1) +// Brand: Fujitsu, Model: AST9RSGCW A/C (ARDB1) +// Brand: Fujitsu, Model: AR-REB1E remote (ARREB1E) +// Brand: Fujitsu, Model: ASYG7LMCA A/C (ARREB1E) +// Brand: Fujitsu, Model: AR-RAE1E remote (ARRAH2E) +// Brand: Fujitsu, Model: AGTV14LAC A/C (ARRAH2E) +// Brand: Fujitsu, Model: AR-RAC1E remote (ARRAH2E) +// Brand: Fujitsu, Model: ASTB09LBC A/C (ARRY4) +// Brand: Fujitsu, Model: AR-RY4 remote (ARRY4) +// Brand: Fujitsu General, Model: AR-JW2 remote (ARJW2) +// Brand: Fujitsu, Model: AR-DL10 remote (ARDB1) +// Brand: Fujitsu, Model: ASU30C1 A/C (ARDB1) +// Brand: Fujitsu, Model: AR-RAH1U remote (ARREB1E) +// Brand: Fujitsu, Model: AR-RAH2U remote (ARRAH2E) +// Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E) +// Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E) +// Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E) +// Brand: Fujitsu, Model: AR-REB4E remote (ARREB1E) +// Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E) +// Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E) +// Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E) +// Brand: Fujitsu, Model: AR-REG1U remote (ARRAH2E) +// Brand: OGeneral, Model: AR-RCL1E remote (ARRAH2E) +// Brand: Fujitsu General, Model: AR-JW17 remote (ARDB1) +// Brand: Fujitsu, Model: AS-AH402M A/C (FUJITSU_AC264 AR-RLB2J) + +#ifndef IR_FUJITSU_H_ +#define IR_FUJITSU_H_ + +#define __STDC_LIMIT_MACROS +#include +#include +#ifdef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +/// Native representation of a Fujitsu A/C message. +union FujitsuProtocol { + struct { + uint8_t longcode[kFujitsuAcStateLength]; ///< The state of the IR remote. + uint8_t shortcode[kFujitsuAcStateLengthShort]; + }; + struct { + // Byte 0~1 + uint64_t :16; // Fixed header + // Byte 2 + uint64_t :4; + uint64_t Id :2; // Device Number/Identifier + uint64_t :2; + // Byte 3-4 + uint64_t :16; + // Byte 5 + uint64_t Cmd :8; // short codes:cmd; long codes:fixed value + // Byte 6 + uint64_t RestLength :8; // Nr. of bytes in the message after this byte. + // Byte 7 + uint64_t Protocol :8; // Seems like a protocol version number. Not sure. + // Byte 8 + uint64_t Power :1; + uint64_t Fahrenheit :1; + uint64_t Temp :6; // Internal representation varies between models. + // Byte 9 + uint64_t Mode :3; + uint64_t Clean :1; // Also 10C Heat in ARREW4E. + uint64_t TimerType :2; + uint64_t :2; + // Byte 10 + uint64_t Fan :3; + uint64_t :1; + uint64_t Swing :2; + uint64_t :2; + // Byte 11~13 + uint64_t OffTimer :11; // Also is the sleep timer value + uint64_t OffTimerEnable :1; + uint64_t OnTimer :11; + uint64_t OnTimerEnable :1; + // Byte 14 + uint64_t :3; + uint64_t Filter :1; + uint64_t :1; + uint64_t unknown :1; + uint64_t :1; + uint64_t OutsideQuiet :1; + // Byte 15 + uint64_t :0; // Checksum + }; +}; + +// Constants +const uint8_t kFujitsuAcModeAuto = 0x0; // 0b000 +const uint8_t kFujitsuAcModeCool = 0x1; // 0b001 +const uint8_t kFujitsuAcModeDry = 0x2; // 0b010 +const uint8_t kFujitsuAcModeFan = 0x3; // 0b011 +const uint8_t kFujitsuAcModeHeat = 0x4; // 0b100 + +const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000 +const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001 +const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010 +const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001 +const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001 +const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100 +const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101 +const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001 +const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010 + +const uint8_t kFujitsuAcFanAuto = 0x00; +const uint8_t kFujitsuAcFanHigh = 0x01; +const uint8_t kFujitsuAcFanMed = 0x02; +const uint8_t kFujitsuAcFanLow = 0x03; +const uint8_t kFujitsuAcFanQuiet = 0x04; + +const float kFujitsuAcMinHeat = 10; // 10C +const float kFujitsuAcMinTemp = 16; // 16C +const float kFujitsuAcMaxTemp = 30; // 30C +const uint8_t kFujitsuAcTempOffsetC = kFujitsuAcMinTemp; +const float kFujitsuAcMinHeatF = 50; // 50F +const float kFujitsuAcMinTempF = 60; // 60F +const float kFujitsuAcMaxTempF = 88; // 88F +const uint8_t kFujitsuAcTempOffsetF = 44; + +const uint8_t kFujitsuAcSwingOff = 0x00; +const uint8_t kFujitsuAcSwingVert = 0x01; +const uint8_t kFujitsuAcSwingHoriz = 0x02; +const uint8_t kFujitsuAcSwingBoth = 0x03; + +const uint8_t kFujitsuAcStopTimers = 0b00; // 0 +const uint8_t kFujitsuAcSleepTimer = 0b01; // 1 +const uint8_t kFujitsuAcOffTimer = 0b10; // 2 +const uint8_t kFujitsuAcOnTimer = 0b11; // 3 +const uint16_t kFujitsuAcTimerMax = 12 * 60; ///< Minutes. + +/// Native representation of a Fujitsu 264 bit A/C message. +union Fujitsu264Protocol { + struct { + uint8_t raw[kFujitsuAc264StateLength]; ///< The state of the IR remote. + }; + struct { + uint8_t middlecode[kFujitsuAc264StateLengthMiddle]; + uint8_t shortcode[kFujitsuAc264StateLengthShort]; + }; + struct { + // Byte 0~1 + uint8_t :8; // Fixed header + uint8_t :8; // Fixed header + // Byte 2 + uint8_t :8; + // Byte 3-4 + uint8_t :8; + uint8_t :8; + // Byte 5 + uint8_t :8; + // Byte 6 + uint8_t RestLength :8; // Nr. of bytes in the message after this byte. + // Byte 7 + uint8_t Protocol :8; // Seems like a protocol version number. + // Byte 8 + uint64_t SubCmd :1; + uint64_t :1; + uint64_t Temp :6; // Temperature in modes except auto + // Byte 9 + uint64_t Mode :4; + uint64_t SleepTimerEnable :1; + uint64_t :3; + // Byte 10 + uint64_t FanSpeed :4; + uint64_t Swing :1; + uint64_t :3; + // Byte 11~12 + uint64_t SleepTimer :12; + uint64_t :4; + // Byte 13 + uint64_t :8; + // Byte 14 + uint64_t TempAuto :8; // Temperature in auto mode + // Byte 15 + uint64_t Economy :1; + uint64_t :7; + // Byte 16 + uint8_t Clean :1; + uint8_t :7; + // Byte 17 + uint8_t :8; + // Byte 18 + uint8_t Cmd :8; + // Byte 19 + uint8_t ClockHours :8; + // Byte 20 + uint8_t ClockMins :8; + // Byte 21 + uint8_t :5; + uint8_t WeakDry :1; // Valid only when mode = dry + uint8_t :2; + // Byte 22 + uint8_t TimerEnable :4; + uint8_t :4; + // Byte 23 + uint8_t OnTimer :8; + // Byte 24 + uint8_t OffTimer :8; + // Byte 25~27 + uint8_t :8; + uint8_t :8; + uint8_t :8; + // Byte 28 + uint8_t FanAngle :4; + uint8_t :4; + // Byte 29~31 + uint8_t :8; + uint8_t :8; + uint8_t :8; + // Byte 32 + uint8_t CheckSum :8; + }; +}; + +// Constants +const uint8_t kFujitsuAc264ModeAuto = 0x0; // 0b0000 +const uint8_t kFujitsuAc264ModeCool = 0x1; // 0b0001 +const uint8_t kFujitsuAc264ModeFan = 0x3; // 0b0011 +const uint8_t kFujitsuAc264ModeHeat = 0x4; // 0b0100 +const uint8_t kFujitsuAc264ModeDry = 0x5; // 0b0101 + +const uint8_t kFujitsuAc264CmdCool = 0x01; // 0b00000001 +const uint8_t kFujitsuAc264CmdHeat = 0x02; // 0b00000010 +const uint8_t kFujitsuAc264CmdDry = 0x03; // 0b00000011 +const uint8_t kFujitsuAc264CmdAuto = 0x04; // 0b00000100 +const uint8_t kFujitsuAc264CmdFan = 0x05; // 0b00000101 +const uint8_t kFujitsuAc264CmdTemp = 0x07; // 0b00000111 +const uint8_t kFujitsuAc264CmdSwing = 0x0B; // 0b00001011 +const uint8_t kFujitsuAc264CmdSleepTime = 0x0E; // 0b00001110 +const uint8_t kFujitsuAc264CmdEconomy = 0x14; // 0b00010100 +const uint8_t kFujitsuAc264CmdClean = 0x1B; // 0b00011011 +const uint8_t kFujitsuAc264CmdFanSpeed = 0x1E; // 0b00011110 +const uint8_t kFujitsuAc264CmdFanAngle = 0x22; // 0b00100010 +const uint8_t kFujitsuAc264CmdCancelSleepTimer = 0x30; // 0b00110000 +const uint8_t kFujitsuAc264CmdOnTimer = 0x39; // 0b00111001 +const uint8_t kFujitsuAc264CmdOffTimer = 0x3A; // 0b00111010 +const uint8_t kFujitsuAc264CmdCancelOnOffTimer = 0x3B; // 0b00111011 + +const uint8_t kFujitsuAc264SpCmdTogglePowerful = 0xF0; // 0b11110000 +const uint8_t kFujitsuAc264SpCmdTurnOff = 0xF1; // 0b11110001 +const uint8_t kFujitsuAc264SpCmdEcoFanOff = 0xF2; // 0b11110010 +const uint8_t kFujitsuAc264SpCmdEcoFanOn = 0xF3; // 0b11110011 +const uint8_t kFujitsuAc264SpCmdOutsideQuietOff = 0xF4; // 0b11110100 +const uint8_t kFujitsuAc264SpCmdOutsideQuietOn = 0xF5; // 0b11110101 +const uint8_t kFujitsuAc264SpCmdToggleSterilization = 0xF6; // 0b11110110 + +const uint8_t kFujitsuAc264FanSpeedAuto = 0x0; // 0b0000 +const uint8_t kFujitsuAc264FanSpeedQuiet = 0x1; // 0b0001 +const uint8_t kFujitsuAc264FanSpeedLow = 0x3; // 0b0011 +const uint8_t kFujitsuAc264FanSpeedMed = 0x6; // 0b0110 +const uint8_t kFujitsuAc264FanSpeedHigh = 0x8; // 0b1000 + +const uint8_t kFujitsuAc264FanAngle1 = 0x1; // 0b0001 +const uint8_t kFujitsuAc264FanAngle2 = 0x2; // 0b0010 +const uint8_t kFujitsuAc264FanAngle3 = 0x3; // 0b0011 +const uint8_t kFujitsuAc264FanAngle4 = 0x4; // 0b0100 +const uint8_t kFujitsuAc264FanAngle5 = 0x5; // 0b0101 +const uint8_t kFujitsuAc264FanAngle6 = 0x6; // 0b0110 +const uint8_t kFujitsuAc264FanAngle7 = 0x7; // 0b0111 +const uint8_t kFujitsuAc264FanAngleStay = 0xF; // 0b1111 + +const uint8_t kFujitsuAc264MinHeat = 16; // 16C +const uint8_t kFujitsuAc264MinTemp = 18; // 18C +const uint8_t kFujitsuAc264MaxTemp = 30; // 30C +const uint8_t kFujitsuAc264TempOffsetC = 16; +const int8_t kFujitsuAc264MinTempAuto = -2; // -2C +const int8_t kFujitsuAc264MaxTempAuto = 2; // 2C + +const uint16_t kFujitsuAc264SleepTimerMax = 12 * 60; ///< Minutes. +const uint8_t kFujitsuAc264OnOffTimerDisable = 0x0; // 0b0000 +const uint8_t kFujitsuAc264OnTimerEnable = 0x1; // 0b0001 +const uint8_t kFujitsuAc264OffTimerEnable = 0x2; // 0b0010 +const uint8_t kFujitsuAc264OnOffTimerEnable = 0x3; // 0b0011 +const uint16_t kFujitsuAc26OnOffTimerMax = 24 * 6 - 1; ///< 10 Minutes. + +/// Special command for Power Off +const uint8_t kFujitsuAc264StatesTurnOff + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD}; +/// Special command for Toggle Powerful +const uint8_t kFujitsuAc264StatesTogglePowerful + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; +/// Special command for Eco Fan Off +const uint8_t kFujitsuAc264StatesEcoFanOff + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x51, 0xAE}; +/// Special command for Eco Fan On +const uint8_t kFujitsuAc264StatesEcoFanOn + [kFujitsuAc264StateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x50, 0xAF}; +/// Special command for Outside Quiet Off +/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, +/// but has different meaning. +const uint8_t kFujitsuAc264StatesOutsideQuietOff + [kFujitsuAc264StateLengthMiddle] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x01, 0x00, 0x00, 0xFE, 0xBF, 0x00, 0x41}; +/// Special command for Outside Quiet Off +/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, +/// but has different meaning. +const uint8_t kFujitsuAc264StatesOutsideQuietOn + [kFujitsuAc264StateLengthMiddle] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x00, 0x00, 0x00, 0xFF, 0xBF, 0x00, 0x41}; +/// Special command for Toggle Sterilization +/// @note This command uses the same protocol with FujitsuAC's ARRAH2E, +/// but has different meaning. +const uint8_t kFujitsuAc264StatesToggleSterilization + [kFujitsuAc264StateLengthMiddle] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x60, 0x03, 0x00, 0x00, 0xFC, 0x9F, 0x00, 0x41}; + +// Legacy defines. +#define FUJITSU_AC_MODE_AUTO kFujitsuAcModeAuto +#define FUJITSU_AC_MODE_COOL kFujitsuAcModeCool +#define FUJITSU_AC_MODE_DRY kFujitsuAcModeDry +#define FUJITSU_AC_MODE_FAN kFujitsuAcModeFan +#define FUJITSU_AC_MODE_HEAT kFujitsuAcModeHeat +#define FUJITSU_AC_CMD_STAY_ON kFujitsuAcCmdStayOn +#define FUJITSU_AC_CMD_TURN_ON kFujitsuAcCmdTurnOn +#define FUJITSU_AC_CMD_TURN_OFF kFujitsuAcCmdTurnOff +#define FUJITSU_AC_CMD_STEP_HORIZ kFujitsuAcCmdStepHoriz +#define FUJITSU_AC_CMD_STEP_VERT kFujitsuAcCmdStepVert +#define FUJITSU_AC_FAN_AUTO kFujitsuAcFanAuto +#define FUJITSU_AC_FAN_HIGH kFujitsuAcFanHigh +#define FUJITSU_AC_FAN_MED kFujitsuAcFanMed +#define FUJITSU_AC_FAN_LOW kFujitsuAcFanLow +#define FUJITSU_AC_FAN_QUIET kFujitsuAcFanQuiet +#define FUJITSU_AC_MIN_TEMP kFujitsuAcMinTemp +#define FUJITSU_AC_MAX_TEMP kFujitsuAcMaxTemp +#define FUJITSU_AC_SWING_OFF kFujitsuAcSwingOff +#define FUJITSU_AC_SWING_VERT kFujitsuAcSwingVert +#define FUJITSU_AC_SWING_HORIZ kFujitsuAcSwingHoriz +#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth + +/// Class for handling detailed Fujitsu A/C messages. +class IRFujitsuAC { + public: + explicit IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model = ARRAH2E, + const bool inverted = false, + const bool use_modulation = true); + void setModel(const fujitsu_ac_remote_model_t model); + fujitsu_ac_remote_model_t getModel(void) const; + void stateReset(void); +#if SEND_FUJITSU_AC + void send(const uint16_t repeat = kFujitsuAcMinRepeat); + /// 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_FUJITSU_AC + void begin(void); + void stepHoriz(void); + void toggleSwingHoriz(const bool update = true); + void stepVert(void); + void toggleSwingVert(const bool update = true); + void setCmd(const uint8_t cmd); + uint8_t getCmd(void) const; + void setTemp(const float temp, const bool useCelsius = true); + float getTemp(void) const; + void setFanSpeed(const uint8_t fan); + uint8_t getFanSpeed(void) const; + void setMode(const uint8_t mode); + uint8_t getMode(void) const; + void setSwing(const uint8_t mode); + uint8_t getSwing(void) const; + uint8_t* getRaw(void); + bool setRaw(const uint8_t newState[], const uint16_t length); + uint8_t getStateLength(void); + static bool validChecksum(uint8_t* state, const uint16_t length); + bool isLongCode(void) const; + void setPower(const bool on); + void off(void); + void on(void); + bool getPower(void) const; + void setClean(const bool on); + bool getClean(void) const; + void setFilter(const bool on); + bool getFilter(void) const; + void set10CHeat(const bool on); + bool get10CHeat(void) const; + void setOutsideQuiet(const bool on); + bool getOutsideQuiet(void) const; + uint8_t getTimerType(void) const; + void setTimerType(const uint8_t timertype); + uint16_t getOnTimer(void) const; + void setOnTimer(const uint16_t nr_mins); + uint16_t getOffSleepTimer(void) const; + void setOffTimer(const uint16_t nr_mins); + void setSleepTimer(const uint16_t nr_mins); + void setId(const uint8_t num); + uint8_t getId(void) const; + void setCelsius(const bool on); + bool getCelsius(void) const; + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(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(const stdAc::state_t *prev = NULL); + String toString(void) const; +#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 + FujitsuProtocol _; + uint8_t _cmd; + fujitsu_ac_remote_model_t _model; + uint8_t _state_length; + uint8_t _state_length_short; + bool _rawstatemodified; + void checkSum(void); + bool updateUseLongOrShort(void); + void buildFromState(const uint16_t length); + void setOffSleepTimer(const uint16_t nr_mins); +}; + +/// Class for handling detailed Fujitsu 264 bit A/C messages. +class IRFujitsuAC264 { + public: + explicit IRFujitsuAC264(const uint16_t pin, + const bool inverted = false, + const bool use_modulation = true); +#if SEND_FUJITSU_AC264 + void send(const uint16_t repeat = kFujitsuAc264DefaultRepeat); + /// 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_FUJITSU_AC264 + void begin(void); + uint8_t* getRaw(void); + bool setRaw(const uint8_t newState[], const uint16_t length); + static bool validChecksum(uint8_t state[], + const uint16_t length = kFujitsuAc264StateLength); + + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void) const; + bool isTempStayed(void) const; + void setTemp(const float temp); + float getTemp(void) const; + void setTempAuto(const float temp); + float getTempAuto(void) const; + void setMode(const uint8_t mode, const bool weakdry = false); + uint8_t getMode(void) const; + bool isWeakDry(void) const; + void setFanSpeed(const uint8_t fanSpeed); + uint8_t getFanSpeed(void) const; + void setFanAngle(const uint8_t fanAngle); + uint8_t getFanAngle(void) const; + void setSwing(const bool on); + bool getSwing(void) const; + void setEconomy(const bool on); + bool getEconomy(void) const; + void setClean(const bool on); + bool getClean(void) const; + + void toggleSterilization(void); + void setOutsideQuiet(const bool on); + bool getOutsideQuiet(void) const; + void setEcoFan(const bool on); + bool getEcoFan(void) const; + void togglePowerful(void); + + void setClock(const uint16_t mins_since_midnight); + uint16_t getClock(void) const; + void setSleepTimer(const uint16_t mins); + uint16_t getSleepTimer(void) const; + void setTimerEnable(const uint8_t timer_enable); + uint8_t getTimerEnable(void) const; + void setOnTimer(const uint8_t mins10); + uint8_t getOnTimer(void) const; + void setOffTimer(const uint8_t mins10); + uint8_t getOffTimer(void) const; + + void setCmd(const uint8_t cmd); + uint8_t getCmd(void) const; + uint8_t getStateLength(void); + + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFanSpeed(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(const stdAc::state_t *prev = NULL); + String toString(void) const; + +#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 + Fujitsu264Protocol _; + uint8_t _cmd; + bool _ispoweredon; + bool _isecofan; + bool _isoutsidequiet; + uint8_t _settemp; + void stateReset(void); + void checkSum(void); + bool isSpecialCommand(void) const; +}; + +#endif // IR_FUJITSU_H_ diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 77cfc97d3..f268b88eb 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -1,1151 +1,1151 @@ -// Copyright 2019 - David Conran (@crankyoldgit) -// The default text to use throughout the library. -// The library will use this text if no locale (_IR_LOCALE_) is set or if -// the locale doesn't define particular values. -// If they are defined, this file should NOT override them. -// -// This file should contain a #define for every translateable/locale dependant -// string used by the library. Language specific files don't have to include -// everything. -// -// NOTE: ASCII/UTF-8 characters only. Unicode is NOT supported. -// -// The defaults are English (AU) / en-AU. Australia (AU) is pretty much the same -// as English (UK) for this libraries use case. -#ifndef LOCALE_DEFAULTS_H_ -#define LOCALE_DEFAULTS_H_ - -#ifndef D_STR_UNKNOWN -#define D_STR_UNKNOWN "UNKNOWN" -#endif // D_STR_UNKNOWN -#ifndef D_STR_PROTOCOL -#define D_STR_PROTOCOL "Protocol" -#endif // D_STR_PROTOCOL -#ifndef D_STR_POWER -#define D_STR_POWER "Power" -#endif // D_STR_POWER -#ifndef D_STR_PREVIOUS -#define D_STR_PREVIOUS "Previous" -#endif // D_STR_PREVIOUS -#ifndef D_STR_ON -#define D_STR_ON "On" -#endif // D_STR_ON -#ifndef D_STR_1 -#define D_STR_1 "1" -#endif // D_STR_1 -#ifndef D_STR_OFF -#define D_STR_OFF "Off" -#endif // D_STR_OFF -#ifndef D_STR_0 -#define D_STR_0 "0" -#endif // D_STR_0 -#ifndef D_STR_MODE -#define D_STR_MODE "Mode" -#endif // D_STR_MODE -#ifndef D_STR_TOGGLE -#define D_STR_TOGGLE "Toggle" -#endif // D_STR_TOGGLE -#ifndef D_STR_TURBO -#define D_STR_TURBO "Turbo" -#endif // D_STR_TURBO -#ifndef D_STR_SUPER -#define D_STR_SUPER "Super" -#endif // D_STR_SUPER -#ifndef D_STR_SLEEP -#define D_STR_SLEEP "Sleep" -#endif // D_STR_SLEEP -#ifndef D_STR_LIGHT -#define D_STR_LIGHT "Light" -#endif // D_STR_LIGHT -#ifndef D_STR_POWERFUL -#define D_STR_POWERFUL "Powerful" -#endif // D_STR_POWERFUL -#ifndef D_STR_QUIET -#define D_STR_QUIET "Quiet" -#endif // D_STR_QUIET -#ifndef D_STR_ECONO -#define D_STR_ECONO "Econo" -#endif // D_STR_ECONO -#ifndef D_STR_SWING -#define D_STR_SWING "Swing" -#endif // D_STR_SWING -#ifndef D_STR_SWINGH -#define D_STR_SWINGH D_STR_SWING"(H)" // Set `D_STR_SWING` first! -#endif // D_STR_SWINGH -#ifndef D_STR_SWINGV -#define D_STR_SWINGV D_STR_SWING"(V)" // Set `D_STR_SWING` first! -#endif // D_STR_SWINGV -#ifndef D_STR_BEEP -#define D_STR_BEEP "Beep" -#endif // D_STR_BEEP -#ifndef D_STR_MOULD -#define D_STR_MOULD "Mould" -#endif // D_STR_MOULD -#ifndef D_STR_CLEAN -#define D_STR_CLEAN "Clean" -#endif // D_STR_CLEAN -#ifndef D_STR_PURIFY -#define D_STR_PURIFY "Purify" -#endif // D_STR_PURIFY -#ifndef D_STR_TIMER -#define D_STR_TIMER "Timer" -#endif // D_STR_TIMER -#ifndef D_STR_ONTIMER -#define D_STR_ONTIMER D_STR_ON " " D_STR_TIMER // Set `D_STR_ON` first! -#endif // D_STR_ONTIMER -#ifndef D_STR_OFFTIMER -#define D_STR_OFFTIMER D_STR_OFF " " D_STR_TIMER // Set `D_STR_OFF` first! -#endif // D_STR_OFFTIMER -#ifndef D_STR_TIMERMODE -#define D_STR_TIMERMODE D_STR_TIMER " " D_STR_MODE // Set `D_STR_MODE` first! -#endif // D_STR_TIMERMODE -#ifndef D_STR_CLOCK -#define D_STR_CLOCK "Clock" -#endif // D_STR_CLOCK -#ifndef D_STR_COMMAND -#define D_STR_COMMAND "Command" -#endif // D_STR_COMMAND -#ifndef D_STR_XFAN -#define D_STR_XFAN "XFan" -#endif // D_STR_XFAN -#ifndef D_STR_HEALTH -#define D_STR_HEALTH "Health" -#endif // D_STR_HEALTH -#ifndef D_STR_MODEL -#define D_STR_MODEL "Model" -#endif // D_STR_MODEL -#ifndef D_STR_TEMP -#define D_STR_TEMP "Temp" -#endif // D_STR_TEMP -#ifndef D_STR_IFEEL -#define D_STR_IFEEL "IFeel" -#endif // D_STR_IFEEL -#ifndef D_STR_ISEE -#define D_STR_ISEE "ISee" -#endif // D_STR_ISEE -#ifndef D_STR_HUMID -#define D_STR_HUMID "Humid" -#endif // D_STR_HUMID -#ifndef D_STR_SAVE -#define D_STR_SAVE "Save" -#endif // D_STR_SAVE -#ifndef D_STR_EYE -#define D_STR_EYE "Eye" -#endif // D_STR_EYE -#ifndef D_STR_FOLLOW -#define D_STR_FOLLOW "Follow" -#endif // D_STR_FOLLOW -#ifndef D_STR_ION -#define D_STR_ION "Ion" -#endif // D_STR_ION -#ifndef D_STR_FRESH -#define D_STR_FRESH "Fresh" -#endif // D_STR_FRESH -#ifndef D_STR_HOLD -#define D_STR_HOLD "Hold" -#endif // D_STR_HOLD -#ifndef D_STR_8C_HEAT -#define D_STR_8C_HEAT "8C " D_STR_HEAT // Set `D_STR_HEAT` first! -#endif // D_STR_8C_HEAT -#ifndef D_STR_10C_HEAT -#define D_STR_10C_HEAT "10C " D_STR_HEAT // Set `D_STR_HEAT` first! -#endif // D_STR_10C_HEAT -#ifndef D_STR_BUTTON -#define D_STR_BUTTON "Button" -#endif // D_STR_BUTTON -#ifndef D_STR_NIGHT -#define D_STR_NIGHT "Night" -#endif // D_STR_NIGHT -#ifndef D_STR_SILENT -#define D_STR_SILENT "Silent" -#endif // D_STR_SILENT -#ifndef D_STR_FILTER -#define D_STR_FILTER "Filter" -#endif // D_STR_FILTER -#ifndef D_STR_3D -#define D_STR_3D "3D" -#endif // D_STR_3D -#ifndef D_STR_CELSIUS -#define D_STR_CELSIUS "Celsius" -#endif // D_STR_CELSIUS -#ifndef D_STR_FAHRENHEIT -#define D_STR_FAHRENHEIT "Fahrenheit" -#endif // D_STR_FAHRENHEIT -#ifndef D_STR_CELSIUS_FAHRENHEIT -#define D_STR_CELSIUS_FAHRENHEIT D_STR_CELSIUS "/" D_STR_FAHRENHEIT -#endif // D_STR_CELSIUS_FAHRENHEIT -#ifndef D_STR_UP -#define D_STR_UP "Up" -#endif // D_STR_UP -#ifndef D_STR_TEMPUP -#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP // Set `D_STR_TEMP` first! -#endif // D_STR_TEMPUP -#ifndef D_STR_DOWN -#define D_STR_DOWN "Down" -#endif // D_STR_DOWN -#ifndef D_STR_TEMPDOWN -#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN // Set `D_STR_TEMP` first! -#endif // D_STR_TEMPDOWN -#ifndef D_STR_CHANGE -#define D_STR_CHANGE "Change" -#endif // D_STR_CHANGE -#ifndef D_STR_START -#define D_STR_START "Start" -#endif // D_STR_START -#ifndef D_STR_STOP -#define D_STR_STOP "Stop" -#endif // D_STR_STOP -#ifndef D_STR_MOVE -#define D_STR_MOVE "Move" -#endif // D_STR_MOVE -#ifndef D_STR_SET -#define D_STR_SET "Set" -#endif // D_STR_SET -#ifndef D_STR_CANCEL -#define D_STR_CANCEL "Cancel" -#endif // D_STR_CANCEL -#ifndef D_STR_COMFORT -#define D_STR_COMFORT "Comfort" -#endif // D_STR_COMFORT -#ifndef D_STR_SENSOR -#define D_STR_SENSOR "Sensor" -#endif // D_STR_SENSOR -#ifndef D_STR_ABSENSEDETECT -#define D_STR_ABSENSEDETECT "Absense detect" -#endif // D_STR_ABSENSEDETECT -#ifndef D_STR_DIRECT -#define D_STR_DIRECT "Direct" -#endif // D_STR_DIRECT -#ifndef D_STR_INDIRECT -#define D_STR_INDIRECT "Indirect" -#endif // D_STR_INDIRECT -#ifndef D_STR_DIRECTINDIRECTMODE -#define D_STR_DIRECTINDIRECTMODE D_STR_DIRECT " / " \ -D_STR_INDIRECT " " D_STR_MODE -#endif // D_STR_DIRECTINDIRECTMODE -#ifndef D_STR_DISPLAY -#define D_STR_DISPLAY "Display" -#endif // D_STR_DISPLAY -#ifndef D_STR_WEEKLY -#define D_STR_WEEKLY "Weekly" -#endif // D_STR_WEEKLY -#ifndef D_STR_WEEKLYTIMER -#define D_STR_WEEKLYTIMER D_STR_WEEKLY " " D_STR_TIMER // Needs `D_STR_WEEKLY`! -#endif // D_STR_WEEKLYTIMER -#ifndef D_STR_WIFI -#define D_STR_WIFI "WiFi" -#endif // D_STR_WIFI -#ifndef D_STR_LAST -#define D_STR_LAST "Last" -#endif // D_STR_LAST -#ifndef D_STR_FAST -#define D_STR_FAST "Fast" -#endif // D_STR_FAST -#ifndef D_STR_SLOW -#define D_STR_SLOW "Slow" -#endif // D_STR_SLOW -#ifndef D_STR_AIRFLOW -#define D_STR_AIRFLOW "Air Flow" -#endif // D_STR_AIRFLOW -#ifndef D_STR_STEP -#define D_STR_STEP "Step" -#endif // D_STR_STEP -#ifndef D_STR_NA -#define D_STR_NA "N/A" -#endif // D_STR_NA -#ifndef D_STR_INSIDE -#define D_STR_INSIDE "Inside" -#endif // D_STR_INSIDE -#ifndef D_STR_OUTSIDE -#define D_STR_OUTSIDE "Outside" -#endif // D_STR_OUTSIDE -#ifndef D_STR_LOUD -#define D_STR_LOUD "Loud" -#endif // D_STR_LOUD -#ifndef D_STR_UPPER -#define D_STR_UPPER "Upper" -#endif // D_STR_UPPER -#ifndef D_STR_LOWER -#define D_STR_LOWER "Lower" -#endif // D_STR_LOWER -#ifndef D_STR_BREEZE -#define D_STR_BREEZE "Breeze" -#endif // D_STR_BREEZE -#ifndef D_STR_CIRCULATE -#define D_STR_CIRCULATE "Circulate" -#endif // D_STR_CIRCULATE -#ifndef D_STR_CEILING -#define D_STR_CEILING "Ceiling" -#endif // D_STR_CEILING -#ifndef D_STR_WALL -#define D_STR_WALL "Wall" -#endif // D_STR_WALL -#ifndef D_STR_ROOM -#define D_STR_ROOM "Room" -#endif // D_STR_ROOM -#ifndef D_STR_6THSENSE -#define D_STR_6THSENSE "6th Sense" -#endif // D_STR_6THSENSE -#ifndef D_STR_ZONEFOLLOW -#define D_STR_ZONEFOLLOW "Zone Follow" -#endif // D_STR_ZONEFOLLOW -#ifndef D_STR_FIXED -#define D_STR_FIXED "Fixed" -#endif // D_STR_FIXED -#ifndef D_STR_TYPE -#define D_STR_TYPE "Type" -#endif // D_STR_TYPE -#ifndef D_STR_SPECIAL -#define D_STR_SPECIAL "Special" -#endif // D_STR_SPECIAL -#ifndef D_STR_RECYCLE -#define D_STR_RECYCLE "Recycle" -#endif // D_STR_RECYCLE -#ifndef D_STR_ID -#define D_STR_ID "Id" -#endif // D_STR_ID -#ifndef D_STR_VANE -#define D_STR_VANE "Vane" -#endif // D_STR_VANE -#ifndef D_STR_LOCK -#define D_STR_LOCK "Lock" -#endif // D_STR_LOCK -#ifndef D_STR_REPORT -#define D_STR_REPORT "Report" -#endif // D_STR_REPORT - -#ifndef D_STR_AUTO -#define D_STR_AUTO "Auto" -#endif // D_STR_AUTO -#ifndef D_STR_AUTOMATIC -#define D_STR_AUTOMATIC "Automatic" -#endif // D_STR_AUTOMATIC -#ifndef D_STR_MANUAL -#define D_STR_MANUAL "Manual" -#endif // D_STR_MANUAL -#ifndef D_STR_COOL -#define D_STR_COOL "Cool" -#endif // D_STR_COOL -#ifndef D_STR_COOLING -#define D_STR_COOLING "Cooling" -#endif // D_STR_COOLING -#ifndef D_STR_HEAT -#define D_STR_HEAT "Heat" -#endif // D_STR_HEAT -#ifndef D_STR_HEATING -#define D_STR_HEATING "Heating" -#endif // D_STR_HEATING -#ifndef D_STR_FAN -#define D_STR_FAN "Fan" -#endif // D_STR_FAN -#ifndef D_STR_FANONLY -#define D_STR_FANONLY "fan-only" -#endif // D_STR_FANONLY -#ifndef D_STR_FAN_ONLY -#define D_STR_FAN_ONLY "fan_only" -#endif // D_STR_FAN_ONLY -#ifndef D_STR_ONLY -#define D_STR_ONLY "Only" -#endif // D_STR_ONLY -#ifndef D_STR_FANSPACEONLY -#define D_STR_FANSPACEONLY D_STR_FAN " " D_STR_ONLY -#endif // D_STR_FANSPACEONLY -#ifndef D_STR_FANONLYNOSPACE -#define D_STR_FANONLYNOSPACE D_STR_FAN D_STR_ONLY -#endif // D_STR_FANONLYNOSPACE -#ifndef D_STR_DRY -#define D_STR_DRY "Dry" -#endif // D_STR_DRY -#ifndef D_STR_DRYING -#define D_STR_DRYING "Drying" -#endif // D_STR_DRYING -#ifndef D_STR_DEHUMIDIFY -#define D_STR_DEHUMIDIFY "Dehumidify" -#endif // D_STR_DEHUMIDIFY - -#ifndef D_STR_MAX -#define D_STR_MAX "Max" -#endif // D_STR_MAX -#ifndef D_STR_MAXIMUM -#define D_STR_MAXIMUM "Maximum" -#endif // D_STR_MAXIMUM -#ifndef D_STR_MIN -#define D_STR_MIN "Min" -#endif // D_STR_MIN -#ifndef D_STR_MINIMUM -#define D_STR_MINIMUM "Minimum" -#endif // D_STR_MINIMUM -#ifndef D_STR_MED -#define D_STR_MED "Med" -#endif // D_STR_MED -#ifndef D_STR_MEDIUM -#define D_STR_MEDIUM "Medium" -#endif // D_STR_MEDIUM -#ifndef D_STR_MED_HIGH -#define D_STR_MED_HIGH D_STR_MED "-" D_STR_HIGH -#endif // D_STR_MED_HIGH - -#ifndef D_STR_HIGHEST -#define D_STR_HIGHEST "Highest" -#endif // D_STR_HIGHEST -#ifndef D_STR_HIGH -#define D_STR_HIGH "High" -#endif // D_STR_HIGH -#ifndef D_STR_HI -#define D_STR_HI "Hi" -#endif // D_STR_HI -#ifndef D_STR_MID -#define D_STR_MID "Mid" -#endif // D_STR_MID -#ifndef D_STR_MIDDLE -#define D_STR_MIDDLE "Middle" -#endif // D_STR_MIDDLE -#ifndef D_STR_LOW -#define D_STR_LOW "Low" -#endif // D_STR_LOW -#ifndef D_STR_LO -#define D_STR_LO "Lo" -#endif // D_STR_LO -#ifndef D_STR_LOWEST -#define D_STR_LOWEST "Lowest" -#endif // D_STR_LOWEST -#ifndef D_STR_RIGHT -#define D_STR_RIGHT "Right" -#endif // D_STR_RIGHT -#ifndef D_STR_MAXRIGHT -#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` first! -#endif // D_STR_MAXRIGHT -#ifndef D_STR_MAXRIGHT_NOSPACE -#define D_STR_MAXRIGHT_NOSPACE D_STR_MAX D_STR_RIGHT // Set `D_STR_MAX` first! -#endif // D_STR_MAXRIGHT_NOSPACE -#ifndef D_STR_RIGHTMAX -#define D_STR_RIGHTMAX D_STR_RIGHT " " D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_RIGHTMAX -#ifndef D_STR_RIGHTMAX_NOSPACE -#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_RIGHTMAX_NOSPACE -#ifndef D_STR_LEFT -#define D_STR_LEFT "Left" -#endif // D_STR_LEFT -#ifndef D_STR_MAXLEFT -#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` first! -#endif // D_STR_MAXLEFT -#ifndef D_STR_MAXLEFT_NOSPACE -#define D_STR_MAXLEFT_NOSPACE D_STR_MAX D_STR_LEFT // Set `D_STR_MAX` first! -#endif // D_STR_MAXLEFT_NOSPACE -#ifndef D_STR_LEFTMAX -#define D_STR_LEFTMAX D_STR_LEFT " " D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_LEFTMAX -#ifndef D_STR_LEFTMAX_NOSPACE -#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` first! -#endif // D_STR_LEFTMAX_NOSPACE -#ifndef D_STR_WIDE -#define D_STR_WIDE "Wide" -#endif // D_STR_WIDE -#ifndef D_STR_CENTRE -#define D_STR_CENTRE "Centre" -#endif // D_STR_CENTRE -#ifndef D_STR_TOP -#define D_STR_TOP "Top" -#endif // D_STR_TOP -#ifndef D_STR_BOTTOM -#define D_STR_BOTTOM "Bottom" -#endif // D_STR_BOTTOM -#ifndef D_STR_UPPER_MIDDLE -#define D_STR_UPPER_MIDDLE D_STR_UPPER "-" D_STR_MIDDLE -#endif // D_STR_UPPER_MIDDLE -#ifndef D_STR_CONFIG -#define D_STR_CONFIG "Config" -#endif // D_STR_CONFIG -#ifndef D_STR_CONTROL -#define D_STR_CONTROL "Control" -#endif // D_STR_CONTROL -#ifndef D_STR_SET_TIMER -#define D_STR_SET_TIMER D_STR_SET " " D_STR_TIMER -#endif // D_STR_AC_TIMER -#ifndef D_STR_SCHEDULE -#define D_STR_SCHEDULE "Schedule" -#endif // D_STR_SCHEDULE -#ifndef D_STR_CH -#define D_STR_CH "CH#" -#endif // D_STR_CH -#ifndef D_STR_TIMER_ACTIVE_DAYS -#define D_STR_TIMER_ACTIVE_DAYS "TimerActiveDays" -#endif // D_STR_TIMER_ACTIVE_DAYS -#ifndef D_STR_KEY -#define D_STR_KEY "Key" -#endif // D_STR_KEY -#ifndef D_STR_VALUE -#define D_STR_VALUE "Value" -#endif // D_STR_VALUE - -// Compound words/phrases/descriptions from pre-defined words. -// Note: Obviously these need to be defined *after* their component words. -#ifndef D_STR_ECONOTOGGLE -#define D_STR_ECONOTOGGLE D_STR_ECONO " " D_STR_TOGGLE -#endif // D_STR_ECONOTOGGLE -#ifndef D_STR_EYEAUTO -#define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO -#endif // D_STR_EYEAUTO -#ifndef D_STR_LIGHTTOGGLE -#define D_STR_LIGHTTOGGLE D_STR_LIGHT " " D_STR_TOGGLE -#endif // D_STR_LIGHTTOGGLE -#ifndef D_STR_OUTSIDEQUIET -#define D_STR_OUTSIDEQUIET D_STR_OUTSIDE " " D_STR_QUIET -#endif // D_STR_OUTSIDEQUIET -#ifndef D_STR_POWERTOGGLE -#define D_STR_POWERTOGGLE D_STR_POWER " " D_STR_TOGGLE -#endif // D_STR_POWERTOGGLE -#ifndef D_STR_POWERBUTTON -#define D_STR_POWERBUTTON D_STR_POWER " " D_STR_BUTTON -#endif // D_STR_POWERBUTTON -#ifndef D_STR_PREVIOUSPOWER -#define D_STR_PREVIOUSPOWER D_STR_PREVIOUS " " D_STR_POWER -#endif // D_STR_PREVIOUSPOWER -#ifndef D_STR_DISPLAYTEMP -#define D_STR_DISPLAYTEMP D_STR_DISPLAY " " D_STR_TEMP -#endif // D_STR_DISPLAYTEMP -#ifndef D_STR_IFEELREPORT -#define D_STR_IFEELREPORT D_STR_IFEEL " " D_STR_REPORT -#endif // D_STR_IFEELREPORT -#ifndef D_STR_SENSORTEMP -#define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP -#endif // D_STR_SENSORTEMP -#ifndef D_STR_SLEEP_TIMER -#define D_STR_SLEEP_TIMER D_STR_SLEEP " " D_STR_TIMER -#endif // D_STR_SLEEP_TIMER -#ifndef D_STR_SWINGVMODE -#define D_STR_SWINGVMODE D_STR_SWINGV " " D_STR_MODE -#endif // D_STR_SWINGVMODE -#ifndef D_STR_SWINGVTOGGLE -#define D_STR_SWINGVTOGGLE D_STR_SWINGV " " D_STR_TOGGLE -#endif // D_STR_SWINGVTOGGLE -#ifndef D_STR_TURBOTOGGLE -#define D_STR_TURBOTOGGLE D_STR_TURBO " " D_STR_TOGGLE -#endif // D_STR_TURBOTOGGLE - -// Separators -#ifndef D_CHR_TIME_SEP -#define D_CHR_TIME_SEP ':' -#endif // D_CHR_TIME_SEP -#ifndef D_STR_SPACELBRACE -#define D_STR_SPACELBRACE " (" -#endif // D_STR_SPACELBRACE -#ifndef D_STR_COMMASPACE -#define D_STR_COMMASPACE ", " -#endif // D_STR_COMMASPACE -#ifndef D_STR_COLONSPACE -#define D_STR_COLONSPACE ": " -#endif // D_STR_COLONSPACE -#ifndef D_STR_DASH -#define D_STR_DASH "-" -#endif // D_STR_DASH - -#ifndef D_STR_DAY -#define D_STR_DAY "Day" -#endif // D_STR_DAY -#ifndef D_STR_DAYS -#define D_STR_DAYS D_STR_DAY "s" -#endif // D_STR_DAYS -#ifndef D_STR_HOUR -#define D_STR_HOUR "Hour" -#endif // D_STR_HOUR -#ifndef D_STR_HOURS -#define D_STR_HOURS D_STR_HOUR "s" -#endif // D_STR_HOURS -#ifndef D_STR_MINUTE -#define D_STR_MINUTE "Minute" -#endif // D_STR_MINUTE -#ifndef D_STR_MINUTES -#define D_STR_MINUTES D_STR_MINUTE "s" -#endif // D_STR_MINUTES -#ifndef D_STR_SECOND -#define D_STR_SECOND "Second" -#endif // D_STR_SECOND -#ifndef D_STR_SECONDS -#define D_STR_SECONDS D_STR_SECOND "s" -#endif // D_STR_SECONDS -#ifndef D_STR_NOW -#define D_STR_NOW "Now" -#endif // D_STR_NOW -#ifndef D_STR_THREELETTERDAYS -#define D_STR_THREELETTERDAYS "SunMonTueWedThuFriSat" -#endif // D_STR_THREELETTERDAYS - -#ifndef D_STR_YES -#define D_STR_YES "Yes" -#endif // D_STR_YES -#ifndef D_STR_NO -#define D_STR_NO "No" -#endif // D_STR_NO -#ifndef D_STR_TRUE -#define D_STR_TRUE "True" -#endif // D_STR_TRUE -#ifndef D_STR_FALSE -#define D_STR_FALSE "False" -#endif // D_STR_FALSE - -#ifndef D_STR_REPEAT -#define D_STR_REPEAT "Repeat" -#endif // D_STR_REPEAT -#ifndef D_STR_CODE -#define D_STR_CODE "Code" -#endif // D_STR_CODE -#ifndef D_STR_BITS -#define D_STR_BITS "Bits" -#endif // D_STR_BITS - -// Model Names -#ifndef D_STR_YAW1F -#define D_STR_YAW1F "YAW1F" -#endif // D_STR_YAW1F -#ifndef D_STR_YBOFB -#define D_STR_YBOFB "YBOFB" -#endif // D_STR_YBOFB -#ifndef D_STR_YX1FSF -#define D_STR_YX1FSF "YX1FSF" -#endif // D_STR_YX1FSF -#ifndef D_STR_V9014557_A -#define D_STR_V9014557_A "V9014557-A" -#endif // D_STR_V9014557_A -#ifndef D_STR_V9014557_B -#define D_STR_V9014557_B "V9014557-B" -#endif // D_STR_V9014557_B -#ifndef D_STR_RLT0541HTA_A -#define D_STR_RLT0541HTA_A "R-LT0541-HTA-A" -#endif // D_STR_RLT0541HTA_A -#ifndef D_STR_RLT0541HTA_B -#define D_STR_RLT0541HTA_B "R-LT0541-HTA-B" -#endif // D_STR_RLT0541HTA_B -#ifndef D_STR_ARRAH2E -#define D_STR_ARRAH2E "ARRAH2E" -#endif // D_STR_ARRAH2E -#ifndef D_STR_ARDB1 -#define D_STR_ARDB1 "ARDB1" -#endif // D_STR_ARDB1 -#ifndef D_STR_ARREB1E -#define D_STR_ARREB1E "ARREB1E" -#endif // D_STR_ARREB1E -#ifndef D_STR_ARJW2 -#define D_STR_ARJW2 "ARJW2" -#endif // D_STR_ARJW2 -#ifndef D_STR_ARRY4 -#define D_STR_ARRY4 "ARRY4" -#endif // D_STR_ARRY4 -#ifndef D_STR_ARREW4E -#define D_STR_ARREW4E "ARREW4E" -#endif // D_STR_ARREW4E -#ifndef D_STR_GE6711AR2853M -#define D_STR_GE6711AR2853M "GE6711AR2853M" -#endif // D_STR_GE6711AR2853M -#ifndef D_STR_AKB75215403 -#define D_STR_AKB75215403 "AKB75215403" -#endif // D_STR_AKB75215403 -#ifndef D_STR_AKB74955603 -#define D_STR_AKB74955603 "AKB74955603" -#endif // D_STR_AKB74955603 -#ifndef D_STR_AKB73757604 -#define D_STR_AKB73757604 "AKB73757604" -#endif // D_STR_AKB73757604 -#ifndef D_STR_LG6711A20083V -#define D_STR_LG6711A20083V "LG6711A20083V" -#endif // D_STR_LG6711A20083V -#ifndef D_STR_KKG9AC1 -#define D_STR_KKG9AC1 "KKG9AC1" -#endif // D_STR_KKG9AC1 -#ifndef D_STR_KKG29AC1 -#define D_STR_KKG29AC1 "KKG29AC1" -#endif // D_STR_KKG9AC1 -#ifndef D_STR_LKE -#define D_STR_LKE "LKE" -#endif // D_STR_LKE -#ifndef D_STR_NKE -#define D_STR_NKE "NKE" -#endif // D_STR_NKE -#ifndef D_STR_DKE -#define D_STR_DKE "DKE" -#endif // D_STR_DKE -#ifndef D_STR_PKR -#define D_STR_PKR "PKR" -#endif // D_STR_PKR -#ifndef D_STR_JKE -#define D_STR_JKE "JKE" -#endif // D_STR_JKE -#ifndef D_STR_CKP -#define D_STR_CKP "CKP" -#endif // D_STR_CKP -#ifndef D_STR_RKR -#define D_STR_RKR "RKR" -#endif // D_STR_RKR -#ifndef D_STR_PANASONICLKE -#define D_STR_PANASONICLKE "PANASONICLKE" -#endif // D_STR_PANASONICLKE -#ifndef D_STR_PANASONICNKE -#define D_STR_PANASONICNKE "PANASONICNKE" -#endif // D_STR_PANASONICNKE -#ifndef D_STR_PANASONICDKE -#define D_STR_PANASONICDKE "PANASONICDKE" -#endif // D_STR_PANASONICDKE -#ifndef D_STR_PANASONICPKR -#define D_STR_PANASONICPKR "PANASONICPKR" -#endif // D_STR_PANASONICPKR -#ifndef D_STR_PANASONICJKE -#define D_STR_PANASONICJKE "PANASONICJKE" -#endif // D_STR_PANASONICJKE -#ifndef D_STR_PANASONICCKP -#define D_STR_PANASONICCKP "PANASONICCKP" -#endif // D_STR_PANASONICCKP -#ifndef D_STR_PANASONICRKR -#define D_STR_PANASONICRKR "PANASONICRKR" -#endif // D_STR_PANASONICRKR -#ifndef D_STR_A907 -#define D_STR_A907 "A907" -#endif // D_STR_A907 -#ifndef D_STR_A705 -#define D_STR_A705 "A705" -#endif // D_STR_A705 -#ifndef D_STR_A903 -#define D_STR_A903 "A903" -#endif // D_STR_A903 -#ifndef D_STR_TAC09CHSD -#define D_STR_TAC09CHSD "TAC09CHSD" -#endif // D_STR_TAC09CHSD -#ifndef D_STR_GZ055BE1 -#define D_STR_GZ055BE1 "GZ055BE1" -#endif // D_STR_GZ055BE1 -#ifndef D_STR_122LZF -#define D_STR_122LZF "122LZF" -#endif // D_STR_122LZF -#ifndef D_STR_DG11J13A -#define D_STR_DG11J13A "DG11J13A" -#endif // D_STR_DG11J13A -#ifndef D_STR_DG11J104 -#define D_STR_DG11J104 "DG11J104" -#endif // D_STR_DG11J104 -#ifndef D_STR_DG11J191 -#define D_STR_DG11J191 "DG11J191" -#endif // D_STR_DG11J191 -#ifndef D_STR_ARGO_WREM2 -#define D_STR_ARGO_WREM2 "WREM2" -#endif // D_STR_ARGO_WREM2 -#ifndef D_STR_ARGO_WREM3 -#define D_STR_ARGO_WREM3 "WREM3" -#endif // D_STR_ARGO_WREM3 - -// Protocols Names -#ifndef D_STR_AIRTON -#define D_STR_AIRTON "AIRTON" -#endif // D_STR_AIRTON -#ifndef D_STR_AIRWELL -#define D_STR_AIRWELL "AIRWELL" -#endif // D_STR_AIRWELL -#ifndef D_STR_AIWA_RC_T501 -#define D_STR_AIWA_RC_T501 "AIWA_RC_T501" -#endif // D_STR_AIWA_RC_T501 -#ifndef D_STR_AMCOR -#define D_STR_AMCOR "AMCOR" -#endif // D_STR_AMCOR -#ifndef D_STR_ARGO -#define D_STR_ARGO "ARGO" -#endif // D_STR_ARGO -#ifndef D_STR_ARRIS -#define D_STR_ARRIS "ARRIS" -#endif // D_STR_ARRIS -#ifndef D_STR_BOSCH -#define D_STR_BOSCH "BOSCH" -#endif // D_STR_BOSCH -#ifndef D_STR_BOSCH144 -#define D_STR_BOSCH144 D_STR_BOSCH "144" -#endif // D_STR_BOSCH144 -#ifndef D_STR_BOSE -#define D_STR_BOSE "BOSE" -#endif // D_STR_BOSE -#ifndef D_STR_CARRIER_AC -#define D_STR_CARRIER_AC "CARRIER_AC" -#endif // D_STR_CARRIER_AC -#ifndef D_STR_CARRIER_AC40 -#define D_STR_CARRIER_AC40 D_STR_CARRIER_AC "40" -#endif // D_STR_CARRIER_AC40 -#ifndef D_STR_CARRIER_AC64 -#define D_STR_CARRIER_AC64 D_STR_CARRIER_AC "64" -#endif // D_STR_CARRIER_AC64 -#ifndef D_STR_CARRIER_AC84 -#define D_STR_CARRIER_AC84 D_STR_CARRIER_AC "84" -#endif // D_STR_CARRIER_AC84 -#ifndef D_STR_CARRIER_AC128 -#define D_STR_CARRIER_AC128 D_STR_CARRIER_AC "128" -#endif // D_STR_CARRIER_AC128 -#ifndef D_STR_CLIMABUTLER -#define D_STR_CLIMABUTLER "CLIMABUTLER" -#endif // D_STR_CLIMABUTLER -#ifndef D_STR_COOLIX -#define D_STR_COOLIX "COOLIX" -#endif // D_STR_COOLIX -#ifndef D_STR_COOLIX48 -#define D_STR_COOLIX48 D_STR_COOLIX "48" -#endif // D_STR_COOLIX48 -#ifndef D_STR_CORONA_AC -#define D_STR_CORONA_AC "CORONA_AC" -#endif // D_STR_CORONA_AC -#ifndef D_STR_DAIKIN -#define D_STR_DAIKIN "DAIKIN" -#endif // D_STR_DAIKIN -#ifndef D_STR_DAIKIN128 -#define D_STR_DAIKIN128 D_STR_DAIKIN "128" -#endif // D_STR_DAIKIN128 -#ifndef D_STR_DAIKIN152 -#define D_STR_DAIKIN152 D_STR_DAIKIN "152" -#endif // D_STR_DAIKIN152 -#ifndef D_STR_DAIKIN160 -#define D_STR_DAIKIN160 D_STR_DAIKIN "160" -#endif // D_STR_DAIKIN160 -#ifndef D_STR_DAIKIN176 -#define D_STR_DAIKIN176 D_STR_DAIKIN "176" -#endif // D_STR_DAIKIN176 -#ifndef D_STR_DAIKIN2 -#define D_STR_DAIKIN2 D_STR_DAIKIN "2" -#endif // D_STR_DAIKIN2 -#ifndef D_STR_DAIKIN200 -#define D_STR_DAIKIN200 D_STR_DAIKIN "200" -#endif // D_STR_DAIKIN200 -#ifndef D_STR_DAIKIN216 -#define D_STR_DAIKIN216 D_STR_DAIKIN "216" -#endif // D_STR_DAIKIN216 -#ifndef D_STR_DAIKIN312 -#define D_STR_DAIKIN312 D_STR_DAIKIN "312" -#endif // D_STR_DAIKIN312 -#ifndef D_STR_DAIKIN64 -#define D_STR_DAIKIN64 D_STR_DAIKIN "64" -#endif // D_STR_DAIKIN64 -#ifndef D_STR_DELONGHI_AC -#define D_STR_DELONGHI_AC "DELONGHI_AC" -#endif // D_STR_DELONGHI_AC -#ifndef D_STR_DENON -#define D_STR_DENON "DENON" -#endif // D_STR_DENON -#ifndef D_STR_DISH -#define D_STR_DISH "DISH" -#endif // D_STR_DISH -#ifndef D_STR_DOSHISHA -#define D_STR_DOSHISHA "DOSHISHA" -#endif // D_STR_DOSHISHA -#ifndef D_STR_ECOCLIM -#define D_STR_ECOCLIM "ECOCLIM" -#endif // D_STR_ECOCLIM -#ifndef D_STR_ELECTRA_AC -#define D_STR_ELECTRA_AC "ELECTRA_AC" -#endif // D_STR_ELECTRA_AC -#ifndef D_STR_ELITESCREENS -#define D_STR_ELITESCREENS "ELITESCREENS" -#endif // D_STR_ELITESCREENS -#ifndef D_STR_EPSON -#define D_STR_EPSON "EPSON" -#endif // D_STR_EPSON -#ifndef D_STR_FUJITSU_AC -#define D_STR_FUJITSU_AC "FUJITSU_AC" -#endif // D_STR_FUJITSU_AC -#ifndef D_STR_FUJITSU_AC264 -#define D_STR_FUJITSU_AC264 "FUJITSU_AC264" -#endif // D_STR_FUJITSU_AC264 -#ifndef D_STR_GICABLE -#define D_STR_GICABLE "GICABLE" -#endif // D_STR_GICABLE -#ifndef D_STR_GLOBALCACHE -#define D_STR_GLOBALCACHE "GLOBALCACHE" -#endif // D_STR_GLOBALCACHE -#ifndef D_STR_GOODWEATHER -#define D_STR_GOODWEATHER "GOODWEATHER" -#endif // D_STR_GOODWEATHER -#ifndef D_STR_GORENJE -#define D_STR_GORENJE "GORENJE" -#endif // D_STR_GORENJE -#ifndef D_STR_GREE -#define D_STR_GREE "GREE" -#endif // D_STR_GREE -#ifndef D_STR_HAIER_AC -#define D_STR_HAIER_AC "HAIER_AC" -#endif // D_STR_HAIER_AC -#ifndef D_STR_HAIER_AC_YRW02 -#define D_STR_HAIER_AC_YRW02 D_STR_HAIER_AC "_YRW02" -#endif // D_STR_HAIER_AC_YRW02 -#ifndef D_STR_HAIER_AC160 -#define D_STR_HAIER_AC160 D_STR_HAIER_AC "160" -#endif // D_STR_HAIER_AC160 -#ifndef D_STR_HAIER_AC176 -#define D_STR_HAIER_AC176 D_STR_HAIER_AC "176" -#endif // D_STR_HAIER_AC176 -#ifndef D_STR_HITACHI_AC -#define D_STR_HITACHI_AC "HITACHI_AC" -#endif // D_STR_HITACHI_AC -#ifndef D_STR_HITACHI_AC1 -#define D_STR_HITACHI_AC1 D_STR_HITACHI_AC "1" -#endif // D_STR_HITACHI_AC1 -#ifndef D_STR_HITACHI_AC2 -#define D_STR_HITACHI_AC2 D_STR_HITACHI_AC "2" -#endif // D_STR_HITACHI_AC2 -#ifndef D_STR_HITACHI_AC3 -#define D_STR_HITACHI_AC3 D_STR_HITACHI_AC "3" -#endif // D_STR_HITACHI_AC3 -#ifndef D_STR_HITACHI_AC264 -#define D_STR_HITACHI_AC264 D_STR_HITACHI_AC "264" -#endif // D_STR_HITACHI_AC264 -#ifndef D_STR_HITACHI_AC296 -#define D_STR_HITACHI_AC296 D_STR_HITACHI_AC "296" -#endif // D_STR_HITACHI_AC296 -#ifndef D_STR_HITACHI_AC344 -#define D_STR_HITACHI_AC344 D_STR_HITACHI_AC "344" -#endif // D_STR_HITACHI_AC344 -#ifndef D_STR_HITACHI_AC424 -#define D_STR_HITACHI_AC424 D_STR_HITACHI_AC "424" -#endif // D_STR_HITACHI_AC424 -#ifndef D_STR_INAX -#define D_STR_INAX "INAX" -#endif // D_STR_INAX -#ifndef D_STR_JVC -#define D_STR_JVC "JVC" -#endif // D_STR_JVC -#ifndef D_STR_KELON -#define D_STR_KELON "KELON" -#endif // D_STR_KELON -#ifndef D_STR_KELON168 -#define D_STR_KELON168 D_STR_KELON "168" -#endif // D_STR_KELON168 -#ifndef D_STR_KELVINATOR -#define D_STR_KELVINATOR "KELVINATOR" -#endif // D_STR_KELVINATOR -#ifndef D_STR_LASERTAG -#define D_STR_LASERTAG "LASERTAG" -#endif // D_STR_LASERTAG -#ifndef D_STR_LEGOPF -#define D_STR_LEGOPF "LEGOPF" -#endif // D_STR_LEGOPF -#ifndef D_STR_LG -#define D_STR_LG "LG" -#endif // D_STR_LG -#ifndef D_STR_LG2 -#define D_STR_LG2 "LG2" -#endif // D_STR_LG2 -#ifndef D_STR_LUTRON -#define D_STR_LUTRON "LUTRON" -#endif // D_STR_LUTRON -#ifndef D_STR_MAGIQUEST -#define D_STR_MAGIQUEST "MAGIQUEST" -#endif // D_STR_MAGIQUEST -#ifndef D_STR_METZ -#define D_STR_METZ "METZ" -#endif // D_STR_METZ -#ifndef D_STR_MIDEA -#define D_STR_MIDEA "MIDEA" -#endif // D_STR_MIDEA -#ifndef D_STR_MIDEA24 -#define D_STR_MIDEA24 "MIDEA24" -#endif // D_STR_MIDEA24 -#ifndef D_STR_MILESTAG2 -#define D_STR_MILESTAG2 "MILESTAG2" -#endif // D_STR_MILESTAG2 -#ifndef D_STR_MIRAGE -#define D_STR_MIRAGE "MIRAGE" -#endif // D_STR_MIRAGE -#ifndef D_STR_MITSUBISHI -#define D_STR_MITSUBISHI "MITSUBISHI" -#endif // D_STR_MITSUBISHI -#ifndef D_STR_MITSUBISHI112 -#define D_STR_MITSUBISHI112 "MITSUBISHI112" -#endif // D_STR_MITSUBISHI112 -#ifndef D_STR_MITSUBISHI136 -#define D_STR_MITSUBISHI136 "MITSUBISHI136" -#endif // D_STR_MITSUBISHI136 -#ifndef D_STR_MITSUBISHI2 -#define D_STR_MITSUBISHI2 "MITSUBISHI2" -#endif // D_STR_MITSUBISHI2 -#ifndef D_STR_MITSUBISHI_AC -#define D_STR_MITSUBISHI_AC "MITSUBISHI_AC" -#endif // D_STR_MITSUBISHI_AC -#ifndef D_STR_MITSUBISHI_HEAVY_152 -#define D_STR_MITSUBISHI_HEAVY_152 "MITSUBISHI_HEAVY_152" -#endif // D_STR_MITSUBISHI_HEAVY_152 -#ifndef D_STR_MITSUBISHI_HEAVY_88 -#define D_STR_MITSUBISHI_HEAVY_88 "MITSUBISHI_HEAVY_88" -#endif // D_STR_MITSUBISHI_HEAVY_88 -#ifndef D_STR_MULTIBRACKETS -#define D_STR_MULTIBRACKETS "MULTIBRACKETS" -#endif // D_STR_MULTIBRACKETS -#ifndef D_STR_MWM -#define D_STR_MWM "MWM" -#endif // D_STR_MWM -#ifndef D_STR_NEC -#define D_STR_NEC "NEC" -#endif // D_STR_NEC -#ifndef D_STR_NEC_LIKE -#define D_STR_NEC_LIKE D_STR_NEC "_LIKE" -#endif // D_STR_NEC_LIKE -#ifndef D_STR_NEC_NON_STRICT -#define D_STR_NEC_NON_STRICT D_STR_NEC " (NON-STRICT)" -#endif // D_STR_NEC_NON_STRICT -#ifndef D_STR_NEOCLIMA -#define D_STR_NEOCLIMA "NEOCLIMA" -#endif // D_STR_NEOCLIMA -#ifndef D_STR_NIKAI -#define D_STR_NIKAI "NIKAI" -#endif // D_STR_NIKAI -#ifndef D_STR_PANASONIC -#define D_STR_PANASONIC "PANASONIC" -#endif // D_STR_PANASONIC -#ifndef D_STR_PANASONIC_AC -#define D_STR_PANASONIC_AC "PANASONIC_AC" -#endif // D_STR_PANASONIC_AC -#ifndef D_STR_PANASONIC_AC32 -#define D_STR_PANASONIC_AC32 D_STR_PANASONIC_AC"32" -#endif // D_STR_PANASONIC_AC32 -#ifndef D_STR_PIONEER -#define D_STR_PIONEER "PIONEER" -#endif // D_STR_PIONEER -#ifndef D_STR_PRONTO -#define D_STR_PRONTO "PRONTO" -#endif // D_STR_PRONTO -#ifndef D_STR_RAW -#define D_STR_RAW "RAW" -#endif // D_STR_RAW -#ifndef D_STR_RC5 -#define D_STR_RC5 "RC5" -#endif // D_STR_RC5 -#ifndef D_STR_RC5X -#define D_STR_RC5X "RC5X" -#endif // D_STR_RC5X -#ifndef D_STR_RC6 -#define D_STR_RC6 "RC6" -#endif // D_STR_RC6 -#ifndef D_STR_RCMM -#define D_STR_RCMM "RCMM" -#endif // D_STR_RCMM -#ifndef D_STR_RHOSS -#define D_STR_RHOSS "RHOSS" -#endif // D_STR_RHOSS -#ifndef D_STR_SAMSUNG -#define D_STR_SAMSUNG "SAMSUNG" -#endif // D_STR_SAMSUNG -#ifndef D_STR_SAMSUNG36 -#define D_STR_SAMSUNG36 "SAMSUNG36" -#endif // D_STR_SAMSUNG36 -#ifndef D_STR_SAMSUNG_AC -#define D_STR_SAMSUNG_AC "SAMSUNG_AC" -#endif // D_STR_SAMSUNG_AC -#ifndef D_STR_SANYO -#define D_STR_SANYO "SANYO" -#endif // D_STR_SANYO -#ifndef D_STR_SANYO_AC -#define D_STR_SANYO_AC D_STR_SANYO "_AC" -#endif // D_STR_SANYO_AC -#ifndef D_STR_SANYO_AC88 -#define D_STR_SANYO_AC88 D_STR_SANYO_AC "88" -#endif // D_STR_SANYO_AC88 -#ifndef D_STR_SANYO_AC152 -#define D_STR_SANYO_AC152 D_STR_SANYO_AC "152" -#endif // D_STR_SANYO_AC152 -#ifndef D_STR_SANYO_LC7461 -#define D_STR_SANYO_LC7461 D_STR_SANYO "_LC7461" -#endif // D_STR_SANYO_LC7461 -#ifndef D_STR_SHARP -#define D_STR_SHARP "SHARP" -#endif // D_STR_SHARP -#ifndef D_STR_SHARP_AC -#define D_STR_SHARP_AC "SHARP_AC" -#endif // D_STR_SHARP_AC -#ifndef D_STR_SHERWOOD -#define D_STR_SHERWOOD "SHERWOOD" -#endif // D_STR_SHERWOOD -#ifndef D_STR_SONY -#define D_STR_SONY "SONY" -#endif // D_STR_SONY -#ifndef D_STR_SONY_38K -#define D_STR_SONY_38K "SONY_38K" -#endif // D_STR_SONY_38K -#ifndef D_STR_SYMPHONY -#define D_STR_SYMPHONY "SYMPHONY" -#endif // D_STR_SYMPHONY -#ifndef D_STR_TCL96AC -#define D_STR_TCL96AC "TCL96AC" -#endif // D_STR_TCL96AC -#ifndef D_STR_TCL112AC -#define D_STR_TCL112AC "TCL112AC" -#endif // D_STR_TCL112AC -#ifndef D_STR_TECHNIBEL_AC -#define D_STR_TECHNIBEL_AC "TECHNIBEL_AC" -#endif // D_STR_TECHNIBEL_AC -#ifndef D_STR_TECO -#define D_STR_TECO "TECO" -#endif // D_STR_TECO -#ifndef D_STR_TEKNOPOINT -#define D_STR_TEKNOPOINT "TEKNOPOINT" -#endif // D_STR_TEKNOPOINT -#ifndef D_STR_TOSHIBA_AC -#define D_STR_TOSHIBA_AC "TOSHIBA_AC" -#endif // D_STR_TOSHIBA_AC -#ifndef D_STR_TOTO -#define D_STR_TOTO "TOTO" -#endif // D_STR_TOTO -#ifndef D_STR_TRANSCOLD -#define D_STR_TRANSCOLD "TRANSCOLD" -#endif // D_STR_TRANSCOLD -#ifndef D_STR_TROTEC -#define D_STR_TROTEC "TROTEC" -#endif // D_STR_TROTEC -#ifndef D_STR_TROTEC_3550 -#define D_STR_TROTEC_3550 D_STR_TROTEC "_3550" -#endif // D_STR_TROTEC_3550 -#ifndef D_STR_TRUMA -#define D_STR_TRUMA "TRUMA" -#endif // D_STR_TRUMA -#ifndef D_STR_UNUSED -#define D_STR_UNUSED "UNUSED" -#endif // D_STR_UNUSED -#ifndef D_STR_VESTEL_AC -#define D_STR_VESTEL_AC "VESTEL_AC" -#endif // D_STR_VESTEL_AC -#ifndef D_STR_VOLTAS -#define D_STR_VOLTAS "VOLTAS" -#endif // D_STR_VOLTAS -#ifndef D_STR_WHIRLPOOL_AC -#define D_STR_WHIRLPOOL_AC "WHIRLPOOL_AC" -#endif // D_STR_WHIRLPOOL_AC -#ifndef D_STR_WHYNTER -#define D_STR_WHYNTER "WHYNTER" -#endif // D_STR_WHYNTER -#ifndef D_STR_WOWWEE -#define D_STR_WOWWEE "WOWWEE" -#endif // D_STR_WOWWEE -#ifndef D_STR_XMP -#define D_STR_XMP "XMP" -#endif // D_STR_XMP -#ifndef D_STR_YORK -#define D_STR_YORK "YORK" -#endif // D_STR_YORK -#ifndef D_STR_ZEPEAL -#define D_STR_ZEPEAL "ZEPEAL" -#endif // D_STR_ZEPEAL - -// IRrecvDumpV2+ -#ifndef D_STR_TIMESTAMP -#define D_STR_TIMESTAMP "Timestamp" -#endif // D_STR_TIMESTAMP -#ifndef D_STR_LIBRARY -#define D_STR_LIBRARY "Library" -#endif // D_STR_LIBRARY -#ifndef D_STR_MESGDESC -#define D_STR_MESGDESC "Mesg Desc." -#endif // D_STR_MESGDESC -#ifndef D_STR_TOLERANCE -#define D_STR_TOLERANCE "Tolerance" -#endif // D_STR_TOLERANCE -#ifndef D_STR_IRRECVDUMP_STARTUP -#define D_STR_IRRECVDUMP_STARTUP \ - "IRrecvDump is now running and waiting for IR input on Pin %d" -#endif // D_STR_IRRECVDUMP_STARTUP -#ifndef D_WARN_BUFFERFULL -#define D_WARN_BUFFERFULL \ - "WARNING: IR code is too big for buffer (>= %d). " \ - "This result shouldn't be trusted until this is resolved. " \ - "Edit & increase `kCaptureBufferSize`." -#endif // D_WARN_BUFFERFULL - -#endif // LOCALE_DEFAULTS_H_ +// Copyright 2019 - David Conran (@crankyoldgit) +// The default text to use throughout the library. +// The library will use this text if no locale (_IR_LOCALE_) is set or if +// the locale doesn't define particular values. +// If they are defined, this file should NOT override them. +// +// This file should contain a #define for every translateable/locale dependant +// string used by the library. Language specific files don't have to include +// everything. +// +// NOTE: ASCII/UTF-8 characters only. Unicode is NOT supported. +// +// The defaults are English (AU) / en-AU. Australia (AU) is pretty much the same +// as English (UK) for this libraries use case. +#ifndef LOCALE_DEFAULTS_H_ +#define LOCALE_DEFAULTS_H_ + +#ifndef D_STR_UNKNOWN +#define D_STR_UNKNOWN "UNKNOWN" +#endif // D_STR_UNKNOWN +#ifndef D_STR_PROTOCOL +#define D_STR_PROTOCOL "Protocol" +#endif // D_STR_PROTOCOL +#ifndef D_STR_POWER +#define D_STR_POWER "Power" +#endif // D_STR_POWER +#ifndef D_STR_PREVIOUS +#define D_STR_PREVIOUS "Previous" +#endif // D_STR_PREVIOUS +#ifndef D_STR_ON +#define D_STR_ON "On" +#endif // D_STR_ON +#ifndef D_STR_1 +#define D_STR_1 "1" +#endif // D_STR_1 +#ifndef D_STR_OFF +#define D_STR_OFF "Off" +#endif // D_STR_OFF +#ifndef D_STR_0 +#define D_STR_0 "0" +#endif // D_STR_0 +#ifndef D_STR_MODE +#define D_STR_MODE "Mode" +#endif // D_STR_MODE +#ifndef D_STR_TOGGLE +#define D_STR_TOGGLE "Toggle" +#endif // D_STR_TOGGLE +#ifndef D_STR_TURBO +#define D_STR_TURBO "Turbo" +#endif // D_STR_TURBO +#ifndef D_STR_SUPER +#define D_STR_SUPER "Super" +#endif // D_STR_SUPER +#ifndef D_STR_SLEEP +#define D_STR_SLEEP "Sleep" +#endif // D_STR_SLEEP +#ifndef D_STR_LIGHT +#define D_STR_LIGHT "Light" +#endif // D_STR_LIGHT +#ifndef D_STR_POWERFUL +#define D_STR_POWERFUL "Powerful" +#endif // D_STR_POWERFUL +#ifndef D_STR_QUIET +#define D_STR_QUIET "Quiet" +#endif // D_STR_QUIET +#ifndef D_STR_ECONO +#define D_STR_ECONO "Econo" +#endif // D_STR_ECONO +#ifndef D_STR_SWING +#define D_STR_SWING "Swing" +#endif // D_STR_SWING +#ifndef D_STR_SWINGH +#define D_STR_SWINGH D_STR_SWING"(H)" // Set `D_STR_SWING` first! +#endif // D_STR_SWINGH +#ifndef D_STR_SWINGV +#define D_STR_SWINGV D_STR_SWING"(V)" // Set `D_STR_SWING` first! +#endif // D_STR_SWINGV +#ifndef D_STR_BEEP +#define D_STR_BEEP "Beep" +#endif // D_STR_BEEP +#ifndef D_STR_MOULD +#define D_STR_MOULD "Mould" +#endif // D_STR_MOULD +#ifndef D_STR_CLEAN +#define D_STR_CLEAN "Clean" +#endif // D_STR_CLEAN +#ifndef D_STR_PURIFY +#define D_STR_PURIFY "Purify" +#endif // D_STR_PURIFY +#ifndef D_STR_TIMER +#define D_STR_TIMER "Timer" +#endif // D_STR_TIMER +#ifndef D_STR_ONTIMER +#define D_STR_ONTIMER D_STR_ON " " D_STR_TIMER // Set `D_STR_ON` first! +#endif // D_STR_ONTIMER +#ifndef D_STR_OFFTIMER +#define D_STR_OFFTIMER D_STR_OFF " " D_STR_TIMER // Set `D_STR_OFF` first! +#endif // D_STR_OFFTIMER +#ifndef D_STR_TIMERMODE +#define D_STR_TIMERMODE D_STR_TIMER " " D_STR_MODE // Set `D_STR_MODE` first! +#endif // D_STR_TIMERMODE +#ifndef D_STR_CLOCK +#define D_STR_CLOCK "Clock" +#endif // D_STR_CLOCK +#ifndef D_STR_COMMAND +#define D_STR_COMMAND "Command" +#endif // D_STR_COMMAND +#ifndef D_STR_XFAN +#define D_STR_XFAN "XFan" +#endif // D_STR_XFAN +#ifndef D_STR_HEALTH +#define D_STR_HEALTH "Health" +#endif // D_STR_HEALTH +#ifndef D_STR_MODEL +#define D_STR_MODEL "Model" +#endif // D_STR_MODEL +#ifndef D_STR_TEMP +#define D_STR_TEMP "Temp" +#endif // D_STR_TEMP +#ifndef D_STR_IFEEL +#define D_STR_IFEEL "IFeel" +#endif // D_STR_IFEEL +#ifndef D_STR_ISEE +#define D_STR_ISEE "ISee" +#endif // D_STR_ISEE +#ifndef D_STR_HUMID +#define D_STR_HUMID "Humid" +#endif // D_STR_HUMID +#ifndef D_STR_SAVE +#define D_STR_SAVE "Save" +#endif // D_STR_SAVE +#ifndef D_STR_EYE +#define D_STR_EYE "Eye" +#endif // D_STR_EYE +#ifndef D_STR_FOLLOW +#define D_STR_FOLLOW "Follow" +#endif // D_STR_FOLLOW +#ifndef D_STR_ION +#define D_STR_ION "Ion" +#endif // D_STR_ION +#ifndef D_STR_FRESH +#define D_STR_FRESH "Fresh" +#endif // D_STR_FRESH +#ifndef D_STR_HOLD +#define D_STR_HOLD "Hold" +#endif // D_STR_HOLD +#ifndef D_STR_8C_HEAT +#define D_STR_8C_HEAT "8C " D_STR_HEAT // Set `D_STR_HEAT` first! +#endif // D_STR_8C_HEAT +#ifndef D_STR_10C_HEAT +#define D_STR_10C_HEAT "10C " D_STR_HEAT // Set `D_STR_HEAT` first! +#endif // D_STR_10C_HEAT +#ifndef D_STR_BUTTON +#define D_STR_BUTTON "Button" +#endif // D_STR_BUTTON +#ifndef D_STR_NIGHT +#define D_STR_NIGHT "Night" +#endif // D_STR_NIGHT +#ifndef D_STR_SILENT +#define D_STR_SILENT "Silent" +#endif // D_STR_SILENT +#ifndef D_STR_FILTER +#define D_STR_FILTER "Filter" +#endif // D_STR_FILTER +#ifndef D_STR_3D +#define D_STR_3D "3D" +#endif // D_STR_3D +#ifndef D_STR_CELSIUS +#define D_STR_CELSIUS "Celsius" +#endif // D_STR_CELSIUS +#ifndef D_STR_FAHRENHEIT +#define D_STR_FAHRENHEIT "Fahrenheit" +#endif // D_STR_FAHRENHEIT +#ifndef D_STR_CELSIUS_FAHRENHEIT +#define D_STR_CELSIUS_FAHRENHEIT D_STR_CELSIUS "/" D_STR_FAHRENHEIT +#endif // D_STR_CELSIUS_FAHRENHEIT +#ifndef D_STR_UP +#define D_STR_UP "Up" +#endif // D_STR_UP +#ifndef D_STR_TEMPUP +#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP // Set `D_STR_TEMP` first! +#endif // D_STR_TEMPUP +#ifndef D_STR_DOWN +#define D_STR_DOWN "Down" +#endif // D_STR_DOWN +#ifndef D_STR_TEMPDOWN +#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN // Set `D_STR_TEMP` first! +#endif // D_STR_TEMPDOWN +#ifndef D_STR_CHANGE +#define D_STR_CHANGE "Change" +#endif // D_STR_CHANGE +#ifndef D_STR_START +#define D_STR_START "Start" +#endif // D_STR_START +#ifndef D_STR_STOP +#define D_STR_STOP "Stop" +#endif // D_STR_STOP +#ifndef D_STR_MOVE +#define D_STR_MOVE "Move" +#endif // D_STR_MOVE +#ifndef D_STR_SET +#define D_STR_SET "Set" +#endif // D_STR_SET +#ifndef D_STR_CANCEL +#define D_STR_CANCEL "Cancel" +#endif // D_STR_CANCEL +#ifndef D_STR_COMFORT +#define D_STR_COMFORT "Comfort" +#endif // D_STR_COMFORT +#ifndef D_STR_SENSOR +#define D_STR_SENSOR "Sensor" +#endif // D_STR_SENSOR +#ifndef D_STR_ABSENSEDETECT +#define D_STR_ABSENSEDETECT "Absense detect" +#endif // D_STR_ABSENSEDETECT +#ifndef D_STR_DIRECT +#define D_STR_DIRECT "Direct" +#endif // D_STR_DIRECT +#ifndef D_STR_INDIRECT +#define D_STR_INDIRECT "Indirect" +#endif // D_STR_INDIRECT +#ifndef D_STR_DIRECTINDIRECTMODE +#define D_STR_DIRECTINDIRECTMODE D_STR_DIRECT " / " \ +D_STR_INDIRECT " " D_STR_MODE +#endif // D_STR_DIRECTINDIRECTMODE +#ifndef D_STR_DISPLAY +#define D_STR_DISPLAY "Display" +#endif // D_STR_DISPLAY +#ifndef D_STR_WEEKLY +#define D_STR_WEEKLY "Weekly" +#endif // D_STR_WEEKLY +#ifndef D_STR_WEEKLYTIMER +#define D_STR_WEEKLYTIMER D_STR_WEEKLY " " D_STR_TIMER // Needs `D_STR_WEEKLY`! +#endif // D_STR_WEEKLYTIMER +#ifndef D_STR_WIFI +#define D_STR_WIFI "WiFi" +#endif // D_STR_WIFI +#ifndef D_STR_LAST +#define D_STR_LAST "Last" +#endif // D_STR_LAST +#ifndef D_STR_FAST +#define D_STR_FAST "Fast" +#endif // D_STR_FAST +#ifndef D_STR_SLOW +#define D_STR_SLOW "Slow" +#endif // D_STR_SLOW +#ifndef D_STR_AIRFLOW +#define D_STR_AIRFLOW "Air Flow" +#endif // D_STR_AIRFLOW +#ifndef D_STR_STEP +#define D_STR_STEP "Step" +#endif // D_STR_STEP +#ifndef D_STR_NA +#define D_STR_NA "N/A" +#endif // D_STR_NA +#ifndef D_STR_INSIDE +#define D_STR_INSIDE "Inside" +#endif // D_STR_INSIDE +#ifndef D_STR_OUTSIDE +#define D_STR_OUTSIDE "Outside" +#endif // D_STR_OUTSIDE +#ifndef D_STR_LOUD +#define D_STR_LOUD "Loud" +#endif // D_STR_LOUD +#ifndef D_STR_UPPER +#define D_STR_UPPER "Upper" +#endif // D_STR_UPPER +#ifndef D_STR_LOWER +#define D_STR_LOWER "Lower" +#endif // D_STR_LOWER +#ifndef D_STR_BREEZE +#define D_STR_BREEZE "Breeze" +#endif // D_STR_BREEZE +#ifndef D_STR_CIRCULATE +#define D_STR_CIRCULATE "Circulate" +#endif // D_STR_CIRCULATE +#ifndef D_STR_CEILING +#define D_STR_CEILING "Ceiling" +#endif // D_STR_CEILING +#ifndef D_STR_WALL +#define D_STR_WALL "Wall" +#endif // D_STR_WALL +#ifndef D_STR_ROOM +#define D_STR_ROOM "Room" +#endif // D_STR_ROOM +#ifndef D_STR_6THSENSE +#define D_STR_6THSENSE "6th Sense" +#endif // D_STR_6THSENSE +#ifndef D_STR_ZONEFOLLOW +#define D_STR_ZONEFOLLOW "Zone Follow" +#endif // D_STR_ZONEFOLLOW +#ifndef D_STR_FIXED +#define D_STR_FIXED "Fixed" +#endif // D_STR_FIXED +#ifndef D_STR_TYPE +#define D_STR_TYPE "Type" +#endif // D_STR_TYPE +#ifndef D_STR_SPECIAL +#define D_STR_SPECIAL "Special" +#endif // D_STR_SPECIAL +#ifndef D_STR_RECYCLE +#define D_STR_RECYCLE "Recycle" +#endif // D_STR_RECYCLE +#ifndef D_STR_ID +#define D_STR_ID "Id" +#endif // D_STR_ID +#ifndef D_STR_VANE +#define D_STR_VANE "Vane" +#endif // D_STR_VANE +#ifndef D_STR_LOCK +#define D_STR_LOCK "Lock" +#endif // D_STR_LOCK +#ifndef D_STR_REPORT +#define D_STR_REPORT "Report" +#endif // D_STR_REPORT + +#ifndef D_STR_AUTO +#define D_STR_AUTO "Auto" +#endif // D_STR_AUTO +#ifndef D_STR_AUTOMATIC +#define D_STR_AUTOMATIC "Automatic" +#endif // D_STR_AUTOMATIC +#ifndef D_STR_MANUAL +#define D_STR_MANUAL "Manual" +#endif // D_STR_MANUAL +#ifndef D_STR_COOL +#define D_STR_COOL "Cool" +#endif // D_STR_COOL +#ifndef D_STR_COOLING +#define D_STR_COOLING "Cooling" +#endif // D_STR_COOLING +#ifndef D_STR_HEAT +#define D_STR_HEAT "Heat" +#endif // D_STR_HEAT +#ifndef D_STR_HEATING +#define D_STR_HEATING "Heating" +#endif // D_STR_HEATING +#ifndef D_STR_FAN +#define D_STR_FAN "Fan" +#endif // D_STR_FAN +#ifndef D_STR_FANONLY +#define D_STR_FANONLY "fan-only" +#endif // D_STR_FANONLY +#ifndef D_STR_FAN_ONLY +#define D_STR_FAN_ONLY "fan_only" +#endif // D_STR_FAN_ONLY +#ifndef D_STR_ONLY +#define D_STR_ONLY "Only" +#endif // D_STR_ONLY +#ifndef D_STR_FANSPACEONLY +#define D_STR_FANSPACEONLY D_STR_FAN " " D_STR_ONLY +#endif // D_STR_FANSPACEONLY +#ifndef D_STR_FANONLYNOSPACE +#define D_STR_FANONLYNOSPACE D_STR_FAN D_STR_ONLY +#endif // D_STR_FANONLYNOSPACE +#ifndef D_STR_DRY +#define D_STR_DRY "Dry" +#endif // D_STR_DRY +#ifndef D_STR_DRYING +#define D_STR_DRYING "Drying" +#endif // D_STR_DRYING +#ifndef D_STR_DEHUMIDIFY +#define D_STR_DEHUMIDIFY "Dehumidify" +#endif // D_STR_DEHUMIDIFY + +#ifndef D_STR_MAX +#define D_STR_MAX "Max" +#endif // D_STR_MAX +#ifndef D_STR_MAXIMUM +#define D_STR_MAXIMUM "Maximum" +#endif // D_STR_MAXIMUM +#ifndef D_STR_MIN +#define D_STR_MIN "Min" +#endif // D_STR_MIN +#ifndef D_STR_MINIMUM +#define D_STR_MINIMUM "Minimum" +#endif // D_STR_MINIMUM +#ifndef D_STR_MED +#define D_STR_MED "Med" +#endif // D_STR_MED +#ifndef D_STR_MEDIUM +#define D_STR_MEDIUM "Medium" +#endif // D_STR_MEDIUM +#ifndef D_STR_MED_HIGH +#define D_STR_MED_HIGH D_STR_MED "-" D_STR_HIGH +#endif // D_STR_MED_HIGH + +#ifndef D_STR_HIGHEST +#define D_STR_HIGHEST "Highest" +#endif // D_STR_HIGHEST +#ifndef D_STR_HIGH +#define D_STR_HIGH "High" +#endif // D_STR_HIGH +#ifndef D_STR_HI +#define D_STR_HI "Hi" +#endif // D_STR_HI +#ifndef D_STR_MID +#define D_STR_MID "Mid" +#endif // D_STR_MID +#ifndef D_STR_MIDDLE +#define D_STR_MIDDLE "Middle" +#endif // D_STR_MIDDLE +#ifndef D_STR_LOW +#define D_STR_LOW "Low" +#endif // D_STR_LOW +#ifndef D_STR_LO +#define D_STR_LO "Lo" +#endif // D_STR_LO +#ifndef D_STR_LOWEST +#define D_STR_LOWEST "Lowest" +#endif // D_STR_LOWEST +#ifndef D_STR_RIGHT +#define D_STR_RIGHT "Right" +#endif // D_STR_RIGHT +#ifndef D_STR_MAXRIGHT +#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` first! +#endif // D_STR_MAXRIGHT +#ifndef D_STR_MAXRIGHT_NOSPACE +#define D_STR_MAXRIGHT_NOSPACE D_STR_MAX D_STR_RIGHT // Set `D_STR_MAX` first! +#endif // D_STR_MAXRIGHT_NOSPACE +#ifndef D_STR_RIGHTMAX +#define D_STR_RIGHTMAX D_STR_RIGHT " " D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_RIGHTMAX +#ifndef D_STR_RIGHTMAX_NOSPACE +#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_RIGHTMAX_NOSPACE +#ifndef D_STR_LEFT +#define D_STR_LEFT "Left" +#endif // D_STR_LEFT +#ifndef D_STR_MAXLEFT +#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` first! +#endif // D_STR_MAXLEFT +#ifndef D_STR_MAXLEFT_NOSPACE +#define D_STR_MAXLEFT_NOSPACE D_STR_MAX D_STR_LEFT // Set `D_STR_MAX` first! +#endif // D_STR_MAXLEFT_NOSPACE +#ifndef D_STR_LEFTMAX +#define D_STR_LEFTMAX D_STR_LEFT " " D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_LEFTMAX +#ifndef D_STR_LEFTMAX_NOSPACE +#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` first! +#endif // D_STR_LEFTMAX_NOSPACE +#ifndef D_STR_WIDE +#define D_STR_WIDE "Wide" +#endif // D_STR_WIDE +#ifndef D_STR_CENTRE +#define D_STR_CENTRE "Centre" +#endif // D_STR_CENTRE +#ifndef D_STR_TOP +#define D_STR_TOP "Top" +#endif // D_STR_TOP +#ifndef D_STR_BOTTOM +#define D_STR_BOTTOM "Bottom" +#endif // D_STR_BOTTOM +#ifndef D_STR_UPPER_MIDDLE +#define D_STR_UPPER_MIDDLE D_STR_UPPER "-" D_STR_MIDDLE +#endif // D_STR_UPPER_MIDDLE +#ifndef D_STR_CONFIG +#define D_STR_CONFIG "Config" +#endif // D_STR_CONFIG +#ifndef D_STR_CONTROL +#define D_STR_CONTROL "Control" +#endif // D_STR_CONTROL +#ifndef D_STR_SET_TIMER +#define D_STR_SET_TIMER D_STR_SET " " D_STR_TIMER +#endif // D_STR_AC_TIMER +#ifndef D_STR_SCHEDULE +#define D_STR_SCHEDULE "Schedule" +#endif // D_STR_SCHEDULE +#ifndef D_STR_CH +#define D_STR_CH "CH#" +#endif // D_STR_CH +#ifndef D_STR_TIMER_ACTIVE_DAYS +#define D_STR_TIMER_ACTIVE_DAYS "TimerActiveDays" +#endif // D_STR_TIMER_ACTIVE_DAYS +#ifndef D_STR_KEY +#define D_STR_KEY "Key" +#endif // D_STR_KEY +#ifndef D_STR_VALUE +#define D_STR_VALUE "Value" +#endif // D_STR_VALUE + +// Compound words/phrases/descriptions from pre-defined words. +// Note: Obviously these need to be defined *after* their component words. +#ifndef D_STR_ECONOTOGGLE +#define D_STR_ECONOTOGGLE D_STR_ECONO " " D_STR_TOGGLE +#endif // D_STR_ECONOTOGGLE +#ifndef D_STR_EYEAUTO +#define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO +#endif // D_STR_EYEAUTO +#ifndef D_STR_LIGHTTOGGLE +#define D_STR_LIGHTTOGGLE D_STR_LIGHT " " D_STR_TOGGLE +#endif // D_STR_LIGHTTOGGLE +#ifndef D_STR_OUTSIDEQUIET +#define D_STR_OUTSIDEQUIET D_STR_OUTSIDE " " D_STR_QUIET +#endif // D_STR_OUTSIDEQUIET +#ifndef D_STR_POWERTOGGLE +#define D_STR_POWERTOGGLE D_STR_POWER " " D_STR_TOGGLE +#endif // D_STR_POWERTOGGLE +#ifndef D_STR_POWERBUTTON +#define D_STR_POWERBUTTON D_STR_POWER " " D_STR_BUTTON +#endif // D_STR_POWERBUTTON +#ifndef D_STR_PREVIOUSPOWER +#define D_STR_PREVIOUSPOWER D_STR_PREVIOUS " " D_STR_POWER +#endif // D_STR_PREVIOUSPOWER +#ifndef D_STR_DISPLAYTEMP +#define D_STR_DISPLAYTEMP D_STR_DISPLAY " " D_STR_TEMP +#endif // D_STR_DISPLAYTEMP +#ifndef D_STR_IFEELREPORT +#define D_STR_IFEELREPORT D_STR_IFEEL " " D_STR_REPORT +#endif // D_STR_IFEELREPORT +#ifndef D_STR_SENSORTEMP +#define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP +#endif // D_STR_SENSORTEMP +#ifndef D_STR_SLEEP_TIMER +#define D_STR_SLEEP_TIMER D_STR_SLEEP " " D_STR_TIMER +#endif // D_STR_SLEEP_TIMER +#ifndef D_STR_SWINGVMODE +#define D_STR_SWINGVMODE D_STR_SWINGV " " D_STR_MODE +#endif // D_STR_SWINGVMODE +#ifndef D_STR_SWINGVTOGGLE +#define D_STR_SWINGVTOGGLE D_STR_SWINGV " " D_STR_TOGGLE +#endif // D_STR_SWINGVTOGGLE +#ifndef D_STR_TURBOTOGGLE +#define D_STR_TURBOTOGGLE D_STR_TURBO " " D_STR_TOGGLE +#endif // D_STR_TURBOTOGGLE + +// Separators +#ifndef D_CHR_TIME_SEP +#define D_CHR_TIME_SEP ':' +#endif // D_CHR_TIME_SEP +#ifndef D_STR_SPACELBRACE +#define D_STR_SPACELBRACE " (" +#endif // D_STR_SPACELBRACE +#ifndef D_STR_COMMASPACE +#define D_STR_COMMASPACE ", " +#endif // D_STR_COMMASPACE +#ifndef D_STR_COLONSPACE +#define D_STR_COLONSPACE ": " +#endif // D_STR_COLONSPACE +#ifndef D_STR_DASH +#define D_STR_DASH "-" +#endif // D_STR_DASH + +#ifndef D_STR_DAY +#define D_STR_DAY "Day" +#endif // D_STR_DAY +#ifndef D_STR_DAYS +#define D_STR_DAYS D_STR_DAY "s" +#endif // D_STR_DAYS +#ifndef D_STR_HOUR +#define D_STR_HOUR "Hour" +#endif // D_STR_HOUR +#ifndef D_STR_HOURS +#define D_STR_HOURS D_STR_HOUR "s" +#endif // D_STR_HOURS +#ifndef D_STR_MINUTE +#define D_STR_MINUTE "Minute" +#endif // D_STR_MINUTE +#ifndef D_STR_MINUTES +#define D_STR_MINUTES D_STR_MINUTE "s" +#endif // D_STR_MINUTES +#ifndef D_STR_SECOND +#define D_STR_SECOND "Second" +#endif // D_STR_SECOND +#ifndef D_STR_SECONDS +#define D_STR_SECONDS D_STR_SECOND "s" +#endif // D_STR_SECONDS +#ifndef D_STR_NOW +#define D_STR_NOW "Now" +#endif // D_STR_NOW +#ifndef D_STR_THREELETTERDAYS +#define D_STR_THREELETTERDAYS "SunMonTueWedThuFriSat" +#endif // D_STR_THREELETTERDAYS + +#ifndef D_STR_YES +#define D_STR_YES "Yes" +#endif // D_STR_YES +#ifndef D_STR_NO +#define D_STR_NO "No" +#endif // D_STR_NO +#ifndef D_STR_TRUE +#define D_STR_TRUE "True" +#endif // D_STR_TRUE +#ifndef D_STR_FALSE +#define D_STR_FALSE "False" +#endif // D_STR_FALSE + +#ifndef D_STR_REPEAT +#define D_STR_REPEAT "Repeat" +#endif // D_STR_REPEAT +#ifndef D_STR_CODE +#define D_STR_CODE "Code" +#endif // D_STR_CODE +#ifndef D_STR_BITS +#define D_STR_BITS "Bits" +#endif // D_STR_BITS + +// Model Names +#ifndef D_STR_YAW1F +#define D_STR_YAW1F "YAW1F" +#endif // D_STR_YAW1F +#ifndef D_STR_YBOFB +#define D_STR_YBOFB "YBOFB" +#endif // D_STR_YBOFB +#ifndef D_STR_YX1FSF +#define D_STR_YX1FSF "YX1FSF" +#endif // D_STR_YX1FSF +#ifndef D_STR_V9014557_A +#define D_STR_V9014557_A "V9014557-A" +#endif // D_STR_V9014557_A +#ifndef D_STR_V9014557_B +#define D_STR_V9014557_B "V9014557-B" +#endif // D_STR_V9014557_B +#ifndef D_STR_RLT0541HTA_A +#define D_STR_RLT0541HTA_A "R-LT0541-HTA-A" +#endif // D_STR_RLT0541HTA_A +#ifndef D_STR_RLT0541HTA_B +#define D_STR_RLT0541HTA_B "R-LT0541-HTA-B" +#endif // D_STR_RLT0541HTA_B +#ifndef D_STR_ARRAH2E +#define D_STR_ARRAH2E "ARRAH2E" +#endif // D_STR_ARRAH2E +#ifndef D_STR_ARDB1 +#define D_STR_ARDB1 "ARDB1" +#endif // D_STR_ARDB1 +#ifndef D_STR_ARREB1E +#define D_STR_ARREB1E "ARREB1E" +#endif // D_STR_ARREB1E +#ifndef D_STR_ARJW2 +#define D_STR_ARJW2 "ARJW2" +#endif // D_STR_ARJW2 +#ifndef D_STR_ARRY4 +#define D_STR_ARRY4 "ARRY4" +#endif // D_STR_ARRY4 +#ifndef D_STR_ARREW4E +#define D_STR_ARREW4E "ARREW4E" +#endif // D_STR_ARREW4E +#ifndef D_STR_GE6711AR2853M +#define D_STR_GE6711AR2853M "GE6711AR2853M" +#endif // D_STR_GE6711AR2853M +#ifndef D_STR_AKB75215403 +#define D_STR_AKB75215403 "AKB75215403" +#endif // D_STR_AKB75215403 +#ifndef D_STR_AKB74955603 +#define D_STR_AKB74955603 "AKB74955603" +#endif // D_STR_AKB74955603 +#ifndef D_STR_AKB73757604 +#define D_STR_AKB73757604 "AKB73757604" +#endif // D_STR_AKB73757604 +#ifndef D_STR_LG6711A20083V +#define D_STR_LG6711A20083V "LG6711A20083V" +#endif // D_STR_LG6711A20083V +#ifndef D_STR_KKG9AC1 +#define D_STR_KKG9AC1 "KKG9AC1" +#endif // D_STR_KKG9AC1 +#ifndef D_STR_KKG29AC1 +#define D_STR_KKG29AC1 "KKG29AC1" +#endif // D_STR_KKG9AC1 +#ifndef D_STR_LKE +#define D_STR_LKE "LKE" +#endif // D_STR_LKE +#ifndef D_STR_NKE +#define D_STR_NKE "NKE" +#endif // D_STR_NKE +#ifndef D_STR_DKE +#define D_STR_DKE "DKE" +#endif // D_STR_DKE +#ifndef D_STR_PKR +#define D_STR_PKR "PKR" +#endif // D_STR_PKR +#ifndef D_STR_JKE +#define D_STR_JKE "JKE" +#endif // D_STR_JKE +#ifndef D_STR_CKP +#define D_STR_CKP "CKP" +#endif // D_STR_CKP +#ifndef D_STR_RKR +#define D_STR_RKR "RKR" +#endif // D_STR_RKR +#ifndef D_STR_PANASONICLKE +#define D_STR_PANASONICLKE "PANASONICLKE" +#endif // D_STR_PANASONICLKE +#ifndef D_STR_PANASONICNKE +#define D_STR_PANASONICNKE "PANASONICNKE" +#endif // D_STR_PANASONICNKE +#ifndef D_STR_PANASONICDKE +#define D_STR_PANASONICDKE "PANASONICDKE" +#endif // D_STR_PANASONICDKE +#ifndef D_STR_PANASONICPKR +#define D_STR_PANASONICPKR "PANASONICPKR" +#endif // D_STR_PANASONICPKR +#ifndef D_STR_PANASONICJKE +#define D_STR_PANASONICJKE "PANASONICJKE" +#endif // D_STR_PANASONICJKE +#ifndef D_STR_PANASONICCKP +#define D_STR_PANASONICCKP "PANASONICCKP" +#endif // D_STR_PANASONICCKP +#ifndef D_STR_PANASONICRKR +#define D_STR_PANASONICRKR "PANASONICRKR" +#endif // D_STR_PANASONICRKR +#ifndef D_STR_A907 +#define D_STR_A907 "A907" +#endif // D_STR_A907 +#ifndef D_STR_A705 +#define D_STR_A705 "A705" +#endif // D_STR_A705 +#ifndef D_STR_A903 +#define D_STR_A903 "A903" +#endif // D_STR_A903 +#ifndef D_STR_TAC09CHSD +#define D_STR_TAC09CHSD "TAC09CHSD" +#endif // D_STR_TAC09CHSD +#ifndef D_STR_GZ055BE1 +#define D_STR_GZ055BE1 "GZ055BE1" +#endif // D_STR_GZ055BE1 +#ifndef D_STR_122LZF +#define D_STR_122LZF "122LZF" +#endif // D_STR_122LZF +#ifndef D_STR_DG11J13A +#define D_STR_DG11J13A "DG11J13A" +#endif // D_STR_DG11J13A +#ifndef D_STR_DG11J104 +#define D_STR_DG11J104 "DG11J104" +#endif // D_STR_DG11J104 +#ifndef D_STR_DG11J191 +#define D_STR_DG11J191 "DG11J191" +#endif // D_STR_DG11J191 +#ifndef D_STR_ARGO_WREM2 +#define D_STR_ARGO_WREM2 "WREM2" +#endif // D_STR_ARGO_WREM2 +#ifndef D_STR_ARGO_WREM3 +#define D_STR_ARGO_WREM3 "WREM3" +#endif // D_STR_ARGO_WREM3 + +// Protocols Names +#ifndef D_STR_AIRTON +#define D_STR_AIRTON "AIRTON" +#endif // D_STR_AIRTON +#ifndef D_STR_AIRWELL +#define D_STR_AIRWELL "AIRWELL" +#endif // D_STR_AIRWELL +#ifndef D_STR_AIWA_RC_T501 +#define D_STR_AIWA_RC_T501 "AIWA_RC_T501" +#endif // D_STR_AIWA_RC_T501 +#ifndef D_STR_AMCOR +#define D_STR_AMCOR "AMCOR" +#endif // D_STR_AMCOR +#ifndef D_STR_ARGO +#define D_STR_ARGO "ARGO" +#endif // D_STR_ARGO +#ifndef D_STR_ARRIS +#define D_STR_ARRIS "ARRIS" +#endif // D_STR_ARRIS +#ifndef D_STR_BOSCH +#define D_STR_BOSCH "BOSCH" +#endif // D_STR_BOSCH +#ifndef D_STR_BOSCH144 +#define D_STR_BOSCH144 D_STR_BOSCH "144" +#endif // D_STR_BOSCH144 +#ifndef D_STR_BOSE +#define D_STR_BOSE "BOSE" +#endif // D_STR_BOSE +#ifndef D_STR_CARRIER_AC +#define D_STR_CARRIER_AC "CARRIER_AC" +#endif // D_STR_CARRIER_AC +#ifndef D_STR_CARRIER_AC40 +#define D_STR_CARRIER_AC40 D_STR_CARRIER_AC "40" +#endif // D_STR_CARRIER_AC40 +#ifndef D_STR_CARRIER_AC64 +#define D_STR_CARRIER_AC64 D_STR_CARRIER_AC "64" +#endif // D_STR_CARRIER_AC64 +#ifndef D_STR_CARRIER_AC84 +#define D_STR_CARRIER_AC84 D_STR_CARRIER_AC "84" +#endif // D_STR_CARRIER_AC84 +#ifndef D_STR_CARRIER_AC128 +#define D_STR_CARRIER_AC128 D_STR_CARRIER_AC "128" +#endif // D_STR_CARRIER_AC128 +#ifndef D_STR_CLIMABUTLER +#define D_STR_CLIMABUTLER "CLIMABUTLER" +#endif // D_STR_CLIMABUTLER +#ifndef D_STR_COOLIX +#define D_STR_COOLIX "COOLIX" +#endif // D_STR_COOLIX +#ifndef D_STR_COOLIX48 +#define D_STR_COOLIX48 D_STR_COOLIX "48" +#endif // D_STR_COOLIX48 +#ifndef D_STR_CORONA_AC +#define D_STR_CORONA_AC "CORONA_AC" +#endif // D_STR_CORONA_AC +#ifndef D_STR_DAIKIN +#define D_STR_DAIKIN "DAIKIN" +#endif // D_STR_DAIKIN +#ifndef D_STR_DAIKIN128 +#define D_STR_DAIKIN128 D_STR_DAIKIN "128" +#endif // D_STR_DAIKIN128 +#ifndef D_STR_DAIKIN152 +#define D_STR_DAIKIN152 D_STR_DAIKIN "152" +#endif // D_STR_DAIKIN152 +#ifndef D_STR_DAIKIN160 +#define D_STR_DAIKIN160 D_STR_DAIKIN "160" +#endif // D_STR_DAIKIN160 +#ifndef D_STR_DAIKIN176 +#define D_STR_DAIKIN176 D_STR_DAIKIN "176" +#endif // D_STR_DAIKIN176 +#ifndef D_STR_DAIKIN2 +#define D_STR_DAIKIN2 D_STR_DAIKIN "2" +#endif // D_STR_DAIKIN2 +#ifndef D_STR_DAIKIN200 +#define D_STR_DAIKIN200 D_STR_DAIKIN "200" +#endif // D_STR_DAIKIN200 +#ifndef D_STR_DAIKIN216 +#define D_STR_DAIKIN216 D_STR_DAIKIN "216" +#endif // D_STR_DAIKIN216 +#ifndef D_STR_DAIKIN312 +#define D_STR_DAIKIN312 D_STR_DAIKIN "312" +#endif // D_STR_DAIKIN312 +#ifndef D_STR_DAIKIN64 +#define D_STR_DAIKIN64 D_STR_DAIKIN "64" +#endif // D_STR_DAIKIN64 +#ifndef D_STR_DELONGHI_AC +#define D_STR_DELONGHI_AC "DELONGHI_AC" +#endif // D_STR_DELONGHI_AC +#ifndef D_STR_DENON +#define D_STR_DENON "DENON" +#endif // D_STR_DENON +#ifndef D_STR_DISH +#define D_STR_DISH "DISH" +#endif // D_STR_DISH +#ifndef D_STR_DOSHISHA +#define D_STR_DOSHISHA "DOSHISHA" +#endif // D_STR_DOSHISHA +#ifndef D_STR_ECOCLIM +#define D_STR_ECOCLIM "ECOCLIM" +#endif // D_STR_ECOCLIM +#ifndef D_STR_ELECTRA_AC +#define D_STR_ELECTRA_AC "ELECTRA_AC" +#endif // D_STR_ELECTRA_AC +#ifndef D_STR_ELITESCREENS +#define D_STR_ELITESCREENS "ELITESCREENS" +#endif // D_STR_ELITESCREENS +#ifndef D_STR_EPSON +#define D_STR_EPSON "EPSON" +#endif // D_STR_EPSON +#ifndef D_STR_FUJITSU_AC +#define D_STR_FUJITSU_AC "FUJITSU_AC" +#endif // D_STR_FUJITSU_AC +#ifndef D_STR_FUJITSU_AC264 +#define D_STR_FUJITSU_AC264 "FUJITSU_AC264" +#endif // D_STR_FUJITSU_AC264 +#ifndef D_STR_GICABLE +#define D_STR_GICABLE "GICABLE" +#endif // D_STR_GICABLE +#ifndef D_STR_GLOBALCACHE +#define D_STR_GLOBALCACHE "GLOBALCACHE" +#endif // D_STR_GLOBALCACHE +#ifndef D_STR_GOODWEATHER +#define D_STR_GOODWEATHER "GOODWEATHER" +#endif // D_STR_GOODWEATHER +#ifndef D_STR_GORENJE +#define D_STR_GORENJE "GORENJE" +#endif // D_STR_GORENJE +#ifndef D_STR_GREE +#define D_STR_GREE "GREE" +#endif // D_STR_GREE +#ifndef D_STR_HAIER_AC +#define D_STR_HAIER_AC "HAIER_AC" +#endif // D_STR_HAIER_AC +#ifndef D_STR_HAIER_AC_YRW02 +#define D_STR_HAIER_AC_YRW02 D_STR_HAIER_AC "_YRW02" +#endif // D_STR_HAIER_AC_YRW02 +#ifndef D_STR_HAIER_AC160 +#define D_STR_HAIER_AC160 D_STR_HAIER_AC "160" +#endif // D_STR_HAIER_AC160 +#ifndef D_STR_HAIER_AC176 +#define D_STR_HAIER_AC176 D_STR_HAIER_AC "176" +#endif // D_STR_HAIER_AC176 +#ifndef D_STR_HITACHI_AC +#define D_STR_HITACHI_AC "HITACHI_AC" +#endif // D_STR_HITACHI_AC +#ifndef D_STR_HITACHI_AC1 +#define D_STR_HITACHI_AC1 D_STR_HITACHI_AC "1" +#endif // D_STR_HITACHI_AC1 +#ifndef D_STR_HITACHI_AC2 +#define D_STR_HITACHI_AC2 D_STR_HITACHI_AC "2" +#endif // D_STR_HITACHI_AC2 +#ifndef D_STR_HITACHI_AC3 +#define D_STR_HITACHI_AC3 D_STR_HITACHI_AC "3" +#endif // D_STR_HITACHI_AC3 +#ifndef D_STR_HITACHI_AC264 +#define D_STR_HITACHI_AC264 D_STR_HITACHI_AC "264" +#endif // D_STR_HITACHI_AC264 +#ifndef D_STR_HITACHI_AC296 +#define D_STR_HITACHI_AC296 D_STR_HITACHI_AC "296" +#endif // D_STR_HITACHI_AC296 +#ifndef D_STR_HITACHI_AC344 +#define D_STR_HITACHI_AC344 D_STR_HITACHI_AC "344" +#endif // D_STR_HITACHI_AC344 +#ifndef D_STR_HITACHI_AC424 +#define D_STR_HITACHI_AC424 D_STR_HITACHI_AC "424" +#endif // D_STR_HITACHI_AC424 +#ifndef D_STR_INAX +#define D_STR_INAX "INAX" +#endif // D_STR_INAX +#ifndef D_STR_JVC +#define D_STR_JVC "JVC" +#endif // D_STR_JVC +#ifndef D_STR_KELON +#define D_STR_KELON "KELON" +#endif // D_STR_KELON +#ifndef D_STR_KELON168 +#define D_STR_KELON168 D_STR_KELON "168" +#endif // D_STR_KELON168 +#ifndef D_STR_KELVINATOR +#define D_STR_KELVINATOR "KELVINATOR" +#endif // D_STR_KELVINATOR +#ifndef D_STR_LASERTAG +#define D_STR_LASERTAG "LASERTAG" +#endif // D_STR_LASERTAG +#ifndef D_STR_LEGOPF +#define D_STR_LEGOPF "LEGOPF" +#endif // D_STR_LEGOPF +#ifndef D_STR_LG +#define D_STR_LG "LG" +#endif // D_STR_LG +#ifndef D_STR_LG2 +#define D_STR_LG2 "LG2" +#endif // D_STR_LG2 +#ifndef D_STR_LUTRON +#define D_STR_LUTRON "LUTRON" +#endif // D_STR_LUTRON +#ifndef D_STR_MAGIQUEST +#define D_STR_MAGIQUEST "MAGIQUEST" +#endif // D_STR_MAGIQUEST +#ifndef D_STR_METZ +#define D_STR_METZ "METZ" +#endif // D_STR_METZ +#ifndef D_STR_MIDEA +#define D_STR_MIDEA "MIDEA" +#endif // D_STR_MIDEA +#ifndef D_STR_MIDEA24 +#define D_STR_MIDEA24 "MIDEA24" +#endif // D_STR_MIDEA24 +#ifndef D_STR_MILESTAG2 +#define D_STR_MILESTAG2 "MILESTAG2" +#endif // D_STR_MILESTAG2 +#ifndef D_STR_MIRAGE +#define D_STR_MIRAGE "MIRAGE" +#endif // D_STR_MIRAGE +#ifndef D_STR_MITSUBISHI +#define D_STR_MITSUBISHI "MITSUBISHI" +#endif // D_STR_MITSUBISHI +#ifndef D_STR_MITSUBISHI112 +#define D_STR_MITSUBISHI112 "MITSUBISHI112" +#endif // D_STR_MITSUBISHI112 +#ifndef D_STR_MITSUBISHI136 +#define D_STR_MITSUBISHI136 "MITSUBISHI136" +#endif // D_STR_MITSUBISHI136 +#ifndef D_STR_MITSUBISHI2 +#define D_STR_MITSUBISHI2 "MITSUBISHI2" +#endif // D_STR_MITSUBISHI2 +#ifndef D_STR_MITSUBISHI_AC +#define D_STR_MITSUBISHI_AC "MITSUBISHI_AC" +#endif // D_STR_MITSUBISHI_AC +#ifndef D_STR_MITSUBISHI_HEAVY_152 +#define D_STR_MITSUBISHI_HEAVY_152 "MITSUBISHI_HEAVY_152" +#endif // D_STR_MITSUBISHI_HEAVY_152 +#ifndef D_STR_MITSUBISHI_HEAVY_88 +#define D_STR_MITSUBISHI_HEAVY_88 "MITSUBISHI_HEAVY_88" +#endif // D_STR_MITSUBISHI_HEAVY_88 +#ifndef D_STR_MULTIBRACKETS +#define D_STR_MULTIBRACKETS "MULTIBRACKETS" +#endif // D_STR_MULTIBRACKETS +#ifndef D_STR_MWM +#define D_STR_MWM "MWM" +#endif // D_STR_MWM +#ifndef D_STR_NEC +#define D_STR_NEC "NEC" +#endif // D_STR_NEC +#ifndef D_STR_NEC_LIKE +#define D_STR_NEC_LIKE D_STR_NEC "_LIKE" +#endif // D_STR_NEC_LIKE +#ifndef D_STR_NEC_NON_STRICT +#define D_STR_NEC_NON_STRICT D_STR_NEC " (NON-STRICT)" +#endif // D_STR_NEC_NON_STRICT +#ifndef D_STR_NEOCLIMA +#define D_STR_NEOCLIMA "NEOCLIMA" +#endif // D_STR_NEOCLIMA +#ifndef D_STR_NIKAI +#define D_STR_NIKAI "NIKAI" +#endif // D_STR_NIKAI +#ifndef D_STR_PANASONIC +#define D_STR_PANASONIC "PANASONIC" +#endif // D_STR_PANASONIC +#ifndef D_STR_PANASONIC_AC +#define D_STR_PANASONIC_AC "PANASONIC_AC" +#endif // D_STR_PANASONIC_AC +#ifndef D_STR_PANASONIC_AC32 +#define D_STR_PANASONIC_AC32 D_STR_PANASONIC_AC"32" +#endif // D_STR_PANASONIC_AC32 +#ifndef D_STR_PIONEER +#define D_STR_PIONEER "PIONEER" +#endif // D_STR_PIONEER +#ifndef D_STR_PRONTO +#define D_STR_PRONTO "PRONTO" +#endif // D_STR_PRONTO +#ifndef D_STR_RAW +#define D_STR_RAW "RAW" +#endif // D_STR_RAW +#ifndef D_STR_RC5 +#define D_STR_RC5 "RC5" +#endif // D_STR_RC5 +#ifndef D_STR_RC5X +#define D_STR_RC5X "RC5X" +#endif // D_STR_RC5X +#ifndef D_STR_RC6 +#define D_STR_RC6 "RC6" +#endif // D_STR_RC6 +#ifndef D_STR_RCMM +#define D_STR_RCMM "RCMM" +#endif // D_STR_RCMM +#ifndef D_STR_RHOSS +#define D_STR_RHOSS "RHOSS" +#endif // D_STR_RHOSS +#ifndef D_STR_SAMSUNG +#define D_STR_SAMSUNG "SAMSUNG" +#endif // D_STR_SAMSUNG +#ifndef D_STR_SAMSUNG36 +#define D_STR_SAMSUNG36 "SAMSUNG36" +#endif // D_STR_SAMSUNG36 +#ifndef D_STR_SAMSUNG_AC +#define D_STR_SAMSUNG_AC "SAMSUNG_AC" +#endif // D_STR_SAMSUNG_AC +#ifndef D_STR_SANYO +#define D_STR_SANYO "SANYO" +#endif // D_STR_SANYO +#ifndef D_STR_SANYO_AC +#define D_STR_SANYO_AC D_STR_SANYO "_AC" +#endif // D_STR_SANYO_AC +#ifndef D_STR_SANYO_AC88 +#define D_STR_SANYO_AC88 D_STR_SANYO_AC "88" +#endif // D_STR_SANYO_AC88 +#ifndef D_STR_SANYO_AC152 +#define D_STR_SANYO_AC152 D_STR_SANYO_AC "152" +#endif // D_STR_SANYO_AC152 +#ifndef D_STR_SANYO_LC7461 +#define D_STR_SANYO_LC7461 D_STR_SANYO "_LC7461" +#endif // D_STR_SANYO_LC7461 +#ifndef D_STR_SHARP +#define D_STR_SHARP "SHARP" +#endif // D_STR_SHARP +#ifndef D_STR_SHARP_AC +#define D_STR_SHARP_AC "SHARP_AC" +#endif // D_STR_SHARP_AC +#ifndef D_STR_SHERWOOD +#define D_STR_SHERWOOD "SHERWOOD" +#endif // D_STR_SHERWOOD +#ifndef D_STR_SONY +#define D_STR_SONY "SONY" +#endif // D_STR_SONY +#ifndef D_STR_SONY_38K +#define D_STR_SONY_38K "SONY_38K" +#endif // D_STR_SONY_38K +#ifndef D_STR_SYMPHONY +#define D_STR_SYMPHONY "SYMPHONY" +#endif // D_STR_SYMPHONY +#ifndef D_STR_TCL96AC +#define D_STR_TCL96AC "TCL96AC" +#endif // D_STR_TCL96AC +#ifndef D_STR_TCL112AC +#define D_STR_TCL112AC "TCL112AC" +#endif // D_STR_TCL112AC +#ifndef D_STR_TECHNIBEL_AC +#define D_STR_TECHNIBEL_AC "TECHNIBEL_AC" +#endif // D_STR_TECHNIBEL_AC +#ifndef D_STR_TECO +#define D_STR_TECO "TECO" +#endif // D_STR_TECO +#ifndef D_STR_TEKNOPOINT +#define D_STR_TEKNOPOINT "TEKNOPOINT" +#endif // D_STR_TEKNOPOINT +#ifndef D_STR_TOSHIBA_AC +#define D_STR_TOSHIBA_AC "TOSHIBA_AC" +#endif // D_STR_TOSHIBA_AC +#ifndef D_STR_TOTO +#define D_STR_TOTO "TOTO" +#endif // D_STR_TOTO +#ifndef D_STR_TRANSCOLD +#define D_STR_TRANSCOLD "TRANSCOLD" +#endif // D_STR_TRANSCOLD +#ifndef D_STR_TROTEC +#define D_STR_TROTEC "TROTEC" +#endif // D_STR_TROTEC +#ifndef D_STR_TROTEC_3550 +#define D_STR_TROTEC_3550 D_STR_TROTEC "_3550" +#endif // D_STR_TROTEC_3550 +#ifndef D_STR_TRUMA +#define D_STR_TRUMA "TRUMA" +#endif // D_STR_TRUMA +#ifndef D_STR_UNUSED +#define D_STR_UNUSED "UNUSED" +#endif // D_STR_UNUSED +#ifndef D_STR_VESTEL_AC +#define D_STR_VESTEL_AC "VESTEL_AC" +#endif // D_STR_VESTEL_AC +#ifndef D_STR_VOLTAS +#define D_STR_VOLTAS "VOLTAS" +#endif // D_STR_VOLTAS +#ifndef D_STR_WHIRLPOOL_AC +#define D_STR_WHIRLPOOL_AC "WHIRLPOOL_AC" +#endif // D_STR_WHIRLPOOL_AC +#ifndef D_STR_WHYNTER +#define D_STR_WHYNTER "WHYNTER" +#endif // D_STR_WHYNTER +#ifndef D_STR_WOWWEE +#define D_STR_WOWWEE "WOWWEE" +#endif // D_STR_WOWWEE +#ifndef D_STR_XMP +#define D_STR_XMP "XMP" +#endif // D_STR_XMP +#ifndef D_STR_YORK +#define D_STR_YORK "YORK" +#endif // D_STR_YORK +#ifndef D_STR_ZEPEAL +#define D_STR_ZEPEAL "ZEPEAL" +#endif // D_STR_ZEPEAL + +// IRrecvDumpV2+ +#ifndef D_STR_TIMESTAMP +#define D_STR_TIMESTAMP "Timestamp" +#endif // D_STR_TIMESTAMP +#ifndef D_STR_LIBRARY +#define D_STR_LIBRARY "Library" +#endif // D_STR_LIBRARY +#ifndef D_STR_MESGDESC +#define D_STR_MESGDESC "Mesg Desc." +#endif // D_STR_MESGDESC +#ifndef D_STR_TOLERANCE +#define D_STR_TOLERANCE "Tolerance" +#endif // D_STR_TOLERANCE +#ifndef D_STR_IRRECVDUMP_STARTUP +#define D_STR_IRRECVDUMP_STARTUP \ + "IRrecvDump is now running and waiting for IR input on Pin %d" +#endif // D_STR_IRRECVDUMP_STARTUP +#ifndef D_WARN_BUFFERFULL +#define D_WARN_BUFFERFULL \ + "WARNING: IR code is too big for buffer (>= %d). " \ + "This result shouldn't be trusted until this is resolved. " \ + "Edit & increase `kCaptureBufferSize`." +#endif // D_WARN_BUFFERFULL + +#endif // LOCALE_DEFAULTS_H_ diff --git a/test/ir_Fujitsu_test.cpp b/test/ir_Fujitsu_test.cpp index e573e4a52..cf3faec2b 100644 --- a/test/ir_Fujitsu_test.cpp +++ b/test/ir_Fujitsu_test.cpp @@ -1,2220 +1,2220 @@ -// Copyright 2017 Jonny Graham, David Conran -// Copyright 2023 Takeshi Shimizu -#include "IRac.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "ir_Fujitsu.h" -#include "gtest/gtest.h" - -// Tests for Fujitsu A/C methods. - -// Test sending typical data only. -TEST(TestIRFujitsuACClass, GetRawDefault) { - IRFujitsuAC ac(kGpioUnused); // AR-RAH2E - ac.setSwing(kFujitsuAcSwingBoth); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setTemp(24); - ac.setCmd(kFujitsuAcCmdTurnOn); - uint8_t expected_arrah2e[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", - ac.toString()); - - uint8_t expected_ardb1[15] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x4D}; - ac.setModel(ARDB1); - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 15 * 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawTurnOff) { - IRFujitsuAC ac(kGpioUnused); - ac.setModel(ARRAH2E); - ac.off(); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", - ac.toString()); - - ac.setModel(ARDB1); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawStepHoriz) { - IRFujitsuAC ac(kGpioUnused); - ac.stepHoriz(); - uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; - EXPECT_STATE_EQ(expected, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(H), Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawStepVert) { - IRFujitsuAC ac(kGpioUnused); - ac.setModel(ARRAH2E); - ac.stepVert(); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(V), Timer: Off", - ac.toString()); - - ac.setModel(ARDB1); - ac.stepVert(); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, - ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: Step Swing(V)", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { - IRFujitsuAC ac(kGpioUnused); - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingHoriz); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setTemp(25); - uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, - 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; - EXPECT_STATE_EQ(expected, ac.getRaw(), 16 * 8); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 25C, " - "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, GetRawWithFan) { - IRFujitsuAC ac(kGpioUnused); - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingHoriz); - ac.setMode(kFujitsuAcModeFan); - ac.setFanSpeed(kFujitsuAcFanMed); - ac.setTemp(20); // temp doesn't matter for fan - // but it is sent by the RC anyway - ac.setModel(ARRAH2E); - uint8_t expected_arrah2e[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x40, 0x03, 0x22, 0x00, 0x00, 0x00, 0x20, 0x4B}; - EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " - "Fan: 2 (Medium), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", - ac.toString()); - - ac.setModel(ARDB1); - uint8_t expected_ardb1[15] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x40, 0x03, 0x02, 0x00, 0x00, 0x00, 0x8B}; - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), ac.getStateLength() * 8); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " - "Fan: 2 (Medium), Command: N/A", ac.toString()); -} - -TEST(TestIRFujitsuACClass, SetRaw) { - IRFujitsuAC ac(kGpioUnused); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - uint8_t expected_default_arrah2e[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; - EXPECT_STATE_EQ(expected_default_arrah2e, ac.getRaw(), - ac.getStateLength() * 8); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, " - "Timer: Off", - ac.toString()); - // Now set a new state via setRaw(); - // This state is a real state from an AR-DB1 remote. - uint8_t new_state1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - ac.setRaw(new_state1, kFujitsuAcStateLength - 1); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_STATE_EQ(new_state1, ac.getRaw(), ac.getStateLength() * 8); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " - "Fan: 0 (Auto), Command: N/A", ac.toString()); -} - -TEST(TestSendFujitsuAC, GenerateMessage) { - IRFujitsuAC ac(kGpioUnused); - IRsendTest irsend(kGpioUnused); - ac.begin(); - irsend.begin(); - - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingBoth); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setTemp(24); - - EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); - EXPECT_EQ(kFujitsuAcModeCool, ac.getMode()); - EXPECT_EQ(24, ac.getTemp()); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - EXPECT_EQ(kFujitsuAcCmdStayOn, ac.getCmd()); - - irsend.reset(); - irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLength); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s8100", - irsend.outputStr()); -} - -TEST(TestSendFujitsuAC, GenerateShortMessage) { - IRFujitsuAC ac(kGpioUnused); - IRsendTest irsend(kGpioUnused); - ac.begin(); - irsend.begin(); - - ac.off(); - - EXPECT_EQ(kFujitsuAcCmdTurnOff, ac.getCmd()); - - irsend.reset(); - irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" - "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" - "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", - irsend.outputStr()); -} - -// Issue #275 -TEST(TestSendFujitsuAC, Issue275) { - IRFujitsuAC ac(kGpioUnused); - IRsendTest irsend(kGpioUnused); - ac.begin(); - irsend.begin(); - irsend.reset(); - - ac.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - // Header - "m3324s1574" - // 0 0 1 0 1 0 0 0 (0x28) - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - // 1 1 0 0 0 1 1 0 (0xC6) - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - // 0 0 0 0 0 0 0 0 (0x00) - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 1 0 0 0 0 0 0 (0x40) - "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" - // 1 0 1 1 1 1 1 1 (0xBF) - "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - // Footer - "m448s8100", irsend.outputStr()); - - irsend.reset(); - // Per report in Issue #275 - uint16_t off[115] = { - 3350, 1650, - 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, - 450, 1250, 450}; - irsend.sendRaw(off, 115, 38); - EXPECT_EQ( - "f38000d50" - // Header - "m3350s1650" - // 0 0 1 0 1 0 0 0 (0x28) - "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" - // 1 1 0 0 0 1 1 0 (0xC6) - "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" - // 0 0 0 0 0 0 0 0 (0x00) - "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 1 0 0 0 0 0 0 (0x40) - "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" - // 1 0 1 1 1 1 1 1 (0xBF) - "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" - // Footer - "m450", - irsend.outputStr()); -} - -TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { - IRsendTest irsend(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - IRrecv irrecv(kGpioUnused); - - irsend.begin(); - irsend.reset(); - - ac.setModel(ARRAH2E); - ac.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); - uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", - IRAcUtils::resultAcToString(&irsend.capture)); - stdAc::state_t r, p; - ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); - - irsend.reset(); - - ac.setModel(ARDB1); - ac.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); -} - -TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { - IRsendTest irsend(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - IRrecv irrecv(kGpioUnused); - irsend.begin(); - - irsend.reset(); - - ac.setModel(ARRAH2E); - ac.setCmd(kFujitsuAcCmdStayOn); - ac.setSwing(kFujitsuAcSwingVert); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setTemp(18); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - ASSERT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - uint8_t expected_arrah2e[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; - EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " - "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 1 (Swing(V)), Command: N/A, " - "Timer: Off", - ac.toString()); - - irsend.reset(); - - ac.setModel(ARDB1); - irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected_ardb1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAB}; - EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " - "Fan: 4 (Quiet), Command: N/A", ac.toString()); -} - -TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - irsend.begin(); - - irsend.reset(); - // "Off" Message recorded from an AR-DB1 remote. - uint16_t rawData[99] = { - 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, - 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, - 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, - 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, - 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, - 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, - 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, - 420, 388, 436}; - irsend.sendRaw(rawData, 99, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); - uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Command: N/A", ac.toString()); -} - -TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - irsend.begin(); - irsend.reset(); - uint16_t rawData1[243] = { - 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, - 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, - 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, - 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, - 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, - 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, - 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, - 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, - 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, - 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, - 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, - 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, - 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, - 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, - 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, - 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, - 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, - 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, - 436, 1216, 436}; - irsend.sendRaw(rawData1, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; - EXPECT_STATE_EQ(expected1, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " - "Fan: 4 (Quiet), Command: N/A", ac.toString()); - - irsend.reset(); - uint16_t rawData2[243] = { - 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, - 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, - 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, - 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, - 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, - 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, - 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, - 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, - 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, - 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, - 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, - 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, - 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, - 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, - 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, - 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, - 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, - 436, 1214, 440}; - irsend.sendRaw(rawData2, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected2[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - EXPECT_STATE_EQ(expected2, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); - EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " - "Fan: 0 (Auto), Command: N/A", ac.toString()); -} - -TEST(TestDecodeFujitsuAC, Issue414) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - // Capture as supplied by arpmota - uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, - 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, - 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, - 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, - 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, - 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, - 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, - 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, - 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, - 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, - 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, - 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, - 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, - 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, - 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, - 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, - 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, - 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, - 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; - uint8_t state[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x2B}; - irsend.begin(); - irsend.reset(); - irsend.sendRaw(rawData, 259, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - - // Resend it using the state this time. - irsend.reset(); - irsend.sendFujitsuAC(state, 16); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" - "m448s8100", irsend.outputStr()); -} - -TEST(TestIRFujitsuACClass, toCommon) { - IRFujitsuAC ac(kGpioUnused); - ac.setMode(kFujitsuAcModeCool); - ac.setTemp(20); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setSwing(kFujitsuAcSwingBoth); - - // Now test it. - ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - ASSERT_TRUE(ac.toCommon().power); - ASSERT_TRUE(ac.toCommon().celsius); - ASSERT_EQ(20, ac.toCommon().degrees); - ASSERT_TRUE(ac.toCommon().quiet); - - ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); - ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); - ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); - ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); - // Unsupported. - ASSERT_FALSE(ac.toCommon().filter); - ASSERT_FALSE(ac.toCommon().clean); - ASSERT_FALSE(ac.toCommon().turbo); - ASSERT_FALSE(ac.toCommon().light); - ASSERT_FALSE(ac.toCommon().econo); - ASSERT_FALSE(ac.toCommon().beep); - ASSERT_EQ(-1, ac.toCommon().sleep); - ASSERT_EQ(-1, ac.toCommon().clock); - - // Check off mode which is special. - ac.off(); - ASSERT_FALSE(ac.toCommon().power); - ac.send(); - ac.stateReset(); - IRrecv irrecv(kGpioUnused); - ac._irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); - - // Now test it. - EXPECT_EQ( // Off mode technically has no temp, mode, fan, etc. - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", - ac.toString()); - ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - ASSERT_FALSE(ac.toCommon().power); - ASSERT_TRUE(ac.toCommon().celsius); - ASSERT_EQ(16, ac.toCommon().degrees); - ASSERT_FALSE(ac.toCommon().quiet); - - ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); - ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); - ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); - ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); - // Unsupported. - ASSERT_FALSE(ac.toCommon().filter); - ASSERT_FALSE(ac.toCommon().clean); - ASSERT_FALSE(ac.toCommon().turbo); - ASSERT_FALSE(ac.toCommon().light); - ASSERT_FALSE(ac.toCommon().econo); - ASSERT_FALSE(ac.toCommon().beep); - ASSERT_EQ(-1, ac.toCommon().sleep); - ASSERT_EQ(-1, ac.toCommon().clock); -} - -TEST(TestDecodeFujitsuAC, Issue716) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - // Powerful command from a raw data capture. - // Capture as supplied by u4mzu4 - uint16_t rawData[115] = { - 3320, 1610, 432, 406, 432, 406, 432, 1220, 432, 406, 432, 1192, 458, 406, - 432, 406, 432, 406, 432, 1218, 432, 1220, 432, 406, 432, 406, 432, 406, - 432, 1192, 458, 1192, 460, 406, 432, 406, 432, 406, 432, 406, 432, 406, - 432, 406, 432, 406, 432, 408, 432, 406, 432, 406, 430, 406, 432, 406, 432, - 406, 432, 1190, 460, 406, 432, 408, 430, 406, 432, 406, 432, 406, 432, - 406, 432, 406, 434, 1192, 458, 406, 432, 406, 432, 406, 432, 1194, 458, - 406, 432, 406, 432, 1194, 456, 1196, 454, 1220, 432, 406, 432, 406, 432, - 408, 430, 1194, 458, 1194, 456, 406, 432, 406, 430, 406, 432, 1194, 458, - 1194, 458}; // FUJITSU_AC - uint8_t powerful[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; - irsend.begin(); - irsend.reset(); - irsend.sendRaw(rawData, 115, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLengthShort * 8, irsend.capture.bits); - EXPECT_STATE_EQ(powerful, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: Powerful, Outside Quiet: Off, " - "Timer: Off", - ac.toString()); - - // Economy (just from the state) - uint8_t econo[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0x09, 0xF6}; - // Make sure we can't accidentally inherit the correct model. - ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, - fujitsu_ac_remote_model_t::ARREB1E); - ac.setModel(fujitsu_ac_remote_model_t::ARDB1); - ac.setRaw(econo, kFujitsuAcStateLengthShort); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: Econo, Outside Quiet: Off, " - "Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, OutsideQuiet) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, - fujitsu_ac_remote_model_t::ARREB1E); - ASSERT_NE(fujitsu_ac_remote_model_t::ARRAH2E, - fujitsu_ac_remote_model_t::ARREB1E); - // States as supplied by u4mzu4 - // https://github.com/crankyoldgit/IRremoteESP8266/issues/716#issuecomment-495852309 - uint8_t off[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; - uint8_t on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xAF}; - // Make sure we can't accidentally inherit the correct model. - ac.setModel(fujitsu_ac_remote_model_t::ARDB1); - ac.setRaw(off, kFujitsuAcStateLength); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_FALSE(ac.getOutsideQuiet()); - // We can really only tell the difference between ARRAH2E & ARREB1E if - // the option is set. Otheriwse they appear the same. - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", ac.toString()); - ac.setModel(fujitsu_ac_remote_model_t::ARREB1E); - EXPECT_EQ( - "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: N/A, Outside Quiet: Off, Timer: Off", - ac.toString()); - - // Make sure we can't accidentally inherit the correct model. - ac.setModel(fujitsu_ac_remote_model_t::ARDB1); - ac.setRaw(on, kFujitsuAcStateLength); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_TRUE(ac.getOutsideQuiet()); - EXPECT_EQ( - "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " - "Command: N/A, Outside Quiet: On, Timer: Off", - ac.toString()); - - ac.setOutsideQuiet(false); - EXPECT_FALSE(ac.getOutsideQuiet()); - ac.setOutsideQuiet(true); - EXPECT_TRUE(ac.getOutsideQuiet()); - ac.setOutsideQuiet(false); - EXPECT_FALSE(ac.getOutsideQuiet()); -} - -TEST(TestIRFujitsuACClass, toggleSwing) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - ac.begin(); - ac.setModel(ARJW2); - ac.setSwing(kFujitsuAcSwingOff); - ac.setCmd(kFujitsuAcCmdStayOn); - ASSERT_EQ(kFujitsuAcSwingOff, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingHoriz, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); - ac.toggleSwingVert(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); - ac.toggleSwingVert(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); - - // Both - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - ac.toggleSwingVert(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); - ac.toggleSwingHoriz(); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - - EXPECT_EQ( - "Model: 4 (ARJW2), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Command: Toggle Swing(H)", - ac.toString()); - - // Test without the update set. - ac.toggleSwingHoriz(false); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); - ac.toggleSwingVert(false); - EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); - EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); -} - -TEST(TestDecodeFujitsuAC, Issue726) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRFujitsuAC ac(kGpioUnused); - - // fan:auto mode:auto temp:24 power:on - // Capture as supplied by huexpub - // Rawdata was very messy. Had to use `./auto_analyse_raw_data.py -r 250` to - // get it to parse due to timings being above tolerances. - uint8_t auto_auto_on_24[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; - irsend.begin(); - irsend.reset(); - irsend.sendFujitsuAC(auto_auto_on_24, 16); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); - EXPECT_STATE_EQ(auto_auto_on_24, irsend.capture.state, irsend.capture.bits); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, Clean) { - IRFujitsuAC ac(kGpioUnused); - // Data from: - // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 - uint8_t clean_off[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; - uint8_t clean_on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08}; - ac.setRaw(clean_on, kFujitsuAcStateLength); - EXPECT_TRUE(ac.getClean()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", - ac.toString()); - ac.setClean(false); - EXPECT_FALSE(ac.getClean()); - EXPECT_STATE_EQ(clean_off, ac.getRaw(), ac.getStateLength() * 8) - ac.setClean(true); - EXPECT_TRUE(ac.getClean()); - EXPECT_STATE_EQ(clean_on, ac.getRaw(), ac.getStateLength() * 8) - ac.setRaw(clean_off, kFujitsuAcStateLength); - EXPECT_FALSE(ac.getClean()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. - ac.setClean(true); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - // But ARRY4 does. - ac.setModel(fujitsu_ac_remote_model_t::ARRY4); - EXPECT_TRUE(ac.getClean()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, Filter) { - IRFujitsuAC ac(kGpioUnused); - // Data from: - // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 - uint8_t filter_on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x07}; - uint8_t filter_off[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; - ac.setRaw(filter_on, kFujitsuAcStateLength); - EXPECT_TRUE(ac.getFilter()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", - ac.toString()); - ac.setFilter(false); - EXPECT_FALSE(ac.getFilter()); - ac.setFilter(true); - EXPECT_TRUE(ac.getFilter()); - ac.setRaw(filter_off, kFujitsuAcStateLength); - EXPECT_FALSE(ac.getFilter()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. - ac.setFilter(true); - EXPECT_FALSE(ac.getFilter()); - // But ARRY4 does. - ac.setModel(fujitsu_ac_remote_model_t::ARRY4); - EXPECT_TRUE(ac.getFilter()); - EXPECT_EQ( - "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", - ac.toString()); -} - -TEST(TestIRFujitsuACClass, Timers) { - IRFujitsuAC ac(kGpioUnused); - // Data from: - // https://github.com/crankyoldgit/IRremoteESP8266/issues/1255#issuecomment-686445720 - const uint8_t timer_on_12h[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x30, 0x01, 0x00, 0x00, 0xAD, 0x20, 0x32}; - ac.setRaw(timer_on_12h, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); - EXPECT_EQ(12 * 60, ac.getOnTimer()); - EXPECT_EQ(0, ac.getOffSleepTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " - "Command: N/A, On Timer: 12:00", - ac.toString()); - - const uint8_t timer_on_8h30m[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x30, 0x01, 0x00, 0xE0, 0x9F, 0x20, 0x60}; - ac.setRaw(timer_on_8h30m, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); - EXPECT_EQ(8 * 60 + 30, ac.getOnTimer()); - EXPECT_EQ(0, ac.getOffSleepTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, On Timer: 08:30", - ac.toString()); - - // TIMER OFF 11H - const uint8_t timer_off_11h[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x20, 0x01, 0x94, 0x0A, 0x00, 0x20, 0x51}; - ac.setRaw(timer_off_11h, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); - EXPECT_EQ(11 * 60, ac.getOffSleepTimer()); - EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Off Timer: 11:00", - ac.toString()); - - // TIMER OFF 0.5H - const uint8_t timer_off_30m[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x20, 0x01, 0x1E, 0x08, 0x00, 0x20, 0xC9}; - ac.setRaw(timer_off_30m, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); - EXPECT_EQ(30, ac.getOffSleepTimer()); - EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", - ac.toString()); - - // TIMER SLEEP 3H - const uint8_t timer_sleep_3h[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0xA0, 0x10, 0x01, 0xB4, 0x08, 0x00, 0x20, 0x43}; - ac.setRaw(timer_sleep_3h, kFujitsuAcStateLength); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(kFujitsuAcSleepTimer, ac.getTimerType()); - EXPECT_EQ(3 * 60, ac.getOffSleepTimer()); - EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", - ac.toString()); - - // Re-construct a known timer state from scratch. - ac.stateReset(); - ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); - ac.setPower(true); - ac.setMode(kFujitsuAcModeAuto); - ac.setTemp(26); - ac.setFanSpeed(1); - ac.setClean(false); - ac.setFilter(false); - ac.setSwing(0); - - ac.setOffTimer(30); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_STATE_EQ(timer_off_30m, ac.getRaw(), ac.getStateLength() * 8); - - ac.setOnTimer(12 * 60); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, On Timer: 12:00", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_EQ(12 * 60, ac.getOnTimer()); - EXPECT_TRUE(ac.getOnTimer()); - EXPECT_STATE_EQ(timer_on_12h, ac.getRaw(), ac.getStateLength() * 8); - EXPECT_EQ(12 * 60, ac.getOnTimer()); - EXPECT_TRUE(ac.getOnTimer()); - - ac.setSleepTimer(3 * 60); - EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " - "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - EXPECT_STATE_EQ(timer_sleep_3h, ac.getRaw(), ac.getStateLength() * 8); -} - -TEST(TestIRFujitsuACClass, ARREW4E) { - IRFujitsuAC ac(kGpioUnused); - - uint8_t on_18_cool_auto[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x50, 0x01, 0x00, 0x21, 0x03, 0x20, 0x20, 0x1A}; - - EXPECT_TRUE(ac.validChecksum(on_18_cool_auto, kFujitsuAcStateLength)); - ac.setRaw(on_18_cool_auto, kFujitsuAcStateLength); - EXPECT_EQ(0, ac.getId()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); - EXPECT_EQ(18, ac.getTemp()); - - uint8_t mode_C_power_on_18[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x51, 0x01, 0x00, 0x17, 0x07, 0x54, 0x20, 0xEB}; - EXPECT_TRUE(ac.validChecksum(mode_C_power_on_18, kFujitsuAcStateLength)); - ac.setRaw(mode_C_power_on_18, kFujitsuAcStateLength); - EXPECT_EQ(2, ac.getId()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); - EXPECT_EQ(18, ac.getTemp()); - - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - - irsend.begin(); - irsend.reset(); - irsend.sendFujitsuAC(mode_C_power_on_18, kFujitsuAcStateLength); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); - EXPECT_STATE_EQ(mode_C_power_on_18, irsend.capture.state, - irsend.capture.bits); -} - -TEST(TestDecodeFujitsuAC, Issue1455) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - irsend.begin(); - irsend.reset(); - uint16_t rawData[259] = { - 3220, 1700, 354, 446, 380, 448, 380, 1296, 352, 446, 382, 1296, 354, 448, - 380, 448, 378, 446, 382, 1296, 352, 1296, 354, 448, 378, 446, 380, 446, - 382, 1294, 354, 1270, 380, 448, 380, 446, 380, 448, 380, 446, 380, 450, - 378, 448, 380, 446, 380, 448, 380, 448, 380, 448, 380, 450, 376, 450, 380, - 448, 378, 1298, 352, 448, 378, 448, 380, 448, 380, 448, 378, 450, 378, - 450, 378, 448, 378, 1296, 354, 446, 382, 446, 380, 448, 378, 448, 380, - 1296, 352, 1296, 354, 1296, 354, 1272, 376, 1272, 378, 1296, 354, 1294, - 354, 1296, 354, 446, 380, 448, 378, 1296, 354, 448, 378, 448, 378, 448, - 380, 446, 380, 1272, 378, 446, 380, 450, 378, 448, 378, 1296, 354, 1296, - 354, 446, 380, 448, 378, 448, 378, 446, 382, 446, 380, 1296, 354, 1296, - 354, 446, 380, 1296, 354, 446, 380, 446, 380, 446, 380, 1294, 354, 448, - 380, 448, 380, 448, 380, 448, 380, 446, 380, 448, 380, 446, 380, 448, - 380, 446, 380, 446, 380, 448, 380, 446, 380, 448, 380, 448, 380, 446, 380, - 1296, 352, 446, 380, 1296, 354, 446, 380, 448, 380, 448, 380, 1296, 354, - 448, 378, 448, 380, 446, 380, 446, 382, 446, 380, 446, 382, 446, 380, - 1272, 378, 446, 380, 446, 382, 1294, 354, 446, 382, 1294, 354, 446, 382, - 446, 382, 446, 380, 448, 380, 448, 380, 448, 380, 448, 378, 1296, 354, - 446, 382, 446, 380, 1296, 354, 446, 382, 1296, 354, 446, 382, 1294, 354, - 446, 382, 446, 380, 446, 382}; // UNKNOWN 8383D7DE - irsend.sendRaw(rawData, 259, 38); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 19C, " - "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - IRAcUtils::resultAcToString(&irsend.capture)); - stdAc::state_t r, p; - ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); -} - -TEST(TestIRFujitsuACClass, Heat10Deg) { - IRFujitsuAC ac(kGpioUnused); - const uint8_t heat_on[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x10, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x69, 0x0B, 0x00, 0x23, 0x06, 0x23, 0x20, 0xEF}; - ac.setRaw(heat_on, kFujitsuAcStateLength); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - ac.toString()); - ac.stateReset(); - ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); - ac.setId(1); - ac.setMode(kFujitsuAcModeFan); - ac.setTemp(21); - ac.setFanSpeed(kFujitsuAcFanAuto); - ac.setSwing(0); - ac.setOutsideQuiet(false); - ac.setPower(true); - ac.set10CHeat(true); - EXPECT_TRUE(ac.get10CHeat()); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - ac.toString()); - EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); - - ac.set10CHeat(false); - EXPECT_FALSE(ac.get10CHeat()); - EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 21C, " - "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off", - ac.toString()); - - // For https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 - ac.set10CHeat(true); - EXPECT_TRUE(ac.get10CHeat()); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setSwing(kFujitsuAcSwingVert); - EXPECT_FALSE(ac.get10CHeat()); - ac.set10CHeat(false); - EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); - EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); - EXPECT_FALSE(ac.get10CHeat()); - ac.set10CHeat(true); - EXPECT_TRUE(ac.get10CHeat()); - EXPECT_EQ(kFujitsuAcFanAuto, ac.getFanSpeed()); - EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); -} - -TEST(TestUtils, Housekeeping) { - ASSERT_EQ("FUJITSU_AC", typeToString(decode_type_t::FUJITSU_AC)); - ASSERT_EQ(decode_type_t::FUJITSU_AC, strToDecodeType("FUJITSU_AC")); - ASSERT_TRUE(hasACState(decode_type_t::FUJITSU_AC)); - ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::FUJITSU_AC)); - ASSERT_EQ(0, IRsend::defaultBits(decode_type_t::FUJITSU_AC)); // No default - ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::FUJITSU_AC)); - - ASSERT_EQ("FUJITSU_AC264", typeToString(decode_type_t::FUJITSU_AC264)); - ASSERT_EQ(decode_type_t::FUJITSU_AC264, strToDecodeType("FUJITSU_AC264")); - ASSERT_TRUE(hasACState(decode_type_t::FUJITSU_AC264)); - ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::FUJITSU_AC264)); - ASSERT_EQ(264, IRsend::defaultBits(decode_type_t::FUJITSU_AC264)); - ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::FUJITSU_AC264)); -} - -TEST(TestIRFujitsuACClass, Temperature) { - IRFujitsuAC ac(kGpioUnused); - // Most models - // Celsius - ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); - ac.setTemp(kFujitsuAcMinTemp); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMinTemp - 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp + 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - // Fahrenheit (can't be used by most model, check it converts correctly) - ac.setTemp(77, false); // 77F is 25C - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(25, ac.getTemp()); - - // ARREW4E is different. - ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); - ac.setTemp(kFujitsuAcMinTemp); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMinTemp - 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); - ac.setTemp(kFujitsuAcMaxTemp + 1); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); - ac.setTemp(22.5); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(22.5, ac.getTemp()); - // Fahrenheit - ac.setTemp(77, false); - EXPECT_FALSE(ac.getCelsius()); - EXPECT_EQ(77, ac.getTemp()); - - // Real example - const uint8_t arrew4e_22c[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x70, 0x01, 0x00, 0x20, 0x03, 0x58, 0x20, 0xC3}; - ac.setRaw(arrew4e_22c, 16); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(22, ac.getTemp()); - const uint8_t arrew4e_25_5c[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x8C, 0x01, 0x00, 0x21, 0x03, 0x12, 0x20, 0xEC}; - ac.setRaw(arrew4e_25_5c, 16); - EXPECT_TRUE(ac.getCelsius()); - EXPECT_EQ(25.5, ac.getTemp()); - const uint8_t arrew4e_69f[16] = { - 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x66, 0x04, 0x00, 0x16, 0x01, 0x32, 0x20, 0xFC}; - ac.setRaw(arrew4e_69f, 16); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); - EXPECT_FALSE(ac.getCelsius()); - EXPECT_EQ(69, ac.getTemp()); -} - -TEST(TestIRFujitsuACClass, ARREW4EShortCodes) { - // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 - IRFujitsuAC ac(kGpioUnused); - ac.setId(3); - ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); - - const uint8_t off[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x02, 0xFD}; - ac.off(); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(off, ac.getRaw(), kFujitsuAcStateLengthShort * 8); - - const uint8_t econo[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x09, 0xF6}; - ac.setCmd(kFujitsuAcCmdEcono); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(econo, ac.getRaw(), kFujitsuAcStateLengthShort * 8); - - const uint8_t powerful[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x39, 0xC6}; - ac.setCmd(kFujitsuAcCmdPowerful); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(powerful, ac.getRaw(), kFujitsuAcStateLengthShort * 8); - - const uint8_t stepvert[kFujitsuAcStateLengthShort] = { - 0x14, 0x63, 0x30, 0x10, 0x10, 0x6C, 0x93}; - ac.setCmd(kFujitsuAcCmdStepVert); - ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); - EXPECT_STATE_EQ(stepvert, ac.getRaw(), kFujitsuAcStateLengthShort * 8); -} - -// https://github.com/crankyoldgit/IRremoteESP8266/discussions/1701#discussioncomment-1910164 -TEST(TestIRFujitsuACClass, Discussion1701) { - IRFujitsuAC ac(kGpioUnused); - IRrecv irrecv(kGpioUnused); - IRac irac(kGpioUnused); - - const String expected_raw_output = - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s1182m448s390m448s390m448s1182m448s390" - "m448s8100"; - const String expected_arrew4e_str = - "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " - "Outside Quiet: Off, Timer: Off"; - const uint8_t expected_arrew4e_state[kFujitsuAcStateLength] = - {0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, - 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4C}; - - // Method used in `TurnOnFujitsuAC` - ac.begin(); - ac.setModel(ARREW4E); - ac.setSwing(kFujitsuAcSwingOff); - ac.setMode(kFujitsuAcModeCool); - ac.setFanSpeed(kFujitsuAcFanHigh); - ac.setTemp(24); // 24C - ac.setCmd(kFujitsuAcCmdTurnOn); - ASSERT_EQ(expected_arrew4e_str, ac.toString()); - ac.send(); - ac._irsend.makeDecodeResult(); - // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) - EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); - EXPECT_EQ(expected_arrew4e_str, - IRAcUtils::resultAcToString(&ac._irsend.capture)); - EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, - ac._irsend.capture.bits); - EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); - - // Now try to reproduce it via the IRac class. - ac._irsend.reset(); - ac.stateReset(); - ASSERT_NE(expected_arrew4e_str, ac.toString()); - - irac.fujitsu(&ac, - ARREW4E, // Model - true, // Power - stdAc::opmode_t::kCool, // Mode - true, // Celsius - 24, // Degrees - stdAc::fanspeed_t::kHigh, // Fan speed - stdAc::swingv_t::kOff, // Vertical swing - stdAc::swingh_t::kOff, // Horizontal swing - false, // Quiet - false, // Turbo (Powerful) - false, // Econo - false, // Filter - false); // Clean - ASSERT_EQ(expected_arrew4e_str, ac.toString()); - ac._irsend.makeDecodeResult(); - // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) - EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); - EXPECT_EQ(expected_arrew4e_str, - IRAcUtils::resultAcToString(&ac._irsend.capture)); - EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, - ac._irsend.capture.bits); - EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); - // Success. -} - -TEST(TestIRFujitsuACClass, toCommon_Issue1780HandlePrev) { - IRFujitsuAC ac(kGpioUnused); - ac.setMode(kFujitsuAcModeCool); - ac.setTemp(20); - ac.setFanSpeed(kFujitsuAcFanQuiet); - ac.setSwing(kFujitsuAcSwingBoth); - ac.on(); - ASSERT_TRUE(ac.toCommon().power); - stdAc::state_t prev = ac.toCommon(); // Copy in the state. - ac.off(); - ASSERT_FALSE(ac.toCommon().power); - ac.send(); // This should send a short code. - prev.degrees = 27; - ac.stateReset(); - IRrecv irrecv(kGpioUnused); - ac._irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); - ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); - ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); - ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - ASSERT_FALSE(ac.toCommon().power); - ASSERT_TRUE(ac.toCommon().celsius); - ASSERT_EQ(16, ac.toCommon().degrees); - ASSERT_EQ(27, ac.toCommon(&prev).degrees); - ASSERT_FALSE(ac.toCommon().quiet); - - ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); - ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon(&prev).mode); - ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); - ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon(&prev).fanspeed); - ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); - ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); - // Unsupported. - ASSERT_FALSE(ac.toCommon().filter); - ASSERT_FALSE(ac.toCommon().clean); - ASSERT_FALSE(ac.toCommon().turbo); - ASSERT_FALSE(ac.toCommon().light); - ASSERT_FALSE(ac.toCommon().econo); - ASSERT_FALSE(ac.toCommon().beep); - ASSERT_EQ(-1, ac.toCommon().sleep); - ASSERT_EQ(-1, ac.toCommon().clock); - - stdAc::state_t result_inc_prev; - ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result_inc_prev, - &prev)); - ASSERT_EQ(27, result_inc_prev.degrees); - ASSERT_EQ(stdAc::opmode_t::kCool, result_inc_prev.mode); - ASSERT_EQ(stdAc::fanspeed_t::kMin, result_inc_prev.fanspeed); -} - -TEST(TestIRFujitsuACClass, Improve10CHeat) { - IRFujitsuAC ac(kGpioUnused); - // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G2 - const uint8_t Arrah2u_10CHeatOn[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x41, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x64}; - ASSERT_FALSE(ac.get10CHeat()); - ac.setRaw(Arrah2u_10CHeatOn, 16); - ASSERT_TRUE(ac.get10CHeat()); - ASSERT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_TRUE(ac.get10CHeat()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); - - ac.stateReset(); - // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G8 - const uint8_t Arreg1u_10CHeatOn[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x61, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x44}; - ASSERT_FALSE(ac.get10CHeat()); - ac.setRaw(Arreg1u_10CHeatOn, 16); - ASSERT_TRUE(ac.get10CHeat()); - ASSERT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " - "Command: N/A, Timer: Off", - ac.toString()); - EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); - ASSERT_TRUE(ac.get10CHeat()); - EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); - EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); -} - -// Tests for Fujitsu A/C 264 methods. -TEST(TestDecodeFujitsuAc264, RealExample) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - uint16_t rawData[531] = {3378, 1534, 498, 322, 526, 294, 524, 1116, 522, 302, - 492, 1166, 472, 350, 470, 352, 466, 352, 468, 1172, 468, 1176, 466, 352, - 466, 354, 466, 356, 466, 1176, 464, 1174, 468, 354, 466, 356, 464, 352, - 468, 356, 466, 352, 466, 354, 466, 356, 464, 354, 468, 352, 466, 356, 466, - 352, 466, 356, 464, 356, 464, 1178, 464, 356, 466, 356, 464, 356, 462, 356, - 466, 356, 464, 356, 462, 356, 464, 1178, 464, 356, 466, 356, 462, 358, 464, - 354, 464, 1176, 464, 1178, 464, 1180, 462, 1176, 454, 1188, 442, 1202, 440, - 1198, 442, 380, 464, 1176, 440, 382, 442, 1200, 438, 1202, 440, 380, 440, - 382, 438, 382, 440, 404, 416, 382, 440, 380, 440, 382, 438, 380, 440, 380, - 440, 1202, 440, 404, 414, 1202, 440, 382, 440, 380, 438, 1204, 440, 380, - 440, 382, 436, 400, 422, 1226, 416, 1226, 416, 404, 416, 406, 414, 406, - 414, 406, 414, 406, 416, 404, 416, 404, 418, 404, 414, 406, 416, 404, 418, - 404, 416, 404, 416, 404, 416, 404, 414, 404, 416, 406, 416, 404, 414, 404, - 416, 406, 414, 406, 414, 406, 416, 404, 414, 406, 414, 408, 414, 404, 414, - 408, 412, 406, 416, 404, 414, 406, 414, 408, 414, 406, 414, 408, 412, 406, - 414, 408, 412, 408, 414, 404, 414, 408, 412, 406, 414, 404, 416, 406, 412, - 408, 414, 404, 414, 410, 412, 408, 412, 406, 412, 408, 414, 408, 414, 406, - 412, 1230, 412, 408, 414, 408, 412, 1228, 414, 408, 412, 408, 412, 406, - 414, 408, 412, 1228, 414, 1228, 412, 408, 416, 408, 410, 408, 412, 408, - 414, 408, 412, 408, 412, 410, 410, 408, 410, 408, 412, 408, 414, 408, 412, - 408, 414, 408, 412, 1228, 414, 408, 412, 408, 410, 410, 410, 410, 412, 406, - 412, 408, 412, 410, 410, 1232, 410, 408, 410, 410, 410, 412, 386, 1254, - 412, 410, 408, 412, 412, 408, 410, 1230, 412, 1232, 410, 1230, 408, 1232, - 410, 1232, 386, 432, 386, 436, 408, 410, 386, 434, 404, 418, 386, 434, 386, - 432, 390, 432, 386, 1254, 388, 1254, 386, 434, 388, 432, 388, 434, 386, - 436, 384, 456, 362, 1256, 386, 434, 386, 434, 386, 434, 384, 436, 386, 434, - 386, 1256, 386, 458, 360, 434, 388, 1254, 386, 434, 388, 434, 386, 434, - 388, 434, 384, 460, 360, 436, 384, 458, 360, 436, 386, 460, 362, 434, 386, - 458, 360, 460, 360, 460, 362, 460, 360, 458, 362, 460, 360, 458, 360, 460, - 362, 460, 360, 460, 360, 460, 362, 456, 362, 458, 360, 462, 360, 458, 362, - 458, 362, 456, 364, 458, 362, 460, 360, 460, 362, 458, 362, 458, 360, 460, - 360, 458, 360, 1282, 360, 1280, 362, 1280, 360, 1282, 360, 1280, 360, 1280, - 360, 1282, 360, 1278, 362, 1280, 360, 1282, 360, 1280, 362, 1280, 360, - 1282, 360, 1280, 362, 1280, 360, 1282, 360, 1282, 362, 1280, 360, 1280, - 360, 1282, 358, 1282, 360, 1282, 360, 1282, 358, 1282, 360, 462, 358, 462, - 358, 462, 360, 460, 360, 462, 360, 484, 334, 462, 360, 462, 358, 458, 360, - 462, 356, 1282, 360, 1282, 360, 1308, 332, 486, 334, 1284, 358, 462, 360 - }; // FUJITSU_AC264 - - uint8_t expectedState[kFujitsuAc264StateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x1A, 0x40, - 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x06, 0x00, 0x01, 0x11, 0x1F, 0x60, 0x10, 0x24, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, - 0x5C}; - - irsend.begin(); - irsend.reset(); - irsend.sendRaw(rawData, 531, 38000); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC264, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAc264Bits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); - IRFujitsuAC264 ac(kGpioUnused); - ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ("Power: On, Mode: 1 (Cool), Temp: 25C, Temp (Auto): 0C, " - "Fan: 0 (Auto), Fan Angle: 15 (Stay), Swing: Off, Economy: Off, " - "Clean: Off, Command: Cool, Current Time: 17:31, Sleep Timer: Off, " - "On Timer: Off, Off Timer: Off", - ac.toString()); -} - -TEST(TestDecodeFujitsuAc264, SyntheticExample) { - IRsendTest irsend(kGpioUnused); - IRrecv irrecv(kGpioUnused); - - uint8_t sendCode[kFujitsuAc264StateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x1A, 0x40, - 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x06, 0x00, 0x01, 0x11, 0x1F, 0x60, 0x10, 0x24, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, - 0x5C}; - - irsend.begin(); - irsend.reset(); - irsend.sendFujitsuAC264(sendCode); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC264, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAc264Bits, irsend.capture.bits); - EXPECT_STATE_EQ(sendCode, irsend.capture.state, irsend.capture.bits); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s390m448s1182m448s390m448s1182m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s1182m448s390" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s1182" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s1182m448s1182m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s390m448s390m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s1182m448s1182m448s390m448s1182m448s390" - "m448s8100", - irsend.outputStr()); -} - -TEST(TestFujitsuAc264Class, toCommon) { - IRFujitsuAC264 ac(kGpioUnused); - ac.setPower(true); - ac.setMode(kFujitsuAc264ModeCool); - ac.setTemp(20); - ac.setFanSpeed(kFujitsuAc264FanSpeedHigh); - ac.setSwing(true); - ac.setEcoFan(false); - ac.setClock(1 * 60 + 23); // "1:23" - ac.setSleepTimer(2 * 60); - // Now test it. - ASSERT_EQ(decode_type_t::FUJITSU_AC264, ac.toCommon().protocol); - ASSERT_EQ(-1, ac.toCommon().model); - ASSERT_TRUE(ac.toCommon().power); - ASSERT_TRUE(ac.toCommon().celsius); - ASSERT_EQ(20, ac.toCommon().degrees); - ASSERT_FALSE(ac.toCommon().quiet); - ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); - ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); - ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); - ASSERT_EQ(83, ac.toCommon().clock); - ASSERT_EQ(120, ac.toCommon().sleep); - // Unsupported. - ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); - ASSERT_FALSE(ac.toCommon().turbo); - ASSERT_FALSE(ac.toCommon().clean); - ASSERT_FALSE(ac.toCommon().econo); - ASSERT_FALSE(ac.toCommon().light); - ASSERT_FALSE(ac.toCommon().filter); - ASSERT_FALSE(ac.toCommon().beep); -} - -TEST(TestFujitsuAc264Class, Power) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Value from getPower is not updated untill ac.send() - ac.on(); - EXPECT_FALSE(ac.getPower()); - - ac.on(); - ac.send(); - EXPECT_TRUE(ac.getPower()); - - // Value from getPower is not updated untill ac.send() - ac.off(); - EXPECT_TRUE(ac.getPower()); - - ac.off(); - ac.send(); - EXPECT_FALSE(ac.getPower()); - - // Value from getPower is not updated untill ac.send() - ac.setPower(true); - EXPECT_FALSE(ac.getPower()); - - ac.setPower(true); - ac.send(); - EXPECT_TRUE(ac.getPower()); - - // Value from getPower is not updated untill ac.send() - ac.setPower(false); - EXPECT_TRUE(ac.getPower()); - - ac.setPower(false); - ac.send(); - EXPECT_FALSE(ac.getPower()); -} - -TEST(TestFujitsuAc264Class, Temperature) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.setMode(kFujitsuAc264ModeHeat); - // Value over maximum is fixed to maximum (30C) - ac.setTemp(40); - EXPECT_EQ(30, ac.getTemp()); - - // Value under minimum is fixed to minimum (16C in Heat) - ac.setTemp(10); - EXPECT_EQ(16, ac.getTemp()); - - ac.setMode(kFujitsuAc264ModeCool); - // Value over maximum is fixed to maximum (30C) - ac.setTemp(40); - EXPECT_EQ(30, ac.getTemp()); - - // Value under minimum is fixed to minimum (18C) - ac.setTemp(10); - EXPECT_EQ(18, ac.getTemp()); - - // Valid values in suppoerted range - ac.setMode(kFujitsuAc264ModeHeat); - for (float i = 16; i <= 30; i += 0.5) { - ac.setTemp(i); - EXPECT_EQ(i, ac.getTemp()); - } - - // Valid values in suppoerted range - ac.setMode(kFujitsuAc264ModeCool); - for (float i = 18; i <= 30; i += 0.5) { - ac.setTemp(i); - EXPECT_EQ(i, ac.getTemp()); - } - - // Value in supported range, but not multiple of 0.5 - // Fractional part of the value which is not multiple of 0.5 is truncated. - ac.setTemp(22.9); - EXPECT_EQ(22.5, ac.getTemp()); - - ac.setTemp(20.1); - EXPECT_EQ(20, ac.getTemp()); -} - -TEST(TestFujitsuAc264Class, TemperatureAuto) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Value over maximum is fixed to maximum (+2C) - ac.setTempAuto(10); - EXPECT_EQ(2, ac.getTempAuto()); - - // Value under minimum is fixed to minimum (-2C) - ac.setTempAuto(-10); - EXPECT_EQ(-2, ac.getTempAuto()); - - // Valid values in suppoerted range - for (float i = -2; i <= 2; i += 0.5) { - ac.setTempAuto(i); - EXPECT_EQ(i, ac.getTempAuto()); - } - - // Value in supported range, but not multiple of 0.5 - // Fractional part of the value which is not multiple of 0.5 is truncated. - ac.setTempAuto(0.8); - EXPECT_EQ(0.5, ac.getTempAuto()); - - ac.setTempAuto(1.2); - EXPECT_EQ(1, ac.getTempAuto()); - - ac.setTempAuto(-1.4); - EXPECT_EQ(-1, ac.getTempAuto()); -} - -TEST(TestFujitsuAc264Class, Mode) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Unexpected value should default to Auto. - ac.setMode(2); - EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); - EXPECT_FALSE(ac.isWeakDry()); - - // Unexpected value should default to Auto. - ac.setMode(255); - EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); - EXPECT_FALSE(ac.isWeakDry()); - - ac.setMode(kFujitsuAc264ModeAuto); - EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); - EXPECT_FALSE(ac.isWeakDry()); - - ac.setMode(kFujitsuAc264ModeCool); - EXPECT_EQ(kFujitsuAc264ModeCool, ac.getMode()); - EXPECT_FALSE(ac.isWeakDry()); - - ac.setMode(kFujitsuAc264ModeFan); - EXPECT_EQ(kFujitsuAc264ModeFan, ac.getMode()); - EXPECT_FALSE(ac.isWeakDry()); - - ac.setMode(kFujitsuAc264ModeHeat); - EXPECT_EQ(kFujitsuAc264ModeHeat, ac.getMode()); - EXPECT_FALSE(ac.isWeakDry()); - - ac.setMode(kFujitsuAc264ModeDry); - EXPECT_EQ(kFujitsuAc264ModeDry, ac.getMode()); - EXPECT_FALSE(ac.isWeakDry()); - - ac.setMode(kFujitsuAc264ModeDry, true); - EXPECT_EQ(kFujitsuAc264ModeDry, ac.getMode()); - EXPECT_TRUE(ac.isWeakDry()); -} - -TEST(TestFujitsuAc264Class, FanSpeed) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Unexpected value should default to Auto. - ac.setFanSpeed(0); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - // Unexpected value should default to Auto. - ac.setFanSpeed(255); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - // Unexpected value should default to Auto. - ac.setFanSpeed(2); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - // Unexpected value should default to Auto. - ac.setFanSpeed(4); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - // Unexpected value should default to Auto. - ac.setFanSpeed(5); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - // Unexpected value should default to Auto. - ac.setFanSpeed(7); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - // Beyond Max should default to Auto. - ac.setFanSpeed(kFujitsuAc264FanSpeedHigh + 1); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - ac.setFanSpeed(kFujitsuAc264FanSpeedAuto); - EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); - - ac.setFanSpeed(kFujitsuAc264FanSpeedQuiet); - EXPECT_EQ(kFujitsuAc264FanSpeedQuiet, ac.getFanSpeed()); - - ac.setFanSpeed(kFujitsuAc264FanSpeedLow); - EXPECT_EQ(kFujitsuAc264FanSpeedLow, ac.getFanSpeed()); - - ac.setFanSpeed(kFujitsuAc264FanSpeedMed); - EXPECT_EQ(kFujitsuAc264FanSpeedMed, ac.getFanSpeed()); - - ac.setFanSpeed(kFujitsuAc264FanSpeedHigh); - EXPECT_EQ(kFujitsuAc264FanSpeedHigh, ac.getFanSpeed()); -} - -TEST(TestFujitsuAc264Class, FanAngle) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Unexpected value should default to Stay. - ac.setFanAngle(0); - EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); - - // Unexpected value should default to Stay. - ac.setFanAngle(255); - EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); - - // Unexpected value should default to Stay. - ac.setFanAngle(8); - EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); - - // Unexpected value should default to Stay. - ac.setFanAngle(13); - EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); - - // Unexpected value should default to Stay. - ac.setFanAngle(32); - EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngle1); - EXPECT_EQ(kFujitsuAc264FanAngle1, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngle2); - EXPECT_EQ(kFujitsuAc264FanAngle2, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngle3); - EXPECT_EQ(kFujitsuAc264FanAngle3, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngle4); - EXPECT_EQ(kFujitsuAc264FanAngle4, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngle5); - EXPECT_EQ(kFujitsuAc264FanAngle5, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngle6); - EXPECT_EQ(kFujitsuAc264FanAngle6, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngle7); - EXPECT_EQ(kFujitsuAc264FanAngle7, ac.getFanAngle()); - - ac.setFanAngle(kFujitsuAc264FanAngleStay); - EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); -} - -TEST(TestFujitsuAc264Class, Swing) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.setSwing(true); - EXPECT_TRUE(ac.getSwing()); - - ac.setSwing(false); - EXPECT_FALSE(ac.getSwing()); -} - -TEST(TestFujitsuAc264Class, Economy) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.setEconomy(true); - EXPECT_TRUE(ac.getEconomy()); - - ac.setEconomy(false); - EXPECT_FALSE(ac.getEconomy()); -} - -TEST(TestFujitsuAc264Class, Clean) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.setClean(true); - EXPECT_TRUE(ac.getClean()); - - ac.setClean(false); - EXPECT_FALSE(ac.getClean()); -} - -TEST(TestFujitsuAc264Class, Clock) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Unexpected value should default to 0. - ac.setClock(2000); - EXPECT_EQ(0, ac.getClock()); - - // Valid values in suppoerted range - for (uint16_t i = 0; i < 1440; ++i) { - ac.setClock(i); - EXPECT_EQ(i, ac.getClock()); - } -} - -TEST(TestFujitsuAc264Class, SleepTimer) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Unexpected value should default to 0 (invalid). - ac.setSleepTimer(12 * 60 + 1); - EXPECT_EQ(0, ac.getSleepTimer()); - - // Valid values in suppoerted range - for (uint16_t i = 0; i <= 12; ++i) { - ac.setSleepTimer(i * 60); - EXPECT_EQ(i * 60, ac.getSleepTimer()); - } -} - -TEST(TestFujitsuAc264Class, OnTimer) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Unexpected value is ignored. - ac.setOnTimer(144); - EXPECT_EQ(0, ac.getOnTimer()); - - // Valid values in suppoerted range - for (uint8_t i = 0; i < 144; ++i) { - ac.setOnTimer(i); - EXPECT_EQ(i, ac.getOnTimer()); - } - - // Unexpected value is ignored. - ac.setOnTimer(255); - EXPECT_EQ(143, ac.getOnTimer()); - - // On timer enabled - ac.setTimerEnable(kFujitsuAc264OnTimerEnable); - EXPECT_EQ(kFujitsuAc264OnTimerEnable, ac.getTimerEnable()); - - // On timer disabled - ac.setTimerEnable(kFujitsuAc264OnOffTimerDisable); - EXPECT_EQ(kFujitsuAc264OnOffTimerDisable, ac.getTimerEnable()); -} - -TEST(TestFujitsuAc264Class, OffTimer) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - // Unexpected value is ignored. - ac.setOffTimer(144); - EXPECT_EQ(0, ac.getOffTimer()); - - // Valid values in suppoerted range - for (uint8_t i = 0; i < 144; ++i) { - ac.setOffTimer(i); - EXPECT_EQ(i, ac.getOffTimer()); - } - - // Unexpected value is ignored. - ac.setOffTimer(255); - EXPECT_EQ(143, ac.getOffTimer()); - - // Off timer enabled - ac.setTimerEnable(kFujitsuAc264OffTimerEnable); - EXPECT_EQ(kFujitsuAc264OffTimerEnable, ac.getTimerEnable()); - - // On & off timer enabled - ac.setTimerEnable(kFujitsuAc264OnOffTimerEnable); - EXPECT_EQ(kFujitsuAc264OnOffTimerEnable, ac.getTimerEnable()); - - // On & Off timer disabled - ac.setTimerEnable(kFujitsuAc264OnOffTimerDisable); - EXPECT_EQ(kFujitsuAc264OnOffTimerDisable, ac.getTimerEnable()); -} - -TEST(TestFujitsuAc264Class, Sterilization) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.on(); - ac.send(); - - // When AC is powered on, sterilization is ignored. - ac.toggleSterilization(); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.off(); - ac.send(); - - // When AC is powered off, sterilization is accepted. - ac.toggleSterilization(); - EXPECT_EQ(kFujitsuAc264SpCmdToggleSterilization, ac.getCmd()); -} - -TEST(TestFujitsuAc264Class, OutsideQuiet) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.on(); - ac.send(); - - // When AC is powered on, outside quiet is ignored. - ac.setOutsideQuiet(true); - ac.send(); - EXPECT_FALSE(ac.getOutsideQuiet()); - - ac.off(); - ac.send(); - - // When AC is powered off, outside quiet is accepted. - ac.setOutsideQuiet(true); - ac.send(); - EXPECT_TRUE(ac.getOutsideQuiet()); - - ac.setOutsideQuiet(false); - ac.send(); - EXPECT_FALSE(ac.getOutsideQuiet()); -} - -TEST(TestFujitsuAc264Class, EcoFan) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.on(); - ac.send(); - - // When AC is powered on, eco fan is ignored. - ac.setEcoFan(true); - ac.send(); - EXPECT_FALSE(ac.getEcoFan()); - - ac.off(); - ac.send(); - - // When AC is powered off, eco fan is accepted. - ac.setEcoFan(true); - ac.send(); - EXPECT_TRUE(ac.getEcoFan()); - - ac.setEcoFan(false); - ac.send(); - EXPECT_FALSE(ac.getEcoFan()); -} - -TEST(TestFujitsuAc264Class, Powerful) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.off(); - ac.send(); - - // When AC is powered off, powerful is ignored. - ac.togglePowerful(); - ac.send(); - EXPECT_EQ(kFujitsuAc264SpCmdTurnOff, ac.getCmd()); - - ac.on(); - ac.send(); - - // When AC is powered on, powerful is accepted. - ac.togglePowerful(); - ac.send(); - EXPECT_EQ(kFujitsuAc264SpCmdTogglePowerful, ac.getCmd()); - - ac.togglePowerful(); - ac.send(); - EXPECT_EQ(kFujitsuAc264SpCmdTogglePowerful, ac.getCmd()); -} - -TEST(TestFujitsuAc264Class, NormalCommands) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - ac.on(); - ac.send(); - - // Special commands are ignored. - ac.setCmd(kFujitsuAc264SpCmdTogglePowerful); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.setCmd(kFujitsuAc264SpCmdTurnOff); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.setCmd(kFujitsuAc264SpCmdEcoFanOff); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.setCmd(kFujitsuAc264SpCmdEcoFanOn); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.setCmd(kFujitsuAc264SpCmdOutsideQuietOff); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.setCmd(kFujitsuAc264SpCmdOutsideQuietOn); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.setCmd(kFujitsuAc264SpCmdToggleSterilization); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - // Normal commands can be set. - ac.setCmd(kFujitsuAc264CmdCool); - EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdHeat); - EXPECT_EQ(kFujitsuAc264CmdHeat, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdDry); - EXPECT_EQ(kFujitsuAc264CmdDry, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdAuto); - EXPECT_EQ(kFujitsuAc264CmdAuto, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdFan); - EXPECT_EQ(kFujitsuAc264CmdFan, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdTemp); - EXPECT_EQ(kFujitsuAc264CmdTemp, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdSwing); - EXPECT_EQ(kFujitsuAc264CmdSwing, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdSleepTime); - EXPECT_EQ(kFujitsuAc264CmdSleepTime, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdEconomy); - EXPECT_EQ(kFujitsuAc264CmdEconomy, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdClean); - EXPECT_EQ(kFujitsuAc264CmdClean, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdFanSpeed); - EXPECT_EQ(kFujitsuAc264CmdFanSpeed, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdFanAngle); - EXPECT_EQ(kFujitsuAc264CmdFanAngle, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdCancelSleepTimer); - EXPECT_EQ(kFujitsuAc264CmdCancelSleepTimer, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdOnTimer); - EXPECT_EQ(kFujitsuAc264CmdOnTimer, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdOffTimer); - EXPECT_EQ(kFujitsuAc264CmdOffTimer, ac.getCmd()); - - ac.setCmd(kFujitsuAc264CmdCancelOnOffTimer); - EXPECT_EQ(kFujitsuAc264CmdCancelOnOffTimer, ac.getCmd()); -} - -TEST(TestFujitsuAc264Class, SpecialCommands) { - IRFujitsuAC264 ac(kGpioUnused); - ac.begin(); - - uint8_t expected_turnoff[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - uint8_t expected_powerful[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; - uint8_t expected_ecofanoff[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x51, 0xAE}; - uint8_t expected_ecofanon[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x50, 0xAF}; - uint8_t expected_outsidequietoff[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, - 0x40, 0x01, 0x00, 0x00, 0xFE, 0xBF, 0x00, 0x41}; - uint8_t expected_outsidequieton[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, - 0x40, 0x00, 0x00, 0x00, 0xFF, 0xBF, 0x00, 0x41}; - uint8_t expected_sterilization[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, - 0x60, 0x03, 0x00, 0x00, 0xFC, 0x9F, 0x00, 0x41}; - - ac.on(); - ac.send(); - - ac.togglePowerful(); - ac.send(); - EXPECT_STATE_EQ(expected_powerful, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); - EXPECT_EQ("Command: Powerful", ac.toString()); - - ac.off(); - ac.send(); - EXPECT_STATE_EQ(expected_turnoff, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); - EXPECT_EQ("Command: Power Off", ac.toString()); - - ac.setEcoFan(true); - ac.send(); - EXPECT_STATE_EQ(expected_ecofanon, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); - EXPECT_EQ("Command: Eco Fan On", ac.toString()); - - ac.setEcoFan(false); - ac.send(); - EXPECT_STATE_EQ(expected_ecofanoff, ac.getRaw(), 7 * 8); - EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); - EXPECT_EQ("Command: Eco Fan Off", ac.toString()); - - ac.setOutsideQuiet(true); - ac.send(); - EXPECT_STATE_EQ(expected_outsidequieton, ac.getRaw(), 16 * 8); - EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); - EXPECT_EQ("Command: Outside Quiet On", ac.toString()); - - ac.setOutsideQuiet(false); - ac.send(); - EXPECT_STATE_EQ(expected_outsidequietoff, ac.getRaw(), 16 * 8); - EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); - EXPECT_EQ("Command: Outside Quiet Off", ac.toString()); - - ac.toggleSterilization(); - ac.send(); - EXPECT_STATE_EQ(expected_sterilization, ac.getRaw(), 16 * 8); - EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); - EXPECT_EQ("Command: Sterilization", ac.toString()); -} +// Copyright 2017 Jonny Graham, David Conran +// Copyright 2023 Takeshi Shimizu +#include "IRac.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Fujitsu.h" +#include "gtest/gtest.h" + +// Tests for Fujitsu A/C methods. + +// Test sending typical data only. +TEST(TestIRFujitsuACClass, GetRawDefault) { + IRFujitsuAC ac(kGpioUnused); // AR-RAH2E + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); + ac.setCmd(kFujitsuAcCmdTurnOn); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", + ac.toString()); + + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x4D}; + ac.setModel(ARDB1); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 15 * 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawTurnOff) { + IRFujitsuAC ac(kGpioUnused); + ac.setModel(ARRAH2E); + ac.off(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", + ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepHoriz) { + IRFujitsuAC ac(kGpioUnused); + ac.stepHoriz(); + uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; + EXPECT_STATE_EQ(expected, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(H), Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepVert) { + IRFujitsuAC ac(kGpioUnused); + ac.setModel(ARRAH2E); + ac.stepVert(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(V), Timer: Off", + ac.toString()); + + ac.setModel(ARDB1); + ac.stepVert(); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, + ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: Step Swing(V)", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { + IRFujitsuAC ac(kGpioUnused); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingHoriz); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setTemp(25); + uint8_t expected[16] = {0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, + 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; + EXPECT_STATE_EQ(expected, ac.getRaw(), 16 * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 25C, " + "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawWithFan) { + IRFujitsuAC ac(kGpioUnused); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingHoriz); + ac.setMode(kFujitsuAcModeFan); + ac.setFanSpeed(kFujitsuAcFanMed); + ac.setTemp(20); // temp doesn't matter for fan + // but it is sent by the RC anyway + ac.setModel(ARRAH2E); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x40, 0x03, 0x22, 0x00, 0x00, 0x00, 0x20, 0x4B}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " + "Fan: 2 (Medium), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", + ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x40, 0x03, 0x02, 0x00, 0x00, 0x00, 0x8B}; + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " + "Fan: 2 (Medium), Command: N/A", ac.toString()); +} + +TEST(TestIRFujitsuACClass, SetRaw) { + IRFujitsuAC ac(kGpioUnused); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + uint8_t expected_default_arrah2e[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x20, 0xFD}; + EXPECT_STATE_EQ(expected_default_arrah2e, ac.getRaw(), + ac.getStateLength() * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, " + "Timer: Off", + ac.toString()); + // Now set a new state via setRaw(); + // This state is a real state from an AR-DB1 remote. + uint8_t new_state1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + ac.setRaw(new_state1, kFujitsuAcStateLength - 1); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_STATE_EQ(new_state1, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestSendFujitsuAC, GenerateMessage) { + IRFujitsuAC ac(kGpioUnused); + IRsendTest irsend(kGpioUnused); + ac.begin(); + irsend.begin(); + + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); + + EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcModeCool, ac.getMode()); + EXPECT_EQ(24, ac.getTemp()); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdStayOn, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLength); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s8100", + irsend.outputStr()); +} + +TEST(TestSendFujitsuAC, GenerateShortMessage) { + IRFujitsuAC ac(kGpioUnused); + IRsendTest irsend(kGpioUnused); + ac.begin(); + irsend.begin(); + + ac.off(); + + EXPECT_EQ(kFujitsuAcCmdTurnOff, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" + "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" + "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", + irsend.outputStr()); +} + +// Issue #275 +TEST(TestSendFujitsuAC, Issue275) { + IRFujitsuAC ac(kGpioUnused); + IRsendTest irsend(kGpioUnused); + ac.begin(); + irsend.begin(); + irsend.reset(); + + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + // Header + "m3324s1574" + // 0 0 1 0 1 0 0 0 (0x28) + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + // 1 1 0 0 0 1 1 0 (0xC6) + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + // 0 0 0 0 0 0 0 0 (0x00) + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 1 0 0 0 0 0 0 (0x40) + "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" + // 1 0 1 1 1 1 1 1 (0xBF) + "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + // Footer + "m448s8100", irsend.outputStr()); + + irsend.reset(); + // Per report in Issue #275 + uint16_t off[115] = { + 3350, 1650, + 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, + 450, 1250, 450}; + irsend.sendRaw(off, 115, 38); + EXPECT_EQ( + "f38000d50" + // Header + "m3350s1650" + // 0 0 1 0 1 0 0 0 (0x28) + "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" + // 1 1 0 0 0 1 1 0 (0xC6) + "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" + // 0 0 0 0 0 0 0 0 (0x00) + "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 1 0 0 0 0 0 0 (0x40) + "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" + // 1 0 1 1 1 1 1 1 (0xBF) + "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" + // Footer + "m450", + irsend.outputStr()); +} + +TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { + IRsendTest irsend(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + irsend.begin(); + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); + + irsend.reset(); + + ac.setModel(ARDB1); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { + IRsendTest irsend(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingVert); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setTemp(18); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + ASSERT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + uint8_t expected_arrah2e[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " + "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 1 (Swing(V)), Command: N/A, " + "Timer: Off", + ac.toString()); + + irsend.reset(); + + ac.setModel(ARDB1); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected_ardb1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAB}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " + "Fan: 4 (Quiet), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + irsend.begin(); + + irsend.reset(); + // "Off" Message recorded from an AR-DB1 remote. + uint16_t rawData[99] = { + 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, + 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, + 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, + 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, + 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, + 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, + 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, + 420, 388, 436}; + irsend.sendRaw(rawData, 99, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + irsend.begin(); + irsend.reset(); + uint16_t rawData1[243] = { + 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, + 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, + 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, + 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, + 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, + 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, + 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, + 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, + 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, + 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, + 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, + 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, + 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, + 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, + 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, + 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, + 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, + 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, + 436, 1216, 436}; + irsend.sendRaw(rawData1, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; + EXPECT_STATE_EQ(expected1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " + "Fan: 4 (Quiet), Command: N/A", ac.toString()); + + irsend.reset(); + uint16_t rawData2[243] = { + 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, + 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, + 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, + 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, + 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, + 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, + 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, + 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, + 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, + 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, + 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, + 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, + 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, + 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, + 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, + 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, + 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, + 436, 1214, 440}; + irsend.sendRaw(rawData2, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected2[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + EXPECT_STATE_EQ(expected2, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " + "Fan: 0 (Auto), Command: N/A", ac.toString()); +} + +TEST(TestDecodeFujitsuAC, Issue414) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + // Capture as supplied by arpmota + uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, + 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, + 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, + 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, + 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, + 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, + 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, + 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, + 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, + 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, + 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, + 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, + 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, + 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, + 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, + 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, + 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, + 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, + 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; + uint8_t state[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x2B}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 259, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + + // Resend it using the state this time. + irsend.reset(); + irsend.sendFujitsuAC(state, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" + "m448s8100", irsend.outputStr()); +} + +TEST(TestIRFujitsuACClass, toCommon) { + IRFujitsuAC ac(kGpioUnused); + ac.setMode(kFujitsuAcModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setSwing(kFujitsuAcSwingBoth); + + // Now test it. + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); + + // Check off mode which is special. + ac.off(); + ASSERT_FALSE(ac.toCommon().power); + ac.send(); + ac.stateReset(); + IRrecv irrecv(kGpioUnused); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + + // Now test it. + EXPECT_EQ( // Off mode technically has no temp, mode, fan, etc. + "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", + ac.toString()); + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_FALSE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(16, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeFujitsuAC, Issue716) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + // Powerful command from a raw data capture. + // Capture as supplied by u4mzu4 + uint16_t rawData[115] = { + 3320, 1610, 432, 406, 432, 406, 432, 1220, 432, 406, 432, 1192, 458, 406, + 432, 406, 432, 406, 432, 1218, 432, 1220, 432, 406, 432, 406, 432, 406, + 432, 1192, 458, 1192, 460, 406, 432, 406, 432, 406, 432, 406, 432, 406, + 432, 406, 432, 406, 432, 408, 432, 406, 432, 406, 430, 406, 432, 406, 432, + 406, 432, 1190, 460, 406, 432, 408, 430, 406, 432, 406, 432, 406, 432, + 406, 432, 406, 434, 1192, 458, 406, 432, 406, 432, 406, 432, 1194, 458, + 406, 432, 406, 432, 1194, 456, 1196, 454, 1220, 432, 406, 432, 406, 432, + 408, 430, 1194, 458, 1194, 456, 406, 432, 406, 430, 406, 432, 1194, 458, + 1194, 458}; // FUJITSU_AC + uint8_t powerful[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 115, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLengthShort * 8, irsend.capture.bits); + EXPECT_STATE_EQ(powerful, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: Powerful, Outside Quiet: Off, " + "Timer: Off", + ac.toString()); + + // Economy (just from the state) + uint8_t econo[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0x09, 0xF6}; + // Make sure we can't accidentally inherit the correct model. + ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, + fujitsu_ac_remote_model_t::ARREB1E); + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(econo, kFujitsuAcStateLengthShort); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 16C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: Econo, Outside Quiet: Off, " + "Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, OutsideQuiet) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + ASSERT_NE(fujitsu_ac_remote_model_t::ARDB1, + fujitsu_ac_remote_model_t::ARREB1E); + ASSERT_NE(fujitsu_ac_remote_model_t::ARRAH2E, + fujitsu_ac_remote_model_t::ARREB1E); + // States as supplied by u4mzu4 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/716#issuecomment-495852309 + uint8_t off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; + uint8_t on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xAF}; + // Make sure we can't accidentally inherit the correct model. + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(off, kFujitsuAcStateLength); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_FALSE(ac.getOutsideQuiet()); + // We can really only tell the difference between ARRAH2E & ARREB1E if + // the option is set. Otheriwse they appear the same. + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", ac.toString()); + ac.setModel(fujitsu_ac_remote_model_t::ARREB1E); + EXPECT_EQ( + "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: N/A, Outside Quiet: Off, Timer: Off", + ac.toString()); + + // Make sure we can't accidentally inherit the correct model. + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(on, kFujitsuAcStateLength); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREB1E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_TRUE(ac.getOutsideQuiet()); + EXPECT_EQ( + "Model: 3 (ARREB1E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Command: N/A, Outside Quiet: On, Timer: Off", + ac.toString()); + + ac.setOutsideQuiet(false); + EXPECT_FALSE(ac.getOutsideQuiet()); + ac.setOutsideQuiet(true); + EXPECT_TRUE(ac.getOutsideQuiet()); + ac.setOutsideQuiet(false); + EXPECT_FALSE(ac.getOutsideQuiet()); +} + +TEST(TestIRFujitsuACClass, toggleSwing) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + ac.begin(); + ac.setModel(ARJW2); + ac.setSwing(kFujitsuAcSwingOff); + ac.setCmd(kFujitsuAcCmdStayOn); + ASSERT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingHoriz, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); + + // Both + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + ac.toggleSwingVert(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + ac.toggleSwingHoriz(); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + + EXPECT_EQ( + "Model: 4 (ARJW2), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), Command: Toggle Swing(H)", + ac.toString()); + + // Test without the update set. + ac.toggleSwingHoriz(false); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdToggleSwingHoriz, ac.getCmd()); + ac.toggleSwingVert(false); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdToggleSwingVert, ac.getCmd()); +} + +TEST(TestDecodeFujitsuAC, Issue726) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRFujitsuAC ac(kGpioUnused); + + // fan:auto mode:auto temp:24 power:on + // Capture as supplied by huexpub + // Rawdata was very messy. Had to use `./auto_analyse_raw_data.py -r 250` to + // get it to parse due to timings being above tolerances. + uint8_t auto_auto_on_24[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; + irsend.begin(); + irsend.reset(); + irsend.sendFujitsuAC(auto_auto_on_24, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(auto_auto_on_24, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 24C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, Clean) { + IRFujitsuAC ac(kGpioUnused); + // Data from: + // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 + uint8_t clean_off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; + uint8_t clean_on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08}; + ac.setRaw(clean_on, kFujitsuAcStateLength); + EXPECT_TRUE(ac.getClean()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", + ac.toString()); + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + EXPECT_STATE_EQ(clean_off, ac.getRaw(), ac.getStateLength() * 8) + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + EXPECT_STATE_EQ(clean_on, ac.getRaw(), ac.getStateLength() * 8) + ac.setRaw(clean_off, kFujitsuAcStateLength); + EXPECT_FALSE(ac.getClean()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. + ac.setClean(true); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + // But ARRY4 does. + ac.setModel(fujitsu_ac_remote_model_t::ARRY4); + EXPECT_TRUE(ac.getClean()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: On, Filter: Off, Swing: 0 (Off), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, Filter) { + IRFujitsuAC ac(kGpioUnused); + // Data from: + // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit#gid=646887633&range=A27:B30 + uint8_t filter_on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x07}; + uint8_t filter_off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10}; + ac.setRaw(filter_on, kFujitsuAcStateLength); + EXPECT_TRUE(ac.getFilter()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", + ac.toString()); + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + ac.setRaw(filter_off, kFujitsuAcStateLength); + EXPECT_FALSE(ac.getFilter()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. + ac.setFilter(true); + EXPECT_FALSE(ac.getFilter()); + // But ARRY4 does. + ac.setModel(fujitsu_ac_remote_model_t::ARRY4); + EXPECT_TRUE(ac.getFilter()); + EXPECT_EQ( + "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 0 (Auto), Clean: Off, Filter: On, Swing: 0 (Off), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, Timers) { + IRFujitsuAC ac(kGpioUnused); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1255#issuecomment-686445720 + const uint8_t timer_on_12h[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x30, 0x01, 0x00, 0x00, 0xAD, 0x20, 0x32}; + ac.setRaw(timer_on_12h, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); + EXPECT_EQ(12 * 60, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffSleepTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, On Timer: 12:00", + ac.toString()); + + const uint8_t timer_on_8h30m[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x30, 0x01, 0x00, 0xE0, 0x9F, 0x20, 0x60}; + ac.setRaw(timer_on_8h30m, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOnTimer, ac.getTimerType()); + EXPECT_EQ(8 * 60 + 30, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffSleepTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, On Timer: 08:30", + ac.toString()); + + // TIMER OFF 11H + const uint8_t timer_off_11h[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x20, 0x01, 0x94, 0x0A, 0x00, 0x20, 0x51}; + ac.setRaw(timer_off_11h, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); + EXPECT_EQ(11 * 60, ac.getOffSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 11:00", + ac.toString()); + + // TIMER OFF 0.5H + const uint8_t timer_off_30m[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x20, 0x01, 0x1E, 0x08, 0x00, 0x20, 0xC9}; + ac.setRaw(timer_off_30m, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcOffTimer, ac.getTimerType()); + EXPECT_EQ(30, ac.getOffSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", + ac.toString()); + + // TIMER SLEEP 3H + const uint8_t timer_sleep_3h[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0xA0, 0x10, 0x01, 0xB4, 0x08, 0x00, 0x20, 0x43}; + ac.setRaw(timer_sleep_3h, kFujitsuAcStateLength); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(kFujitsuAcSleepTimer, ac.getTimerType()); + EXPECT_EQ(3 * 60, ac.getOffSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", + ac.toString()); + + // Re-construct a known timer state from scratch. + ac.stateReset(); + ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); + ac.setPower(true); + ac.setMode(kFujitsuAcModeAuto); + ac.setTemp(26); + ac.setFanSpeed(1); + ac.setClean(false); + ac.setFilter(false); + ac.setSwing(0); + + ac.setOffTimer(30); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_STATE_EQ(timer_off_30m, ac.getRaw(), ac.getStateLength() * 8); + + ac.setOnTimer(12 * 60); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, On Timer: 12:00", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ(12 * 60, ac.getOnTimer()); + EXPECT_TRUE(ac.getOnTimer()); + EXPECT_STATE_EQ(timer_on_12h, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ(12 * 60, ac.getOnTimer()); + EXPECT_TRUE(ac.getOnTimer()); + + ac.setSleepTimer(3 * 60); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_STATE_EQ(timer_sleep_3h, ac.getRaw(), ac.getStateLength() * 8); +} + +TEST(TestIRFujitsuACClass, ARREW4E) { + IRFujitsuAC ac(kGpioUnused); + + uint8_t on_18_cool_auto[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x50, 0x01, 0x00, 0x21, 0x03, 0x20, 0x20, 0x1A}; + + EXPECT_TRUE(ac.validChecksum(on_18_cool_auto, kFujitsuAcStateLength)); + ac.setRaw(on_18_cool_auto, kFujitsuAcStateLength); + EXPECT_EQ(0, ac.getId()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); + EXPECT_EQ(18, ac.getTemp()); + + uint8_t mode_C_power_on_18[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x51, 0x01, 0x00, 0x17, 0x07, 0x54, 0x20, 0xEB}; + EXPECT_TRUE(ac.validChecksum(mode_C_power_on_18, kFujitsuAcStateLength)); + ac.setRaw(mode_C_power_on_18, kFujitsuAcStateLength); + EXPECT_EQ(2, ac.getId()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); + EXPECT_EQ(18, ac.getTemp()); + + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + irsend.begin(); + irsend.reset(); + irsend.sendFujitsuAC(mode_C_power_on_18, kFujitsuAcStateLength); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(mode_C_power_on_18, irsend.capture.state, + irsend.capture.bits); +} + +TEST(TestDecodeFujitsuAC, Issue1455) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + uint16_t rawData[259] = { + 3220, 1700, 354, 446, 380, 448, 380, 1296, 352, 446, 382, 1296, 354, 448, + 380, 448, 378, 446, 382, 1296, 352, 1296, 354, 448, 378, 446, 380, 446, + 382, 1294, 354, 1270, 380, 448, 380, 446, 380, 448, 380, 446, 380, 450, + 378, 448, 380, 446, 380, 448, 380, 448, 380, 448, 380, 450, 376, 450, 380, + 448, 378, 1298, 352, 448, 378, 448, 380, 448, 380, 448, 378, 450, 378, + 450, 378, 448, 378, 1296, 354, 446, 382, 446, 380, 448, 378, 448, 380, + 1296, 352, 1296, 354, 1296, 354, 1272, 376, 1272, 378, 1296, 354, 1294, + 354, 1296, 354, 446, 380, 448, 378, 1296, 354, 448, 378, 448, 378, 448, + 380, 446, 380, 1272, 378, 446, 380, 450, 378, 448, 378, 1296, 354, 1296, + 354, 446, 380, 448, 378, 448, 378, 446, 382, 446, 380, 1296, 354, 1296, + 354, 446, 380, 1296, 354, 446, 380, 446, 380, 446, 380, 1294, 354, 448, + 380, 448, 380, 448, 380, 448, 380, 446, 380, 448, 380, 446, 380, 448, + 380, 446, 380, 446, 380, 448, 380, 446, 380, 448, 380, 448, 380, 446, 380, + 1296, 352, 446, 380, 1296, 354, 446, 380, 448, 380, 448, 380, 1296, 354, + 448, 378, 448, 380, 446, 380, 446, 382, 446, 380, 446, 382, 446, 380, + 1272, 378, 446, 380, 446, 382, 1294, 354, 446, 382, 1294, 354, 446, 382, + 446, 382, 446, 380, 448, 380, 448, 380, 448, 380, 448, 378, 1296, 354, + 446, 382, 446, 380, 1296, 354, 446, 382, 1296, 354, 446, 382, 1294, 354, + 446, 382, 446, 380, 446, 382}; // UNKNOWN 8383D7DE + irsend.sendRaw(rawData, 259, 38); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, irsend.capture.bits); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 19C, " + "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestIRFujitsuACClass, Heat10Deg) { + IRFujitsuAC ac(kGpioUnused); + const uint8_t heat_on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x10, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x69, 0x0B, 0x00, 0x23, 0x06, 0x23, 0x20, 0xEF}; + ac.setRaw(heat_on, kFujitsuAcStateLength); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + ac.toString()); + ac.stateReset(); + ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); + ac.setId(1); + ac.setMode(kFujitsuAcModeFan); + ac.setTemp(21); + ac.setFanSpeed(kFujitsuAcFanAuto); + ac.setSwing(0); + ac.setOutsideQuiet(false); + ac.setPower(true); + ac.set10CHeat(true); + EXPECT_TRUE(ac.get10CHeat()); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + ac.toString()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + + ac.set10CHeat(false); + EXPECT_FALSE(ac.get10CHeat()); + EXPECT_EQ( + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 21C, " + "Fan: 0 (Auto), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off", + ac.toString()); + + // For https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 + ac.set10CHeat(true); + EXPECT_TRUE(ac.get10CHeat()); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setSwing(kFujitsuAcSwingVert); + EXPECT_FALSE(ac.get10CHeat()); + ac.set10CHeat(false); + EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcSwingVert, ac.getSwing()); + EXPECT_FALSE(ac.get10CHeat()); + ac.set10CHeat(true); + EXPECT_TRUE(ac.get10CHeat()); + EXPECT_EQ(kFujitsuAcFanAuto, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcSwingOff, ac.getSwing()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("FUJITSU_AC", typeToString(decode_type_t::FUJITSU_AC)); + ASSERT_EQ(decode_type_t::FUJITSU_AC, strToDecodeType("FUJITSU_AC")); + ASSERT_TRUE(hasACState(decode_type_t::FUJITSU_AC)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::FUJITSU_AC)); + ASSERT_EQ(0, IRsend::defaultBits(decode_type_t::FUJITSU_AC)); // No default + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::FUJITSU_AC)); + + ASSERT_EQ("FUJITSU_AC264", typeToString(decode_type_t::FUJITSU_AC264)); + ASSERT_EQ(decode_type_t::FUJITSU_AC264, strToDecodeType("FUJITSU_AC264")); + ASSERT_TRUE(hasACState(decode_type_t::FUJITSU_AC264)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::FUJITSU_AC264)); + ASSERT_EQ(264, IRsend::defaultBits(decode_type_t::FUJITSU_AC264)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::FUJITSU_AC264)); +} + +TEST(TestIRFujitsuACClass, Temperature) { + IRFujitsuAC ac(kGpioUnused); + // Most models + // Celsius + ac.setModel(fujitsu_ac_remote_model_t::ARRAH2E); + ac.setTemp(kFujitsuAcMinTemp); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMinTemp - 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp + 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + // Fahrenheit (can't be used by most model, check it converts correctly) + ac.setTemp(77, false); // 77F is 25C + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(25, ac.getTemp()); + + // ARREW4E is different. + ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); + ac.setTemp(kFujitsuAcMinTemp); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMinTemp - 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMinTemp, ac.getTemp()); + ac.setTemp(kFujitsuAcMaxTemp + 1); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(kFujitsuAcMaxTemp, ac.getTemp()); + ac.setTemp(22.5); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(22.5, ac.getTemp()); + // Fahrenheit + ac.setTemp(77, false); + EXPECT_FALSE(ac.getCelsius()); + EXPECT_EQ(77, ac.getTemp()); + + // Real example + const uint8_t arrew4e_22c[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x70, 0x01, 0x00, 0x20, 0x03, 0x58, 0x20, 0xC3}; + ac.setRaw(arrew4e_22c, 16); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(22, ac.getTemp()); + const uint8_t arrew4e_25_5c[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x8C, 0x01, 0x00, 0x21, 0x03, 0x12, 0x20, 0xEC}; + ac.setRaw(arrew4e_25_5c, 16); + EXPECT_TRUE(ac.getCelsius()); + EXPECT_EQ(25.5, ac.getTemp()); + const uint8_t arrew4e_69f[16] = { + 0x14, 0x63, 0x20, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x66, 0x04, 0x00, 0x16, 0x01, 0x32, 0x20, 0xFC}; + ac.setRaw(arrew4e_69f, 16); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARREW4E, ac.getModel()); + EXPECT_FALSE(ac.getCelsius()); + EXPECT_EQ(69, ac.getTemp()); +} + +TEST(TestIRFujitsuACClass, ARREW4EShortCodes) { + // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1455#issuecomment-817339816 + IRFujitsuAC ac(kGpioUnused); + ac.setId(3); + ac.setModel(fujitsu_ac_remote_model_t::ARREW4E); + + const uint8_t off[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x02, 0xFD}; + ac.off(); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(off, ac.getRaw(), kFujitsuAcStateLengthShort * 8); + + const uint8_t econo[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x09, 0xF6}; + ac.setCmd(kFujitsuAcCmdEcono); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(econo, ac.getRaw(), kFujitsuAcStateLengthShort * 8); + + const uint8_t powerful[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x39, 0xC6}; + ac.setCmd(kFujitsuAcCmdPowerful); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(powerful, ac.getRaw(), kFujitsuAcStateLengthShort * 8); + + const uint8_t stepvert[kFujitsuAcStateLengthShort] = { + 0x14, 0x63, 0x30, 0x10, 0x10, 0x6C, 0x93}; + ac.setCmd(kFujitsuAcCmdStepVert); + ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_STATE_EQ(stepvert, ac.getRaw(), kFujitsuAcStateLengthShort * 8); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/discussions/1701#discussioncomment-1910164 +TEST(TestIRFujitsuACClass, Discussion1701) { + IRFujitsuAC ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRac irac(kGpioUnused); + + const String expected_raw_output = + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s1182m448s390m448s390m448s1182m448s390" + "m448s8100"; + const String expected_arrew4e_str = + "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off"; + const uint8_t expected_arrew4e_state[kFujitsuAcStateLength] = + {0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4C}; + + // Method used in `TurnOnFujitsuAC` + ac.begin(); + ac.setModel(ARREW4E); + ac.setSwing(kFujitsuAcSwingOff); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); // 24C + ac.setCmd(kFujitsuAcCmdTurnOn); + ASSERT_EQ(expected_arrew4e_str, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) + EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); + EXPECT_EQ(expected_arrew4e_str, + IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); + + // Now try to reproduce it via the IRac class. + ac._irsend.reset(); + ac.stateReset(); + ASSERT_NE(expected_arrew4e_str, ac.toString()); + + irac.fujitsu(&ac, + ARREW4E, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + true, // Celsius + 24, // Degrees + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Vertical swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo (Powerful) + false, // Econo + false, // Filter + false); // Clean + ASSERT_EQ(expected_arrew4e_str, ac.toString()); + ac._irsend.makeDecodeResult(); + // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) + EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); + EXPECT_EQ(expected_arrew4e_str, + IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); + // Success. +} + +TEST(TestIRFujitsuACClass, toCommon_Issue1780HandlePrev) { + IRFujitsuAC ac(kGpioUnused); + ac.setMode(kFujitsuAcModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setSwing(kFujitsuAcSwingBoth); + ac.on(); + ASSERT_TRUE(ac.toCommon().power); + stdAc::state_t prev = ac.toCommon(); // Copy in the state. + ac.off(); + ASSERT_FALSE(ac.toCommon().power); + ac.send(); // This should send a short code. + prev.degrees = 27; + ac.stateReset(); + IRrecv irrecv(kGpioUnused); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + ASSERT_FALSE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(16, ac.toCommon().degrees); + ASSERT_EQ(27, ac.toCommon(&prev).degrees); + ASSERT_FALSE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon(&prev).mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon(&prev).fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); + + stdAc::state_t result_inc_prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result_inc_prev, + &prev)); + ASSERT_EQ(27, result_inc_prev.degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, result_inc_prev.mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, result_inc_prev.fanspeed); +} + +TEST(TestIRFujitsuACClass, Improve10CHeat) { + IRFujitsuAC ac(kGpioUnused); + // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G2 + const uint8_t Arrah2u_10CHeatOn[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x41, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x64}; + ASSERT_FALSE(ac.get10CHeat()); + ac.setRaw(Arrah2u_10CHeatOn, 16); + ASSERT_TRUE(ac.get10CHeat()); + ASSERT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.get10CHeat()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); + + ac.stateReset(); + // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G8 + const uint8_t Arreg1u_10CHeatOn[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x61, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x44}; + ASSERT_FALSE(ac.get10CHeat()); + ac.setRaw(Arreg1u_10CHeatOn, 16); + ASSERT_TRUE(ac.get10CHeat()); + ASSERT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.get10CHeat()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); +} + +// Tests for Fujitsu 264 bit A/C methods. +TEST(TestDecodeFujitsuAc264, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + uint16_t rawData[531] = {3378, 1534, 498, 322, 526, 294, 524, 1116, 522, 302, + 492, 1166, 472, 350, 470, 352, 466, 352, 468, 1172, 468, 1176, 466, 352, + 466, 354, 466, 356, 466, 1176, 464, 1174, 468, 354, 466, 356, 464, 352, + 468, 356, 466, 352, 466, 354, 466, 356, 464, 354, 468, 352, 466, 356, 466, + 352, 466, 356, 464, 356, 464, 1178, 464, 356, 466, 356, 464, 356, 462, 356, + 466, 356, 464, 356, 462, 356, 464, 1178, 464, 356, 466, 356, 462, 358, 464, + 354, 464, 1176, 464, 1178, 464, 1180, 462, 1176, 454, 1188, 442, 1202, 440, + 1198, 442, 380, 464, 1176, 440, 382, 442, 1200, 438, 1202, 440, 380, 440, + 382, 438, 382, 440, 404, 416, 382, 440, 380, 440, 382, 438, 380, 440, 380, + 440, 1202, 440, 404, 414, 1202, 440, 382, 440, 380, 438, 1204, 440, 380, + 440, 382, 436, 400, 422, 1226, 416, 1226, 416, 404, 416, 406, 414, 406, + 414, 406, 414, 406, 416, 404, 416, 404, 418, 404, 414, 406, 416, 404, 418, + 404, 416, 404, 416, 404, 416, 404, 414, 404, 416, 406, 416, 404, 414, 404, + 416, 406, 414, 406, 414, 406, 416, 404, 414, 406, 414, 408, 414, 404, 414, + 408, 412, 406, 416, 404, 414, 406, 414, 408, 414, 406, 414, 408, 412, 406, + 414, 408, 412, 408, 414, 404, 414, 408, 412, 406, 414, 404, 416, 406, 412, + 408, 414, 404, 414, 410, 412, 408, 412, 406, 412, 408, 414, 408, 414, 406, + 412, 1230, 412, 408, 414, 408, 412, 1228, 414, 408, 412, 408, 412, 406, + 414, 408, 412, 1228, 414, 1228, 412, 408, 416, 408, 410, 408, 412, 408, + 414, 408, 412, 408, 412, 410, 410, 408, 410, 408, 412, 408, 414, 408, 412, + 408, 414, 408, 412, 1228, 414, 408, 412, 408, 410, 410, 410, 410, 412, 406, + 412, 408, 412, 410, 410, 1232, 410, 408, 410, 410, 410, 412, 386, 1254, + 412, 410, 408, 412, 412, 408, 410, 1230, 412, 1232, 410, 1230, 408, 1232, + 410, 1232, 386, 432, 386, 436, 408, 410, 386, 434, 404, 418, 386, 434, 386, + 432, 390, 432, 386, 1254, 388, 1254, 386, 434, 388, 432, 388, 434, 386, + 436, 384, 456, 362, 1256, 386, 434, 386, 434, 386, 434, 384, 436, 386, 434, + 386, 1256, 386, 458, 360, 434, 388, 1254, 386, 434, 388, 434, 386, 434, + 388, 434, 384, 460, 360, 436, 384, 458, 360, 436, 386, 460, 362, 434, 386, + 458, 360, 460, 360, 460, 362, 460, 360, 458, 362, 460, 360, 458, 360, 460, + 362, 460, 360, 460, 360, 460, 362, 456, 362, 458, 360, 462, 360, 458, 362, + 458, 362, 456, 364, 458, 362, 460, 360, 460, 362, 458, 362, 458, 360, 460, + 360, 458, 360, 1282, 360, 1280, 362, 1280, 360, 1282, 360, 1280, 360, 1280, + 360, 1282, 360, 1278, 362, 1280, 360, 1282, 360, 1280, 362, 1280, 360, + 1282, 360, 1280, 362, 1280, 360, 1282, 360, 1282, 362, 1280, 360, 1280, + 360, 1282, 358, 1282, 360, 1282, 360, 1282, 358, 1282, 360, 462, 358, 462, + 358, 462, 360, 460, 360, 462, 360, 484, 334, 462, 360, 462, 358, 458, 360, + 462, 356, 1282, 360, 1282, 360, 1308, 332, 486, 334, 1284, 358, 462, 360 + }; // FUJITSU_AC264 + + uint8_t expectedState[kFujitsuAc264StateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x1A, 0x40, + 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x06, 0x00, 0x01, 0x11, 0x1F, 0x60, 0x10, 0x24, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, + 0x5C}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 531, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC264, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAc264Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRFujitsuAC264 ac(kGpioUnused); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ("Power: On, Mode: 1 (Cool), Temp: 25C, Temp (Auto): 0C, " + "Fan: 0 (Auto), Fan Angle: 15 (Stay), Swing: Off, Economy: Off, " + "Clean: Off, Command: Cool, Current Time: 17:31, Sleep Timer: Off, " + "On Timer: Off, Off Timer: Off", + ac.toString()); +} + +TEST(TestDecodeFujitsuAc264, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + uint8_t sendCode[kFujitsuAc264StateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x1A, 0x40, + 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x06, 0x00, 0x01, 0x11, 0x1F, 0x60, 0x10, 0x24, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, + 0x5C}; + + irsend.begin(); + irsend.reset(); + irsend.sendFujitsuAC264(sendCode); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC264, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAc264Bits, irsend.capture.bits); + EXPECT_STATE_EQ(sendCode, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s390m448s1182m448s390m448s1182m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s1182m448s390" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s390m448s390m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s1182m448s1182m448s390m448s1182m448s390" + "m448s8100", + irsend.outputStr()); +} + +TEST(TestFujitsuAc264Class, toCommon) { + IRFujitsuAC264 ac(kGpioUnused); + ac.setPower(true); + ac.setMode(kFujitsuAc264ModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAc264FanSpeedHigh); + ac.setSwing(true); + ac.setEcoFan(false); + ac.setClock(1 * 60 + 23); // "1:23" + ac.setSleepTimer(2 * 60); + // Now test it. + ASSERT_EQ(decode_type_t::FUJITSU_AC264, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(83, ac.toCommon().clock); + ASSERT_EQ(120, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); +} + +TEST(TestFujitsuAc264Class, Power) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Value from getPower is not updated untill ac.send() + ac.on(); + EXPECT_FALSE(ac.getPower()); + + ac.on(); + ac.send(); + EXPECT_TRUE(ac.getPower()); + + // Value from getPower is not updated untill ac.send() + ac.off(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + ac.send(); + EXPECT_FALSE(ac.getPower()); + + // Value from getPower is not updated untill ac.send() + ac.setPower(true); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + ac.send(); + EXPECT_TRUE(ac.getPower()); + + // Value from getPower is not updated untill ac.send() + ac.setPower(false); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + ac.send(); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestFujitsuAc264Class, Temperature) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setMode(kFujitsuAc264ModeHeat); + // Value over maximum is fixed to maximum (30C) + ac.setTemp(40); + EXPECT_EQ(30, ac.getTemp()); + + // Value under minimum is fixed to minimum (16C in Heat) + ac.setTemp(10); + EXPECT_EQ(16, ac.getTemp()); + + ac.setMode(kFujitsuAc264ModeCool); + // Value over maximum is fixed to maximum (30C) + ac.setTemp(40); + EXPECT_EQ(30, ac.getTemp()); + + // Value under minimum is fixed to minimum (18C) + ac.setTemp(10); + EXPECT_EQ(18, ac.getTemp()); + + // Valid values in suppoerted range + ac.setMode(kFujitsuAc264ModeHeat); + for (float i = 16; i <= 30; i += 0.5) { + ac.setTemp(i); + EXPECT_EQ(i, ac.getTemp()); + } + + // Valid values in suppoerted range + ac.setMode(kFujitsuAc264ModeCool); + for (float i = 18; i <= 30; i += 0.5) { + ac.setTemp(i); + EXPECT_EQ(i, ac.getTemp()); + } + + // Value in supported range, but not multiple of 0.5 + // Fractional part of the value which is not multiple of 0.5 is truncated. + ac.setTemp(22.9); + EXPECT_EQ(22.5, ac.getTemp()); + + ac.setTemp(20.1); + EXPECT_EQ(20, ac.getTemp()); +} + +TEST(TestFujitsuAc264Class, TemperatureAuto) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Value over maximum is fixed to maximum (+2C) + ac.setTempAuto(10); + EXPECT_EQ(2, ac.getTempAuto()); + + // Value under minimum is fixed to minimum (-2C) + ac.setTempAuto(-10); + EXPECT_EQ(-2, ac.getTempAuto()); + + // Valid values in suppoerted range + for (float i = -2; i <= 2; i += 0.5) { + ac.setTempAuto(i); + EXPECT_EQ(i, ac.getTempAuto()); + } + + // Value in supported range, but not multiple of 0.5 + // Fractional part of the value which is not multiple of 0.5 is truncated. + ac.setTempAuto(0.8); + EXPECT_EQ(0.5, ac.getTempAuto()); + + ac.setTempAuto(1.2); + EXPECT_EQ(1, ac.getTempAuto()); + + ac.setTempAuto(-1.4); + EXPECT_EQ(-1, ac.getTempAuto()); +} + +TEST(TestFujitsuAc264Class, Mode) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setMode(2); + EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + // Unexpected value should default to Auto. + ac.setMode(255); + EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeAuto); + EXPECT_EQ(kFujitsuAc264ModeAuto, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeCool); + EXPECT_EQ(kFujitsuAc264ModeCool, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeFan); + EXPECT_EQ(kFujitsuAc264ModeFan, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeHeat); + EXPECT_EQ(kFujitsuAc264ModeHeat, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeDry); + EXPECT_EQ(kFujitsuAc264ModeDry, ac.getMode()); + EXPECT_FALSE(ac.isWeakDry()); + + ac.setMode(kFujitsuAc264ModeDry, true); + EXPECT_EQ(kFujitsuAc264ModeDry, ac.getMode()); + EXPECT_TRUE(ac.isWeakDry()); +} + +TEST(TestFujitsuAc264Class, FanSpeed) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFanSpeed(0); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(255); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(2); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(4); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(5); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Unexpected value should default to Auto. + ac.setFanSpeed(7); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + // Beyond Max should default to Auto. + ac.setFanSpeed(kFujitsuAc264FanSpeedHigh + 1); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedAuto); + EXPECT_EQ(kFujitsuAc264FanSpeedAuto, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedQuiet); + EXPECT_EQ(kFujitsuAc264FanSpeedQuiet, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedLow); + EXPECT_EQ(kFujitsuAc264FanSpeedLow, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedMed); + EXPECT_EQ(kFujitsuAc264FanSpeedMed, ac.getFanSpeed()); + + ac.setFanSpeed(kFujitsuAc264FanSpeedHigh); + EXPECT_EQ(kFujitsuAc264FanSpeedHigh, ac.getFanSpeed()); +} + +TEST(TestFujitsuAc264Class, FanAngle) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to Stay. + ac.setFanAngle(0); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(255); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(8); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(13); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + // Unexpected value should default to Stay. + ac.setFanAngle(32); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle1); + EXPECT_EQ(kFujitsuAc264FanAngle1, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle2); + EXPECT_EQ(kFujitsuAc264FanAngle2, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle3); + EXPECT_EQ(kFujitsuAc264FanAngle3, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle4); + EXPECT_EQ(kFujitsuAc264FanAngle4, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle5); + EXPECT_EQ(kFujitsuAc264FanAngle5, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle6); + EXPECT_EQ(kFujitsuAc264FanAngle6, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngle7); + EXPECT_EQ(kFujitsuAc264FanAngle7, ac.getFanAngle()); + + ac.setFanAngle(kFujitsuAc264FanAngleStay); + EXPECT_EQ(kFujitsuAc264FanAngleStay, ac.getFanAngle()); +} + +TEST(TestFujitsuAc264Class, Swing) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setSwing(true); + EXPECT_TRUE(ac.getSwing()); + + ac.setSwing(false); + EXPECT_FALSE(ac.getSwing()); +} + +TEST(TestFujitsuAc264Class, Economy) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setEconomy(true); + EXPECT_TRUE(ac.getEconomy()); + + ac.setEconomy(false); + EXPECT_FALSE(ac.getEconomy()); +} + +TEST(TestFujitsuAc264Class, Clean) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); +} + +TEST(TestFujitsuAc264Class, Clock) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to 0. + ac.setClock(2000); + EXPECT_EQ(0, ac.getClock()); + + // Valid values in suppoerted range + for (uint16_t i = 0; i < 1440; ++i) { + ac.setClock(i); + EXPECT_EQ(i, ac.getClock()); + } +} + +TEST(TestFujitsuAc264Class, SleepTimer) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value should default to 0 (invalid). + ac.setSleepTimer(12 * 60 + 1); + EXPECT_EQ(0, ac.getSleepTimer()); + + // Valid values in suppoerted range + for (uint16_t i = 0; i <= 12; ++i) { + ac.setSleepTimer(i * 60); + EXPECT_EQ(i * 60, ac.getSleepTimer()); + } +} + +TEST(TestFujitsuAc264Class, OnTimer) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value is ignored. + ac.setOnTimer(144); + EXPECT_EQ(0, ac.getOnTimer()); + + // Valid values in suppoerted range + for (uint8_t i = 0; i < 144; ++i) { + ac.setOnTimer(i); + EXPECT_EQ(i, ac.getOnTimer()); + } + + // Unexpected value is ignored. + ac.setOnTimer(255); + EXPECT_EQ(143, ac.getOnTimer()); + + // On timer enabled + ac.setTimerEnable(kFujitsuAc264OnTimerEnable); + EXPECT_EQ(kFujitsuAc264OnTimerEnable, ac.getTimerEnable()); + + // On timer disabled + ac.setTimerEnable(kFujitsuAc264OnOffTimerDisable); + EXPECT_EQ(kFujitsuAc264OnOffTimerDisable, ac.getTimerEnable()); +} + +TEST(TestFujitsuAc264Class, OffTimer) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + // Unexpected value is ignored. + ac.setOffTimer(144); + EXPECT_EQ(0, ac.getOffTimer()); + + // Valid values in suppoerted range + for (uint8_t i = 0; i < 144; ++i) { + ac.setOffTimer(i); + EXPECT_EQ(i, ac.getOffTimer()); + } + + // Unexpected value is ignored. + ac.setOffTimer(255); + EXPECT_EQ(143, ac.getOffTimer()); + + // Off timer enabled + ac.setTimerEnable(kFujitsuAc264OffTimerEnable); + EXPECT_EQ(kFujitsuAc264OffTimerEnable, ac.getTimerEnable()); + + // On & off timer enabled + ac.setTimerEnable(kFujitsuAc264OnOffTimerEnable); + EXPECT_EQ(kFujitsuAc264OnOffTimerEnable, ac.getTimerEnable()); + + // On & Off timer disabled + ac.setTimerEnable(kFujitsuAc264OnOffTimerDisable); + EXPECT_EQ(kFujitsuAc264OnOffTimerDisable, ac.getTimerEnable()); +} + +TEST(TestFujitsuAc264Class, Sterilization) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // When AC is powered on, sterilization is ignored. + ac.toggleSterilization(); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.off(); + ac.send(); + + // When AC is powered off, sterilization is accepted. + ac.toggleSterilization(); + EXPECT_EQ(kFujitsuAc264SpCmdToggleSterilization, ac.getCmd()); +} + +TEST(TestFujitsuAc264Class, OutsideQuiet) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // When AC is powered on, outside quiet is ignored. + ac.setOutsideQuiet(true); + ac.send(); + EXPECT_FALSE(ac.getOutsideQuiet()); + + ac.off(); + ac.send(); + + // When AC is powered off, outside quiet is accepted. + ac.setOutsideQuiet(true); + ac.send(); + EXPECT_TRUE(ac.getOutsideQuiet()); + + ac.setOutsideQuiet(false); + ac.send(); + EXPECT_FALSE(ac.getOutsideQuiet()); +} + +TEST(TestFujitsuAc264Class, EcoFan) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // When AC is powered on, eco fan is ignored. + ac.setEcoFan(true); + ac.send(); + EXPECT_FALSE(ac.getEcoFan()); + + ac.off(); + ac.send(); + + // When AC is powered off, eco fan is accepted. + ac.setEcoFan(true); + ac.send(); + EXPECT_TRUE(ac.getEcoFan()); + + ac.setEcoFan(false); + ac.send(); + EXPECT_FALSE(ac.getEcoFan()); +} + +TEST(TestFujitsuAc264Class, Powerful) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.off(); + ac.send(); + + // When AC is powered off, powerful is ignored. + ac.togglePowerful(); + ac.send(); + EXPECT_EQ(kFujitsuAc264SpCmdTurnOff, ac.getCmd()); + + ac.on(); + ac.send(); + + // When AC is powered on, powerful is accepted. + ac.togglePowerful(); + ac.send(); + EXPECT_EQ(kFujitsuAc264SpCmdTogglePowerful, ac.getCmd()); + + ac.togglePowerful(); + ac.send(); + EXPECT_EQ(kFujitsuAc264SpCmdTogglePowerful, ac.getCmd()); +} + +TEST(TestFujitsuAc264Class, NormalCommands) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + ac.on(); + ac.send(); + + // Special commands are ignored. + ac.setCmd(kFujitsuAc264SpCmdTogglePowerful); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdTurnOff); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdEcoFanOff); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdEcoFanOn); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdOutsideQuietOff); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdOutsideQuietOn); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264SpCmdToggleSterilization); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + // Normal commands can be set. + ac.setCmd(kFujitsuAc264CmdCool); + EXPECT_EQ(kFujitsuAc264CmdCool, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdHeat); + EXPECT_EQ(kFujitsuAc264CmdHeat, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdDry); + EXPECT_EQ(kFujitsuAc264CmdDry, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdAuto); + EXPECT_EQ(kFujitsuAc264CmdAuto, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdFan); + EXPECT_EQ(kFujitsuAc264CmdFan, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdTemp); + EXPECT_EQ(kFujitsuAc264CmdTemp, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdSwing); + EXPECT_EQ(kFujitsuAc264CmdSwing, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdSleepTime); + EXPECT_EQ(kFujitsuAc264CmdSleepTime, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdEconomy); + EXPECT_EQ(kFujitsuAc264CmdEconomy, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdClean); + EXPECT_EQ(kFujitsuAc264CmdClean, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdFanSpeed); + EXPECT_EQ(kFujitsuAc264CmdFanSpeed, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdFanAngle); + EXPECT_EQ(kFujitsuAc264CmdFanAngle, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdCancelSleepTimer); + EXPECT_EQ(kFujitsuAc264CmdCancelSleepTimer, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdOnTimer); + EXPECT_EQ(kFujitsuAc264CmdOnTimer, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdOffTimer); + EXPECT_EQ(kFujitsuAc264CmdOffTimer, ac.getCmd()); + + ac.setCmd(kFujitsuAc264CmdCancelOnOffTimer); + EXPECT_EQ(kFujitsuAc264CmdCancelOnOffTimer, ac.getCmd()); +} + +TEST(TestFujitsuAc264Class, SpecialCommands) { + IRFujitsuAC264 ac(kGpioUnused); + ac.begin(); + + uint8_t expected_turnoff[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + uint8_t expected_powerful[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6}; + uint8_t expected_ecofanoff[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x51, 0xAE}; + uint8_t expected_ecofanon[7] = {0x14, 0x63, 0x00, 0x10, 0x10, 0x50, 0xAF}; + uint8_t expected_outsidequietoff[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x01, 0x00, 0x00, 0xFE, 0xBF, 0x00, 0x41}; + uint8_t expected_outsidequieton[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x40, 0x00, 0x00, 0x00, 0xFF, 0xBF, 0x00, 0x41}; + uint8_t expected_sterilization[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0xC1, + 0x60, 0x03, 0x00, 0x00, 0xFC, 0x9F, 0x00, 0x41}; + + ac.on(); + ac.send(); + + ac.togglePowerful(); + ac.send(); + EXPECT_STATE_EQ(expected_powerful, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Powerful", ac.toString()); + + ac.off(); + ac.send(); + EXPECT_STATE_EQ(expected_turnoff, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Power Off", ac.toString()); + + ac.setEcoFan(true); + ac.send(); + EXPECT_STATE_EQ(expected_ecofanon, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Eco Fan On", ac.toString()); + + ac.setEcoFan(false); + ac.send(); + EXPECT_STATE_EQ(expected_ecofanoff, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthShort, ac.getStateLength()); + EXPECT_EQ("Command: Eco Fan Off", ac.toString()); + + ac.setOutsideQuiet(true); + ac.send(); + EXPECT_STATE_EQ(expected_outsidequieton, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); + EXPECT_EQ("Command: Outside Quiet On", ac.toString()); + + ac.setOutsideQuiet(false); + ac.send(); + EXPECT_STATE_EQ(expected_outsidequietoff, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); + EXPECT_EQ("Command: Outside Quiet Off", ac.toString()); + + ac.toggleSterilization(); + ac.send(); + EXPECT_STATE_EQ(expected_sterilization, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAc264StateLengthMiddle, ac.getStateLength()); + EXPECT_EQ("Command: Sterilization", ac.toString()); +}