From 24bcdbc97ceea78323e3a2bd4db161f0e45a91e8 Mon Sep 17 00:00:00 2001 From: Florian <1technophile@users.noreply.github.com> Date: Mon, 8 Jul 2019 22:21:02 +0200 Subject: [PATCH] Update IRRemote ESP8266 to 2.6.2 and add ESP32 support --- ZgatewayIR.ino | 42 +- config_IR.h | 17 +- lib/IRremoteESP8266/.github/Contributors.md | 3 +- lib/IRremoteESP8266/.github/issue_template.md | 4 +- lib/IRremoteESP8266/.gitignore | 4 + lib/IRremoteESP8266/.travis.yml | 11 +- lib/IRremoteESP8266/README.md | 11 +- lib/IRremoteESP8266/ReleaseNotes.md | 102 + lib/IRremoteESP8266/SupportedProtocols.md | 129 + .../ControlSamsungAC/ControlSamsungAC.ino | 2 - .../examples/ControlSamsungAC/platformio.ini | 12 + .../DumbIRRepeater/DumbIRRepeater.ino | 130 + .../examples/DumbIRRepeater/platformio.ini | 29 + .../examples/IRGCSendDemo/IRGCSendDemo.ino | 2 - .../examples/IRGCSendDemo/platformio.ini | 12 + .../examples/IRGCTCPServer/IRGCTCPServer.ino | 7 +- .../examples/IRGCTCPServer/platformio.ini | 12 + .../examples/IRMQTTServer/IRMQTTServer.h | 334 ++ .../examples/IRMQTTServer/IRMQTTServer.ino | 2909 +++++++++++++---- .../examples/IRMQTTServer/platformio.ini | 43 +- .../examples/IRServer/IRServer.ino | 34 +- .../examples/IRServer/platformio.ini | 12 + .../examples/IRrecvDemo/IRrecvDemo.ino | 2 - .../examples/IRrecvDemo/platformio.ini | 12 + .../examples/IRrecvDump/IRrecvDump.ino | 2 - .../examples/IRrecvDump/platformio.ini | 12 + .../examples/IRrecvDumpV2/IRrecvDumpV2.ino | 62 +- .../examples/IRrecvDumpV2/platformio.ini | 12 + .../examples/IRsendDemo/IRsendDemo.ino | 6 +- .../examples/IRsendDemo/platformio.ini | 12 + .../IRsendProntoDemo/IRsendProntoDemo.ino | 2 - .../examples/IRsendProntoDemo/platformio.ini | 12 + .../JVCPanasonicSendDemo.ino | 2 - .../JVCPanasonicSendDemo/platformio.ini | 12 + .../examples/LGACSend/platformio.ini | 12 + .../SmartIRRepeater/SmartIRRepeater.ino | 143 + .../examples/SmartIRRepeater/platformio.ini | 29 + .../examples/TurnOnArgoAC/TurnOnArgoAC.ino | 4 +- .../examples/TurnOnArgoAC/platformio.ini | 12 + .../TurnOnDaikinAC/TurnOnDaikinAC.ino | 2 - .../examples/TurnOnDaikinAC/platformio.ini | 12 + .../TurnOnFujitsuAC/TurnOnFujitsuAC.ino | 8 +- .../examples/TurnOnFujitsuAC/platformio.ini | 12 + .../TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino | 2 - .../TurnOnKelvinatorAC/platformio.ini | 12 + .../TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino | 2 - .../TurnOnMitsubishiAC/platformio.ini | 12 + .../TurnOnMitsubishiHeavyAc.ino | 70 + .../TurnOnMitsubishiHeavyAc/platformio.ini | 29 + .../TurnOnPanasonicAC/TurnOnPanasonicAC.ino | 72 + .../examples/TurnOnPanasonicAC/platformio.ini | 29 + .../TurnOnToshibaAC/TurnOnToshibaAC.ino | 2 - .../examples/TurnOnToshibaAC/platformio.ini | 12 + .../TurnOnTrotecAC/TurnOnTrotecAC.ino | 2 - .../examples/TurnOnTrotecAC/platformio.ini | 12 + lib/IRremoteESP8266/keywords.txt | 457 ++- lib/IRremoteESP8266/library.json | 6 +- lib/IRremoteESP8266/library.properties | 8 +- lib/IRremoteESP8266/platformio.ini | 13 + lib/IRremoteESP8266/src/IRac.cpp | 1292 ++++++++ lib/IRremoteESP8266/src/IRac.h | 244 ++ lib/IRremoteESP8266/src/IRrecv.cpp | 162 +- lib/IRremoteESP8266/src/IRrecv.h | 129 +- lib/IRremoteESP8266/src/IRremoteESP8266.h | 94 +- lib/IRremoteESP8266/src/IRsend.cpp | 156 +- lib/IRremoteESP8266/src/IRsend.h | 272 +- lib/IRremoteESP8266/src/IRtimer.cpp | 33 +- lib/IRremoteESP8266/src/IRtimer.h | 12 + lib/IRremoteESP8266/src/IRutils.cpp | 544 ++- lib/IRremoteESP8266/src/IRutils.h | 34 +- lib/IRremoteESP8266/src/ir_Aiwa.cpp | 2 + lib/IRremoteESP8266/src/ir_Argo.cpp | 424 ++- lib/IRremoteESP8266/src/ir_Argo.h | 138 +- lib/IRremoteESP8266/src/ir_Carrier.cpp | 19 +- lib/IRremoteESP8266/src/ir_Coolix.cpp | 227 +- lib/IRremoteESP8266/src/ir_Coolix.h | 40 +- lib/IRremoteESP8266/src/ir_Daikin.cpp | 1756 ++++++---- lib/IRremoteESP8266/src/ir_Daikin.h | 341 +- lib/IRremoteESP8266/src/ir_Denon.cpp | 14 +- lib/IRremoteESP8266/src/ir_Dish.cpp | 9 +- lib/IRremoteESP8266/src/ir_Electra.cpp | 30 +- lib/IRremoteESP8266/src/ir_Fujitsu.cpp | 496 ++- lib/IRremoteESP8266/src/ir_Fujitsu.h | 109 +- lib/IRremoteESP8266/src/ir_GICable.cpp | 7 +- lib/IRremoteESP8266/src/ir_GlobalCache.cpp | 12 +- lib/IRremoteESP8266/src/ir_Goodweather.cpp | 494 +++ lib/IRremoteESP8266/src/ir_Goodweather.h | 136 + lib/IRremoteESP8266/src/ir_Gree.cpp | 269 +- lib/IRremoteESP8266/src/ir_Gree.h | 85 +- lib/IRremoteESP8266/src/ir_Haier.cpp | 577 +++- lib/IRremoteESP8266/src/ir_Haier.h | 136 +- lib/IRremoteESP8266/src/ir_Hitachi.cpp | 190 +- lib/IRremoteESP8266/src/ir_Hitachi.h | 52 +- lib/IRremoteESP8266/src/ir_Inax.cpp | 96 + lib/IRremoteESP8266/src/ir_JVC.cpp | 6 - lib/IRremoteESP8266/src/ir_Kelvinator.cpp | 308 +- lib/IRremoteESP8266/src/ir_Kelvinator.h | 98 +- lib/IRremoteESP8266/src/ir_LG.cpp | 21 +- lib/IRremoteESP8266/src/ir_LG.h | 10 +- lib/IRremoteESP8266/src/ir_Lasertag.cpp | 9 +- lib/IRremoteESP8266/src/ir_Lego.cpp | 128 + lib/IRremoteESP8266/src/ir_Lutron.cpp | 7 +- lib/IRremoteESP8266/src/ir_MWM.cpp | 16 +- lib/IRremoteESP8266/src/ir_Magiquest.h | 4 +- lib/IRremoteESP8266/src/ir_Midea.cpp | 196 +- lib/IRremoteESP8266/src/ir_Midea.h | 61 +- lib/IRremoteESP8266/src/ir_Mitsubishi.cpp | 309 +- lib/IRremoteESP8266/src/ir_Mitsubishi.h | 98 +- .../src/ir_MitsubishiHeavy.cpp | 1141 +++++++ lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h | 271 ++ lib/IRremoteESP8266/src/ir_NEC.cpp | 10 +- lib/IRremoteESP8266/src/ir_NEC.h | 10 +- lib/IRremoteESP8266/src/ir_Nikai.cpp | 8 +- lib/IRremoteESP8266/src/ir_Panasonic.cpp | 435 ++- lib/IRremoteESP8266/src/ir_Panasonic.h | 104 +- lib/IRremoteESP8266/src/ir_Pioneer.cpp | 8 +- lib/IRremoteESP8266/src/ir_Pronto.cpp | 8 +- lib/IRremoteESP8266/src/ir_RC5_RC6.cpp | 12 +- lib/IRremoteESP8266/src/ir_RCMM.cpp | 13 +- lib/IRremoteESP8266/src/ir_Samsung.cpp | 359 +- lib/IRremoteESP8266/src/ir_Samsung.h | 98 +- lib/IRremoteESP8266/src/ir_Sanyo.cpp | 6 - lib/IRremoteESP8266/src/ir_Sharp.cpp | 415 ++- lib/IRremoteESP8266/src/ir_Sharp.h | 98 + lib/IRremoteESP8266/src/ir_Sherwood.cpp | 12 +- lib/IRremoteESP8266/src/ir_Sony.cpp | 8 +- lib/IRremoteESP8266/src/ir_Tcl.cpp | 291 +- lib/IRremoteESP8266/src/ir_Tcl.h | 66 +- lib/IRremoteESP8266/src/ir_Teco.cpp | 138 +- lib/IRremoteESP8266/src/ir_Teco.h | 20 +- lib/IRremoteESP8266/src/ir_Toshiba.cpp | 216 +- lib/IRremoteESP8266/src/ir_Toshiba.h | 70 +- lib/IRremoteESP8266/src/ir_Trotec.cpp | 356 +- lib/IRremoteESP8266/src/ir_Trotec.h | 66 +- lib/IRremoteESP8266/src/ir_Vestel.cpp | 271 +- lib/IRremoteESP8266/src/ir_Vestel.h | 58 +- lib/IRremoteESP8266/src/ir_Whirlpool.cpp | 378 ++- lib/IRremoteESP8266/src/ir_Whirlpool.h | 88 +- lib/IRremoteESP8266/src/ir_Whynter.cpp | 15 +- lib/IRremoteESP8266/test/IRac_test.cpp | 998 ++++++ lib/IRremoteESP8266/test/IRrecv_test.cpp | 8 +- lib/IRremoteESP8266/test/IRsend_test.cpp | 179 +- lib/IRremoteESP8266/test/IRsend_test.h | 22 +- lib/IRremoteESP8266/test/IRutils_test.cpp | 94 + lib/IRremoteESP8266/test/Makefile | 203 +- lib/IRremoteESP8266/test/ir_Aiwa_test.cpp | 6 + lib/IRremoteESP8266/test/ir_Argo_test.cpp | 221 ++ lib/IRremoteESP8266/test/ir_Carrier_test.cpp | 4 + lib/IRremoteESP8266/test/ir_Coolix_test.cpp | 155 + lib/IRremoteESP8266/test/ir_Daikin_test.cpp | 1261 +++++-- lib/IRremoteESP8266/test/ir_Denon_test.cpp | 40 +- lib/IRremoteESP8266/test/ir_Dish_test.cpp | 8 + lib/IRremoteESP8266/test/ir_Electra_test.cpp | 19 +- lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp | 611 ++-- lib/IRremoteESP8266/test/ir_GICable_test.cpp | 6 + .../test/ir_GlobalCache_test.cpp | 2 + .../test/ir_Goodweather_test.cpp | 511 +++ lib/IRremoteESP8266/test/ir_Gree_test.cpp | 54 +- lib/IRremoteESP8266/test/ir_Haier_test.cpp | 170 +- lib/IRremoteESP8266/test/ir_Hitachi_test.cpp | 35 + lib/IRremoteESP8266/test/ir_Inax_test.cpp | 119 + lib/IRremoteESP8266/test/ir_JVC_test.cpp | 5 + .../test/ir_Kelvinator_test.cpp | 40 + lib/IRremoteESP8266/test/ir_LG_test.cpp | 9 +- lib/IRremoteESP8266/test/ir_Lasertag_test.cpp | 9 +- lib/IRremoteESP8266/test/ir_Lego_test.cpp | 196 ++ lib/IRremoteESP8266/test/ir_Lutron_test.cpp | 9 +- lib/IRremoteESP8266/test/ir_MWM_test.cpp | 2 + .../test/ir_Magiquest_test.cpp | 3 + lib/IRremoteESP8266/test/ir_Midea_test.cpp | 35 + .../test/ir_MitsubishiHeavy_test.cpp | 920 ++++++ .../test/ir_Mitsubishi_test.cpp | 46 + lib/IRremoteESP8266/test/ir_NEC_test.cpp | 9 +- lib/IRremoteESP8266/test/ir_Nikai_test.cpp | 3 + .../test/ir_Panasonic_test.cpp | 42 + lib/IRremoteESP8266/test/ir_Pioneer_test.cpp | 3 + lib/IRremoteESP8266/test/ir_Pronto_test.cpp | 20 +- lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp | 26 + lib/IRremoteESP8266/test/ir_RCMM_test.cpp | 5 + lib/IRremoteESP8266/test/ir_Samsung_test.cpp | 362 +- lib/IRremoteESP8266/test/ir_Sanyo_test.cpp | 2 + lib/IRremoteESP8266/test/ir_Sharp_test.cpp | 353 ++ lib/IRremoteESP8266/test/ir_Sherwood_test.cpp | 3 + lib/IRremoteESP8266/test/ir_Sony_test.cpp | 7 + lib/IRremoteESP8266/test/ir_Tcl_test.cpp | 239 +- lib/IRremoteESP8266/test/ir_Teco_test.cpp | 41 +- lib/IRremoteESP8266/test/ir_Toshiba_test.cpp | 35 + lib/IRremoteESP8266/test/ir_Trotec_test.cpp | 179 + lib/IRremoteESP8266/test/ir_Vestel_test.cpp | 33 + .../test/ir_Whirlpool_test.cpp | 34 + lib/IRremoteESP8266/test/ir_Whynter_test.cpp | 8 + lib/IRremoteESP8266/tools/Makefile | 71 +- .../tools/auto_analyse_raw_data.py | 8 +- .../tools/auto_analyse_raw_data_test.py | 28 +- .../tools/scrape_supported_devices.py | 255 ++ 195 files changed, 22963 insertions(+), 4737 deletions(-) create mode 100644 lib/IRremoteESP8266/SupportedProtocols.md create mode 100644 lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino create mode 100644 lib/IRremoteESP8266/examples/DumbIRRepeater/platformio.ini create mode 100644 lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h create mode 100644 lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino create mode 100644 lib/IRremoteESP8266/examples/SmartIRRepeater/platformio.ini create mode 100644 lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino create mode 100644 lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/platformio.ini create mode 100644 lib/IRremoteESP8266/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino create mode 100644 lib/IRremoteESP8266/examples/TurnOnPanasonicAC/platformio.ini create mode 100644 lib/IRremoteESP8266/src/IRac.cpp create mode 100644 lib/IRremoteESP8266/src/IRac.h create mode 100644 lib/IRremoteESP8266/src/ir_Goodweather.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Goodweather.h create mode 100644 lib/IRremoteESP8266/src/ir_Inax.cpp create mode 100644 lib/IRremoteESP8266/src/ir_Lego.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h create mode 100644 lib/IRremoteESP8266/src/ir_Sharp.h create mode 100644 lib/IRremoteESP8266/test/IRac_test.cpp create mode 100644 lib/IRremoteESP8266/test/ir_Argo_test.cpp create mode 100644 lib/IRremoteESP8266/test/ir_Goodweather_test.cpp create mode 100644 lib/IRremoteESP8266/test/ir_Inax_test.cpp create mode 100644 lib/IRremoteESP8266/test/ir_Lego_test.cpp create mode 100644 lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp create mode 100644 lib/IRremoteESP8266/test/ir_Trotec_test.cpp create mode 100644 lib/IRremoteESP8266/tools/scrape_supported_devices.py diff --git a/ZgatewayIR.ino b/ZgatewayIR.ino index e9829b5..9e1c85a 100644 --- a/ZgatewayIR.ino +++ b/ZgatewayIR.ino @@ -27,7 +27,7 @@ */ #ifdef ZgatewayIR -#ifdef ESP8266 +#if defined(ESP8266) || defined(ESP32) #include #include // Needed if you want to send IR commands. #include // Needed if you want to receive IR commands. @@ -76,8 +76,8 @@ void setupIR() { - //IR init parameters -#ifdef ESP8266 +//IR init parameters +#if defined(ESP8266) || defined(ESP32) irsend.begin(); #endif @@ -107,14 +107,14 @@ void IRtoMQTT(){ IRdata.set("value", (unsigned long)(results.value)); IRdata.set("protocol", (int)(results.decode_type)); IRdata.set("bits",(int)(results.bits)); - #ifdef ESP8266 //resultToHexidecimal is only available with IRremoteESP8266 + #if defined(ESP8266) || defined(ESP32) //resultToHexidecimal is only available with IRremoteESP8266 String hex = resultToHexidecimal(&results); IRdata.set("hex", (char *)hex.c_str()); #endif String rawCode = ""; // Dump data for (uint16_t i = 1; i < results.rawlen; i++) { - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) if (i % 100 == 0) yield(); // Preemptive yield every 100th entry to feed the WDT. rawCode = rawCode + (results.rawbuf[i] * RAWTICK); #else @@ -126,7 +126,7 @@ void IRtoMQTT(){ IRdata.set("raw", rawCode); // if needed we directly resend the raw code if (RawDirectForward){ - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) uint16_t rawsend[results.rawlen]; for (uint16_t i = 1; i < results.rawlen; i++) { if (i % 100 == 0) yield(); // Preemptive yield every 100th entry to feed the WDT. @@ -204,7 +204,7 @@ void IRtoMQTT(){ else if(strstr(topicOri, "IR_Raw") != NULL){ // sending Raw data trc(F("IR_Raw")); //buffer allocation from char datacallback - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) uint16_t Raw[count+1]; #else unsigned int Raw[count+1]; @@ -253,7 +253,7 @@ void IRtoMQTT(){ }else{ trc(F("Using NEC protocol")); if (valueBITS == 0) valueBITS = NEC_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendNEC(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendNEC(data, valueBITS); @@ -326,7 +326,7 @@ void IRtoMQTT(){ if(strstr(protocol_name, "IR_Raw") != NULL){ // sending Raw data trc(F("IR_Raw")); //buffer allocation from char datacallback - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) uint16_t Raw[count+1]; #else unsigned int Raw[count+1]; @@ -354,7 +354,7 @@ void IRtoMQTT(){ }else{ trc(F("Using NEC protocol")); if (valueBITS == 0) valueBITS = NEC_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendNEC(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendNEC(data, valueBITS); @@ -391,7 +391,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_Whynter if (strstr(protocol_name, "IR_Whynter") != NULL){ if (valueBITS == 0) valueBITS = WHYNTER_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendWhynter(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendWhynter(data, valueBITS); @@ -402,7 +402,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_LG if (strstr(protocol_name, "IR_LG") != NULL){ if (valueBITS == 0) valueBITS = LG_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendLG(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendLG(data, valueBITS); @@ -413,7 +413,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_Sony if (strstr(protocol_name, "IR_Sony") != NULL){ if (valueBITS == 0) valueBITS = SONY_12_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendSony(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendSony(data, valueBITS); @@ -424,7 +424,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_DISH if (strstr(protocol_name, "IR_DISH") != NULL){ if (valueBITS == 0) valueBITS = DISH_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendDISH(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendDISH(data, valueBITS); @@ -435,7 +435,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_RC5 if (strstr(protocol_name, "IR_RC5") != NULL){ if (valueBITS == 0) valueBITS = RC5_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendRC5(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendRC5(data, valueBITS); @@ -446,7 +446,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_RC6 if (strstr(protocol_name, "IR_RC6") != NULL){ if (valueBITS == 0) valueBITS = RC6_MODE0_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendRC6(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendRC6(data, valueBITS); @@ -457,7 +457,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_Sharp if (strstr(protocol_name, "IR_Sharp") != NULL){ if (valueBITS == 0) valueBITS = SHARP_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendSharpRaw(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendSharpRaw(data, valueBITS); @@ -468,7 +468,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_SAMSUNG if (strstr(protocol_name, "IR_SAMSUNG") != NULL){ if (valueBITS == 0) valueBITS = SAMSUNG_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendSAMSUNG(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendSAMSUNG(data, valueBITS); @@ -479,7 +479,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #ifdef IR_JVC if (strstr(protocol_name, "IR_JVC") != NULL){ if (valueBITS == 0) valueBITS = JVC_BITS; - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) irsend.sendJVC(data, valueBITS, valueRPT); #else for (int i=0; i <= valueRPT; i++) irsend.sendJVC(data, valueBITS); @@ -489,7 +489,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c #endif #ifdef IR_PANASONIC if (strstr(protocol_name, "IR_PANASONIC") != NULL){ - #ifdef ESP8266 + #if defined(ESP8266) || defined(ESP32) if (valueBITS == 0) valueBITS = PANASONIC_BITS; irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT); #else @@ -499,7 +499,7 @@ boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, c } #endif - #ifdef ESP8266 // sendings not available on arduino + #if defined(ESP8266) || defined(ESP32) // sendings not available on arduino #ifdef IR_COOLIX if (strstr(protocol_name, "IR_COOLIX") != NULL){ if (valueBITS == 0) valueBITS = kCoolixBits; diff --git a/config_IR.h b/config_IR.h index 3e81fbd..399d730 100644 --- a/config_IR.h +++ b/config_IR.h @@ -43,7 +43,7 @@ #define pubIRunknownPrtcl false // key to avoid mqtt publication of unknown IR protocol (set to true if you want to publish unknown protocol) #define PanasonicAddress 0x4004 // Panasonic address (Pre data) -#ifdef ESP8266 //IR supported protocols on ESP8266, all supported per default +#if defined(ESP8266) || defined(ESP32) //IR supported protocols on ESP8266, all supported per default #define IR_GC #define IR_Raw #define IR_COOLIX @@ -98,17 +98,6 @@ #define IR_SAMSUNG36 #define IR_TCL112AC #define IR_TECO -#elif ESP32 - #define IR_Raw - #define IR_COOLIX - #define IR_Whynter - #define IR_Sony - #define IR_LG - #define IR_DISH - #define IR_RC5 - #define IR_Sharp - #define IR_SAMSUNG - #define IR_PANASONIC #elif __AVR_ATmega2560__ #define IR_COOLIX #define IR_Whynter @@ -141,9 +130,9 @@ #define IR_RECEIVER_PIN 27 #define IR_EMITTER_PIN 14 #elif __AVR_ATmega2560__ - #define IR_RECEIVER_PIN 2 // 2 = D2 on mega + #define IR_RECEIVER_PIN 2 // 2 = D2 on arduino mega #define IR_EMITTER_PIN 9 #else - #define IR_RECEIVER_PIN 0 // 0 = D2 on arduino + #define IR_RECEIVER_PIN 0 // 0 = D2 on arduino UNO #define IR_EMITTER_PIN 9 #endif diff --git a/lib/IRremoteESP8266/.github/Contributors.md b/lib/IRremoteESP8266/.github/Contributors.md index a330f1b..ba43bc0 100644 --- a/lib/IRremoteESP8266/.github/Contributors.md +++ b/lib/IRremoteESP8266/.github/Contributors.md @@ -2,7 +2,7 @@ ### Main contributors & maintainers - [Mark Szabo](https://github.com/markszabo/) : Initial IR sending on ESP8266 - [Sébastien Warin](https://github.com/sebastienwarin/) (http://sebastien.warin.fr) : Initial IR receiving on ESP8266 -- [David Conran](https://github.com/crankyoldgit/) +- [David Conran](https://github.com/crankyoldgit/) : ESP32 support and pretty much everything else. - [Roi Dayan](https://github.com/roidayan/) - [Marcos de Alcântara Marinho](https://github.com/marcosamarinho/) - [Massimiliano Pinto](https://github.com/pintomax/) @@ -13,6 +13,7 @@ - [Denes Varga](https://github.com/denxhun/) - [Brett T. Warden](https://github.com/bwarden/) - [Fabien Valthier](https://github.com/hcoohb) +- [Ajay Pala](https://github.com/ajaypala/) All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). diff --git a/lib/IRremoteESP8266/.github/issue_template.md b/lib/IRremoteESP8266/.github/issue_template.md index 024a039..d6be2c8 100644 --- a/lib/IRremoteESP8266/.github/issue_template.md +++ b/lib/IRremoteESP8266/.github/issue_template.md @@ -1,6 +1,6 @@ _(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_ -### Version/revison of the library used +### Version/revision of the library used _Typically located in the `library.json` & `src/IRremoteESP8266.h` files in the root directory of the library. e.g. v2.0.0, or 'master' as at 1st of June, 2017. etc._ @@ -30,7 +30,7 @@ _What can we do to (pref. reliably) repeat what is happening?_ _Include all relevant code snippets or links to the actual code files. Tip: [How to quote your code so it is still readable](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)._ #### Circuit diagram and hardware used (if applicable) -_Link to an image of the circuit diagram used. Part number of the IR receiver module etc._ +_Link to an image of the circuit diagram used. Part number of the IR receiver module etc. ESP8266 or ESP32 board type._ ### I have followed the steps in the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) _Yes/No._ diff --git a/lib/IRremoteESP8266/.gitignore b/lib/IRremoteESP8266/.gitignore index 23e21ca..4441365 100644 --- a/lib/IRremoteESP8266/.gitignore +++ b/lib/IRremoteESP8266/.gitignore @@ -8,8 +8,12 @@ # vi/vim **/*.swp +# vscode +.vscode + ## Build environments # Platformio +**/.pio/ **/.pioenvs/ **/.piolibdeps/ **/.clang_complete diff --git a/lib/IRremoteESP8266/.travis.yml b/lib/IRremoteESP8266/.travis.yml index 39d2d0b..e6ddc10 100644 --- a/lib/IRremoteESP8266/.travis.yml +++ b/lib/IRremoteESP8266/.travis.yml @@ -15,12 +15,14 @@ install: - ln -s $PWD /usr/local/share/arduino/libraries/ - git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager - git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient + - git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs - arduino --install-boards esp8266:esp8266 - arduino --board $BD --save-prefs - arduino --pref "compiler.warning_level=all" --save-prefs - sudo apt-get install jq - - sudo pip install pylint + - sudo apt-get purge python-enum34 + - sudo apt-get install pylint3 script: # Check that everything compiles. - arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino @@ -41,12 +43,17 @@ script: - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino - arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino + - arduino --verify --board $BD $PWD/examples/DumbIRRepeater/DumbIRRepeater.ino + - arduino --verify --board $BD $PWD/examples/SmartIRRepeater/SmartIRRepeater.ino + # Also check the tools programs compile. - (cd tools; make all) # Check for lint issues. - shopt -s nullglob - python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino} - - pylint {src,test,tools}/*.py + - pylint3 -d F0001 {src,test,tools}/*.py - shopt -u nullglob # Build and run the unit tests. - (cd test; make run) diff --git a/lib/IRremoteESP8266/README.md b/lib/IRremoteESP8266/README.md index 03c09a2..e611601 100644 --- a/lib/IRremoteESP8266/README.md +++ b/lib/IRremoteESP8266/README.md @@ -6,10 +6,11 @@ [![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open") [![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266) -This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc. +This library enables you to **send _and_ receive** infra-red signals on an [ESP8266](https://github.com/esp8266/Arduino) or an +[ESP32](https://github.com/espressif/arduino-esp32) using the [Arduino framework](https://www.arduino.cc/) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc. -## v2.5.6 Now Available -Version 2.5.6 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. +## v2.6.2 Now Available +Version 2.6.2 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. #### Upgrading from pre-v2.0 Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. @@ -29,6 +30,10 @@ something you likely should not have. You should be able to quickly determine the new name from the old. e.g. `CONSTANT_NAME` to `kConstantName`. Use common sense or examining the library's code if this does affect code. +## Supported protocols +You can find the details of which protocols & devices are supported +[here](https://github.com/markszabo/IRremoteESP8266/blob/master/SupportedProtocols.md). + ## Troubleshooting Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) first. diff --git a/lib/IRremoteESP8266/ReleaseNotes.md b/lib/IRremoteESP8266/ReleaseNotes.md index ed7c710..93613c0 100644 --- a/lib/IRremoteESP8266/ReleaseNotes.md +++ b/lib/IRremoteESP8266/ReleaseNotes.md @@ -1,5 +1,107 @@ # Release Notes +## _v2.6.2 (20190616)_ + +**[Features]** +- Initial support for the ESP32 architecture & boards. (#742) +- Add changable GPIO settings to IRMQTTServer. (#730) +- IRMQTTServer: Enforce a repeat for all Coolix calls (#752) +- Basic DAIKIN 160bit send and decode. (#754) +- Add example code for a Smart(er) IR Repeater. (#740) +- Enforce Samsung A/C Quiet & Powerful mutual exclusivity. + +**[Misc]** +- IRMQTTServer: Add some memory alloc safety checks. (#749) +- Move some ToString() functions to IRac.cpp (#748) +- Increase tolerance value for TCL112AC protocol. (#745) +- Fix compiler warning in IRutils_test.cpp (#756) +- Scrape Supported Protocols and generate SupportedProtocols.md (#755) +- Make supported device info more organised. (#753) + + +## _v2.6.1 (20190609)_ + +**[Breaking Changes]** +- Major rework/breaking changes to Argo A/C support. (#705) + +**[Bug Fixes]** +- Correct `set/getQuiet` for Samsung A/C (#736) +- Add missing `on/off()` to IRCoolixAC class. (#725) +- Daikin `set/getEye()` uses wrong bit. (#711) +- IRMQTTServer: Continue to use same Temperature units. (#710) +- Fixed a bug with `setMode()`/`getMode()` for HAIER_AC. (#705) + +**[Features]** +- Add set/getPowerful for Samsung A/C (#736) +- Add `calibrate()` to all the A/C classes. (#735) +- IRMQTTServer: Add sequencing for sending MQTT IR commands. (#723) +- Add support for Fujitsu AR-REB1E & AR-JW2 remotes. (#718) +- Add Beta `decodeTrotec()` support. (#719) +- Add experimental `decodeArgo()` support. (#717) +- Support for Goodweather A/Cs. (#715) +- Add `DISABLE_CAPTURE_WHILE_TRANSMITTING` feature to IRMQTTServer. (#713) +- Support for Lixil Inax Toilet protocol. (#712) +- Add `set/getWeeklyTimerEnable()` to Daikin (#711) +- IRMQTTServer: Update Common A/C settings based on received IR messages. (#705) +- Add day of week to DAIKIN protocol (#699) +- Add limited support for Sharp A/C (#696) +- SAMSUNG_AC: Make sure special power mode messages are sent. (#695) +- Add `set/getPowerful()` (turbo) to DAIKIN216 (#693) + +**[Misc]** +- Add kPeriodOffset for CPU Freq of 160MHz. (#729) +- Example code for a Dumb IR repeater. (#737) +- Update swing handling for Fujitsu A/Cs. (#724) +- Add function to convert `decode_results` to `sendRaw()` array. (#721) +- Attempt to reduce heap fragmentation from strings. (#707) +- Update Fujitsu A/C example code to safer settings (#716) +- Enforce better `const` usage in IRUtils. (#708) +- Attempt to reduce heap fragmentation by A/C `toString()`s. (#694) +- Minor changes to DAIKIN216 timings and features. (#693) + + +## _v2.6.0 (20190430)_ + +**[Bug Fixes]** +- Fixed problem where LG protocol used wrong duty cycle for repeat. (#687) +- Fix checksum calculation for Daikin protocols. (#678) +- Fix the byte array version of sendGree() (#684, #685) +- Fix artificial vs. real state creation on HaierAC. (#668, #671) +- Fix issues caused by having `MQTT_ENABLE` set to false. (#677) +- Fix compile problem when DEBUG is defined. (#673, #674) +- Fix Minor bug with MQTT_ENABLE False condition (#654) + +**[Features]** +- Experimental support for DAIKIN216 (ARC433B69) (#690) +- Experimental support for Mitsubishi Heavy Industries A/Cs. (#660, #665, #667) +- Support more features of TCL A/C (#656) +- Add LEGO(TM) Power Functions IR protocol. (#655) +- Add Panasonic AC RKR model & Example (#649) +- DAIKIN/IRDaikinESP overhaul and add Comfort mode support. (#678) + **WARNING**: Previous `sendDaikin()` calls may not work. + Please recapture codes or use `kDaikinStateLengthShort` for + `nbytes` in those calls. +- IRMQTTServer: Move MQTT server and other parameters to WifiManager. (#680) + **WARNING**: Previous users may need to fully wipe/reset the + SPIFFS/WifiManager settings by visiting + `http:///reset` prior to or + after update. +- Add Wifi filtering options to IRMQTTServer. (#679) +- Add advanced aircon/climate functionality to IRMQTTServer (#677) +- Initial prototype of a common interface for all A/Cs. (#664) +- Improve MQTT topic usage for feedback messages. (#663) +- Add multiple independent GPIO sending support via MQTT. (#661) + +**[Misc]** +- Adjust kGreeHdrSpace to 4500 (#684, #686) +- Add Home Assistant mqtt climate instructions. (#682) +- Implement htmlEscape() to prevent XSS etc. (#681) +- Add F() Macros (#670) +- Update Daikin2's Cool mode min temp to 18C (#658) +- Change per byte bit-order in Electra protocol. (#648) +- Improve Daikin2 power on/off. (#647) + + ## _v2.5.6 (20190316)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266/SupportedProtocols.md b/lib/IRremoteESP8266/SupportedProtocols.md new file mode 100644 index 0000000..ead1cbd --- /dev/null +++ b/lib/IRremoteESP8266/SupportedProtocols.md @@ -0,0 +1,129 @@ + +# IR Protocols supported by this library + +| Protocol | Brand | Model | A/C Model | Detailed A/C Support | +| --- | --- | --- | --- | --- | +| [Aiwa](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Aiwa.cpp) | **Aiwa** | RC-T501 RCU | | - | +| [Argo](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Argo.cpp) | **[Argo](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Argo.h)** | Ulisse 13 DCI Mobile Split A/C | | Yes | +| [Carrier](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **Carrier/Surrey** | 42QG5A55970 remote
53NGK009/012 Inverter
619EGX0090E0 A/C
619EGX0120E0 A/C
619EGX0180E0 A/C
619EGX0220E0 A/C | | - | +| [Coolix](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Beko](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | BINR 070/071 split-type A/C
BINR 070/071 split-type A/C
RG57K7(B)/BGEF Remote
RG57K7(B)/BGEF Remote | | Yes | +| [Coolix](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Midea](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | MS12FU-10HRDN1-QRD0GW(B) A/C
MS12FU-10HRDN1-QRD0GW(B) A/C
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
RG52D/BGE Remote
RG52D/BGE Remote | | Yes | +| [Daikin](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | ARC423A5 remote
ARC433** remote
ARC433B69 remote
ARC477A1 remote
FTXZ25NV1B A/C
FTXZ35NV1B A/C
FTXZ50NV1B A/C | | Yes | +| [Denon](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Unknown** | | | - | +| [Dish](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - | +| [Electra](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **Unknown** | | | - | +| [Fujitsu](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-DB1 remote
AR-RAE1E remote
AR-RAH2E remote
AR-REB1E remote
AST9RSGCW A/C
ASYG30LFCA A/C
ASYG7LMCA A/C | ARDB1
ARJW2
ARRAH2E
ARREB1E | Yes | +| [Fujitsu](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu General](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-JW2 remote | ARDB1
ARJW2
ARRAH2E
ARREB1E | Yes | +| [GICable](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **Unknown** | | | - | +| [GlobalCache](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_GlobalCache.cpp) | **Unknown** | | | - | +| [Goodweather](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Goodweather.cpp) | **[Goodweather](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Goodweather.h)** | ZH/JT-03 remote | | Yes | +| [Gree](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[EKOKAI](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.h)** | A/C | | Yes | +| [Gree](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | | Yes | +| [Haier](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C
HSU07-HEA03 remote
YR-W02 remote | | Yes | +| [Hitachi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote
RAS-35THA6 remote
Series VI A/C (Circa 2007) | | Yes | +| [Inax](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - | +| [JVC](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - | +| [Kelvinator](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C
KSV26HRC A/C
KSV35CRC A/C
KSV35HRC A/C
KSV53HRC A/C
KSV62HRC A/C
KSV70CRC A/C
KSV70HRC A/C
KSV80HRC A/C
YALIF Remote | | Yes | +| [LG](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[LG](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711A20083V remote
6711A20083V remote
AKB74395308 remote
AKB74395308 remote | | Yes | +| [Lasertag](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Unknown** | | | - | +| [Lego](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Lego.cpp) | **LEGO Power Functions** | IR Receiver | | - | +| [Lutron](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Lutron.cpp) | **Unknown** | | | - | +| [MWM](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_MWM.cpp) | **Unknown** | | | - | +| [Magiquest](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Magiquest.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Magiquest.h)** | | | Yes | +| [Midea](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Pioneer System](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RUBO18GMFILCAD A/C (18K BTU)
RYBO12GMFILCAD A/C (12K BTU) | | Yes | +| [Mitsubishi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector
TV | | Yes | +| [MitsubishiHeavy](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote
RLA502A700B remote
SRKxxZJ-S A/C
SRKxxZM-S A/C
SRKxxZMXA-S A/C | | Yes | +| [NEC](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_NEC.h)** | | | Yes | +| [Nikai](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Nikai.cpp) | **Unknown** | | | - | +| [Panasonic](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Panasonic.cpp) | **[Panasonic](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Panasonic.h)** | A75C2311 remote (CKP)
A75C3704 remote
A75C3747 remote
A75C3747 remote
A75C3747 remote
A75C3747 remote
CKP series A/C
CS-ME10CKPG A/C
CS-ME12CKPG A/C
CS-ME14CKPG A/C
CS-YW9MKD A/C
CS-Z9RKR A/C
DKE series A/C
JKE series A/C
NKE series A/C
RKR series A/C
TV | CKP
DKE
JKE
LKE
NKE
RKR | Yes | +| [Pioneer](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Pioneer.cpp) | **Unknown** | | | - | +| [Pronto](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Pronto.cpp) | **Unknown** | | | - | +| [RC5_RC6](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_RC5_RC6.cpp) | **Unknown** | | | - | +| [RCMM](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_RCMM.cpp) | **Microsoft** | XBOX 360 | | - | +| [Samsung](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Samsung.cpp) | **[Samsung](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Samsung.h)** | AR12KSFPEWQNET A/C
IEC-R03 remote
UA55H6300 TV | | Yes | +| [Sanyo](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sanyo.cpp) | **Unknown** | | | - | +| [Sharp](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AY-ZP40KR A/C
LC-52D62U TV | | Yes | +| [Sherwood](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sherwood.cpp) | **Sherwood** | RC-138 remote
RD6505(B) Receiver | | - | +| [Sony](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Sony.cpp) | **Unknown** | | | - | +| [Tcl](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | | | Yes | +| [Teco](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Teco.h)** | | | Yes | +| [Toshiba](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Toshiba](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | Akita EVO II
RAS 18SKP-ES
RAS-B13N3KV2
RAS-B13N3KVP-E
WC-L03SE
WH-TA04NE | | Yes | +| [Trotec](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Unknown](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | | | Yes | +| [Vestel](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Vestel.cpp) | **[Vestel](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Vestel.h)** | BIOX CXP-9 A/C (9K BTU) | | Yes | +| [Whirlpool](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Whirlpool.cpp) | **[Whirlpool](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Whirlpool.h)** | DG11J1-04 remote
DG11J1-3A remote
DG11J1-91 remote
SPIS409L A/C
SPIS412L A/C
SPIW409L A/C
SPIW412L A/C
SPIW418L A/C | DG11J13A
DG11J191 | Yes | +| [Whynter](https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_Whynter.cpp) | **Whynter** | ARC-110WD A/C | | - | + + +## Send only protocols: + +- GLOBALCACHE +- PRONTO +- RAW +- SHERWOOD + + +## Send & decodable protocols: + +- AIWA_RC_T501 +- ARGO +- CARRIER_AC +- COOLIX +- DAIKIN +- DAIKIN160 +- DAIKIN2 +- DAIKIN216 +- DENON +- DISH +- ELECTRA_AC +- FUJITSU_AC +- GICABLE +- GOODWEATHER +- GREE +- HAIER_AC +- HAIER_AC_YRW02 +- HITACHI_AC +- HITACHI_AC1 +- HITACHI_AC2 +- INAX +- JVC +- KELVINATOR +- LASERTAG +- LEGOPF +- LG +- LG2 +- LUTRON +- MAGIQUEST +- MIDEA +- MITSUBISHI +- MITSUBISHI2 +- MITSUBISHI_AC +- MITSUBISHI_HEAVY_152 +- MITSUBISHI_HEAVY_88 +- MWM +- NEC +- NEC_LIKE +- NIKAI +- PANASONIC +- PANASONIC_AC +- PIONEER +- RC5 +- RC5X +- RC6 +- RCMM +- SAMSUNG +- SAMSUNG36 +- SAMSUNG_AC +- SANYO +- SANYO_LC7461 +- SHARP +- SHARP_AC +- SONY +- TCL112AC +- TECO +- TOSHIBA_AC +- TROTEC +- VESTEL_AC +- WHIRLPOOL_AC +- WHYNTER diff --git a/lib/IRremoteESP8266/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/IRremoteESP8266/examples/ControlSamsungAC/ControlSamsungAC.ino index df910fe..aea7208 100644 --- a/lib/IRremoteESP8266/examples/ControlSamsungAC/ControlSamsungAC.ino +++ b/lib/IRremoteESP8266/examples/ControlSamsungAC/ControlSamsungAC.ino @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino b/lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino new file mode 100644 index 0000000..9faeafc --- /dev/null +++ b/lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino @@ -0,0 +1,130 @@ +/* + * IRremoteESP8266: DumbIRRepeater.ino - Record and playback IR codes. + * Copyright 2019 David Conran (crankyoldgit) + * + * This program will try to capture incoming IR messages and replay them back. + * It doesn't use any of the advanced detection features, thus it will just + * replay the messages at fixed modulated frequency (kFrequency) and a 50% duty + * cycle. + * + * Note: + * This might NOT be the frequency of the incoming message, so some replayed + * messages may not work. The frequency of incoming messages & duty cycle is + * lost at the point of the Hardware IR demodulator. The ESP can't see it. + * + * W A R N I N G + * This code is just for educational/example use only. No help will be given + * to you to make it do something else, or to make it work with some + * weird device or circuit, or to make it more usable or practical. + * If it works for you. Great. If not, Congratulations on changing/fixing it. + * + * An IR detector/demodulator must be connected to the input, kRecvPin. + * An IR LED circuit must be connected to the output, kIrLedPin. + * + * Example circuit diagrams (both are needed): + * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving + * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * + * Common mistakes & tips: + * * Don't just connect the IR LED directly to the pin, it won't + * have enough current to drive the IR LED effectively. + * * Make sure you have the IR LED polarity correct. + * See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity + * * Some digital camera/phones can be used to see if the IR LED is flashed. + * Replace the IR LED with a normal LED if you don't have a digital camera + * when debugging. + * * Avoid using the following pins unless you really know what you are doing: + * * Pin 0/D3: Can interfere with the boot/program mode & support circuits. + * * Pin 1/TX/TXD0: Any serial transmissions from the ESP will interfere. + * * Pin 3/RX/RXD0: Any serial transmissions to the ESP will interfere. + * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs + * for your first time. e.g. ESP-12 etc. + * + * Changes: + * Version 1.0: June, 2019 + * - Initial version. + */ + +#include +#include +#include +#include +#include + +// ==================== start of TUNEABLE PARAMETERS ==================== + +// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) +const uint16_t kRecvPin = 14; + +// GPIO to use to control the IR LED circuit. Recommended: 4 (D2). +const uint16_t kIrLedPin = 4; + +// The Serial connection baud rate. +// NOTE: Make sure you set your Serial Monitor to the same speed. +const uint32_t kBaudRate = 115200; + +// As this program is a special purpose capture/resender, let's use a larger +// than expected buffer so we can handle very large IR messages. +// i.e. Up to 512 bits. +const uint16_t kCaptureBufferSize = 1024; + +// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a +// message ended. +const uint8_t kTimeout = 50; // Milli-Seconds + +// kFrequency is the modulation frequency all messages will be replayed at. +const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz. + +// ==================== end of TUNEABLE PARAMETERS ==================== + +// The IR transmitter. +IRsend irsend(kIrLedPin); +// The IR receiver. +IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false); +// Somewhere to store the captured message. +decode_results results; + +// This section of code runs only once at start-up. +void setup() { + irrecv.enableIRIn(); // Start up the IR receiver. + irsend.begin(); // Start up the IR sender. + + Serial.begin(kBaudRate, SERIAL_8N1); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + + Serial.print("DumbIRRepeater is now running and waiting for IR input " + "on Pin "); + Serial.println(kRecvPin); + Serial.print("and will retransmit it on Pin "); + Serial.println(kIrLedPin); +} + +// The repeating section of the code +void loop() { + // Check if an IR message has been received. + if (irrecv.decode(&results)) { // We have captured something. + // The capture has stopped at this point. + + // Convert the results into an array suitable for sendRaw(). + // resultToRawArray() allocates the memory we need for the array. + uint16_t *raw_array = resultToRawArray(&results); + // Find out how many elements are in the array. + uint16_t length = getCorrectedRawLength(&results); + // Send it out via the IR LED circuit. + irsend.sendRaw(raw_array, length, kFrequency); + // Resume capturing IR messages. It was not restarted until after we sent + // the message so we didn't capture our own message. + irrecv.resume(); + // Deallocate the memory allocated by resultToRawArray(). + delete [] raw_array; + + // Display a crude timestamp & notification. + uint32_t now = millis(); + Serial.printf( + "%06u.%03u: A message that was %d entries long was retransmitted.\n", + now / 1000, now % 1000, length); + } + yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset. +} diff --git a/lib/IRremoteESP8266/examples/DumbIRRepeater/platformio.ini b/lib/IRremoteESP8266/examples/DumbIRRepeater/platformio.ini new file mode 100644 index 0000000..e11c48d --- /dev/null +++ b/lib/IRremoteESP8266/examples/DumbIRRepeater/platformio.ini @@ -0,0 +1,29 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino index 03c80e1..c59372c 100644 --- a/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino +++ b/lib/IRremoteESP8266/examples/IRGCSendDemo/IRGCSendDemo.ino @@ -36,9 +36,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino index 69f7299..b69373d 100644 --- a/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino +++ b/lib/IRremoteESP8266/examples/IRGCTCPServer/IRGCTCPServer.ino @@ -37,10 +37,13 @@ * can check your wifi router for it's address. */ -#ifndef UNIT_TEST #include -#endif +#if defined(ESP8266) #include +#endif // ESP8266 +#if defined(ESP32) +#include +#endif // ESP32 #include #include #include diff --git a/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h new file mode 100644 index 0000000..87fb4f9 --- /dev/null +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h @@ -0,0 +1,334 @@ +/* + * Send & receive arbitrary IR codes via a web server or MQTT. + * Copyright David Conran 2016, 2017, 2018, 2019 + */ +#ifndef EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ +#define EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ + +#include +#include +#include +#include +#include +#include + +// ---------------- Start of User Configuration Section ------------------------ + +#ifndef MQTT_ENABLE +#define MQTT_ENABLE true // Whether or not MQTT is used at all. +#endif // MQTT_ENABLE + +// ---------------------- Board Related Settings ------------------------------- +// NOTE: Make sure you set your Serial Monitor to the same speed. +#define BAUD_RATE 115200 // Serial port Baud rate. + +// Change if you need multiple independent send gpios & topics. (MQTT only) +const uint8_t kNrOfIrTxGpios = 1; +// Default GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. +// For an ESP-01 we suggest you use RX/GPIO3/Pin 7. i.e. kDefaultIrLed = 3 +// Note: A value of -1 means unused. +const int8_t kDefaultIrLed = 4; // <=- CHANGE_ME (optional) +// Default GPIO the IR demodulator is connected to/controlled by. GPIO 14 = D5. +const int8_t kDefaultIrRx = 14; // <=- CHANGE_ME (optional) + +// Enable/disable receiving/decoding IR messages entirely. +// Note: IR_RX costs about 40k+ of program memory. +#define IR_RX true + +// Should we use PULLUP on the IR Rx gpio? +#define IR_RX_PULLUP false + +// --------------------- Network Related Settings ------------------------------ +const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on. +// Change to 'true'/'false' if you do/don't want these features or functions. +#define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP. +// We obtain our network config via DHCP by default but allow an easy way to +// use a static IP config. +#if USE_STATIC_IP +const IPAddress kIPAddress = IPAddress(10, 0, 1, 78); +const IPAddress kGateway = IPAddress(10, 0, 1, 1); +const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); +#endif // USE_STATIC_IP + +// See: https://github.com/tzapu/WiFiManager#filter-networks for these settings. +#define HIDE_DUPLIATE_NETWORKS false // Should WifiManager hide duplicate SSIDs +// #define MIN_SIGNAL_STRENGTH 20 // Minimum WiFi signal stength (percentage) + // before we will connect. + // The unset default is 8%. + // (Uncomment to enable) +// Do you want/need mdns enabled? (https://en.wikipedia.org/wiki/Multicast_DNS) +#define MDNS_ENABLE true // `false` to disable and save ~21k of program space. + +// ----------------------- HTTP Related Settings ------------------------------- +#define FIRMWARE_OTA true // Allow remote update of the firmware via http. + // Less secure if enabled. + // Note: Firmware OTA is also disabled until + // a password is set. +#define HTML_PASSWORD_ENABLE false // Protect access to the HTML interface. + // Note: OTA & GPIO updates are always + // passworded. +// If you do not set a password, Firmware OTA & GPIO updates will be blocked. + +// ----------------------- MQTT Related Settings ------------------------------- +#if MQTT_ENABLE +const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. + +#define MQTT_ACK "sent" // Sub-topic we send back acknowledgements on. +#define MQTT_SEND "send" // Sub-topic we get new commands from. +#define MQTT_RECV "received" // Topic we send received IRs to. +#define MQTT_LOG "log" // Topic we send log messages to. +#define MQTT_LWT "status" // Topic for the Last Will & Testament. +#define MQTT_CLIMATE "ac" // Sub-topic for the climate topics. +#define MQTT_CLIMATE_CMND "cmnd" // Sub-topic for the climate command topics. +#define MQTT_CLIMATE_STAT "stat" // Sub-topic for the climate stat topics. +#define MQTTbroadcastInterval 10 * 60 // Seconds between rebroadcasts + +#define QOS 1 // MQTT broker should queue up any unreceived messages for us +// #define QOS 0 // MQTT broker WON'T queue up messages for us. Fire & Forget. +#endif // MQTT_ENABLE + +// ------------------------ IR Capture Settings -------------------------------- +// Should we stop listening for IR messages when we send a message via IR? +// Set this to `true` if your IR demodulator is picking up self transmissions. +// Use `false` if it isn't or can't see the self-sent transmissions +// Using `true` may mean some incoming IR messages are lost or garbled. +// i.e. `false` is better if you can get away with it. +#define DISABLE_CAPTURE_WHILE_TRANSMITTING true +// Let's use a larger than normal buffer so we can handle AirCon remote codes. +const uint16_t kCaptureBufferSize = 1024; +#if DECODE_AC +// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator +// A value this large may swallow repeats of some protocols +const uint8_t kCaptureTimeout = 50; // Milliseconds +#else // DECODE_AC +// Suits most messages, while not swallowing many repeats. +const uint8_t kCaptureTimeout = 15; // Milliseconds +#endif // DECODE_AC +// Ignore unknown messages with <10 pulses (see also REPORT_UNKNOWNS) +const uint16_t kMinUnknownSize = 2 * 10; +#define REPORT_UNKNOWNS false // Report inbound IR messages that we don't know. +#define REPORT_RAW_UNKNOWNS false // Report the whole buffer, recommended: + // MQTT_MAX_PACKET_SIZE of 1024 or more + +// Should we use and report individual A/C settings we capture via IR if we +// can understand the individual settings of the remote. +// e.g. Aquire the A/C settings from an actual A/C IR remote and override +// any local settings set via MQTT/HTTP etc. +#define USE_DECODED_AC_SETTINGS true // `false` to disable. `true` to enable. +// Should we allow or ignore an A/C IR remote to override the A/C protocol/model +// as set via MQTT or HTTP? +// e.g. If `true`, you can use any fully supported A/C remote to control +// another brand's or model's A/C unit. `false` means change to the new +// protocol/model if we support it via `USE_DECODED_AC_SETTINGS`. +#define IGNORE_DECODED_AC_PROTOCOL true +// Do we (re-)send the captured & decoded A/C message via the IR_LED? +// `false` if you don't want to repeat the captured message. +// e.g. Useful if the IR demodulator is located in the path between the remote +// and the A/C unit so the command isn't sent twice. +// `true` if want it sent anyway. +// e.g. The IR demodulator is in a completely different location than than the +// actual a/c unit. +#define REPLAY_DECODED_AC_MESSAGE false + +// ------------------------ Advanced Usage Only -------------------------------- + +#define KEY_PROTOCOL "protocol" +#define KEY_MODEL "model" +#define KEY_POWER "power" +#define KEY_MODE "mode" +#define KEY_TEMP "temp" +#define KEY_FANSPEED "fanspeed" +#define KEY_SWINGV "swingv" +#define KEY_SWINGH "swingh" +#define KEY_QUIET "quiet" +#define KEY_TURBO "turbo" +#define KEY_LIGHT "light" +#define KEY_BEEP "beep" +#define KEY_ECONO "econo" +#define KEY_SLEEP "sleep" +#define KEY_FILTER "filter" +#define KEY_CLEAN "clean" +#define KEY_CELSIUS "use_celsius" + +// HTML arguments we will parse for IR code information. +#define KEY_TYPE "type" // KEY_PROTOCOL is also checked too. +#define KEY_CODE "code" +#define KEY_BITS "bits" +#define KEY_REPEAT "repeats" + +// GPIO html/config keys +#define KEY_TX_GPIO "tx" +#define KEY_RX_GPIO "rx" + +// Text for Last Will & Testament status messages. +const char* kLwtOnline = "Online"; +const char* kLwtOffline = "Offline"; + +const uint8_t kHostnameLength = 30; +const uint8_t kPortLength = 5; // Largest value of uint16_t is "65535". +const uint8_t kUsernameLength = 15; +const uint8_t kPasswordLength = 20; + +// -------------------------- Debug Settings ----------------------------------- +// Debug output is disabled if any of the IR pins are on the TX (D1) pin. +// See `isSerialGpioUsedByIr()`. +// Note: Debug costs ~6k of program space. +#ifndef DEBUG +#define DEBUG false // Change to 'true' for serial debug output. +#endif // DEBUG + +// ----------------- End of User Configuration Section ------------------------- + +// Constants +#define _MY_VERSION_ "v1.2.0" + +const uint8_t kRebootTime = 15; // Seconds +const uint8_t kQuickDisplayTime = 2; // Seconds + +// Gpio related +const int8_t kGpioUnused = -1; +#if defined(ESP8266) +const int8_t kTxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16}; +const int8_t kRxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15}; +#endif // ESP8266 +#if defined(ESP32) +const int8_t kTxGpios[] = { + -1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33}; +const int8_t kRxGpios[] = { + -1, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}; +#endif // ESP32 + +// JSON stuff +// Name of the json config file in SPIFFS. +const char* kConfigFile = "/config.json"; +const char* kMqttServerKey = "mqtt_server"; +const char* kMqttPortKey = "mqtt_port"; +const char* kMqttUserKey = "mqtt_user"; +const char* kMqttPassKey = "mqtt_pass"; +const char* kMqttPrefixKey = "mqtt_prefix"; +const char* kHostnameKey = "hostname"; +const char* kHttpUserKey = "http_user"; +const char* kHttpPassKey = "http_pass"; +const char* kCommandDelimiter = ","; + +// URLs +const char* kUrlRoot = "/"; +const char* kUrlAdmin = "/admin"; +const char* kUrlAircon = "/aircon"; +const char* kUrlSendDiscovery = "/send_discovery"; +const char* kUrlExamples = "/examples"; +const char* kUrlGpio = "/gpio"; +const char* kUrlGpioSet = "/gpio/set"; +const char* kUrlInfo = "/info"; +const char* kUrlReboot = "/quitquitquit"; +const char* kUrlWipe = "/reset"; + +#if MQTT_ENABLE +const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds. +const uint32_t kStatListenPeriodMs = 5 * 1000; // mSeconds +const int32_t kMaxPauseMs = 10000; // 10 Seconds. +const char* kSequenceDelimiter = ";"; +const char kPauseChar = 'P'; +#if defined(ESP8266) +const uint32_t kChipId = ESP.getChipId(); +#endif // ESP8266 +#if defined(ESP32) +const uint32_t kChipId = ESP.getEfuseMac(); // Discard the top 16 bits. +#endif // ESP32 + +const char* kClimateTopics = + "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" + KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" + KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" + KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
"; + +void mqttCallback(char* topic, byte* payload, unsigned int length); +String listOfCommandTopics(void); +void handleSendMqttDiscovery(void); +void subscribing(const String topic_name); +void unsubscribing(const String topic_name); +void mqttLog(const String mesg); +bool mountSpiffs(void); +bool reconnect(void); +void receivingMQTT(String const topic_name, String const callback_str); +void callback(char* topic, byte* payload, unsigned int length); +void sendMQTTDiscovery(const char *topic); +void doBroadcast(TimerMs *timer, const uint32_t interval, + const stdAc::state_t state, const bool retain, + const bool force); +#endif // MQTT_ENABLE +bool isSerialGpioUsedByIr(void); +void debug(const char *str); +void saveWifiConfigCallback(void); +void saveWifiConfig(void); +void loadWifiConfigFile(void); +String msToHumanString(uint32_t const msecs); +String timeElapsed(uint32_t const msec); +String timeSince(uint32_t const start); +String gpioToString(const int16_t gpio); +uint8_t getDefaultIrSendIdx(void); +IRsend* getDefaultIrSendPtr(void); +int8_t getDefaultTxGpio(void); +String listOfTxGpios(void); +bool hasUnsafeHTMLChars(String input); +String htmlHeader(const String title, const String h1_text = ""); +String htmlEnd(void); +String htmlButton(const String url, const String button, + const String text = ""); +String htmlMenu(void); +void handleRoot(void); +String addJsReloadUrl(const String url, const uint16_t timeout_s, + const bool notify); +void handleExamples(void); +String htmlSelectBool(const String name, const bool def); +String htmlSelectProtocol(const String name, const decode_type_t def); +String htmlSelectModel(const String name, const int16_t def); +String htmlSelectMode(const String name, const stdAc::opmode_t def); +String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def); +String htmlSelectSwingv(const String name, const stdAc::swingv_t def); +String htmlSelectSwingh(const String name, const stdAc::swingh_t def); +void handleAirCon(void); +void handleAirConSet(void); +void handleAdmin(void); +void handleInfo(void); +void handleReset(void); +void handleReboot(void); +bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, + const String str); +uint16_t countValuesInStr(const String str, char sep); +uint16_t * newCodeArray(const uint16_t size); +#if SEND_GLOBALCACHE +bool parseStringAndSendGC(IRsend *irsend, const String str); +#endif // SEND_GLOBALCACHE +#if SEND_PRONTO +bool parseStringAndSendPronto(IRsend *irsend, const String str, + uint16_t repeats); +#endif // SEND_PRONTO +#if SEND_RAW +bool parseStringAndSendRaw(IRsend *irsend, const String str); +#endif // SEND_RAW +void handleIr(void); +void handleNotFound(void); +void setup_wifi(void); +void init_vars(void); +void setup(void); +void loop(void); +uint64_t getUInt64fromHex(char const *str); +bool sendIRCode(IRsend *irsend, int const ir_type, + uint64_t const code, char const * code_str, uint16_t bits, + uint16_t repeat); +bool sendInt(const String topic, const int32_t num, const bool retain); +bool sendBool(const String topic, const bool on, const bool retain); +bool sendString(const String topic, const String str, const bool retain); +bool sendFloat(const String topic, const float_t temp, const bool retain); +stdAc::state_t updateClimate(stdAc::state_t current, const String str, + const String prefix, const String payload); +bool cmpClimate(const stdAc::state_t a, const stdAc::state_t b); +bool sendClimate(const stdAc::state_t prev, const stdAc::state_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR, + const bool enableIR = true); +bool decodeCommonAc(const decode_results *decode); +#endif // EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino index a1e8d23..e30f772 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -1,53 +1,66 @@ /* * Send & receive arbitrary IR codes via a web server or MQTT. - * Copyright David Conran 2016, 2017, 2018 + * Copyright David Conran 2016, 2017, 2018, 2019 * - * NOTE: An IR LED circuit *MUST* be connected to ESP8266 GPIO4 (D2) if - * you want to send IR messages. See IR_LED below. - * A compatible IR RX modules *MUST* be connected to ESP8266 GPIO14 (D5) - * if you want to capture & decode IR nessages. See IR_RX below. + * Copyright: + * Code for this has been borrowed from lots of other OpenSource projects & + * resources. I'm *NOT* claiming complete Copyright ownership of all the code. + * Likewise, feel free to borrow from this as much as you want. * - * WARN: This is very advanced & complicated example code. Not for beginners. - * You are strongly suggested to try & look at other example code first. + * NOTE: An IR LED circuit SHOULD be connected to the ESP if + * you want to send IR messages. e.g. GPIO4 (D2) + * A compatible IR RX modules SHOULD be connected to ESP + * if you want to capture & decode IR nessages. e.g. GPIO14 (D5) + * See 'IR_RX' in IRMQTTServer.h. + * GPIOs are configurable from the http:///gpio + * page. + * + * WARN: This is *very* advanced & complicated example code. Not for beginners. + * You are strongly suggested to try & look at other example code first + * to understand how this library works. * * # Instructions * * ## Before First Boot (i.e. Compile time) - * - Either: - * o Set the MQTT_SERVER define below to the address of your MQTT server. - * or - * o Disable MQTT (see '#define MQTT_ENABLE' below). + * - Disable MQTT if desired. (see '#define MQTT_ENABLE' in IRMQTTServer.h). * * - Site specific settings: - * o Search for 'CHANGE_ME' for the things you probably need to change for - * your particular situation. + * o Search for 'CHANGE_ME' in IRMQTTServer.h for the things you probably + * need to change for your particular situation. + * o All user changable settings are in the file IRMQTTServer.h. * * - Arduino IDE: * o Install the following libraries via Library Manager - * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) + * - ArduinoJson (https://arduinojson.org/) (Version >= 5.0 and < 6.0) * - PubSubClient (https://pubsubclient.knolleary.net/) + * - WiFiManager (https://github.com/tzapu/WiFiManager) + * (ESP8266: Version >= 0.14, ESP32: 'development' branch.) * o You MUST change to have the following (or larger) value: * (with REPORT_RAW_UNKNOWNS 1024 or more is recommended) - * #define MQTT_MAX_PACKET_SIZE 512 + * #define MQTT_MAX_PACKET_SIZE 768 * - PlatformIO IDE: * If you are using PlatformIO, this should already been done for you in * the accompanying platformio.ini file. * * ## First Boot (Initial setup) - * The ESP8266 board will boot into the WiFiManager's AP mode. + * The ESP board will boot into the WiFiManager's AP mode. * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc. * Connect to that SSID. Then point your browser to http://192.168.4.1/ and - * configure the ESP8266 to connect to your desired WiFi network. - * It will remember the new WiFi connection details on next boot. + * configure the ESP to connect to your desired WiFi network and associated + * required settings. It will remember these details on next boot if the device + * connects successfully. * More information can be found here: * https://github.com/tzapu/WiFiManager#how-it-works * - * If you need to reset the WiFi settings, visit: - * http:///reset + * If you need to reset the WiFi and saved settings to go back to "First Boot", + * visit: http:///reset * - * ## Normal Use (After setup) + * ## Normal Use (After initial setup) * Enter 'http:///gpio page to configure the GPIOs + * for the IR LED(s) and/or IR RX demodulator. + * * You can send URLs like the following, with similar data type limitations as * the MQTT formating in the next section. e.g: * http:///ir?type=7&code=E0E09966 @@ -59,33 +72,63 @@ * * or * - * Send a MQTT message to the topic 'ir_server/send' using the following - * format (Order is important): - * protocol_num,hexcode e.g. 7,E0E09966 which is Samsung(7), Power On code, - * default bit size, default nr. of repeats. - * protocol_num,hexcode,bits e.g. 4,f50,12 which is Sony(4), Power Off code, - * 12 bits & default nr. of repeats. - * protocol_num,hexcode,bits,repeats e.g. 19,C1A2E21D,0,8 which is - * Sherwood(19), Vol Up, default bit size & - * repeated 8 times. - * 30,frequency,raw_string e.g. 30,38000,9000,4500,500,1500,500,750,500,750 - * Raw (30) @ 38kHz with a raw code of "9000,4500,500,1500,500,750,500,750" - * 31,code_string e.g. 31,40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 - * GlobalCache (31) & "40000,1,1,96,..." (Sony Vol Up) - * 25,Rrepeats,hex_code_string e.g. 25,R1,0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 - * Pronto (25), 1 repeat, & "0000 006E 0022 0002 ..." (Sherwood Amp Tape Input) - * ac_protocol_num,really_long_hexcode e.g. 18,190B8050000000E0190B8070000010F0 - * Kelvinator (18) Air Con on, Low Fan, 25 deg etc. - * NOTE: Ensure you zero-pad to the correct number of - * digits for the bit/byte size you want to send - * as some A/C units have units have different - * sized messages. e.g. Fujitsu A/C units. + * Send a MQTT message to the topic 'ir_server/send' (or 'ir_server/send_0' etc) + * using the following format (Order is important): + * protocol_num,hexcode + * e.g. 7,E0E09966 + * which is: Samsung(7), Power On code, default bit size, + * default nr. of repeats. + * + * protocol_num,hexcode,bits + * e.g. 4,f50,12 + * which is: Sony(4), Power Off code, 12 bits & default nr. of repeats. + * + * protocol_num,hexcode,bits,repeats + * e.g. 19,C1A2E21D,0,8 + * which is: Sherwood(19), Vol Up, default bit size & repeated 8 times. + * + * 30,frequency,raw_string + * e.g. 30,38000,9000,4500,500,1500,500,750,500,750 + * which is: Raw (30) @ 38kHz with a raw code of + * "9000,4500,500,1500,500,750,500,750" + * + * 31,code_string + * e.g. 31,40000,1,1,96,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,48,24,24,24,24,24,24,24,24,1058 + * which is: GlobalCache (31) & "40000,1,1,96,..." (Sony Vol Up) + * + * 25,Rrepeats,hex_code_string + * e.g. 25,R1,0000,006E,0022,0002,0155,00AA,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0040,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0015,0040,0015,0015,0015,0015,0015,0040,0015,0040,0015,0040,0015,0040,0015,0040,0015,0640,0155,0055,0015,0E40 + * which is: Pronto (25), 1 repeat, & "0000 006E 0022 0002 ..." + * aka a "Sherwood Amp Tape Input" message. + * + * ac_protocol_num,really_long_hexcode + * e.g. 18,190B8050000000E0190B8070000010F0 + * which is: Kelvinator (18) Air Con on, Low Fan, 25 deg etc. + * NOTE: Ensure you zero-pad to the correct number of digits for the + * bit/byte size you want to send as some A/C units have units + * have different sized messages. e.g. Fujitsu A/C units. + * + * Sequences. + * You can send a sequence of IR messages via MQTT using the above methods + * if you separate them with a ';' character. In addition you can add a + * pause/gap between sequenced messages by using 'P' followed immediately by + * the number of milliseconds you wish to wait (up to a max of kMaxPauseMs). + * e.g. 7,E0E09966;4,f50,12 + * Send a Samsung(7) TV Power on code, followed immediately by a Sony(4) + * TV power off message. + * or: 19,C1A28877;P500;19,C1A25AA5;P500;19,C1A2E21D,0,30 + * Turn on a Sherwood(19) Amplifier, Wait 1/2 a second, Switch the + * Amplifier to Video input 2, wait 1/2 a second, then send the Sherwood + * Amp the "Volume Up" message 30 times. + * * In short: * No spaces after/before commas. * Values are comma separated. * The first value is always in Decimal. * For simple protocols, the next value (hexcode) is always hexadecimal. * The optional bit size is in decimal. + * CAUTION: Some AC protocols DO NOT use the really_long_hexcode method. + * e.g. < 64bit AC protocols. * * Unix command line usage example: * # Install a MQTT client @@ -96,8 +139,7 @@ * This server will send (back) what ever IR message it just transmitted to * the MQTT topic 'ir_server/sent' to confirm it has been performed. This works * for messages requested via MQTT or via HTTP. - * Note: Other status messages are also sent to 'ir_server/sent' from time to - * time. + * * Unix command line usage example: * # Listen to MQTT acknowledgements. * $ mosquitto_sub -h 10.0.0.4 -t ir_server/sent @@ -115,8 +157,116 @@ * # Listen via MQTT for IR messages captured by this server. * $ mosquitto_sub -h 10.0.0.4 -t ir_server/received * + * Note: General logging messages are also sent to 'ir_server/log' from + * time to time. + * + * ## Climate (AirCon) interface. (Advanced use) + * You can now control Air Conditioner devices that have full/detailed support + * from the IRremoteESP8266 library. See the "Aircon" page for list of supported + * devices. You can do this via HTTP/HTML or via MQTT. + * + * NOTE: It will only change the attributes you change/set. It's up to you to + * maintain a consistent set of attributes for your particular aircon. + * + * TIP: Use "-1" for 'model' if your A/C doesn't have a specific `setModel()` + * or IR class attribute. Most don't. Some do. + * e.g. PANASONIC_AC, FUJITSU_AC, WHIRLPOOL_AC + * + * ### via MQTT: + * The code listen for commands (via wildcard) on the MQTT topics at the + * `ir_server/ac/cmnd/+` level, such as: + * i.e. protocol, model, power, mode, temp, fanspeed, swingv, swingh, quiet, + * turbo, light, beep, econo, sleep, filter, clean, use_celsius + * e.g. ir_server/ac/cmnd/power, ir_server/ac/cmnd/temp, etc. + * It will process them, and if successful and it caused a change, it will + * acknowledge this via the relevant state topic for that command. + * e.g. If the aircon/climate changes from power off to power on, it will + * send an "on" payload to "ir_server/ac/stat/power" + * NOTE: These "stat" messages have the MQTT retain flag set to on. Thus the + * MQTT broker will remember them until reset/restarted etc. + * + * The code will also periodically broadcast all possible aircon/climate state + * attributes to their corresponding "ir_server/ac/stat" topics. This ensures + * any updates to the ESP's knowledge that may have been lost in transmission + * are re-communicated. e.g. The MQTT broker being offline. + * This also helps with Home Assistant MQTT discovery. + * + * The program on boot & first successful connection to the MQTT broker, will + * try to re-acquire any previous aircon/climate state information and act + * accordingly. This will typically result in A/C IR message being sent as and + * saved state will probably be different from the defaults. + * + * NOTE: Command attributes are processed sequentially. + * e.g. Going from "25C, cool, fan low" to "27C, heat, fan high" may go + * via "27C, cool, fan low" & "27C, heat, fan low" depending on the order + * of arrival & processing of the MQTT commands. + * + * ### Home Assistant (HA) MQTT climate integration + * After you have set the Protocol (required) & Model (if needed) and any of + * the other misc aircon settings you desire, you can then add the following to + * your Home Assistant configuration, and it should allow you to + * control most of the important settings. Google Home/Assistant (via HA) + * can also control the device, but you will need to configure Home Assistant + * via it's documentation for that. It has even more limited control. + * It's far beyond the scope of these instructions to guide you through setting + * up HA and Google Home integration. See https://www.home-assistant.io/ + * + * In HA's configuration.yaml, add: + * + * climate: + * platform: mqtt + * name: Living Room Aircon + * modes: + * - "off" + * - "auto" + * - "cool" + * - "heat" + * - "dry" + * - "fan_only" + * fan_modes: + * - "auto" + * - "min" + * - "low" + * - "medium" + * - "high" + * - "max" + * swing_modes: + * - "off" + * - "auto" + * - "highest" + * - "high" + * - "middle" + * - "low" + * - "lowest" + * power_command_topic: "ir_server/ac/cmnd/power" + * mode_command_topic: "ir_server/ac/cmnd/mode" + * mode_state_topic: "ir_server/ac/stat/mode" + * temperature_command_topic: "ir_server/ac/cmnd/temp" + * temperature_state_topic: "ir_server/ac/stat/temp" + * fan_mode_command_topic: "ir_server/ac/cmnd/fanspeed" + * fan_mode_state_topic: "ir_server/ac/stat/fanspeed" + * swing_mode_command_topic: "ir_server/ac/cmnd/swingv" + * swing_mode_state_topic: "ir_server/ac/stat/swingv" + * min_temp: 16 + * max_temp: 32 + * temp_step: 1 + * retain: false + * + * ### via HTTP: + * Use the "http:///aircon/set" URL and pass on + * the arguments as needed to control your device. See the `KEY_*` #defines + * in the code for all the parameters. + * i.e. protocol, model, power, mode, temp, fanspeed, swingv, swingh, quiet, + * turbo, light, beep, econo, sleep, filter, clean, use_celsius + * Example: + * http:///aircon/set?protocol=PANASONIC_AC&model=LKE&power=on&mode=auto&fanspeed=min&temp=23 + * + * ## Debugging & Logging * If DEBUG is turned on, there is additional information printed on the Serial - * Port. + * Port. Serial Port output may be disabled if the GPIO is used for IR. + * + * If MQTT is enabled, some information/logging is sent to the MQTT topic: + * `ir_server/log` * * ## Updates * You can upload new firmware over the air (OTA) via the form on the device's @@ -137,185 +287,321 @@ * potentially compromise your network. OTA updates are password protected by * default. If you are sufficiently paranoid, you SHOULD disable uploading * firmware via OTA. (see 'FIRMWARE_OTA') - * You SHOULD also (re)set/change all usernames & passwords. (See `CHANGE_ME`s) + * You SHOULD also set/change all usernames & passwords. * For extra bonus points: Use a separate untrusted SSID/vlan/network/ segment * for your IoT stuff, including this device. * Caveat Emptor. You have now been suitably warned. * - * - * Copyright Notice: - * Code for this has been borrowed from lots of other OpenSource projects & - * resources. I'm *NOT* claiming complete Copyright ownership of all the code. - * Likewise, feel free to borrow from this as much as you want. */ -// ---------------- Start of User Configuration Section ------------------------ -// Change to 'true'/'false' if you do/don't want these features or functions. -#define USE_STATIC_IP false // Change to 'true' if you don't want to use DHCP. -#define REPORT_UNKNOWNS false // Report inbound IR messages that we don't know. -#define REPORT_RAW_UNKNOWNS false // Report the whole buffer, recommended: - // MQTT_MAX_PACKET_SIZE of 1024 or more -#define MQTT_ENABLE true // Whether or not MQTT is used at all. -// 'kHtmlUsername' & 'kHtmlPassword' are used by the following two items: -#define FIRMWARE_OTA true // Allow remote update of the firmware via http. - // Less secure if enabled. - // Note: Firmware OTA is also disabled until - // 'kHtmlPassword' is changed from the default. -#define HTML_PASSWORD_ENABLE false // Protect access to the HTML interface. - // Note: OTA update is always passworded. +#include "IRMQTTServer.h" #include +#include +#include +#if defined(ESP8266) #include +#include +#include +#endif // ESP8266 +#if defined(ESP32) +#include +#include +#include +#include +#include +#endif // ESP32 #include #include -#include #include -#include #include #include #include +#include #include +#include #if MQTT_ENABLE // -------------------------------------------------------------------- // * * * IMPORTANT * * * // You must change to have the following value. -// #define MQTT_MAX_PACKET_SIZE 512 +// #define MQTT_MAX_PACKET_SIZE 768 // -------------------------------------------------------------------- #include #endif // MQTT_ENABLE -#include +#include // NOLINT(build/include) +#include #include -// Configuration parameters -// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. -#define IR_LED 4 // <=- CHANGE_ME (optional) -// define IR_LED 3 // For an ESP-01 we suggest you use RX/GPIO3/Pin 7. -// -// GPIO the IR RX module is connected to/controlled by. GPIO 14 = D5. -// Comment this out to disable receiving/decoding IR messages entirely. -#define IR_RX 14 // <=- CHANGE_ME (optional) -#define IR_RX_PULLUP false -const uint16_t kHttpPort = 80; // The TCP port the HTTP server is listening on. -// Name of the device you want in mDNS. -// NOTE: Changing this will change the MQTT path too unless you override it -// via MQTTprefix below. -#define HOSTNAME "ir_server" // <=- CHANGE_ME (optional) - -// We obtain our network config via DHCP by default but allow an easy way to -// use a static IP config. -#if USE_STATIC_IP -const IPAddress kIPAddress = IPAddress(10, 0, 1, 78); -const IPAddress kGateway = IPAddress(10, 0, 1, 1); -const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); -#endif // USE_STATIC_IP - -#if MQTT_ENABLE -// Address of your MQTT server. -#define MQTT_SERVER "10.0.0.4" // <=- CHANGE_ME -const uint16_t kMqttPort = 1883; // Default port used by MQTT servers. -// Set if your MQTT server requires a Username & Password to connect -// ... and it probably should if you want to be more secure. -const char* kMqttUsername = ""; // <=- CHANGE_ME (optional) -const char* kMqttPassword = ""; // <=- CHANGE_ME (optional) -const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. - -#define MQTTprefix HOSTNAME // Change this if you want the MQTT topic to be - // independent of the hostname. -#define MQTTack MQTTprefix "/sent" // Topic we send back acknowledgements on -#define MQTTcommand MQTTprefix "/send" // Topic we get new commands from. -#define MQTTrecv MQTTprefix "/received" // Topic we send received IRs to. -#endif // MQTT_ENABLE - -const char* kHtmlUsername = "admin"; // <=- CHANGE_ME (optional) -const char* kHtmlPassword = "esp8266"; // <=- CHANGE_ME (required) -// If you do not change 'kHtmlPassword', Firmware OTA updates will be blocked. - -// This is what the default password is. People should NEVER use this password. -// Firmware uploads are blocked until the user changes kHtmlPassword to a -// different value than this. -const char* kDefaultPassword = "esp8266"; // Do NOT change this. - -// Let's use a larger than normal buffer so we can handle AirCon remote codes. -const uint16_t kCaptureBufferSize = 1024; -#if DECODE_AC -// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator -// A value this large may swallow repeats of some protocols -const uint8_t kCaptureTimeout = 50; // Milliseconds -#else // DECODE_AC -// Suits most messages, while not swallowing many repeats. -const uint8_t kCaptureTimeout = 15; // Milliseconds -#endif // DECODE_AC -// Ignore unknown messages with <10 pulses (see also REPORT_UNKNOWNS) -const uint16_t kMinUnknownSize = 2 * 10; - -// Disable debug output if any of the IR pins are on the TX (D1) pin. -#if (IR_LED != 1 && IR_RX != 1) -#undef DEBUG -#define DEBUG true // Change to 'false' to disable all serial output. -#else -#undef DEBUG -#define DEBUG false -#endif -// NOTE: Make sure you set your Serial Monitor to the same speed. -#define BAUD_RATE 115200 // Serial port Baud rate. - -// ----------------- End of User Configuration Section ------------------------- - // Globals -#define _MY_VERSION_ "v0.8.4" -// HTML arguments we will parse for IR code information. -#define argType "type" -#define argData "code" -#define argBits "bits" -#define argRepeat "repeats" - +#if defined(ESP8266) ESP8266WebServer server(kHttpPort); -IRsend irsend = IRsend(IR_LED); -#ifdef IR_RX -IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); -decode_results capture; // Somewhere to store inbound IR messages. -#endif // IR_RX +#endif // ESP8266 +#if defined(ESP32) +WebServer server(kHttpPort); +#endif // ESP32 +#if MDNS_ENABLE MDNSResponder mdns; +#endif // MDNS_ENABLE WiFiClient espClient; WiFiManager wifiManager; - +bool flagSaveWifiConfig = false; +char HttpUsername[kUsernameLength + 1] = "admin"; // Default HTT username. +char HttpPassword[kPasswordLength + 1] = ""; // No HTTP password by default. +char Hostname[kHostnameLength + 1] = "ir_server"; // Default hostname. uint16_t *codeArray; uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number bool boot = true; -bool ir_lock = false; // Primitive locking for gating the IR LED. +volatile bool lockIr = false; // Primitive locking for gating the IR LED. uint32_t sendReqCounter = 0; bool lastSendSucceeded = false; // Store the success status of the last send. uint32_t lastSendTime = 0; int8_t offset; // The calculated period offset for this chip and library. - -#if MQTT_ENABLE -String lastMqttCmd = "None"; -uint32_t lastMqttCmdTime = 0; -uint32_t lastConnectedTime = 0; -uint32_t lastDisconnectedTime = 0; -uint32_t mqttDisconnectCounter = 0; -bool wasConnected = true; -#ifdef IR_RX +IRsend *IrSendTable[kNrOfIrTxGpios]; +int8_t txGpioTable[kNrOfIrTxGpios] = {kDefaultIrLed}; +String lastClimateSource; +#if IR_RX +IRrecv *irrecv = NULL; +decode_results capture; // Somewhere to store inbound IR messages. +int8_t rx_gpio = kDefaultIrRx; String lastIrReceived = "None"; uint32_t lastIrReceivedTime = 0; uint32_t irRecvCounter = 0; #endif // IR_RX +// Climate stuff +stdAc::state_t climate; +stdAc::state_t climate_prev; +IRac *commonAc = NULL; -// MQTT client parameters -void callback(char* topic, byte* payload, unsigned int length); -PubSubClient mqtt_client(MQTT_SERVER, kMqttPort, callback, espClient); -// Create a unique MQTT client id. -String mqtt_clientid = MQTTprefix + String(ESP.getChipId(), HEX); +TimerMs lastClimateIr = TimerMs(); // When we last sent the IR Climate mesg. +uint32_t irClimateCounter = 0; // How many have we sent? +// Store the success status of the last climate send. +bool lastClimateSucceeded = false; +bool hasClimateBeenSent = false; // Has the Climate ever been sent? + +#if MQTT_ENABLE +PubSubClient mqtt_client(espClient); +String lastMqttCmd = "None"; +String lastMqttCmdTopic = "None"; +uint32_t lastMqttCmdTime = 0; +uint32_t lastConnectedTime = 0; +uint32_t lastDisconnectedTime = 0; +uint32_t mqttDisconnectCounter = 0; +uint32_t mqttSentCounter = 0; +uint32_t mqttRecvCounter = 0; +bool wasConnected = true; + +char MqttServer[kHostnameLength + 1] = "10.0.0.4"; +char MqttPort[kPortLength + 1] = "1883"; +char MqttUsername[kUsernameLength + 1] = ""; +char MqttPassword[kPasswordLength + 1] = ""; +char MqttPrefix[kHostnameLength + 1] = ""; + +String MqttAck; // Sub-topic we send back acknowledgements on. +String MqttSend; // Sub-topic we get new commands from. +String MqttRecv; // Topic we send received IRs to. +String MqttLog; // Topic we send log messages to. +String MqttLwt; // Topic for the Last Will & Testament. +String MqttClimate; // Sub-topic for the climate topics. +String MqttClimateCmnd; // Sub-topic for the climate command topics. +String MqttClimateStat; // Sub-topic for the climate stat topics. +String MqttDiscovery; +String MqttHAName; +String MqttClientId; + +// Primative lock file for gating MQTT state broadcasts. +bool lockMqttBroadcast = true; +TimerMs lastBroadcast = TimerMs(); // When we last sent a broadcast. +bool hasBroadcastBeenSent = false; +TimerMs lastDiscovery = TimerMs(); // When we last sent a Discovery. +bool hasDiscoveryBeenSent = false; +TimerMs statListenTime = TimerMs(); // How long we've been listening for. #endif // MQTT_ENABLE +bool isSerialGpioUsedByIr(void) { + const int8_t kSerialTxGpio = 1; // The GPIO serial output is sent to. + // Note: *DOES NOT* control Serial output. +#if defined(ESP32) + const int8_t kSerialRxGpio = 3; // The GPIO serial input is received on. +#endif // ESP32 + // Ensure we are not trodding on anything IR related. +#if IR_RX + switch (rx_gpio) { +#if defined(ESP32) + case kSerialRxGpio: +#endif // ESP32 + case kSerialTxGpio: + return true; // Serial port is in use by IR capture. Abort. + } +#endif // IR_RX + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) + switch (txGpioTable[i]) { +#if defined(ESP32) + case kSerialRxGpio: +#endif // ESP32 + case kSerialTxGpio: + return true; // Serial port is in use for IR sending. Abort. + } + return false; // Not in use as far as we can tell. +} + // Debug messages get sent to the serial port. -void debug(String str) { +void debug(const char *str) { #if DEBUG + if (isSerialGpioUsedByIr()) return; // Abort. uint32_t now = millis(); - Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str.c_str()); + Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str); #endif // DEBUG } +// callback notifying us of the need to save the wifi config +void saveWifiConfigCallback(void) { + debug("saveWifiConfigCallback called."); + flagSaveWifiConfig = true; +} + +// Forcibly mount the SPIFFS. Formatting the SPIFFS if needed. +// +// Returns: +// A boolean indicating success or failure. +bool mountSpiffs(void) { + debug("Mounting SPIFFS..."); + if (SPIFFS.begin()) return true; // We mounted it okay. + // We failed the first time. + debug("Failed to mount SPIFFS!\nFormatting SPIFFS and trying again..."); + SPIFFS.format(); + if (!SPIFFS.begin()) { // Did we fail? + debug("DANGER: Failed to mount SPIFFS even after formatting!"); + delay(10000); // Make sure the debug message doesn't just float by. + return false; + } + return true; // Success! +} + +bool saveConfig(void) { + debug("Saving the config."); + bool success = false; + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); +#if MQTT_ENABLE + json[kMqttServerKey] = MqttServer; + json[kMqttPortKey] = MqttPort; + json[kMqttUserKey] = MqttUsername; + json[kMqttPassKey] = MqttPassword; + json[kMqttPrefixKey] = MqttPrefix; +#endif // MQTT_ENABLE + json[kHostnameKey] = Hostname; + json[kHttpUserKey] = HttpUsername; + json[kHttpPassKey] = HttpPassword; +#if IR_RX + json[KEY_RX_GPIO] = static_cast(rx_gpio); +#endif // IR_RX + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + const String key = KEY_TX_GPIO + String(i); + json[key] = static_cast(txGpioTable[i]); + } + + if (mountSpiffs()) { + File configFile = SPIFFS.open(kConfigFile, "w"); + if (!configFile) { + debug("Failed to open config file for writing."); + } else { + debug("Writing out the config file."); + json.printTo(configFile); + configFile.close(); + debug("Finished writing config file."); + success = true; + } + SPIFFS.end(); + } + return success; +} + +bool loadConfigFile(void) { + bool success = false; + if (mountSpiffs()) { + debug("mounted the file system"); + if (SPIFFS.exists(kConfigFile)) { + debug("config file exists"); + + File configFile = SPIFFS.open(kConfigFile, "r"); + if (configFile) { + debug("Opened config file"); + size_t size = configFile.size(); + // Allocate a buffer to store contents of the file. + std::unique_ptr buf(new char[size]); + + configFile.readBytes(buf.get(), size); + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(buf.get()); + if (json.success()) { + debug("Json config file parsed ok."); +#if MQTT_ENABLE + strncpy(MqttServer, json[kMqttServerKey] | "", kHostnameLength); + strncpy(MqttPort, json[kMqttPortKey] | "1883", kPortLength); + strncpy(MqttUsername, json[kMqttUserKey] | "", kUsernameLength); + strncpy(MqttPassword, json[kMqttPassKey] | "", kPasswordLength); + strncpy(MqttPrefix, json[kMqttPrefixKey] | "", kHostnameLength); +#endif // MQTT_ENABLE + strncpy(Hostname, json[kHostnameKey] | "", kHostnameLength); + strncpy(HttpUsername, json[kHttpUserKey] | "", kUsernameLength); + strncpy(HttpPassword, json[kHttpPassKey] | "", kPasswordLength); + // Read in the GPIO settings. +#if IR_RX + // Single RX gpio + rx_gpio = json[KEY_RX_GPIO] | kDefaultIrRx; +#endif // IR_RX + // Potentially multiple TX gpios + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) + txGpioTable[i] = json[String(KEY_TX_GPIO + String(i)).c_str()] | + kDefaultIrLed; + debug("Recovered Json fields."); + success = true; + } else { + debug("Failed to load json config"); + } + debug("Closing the config file."); + configFile.close(); + } + } else { + debug("Config file doesn't exist!"); + } + debug("Unmounting SPIFFS."); + SPIFFS.end(); + } + return success; +} + +String msToHumanString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return "Now"; + + // Note: millis() can only count 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 = ""; + if (days) result += String(days) + " day"; + if (days > 1) result += 's'; + if (hours) result += ' ' + String(hours) + " hour"; + if (hours > 1) result += 's'; + if (minutes) result += ' ' + String(minutes) + " minute"; + if (minutes > 1) result += 's'; + if (seconds) result += ' ' + String(seconds) + " second"; + if (seconds > 1) result += 's'; + result.trim(); + return result; +} + +String timeElapsed(uint32_t const msec) { + String result = msToHumanString(msec); + if (result.equalsIgnoreCase("Now")) + return result; + else + return result + " ago"; +} + String timeSince(uint32_t const start) { if (start == 0) return "Never"; @@ -325,120 +611,60 @@ String timeSince(uint32_t const start) { diff = now - start; else diff = UINT32_MAX - start + now; - diff /= 1000; // Convert to seconds. - if (diff == 0) return "Now"; - - // Note: millis() can only count up to 45 days, so uint8_t is safe. - uint8_t days = diff / (60 * 60 * 24); - uint8_t hours = (diff / (60 * 60)) % 24; - uint8_t minutes = (diff / 60) % 60; - uint8_t seconds = diff % 60; - - String result = ""; - if (days) - result += String(days) + " day"; - if (days > 1) result += "s"; - if (hours) - result += " " + String(hours) + " hour"; - if (hours > 1) result += "s"; - if (minutes) - result += " " + String(minutes) + " minute"; - if (minutes > 1) result += "s"; - if (seconds) - result += " " + String(seconds) + " second"; - if (seconds > 1) result += "s"; - result.trim(); - return result + " ago"; + return msToHumanString(diff) + " ago"; } -// Quick and dirty check for any unsafe chars in a string -// that may cause HTML shenanigans. e.g. An XSS. -bool hasUnsafeHTMLChars(String input) { - static char unsafe[] = "';!-\"<>=&{}()"; - for (uint8_t i = 0; unsafe[i]; i++) - if (input.indexOf(unsafe[i]) != -1) return true; - return false; +String gpioToString(const int16_t gpio) { + if (gpio == kGpioUnused) + return "Unused"; + else + return String(gpio); +} + +int8_t getDefaultTxGpio(void) { + for (int8_t i = 0; i < kNrOfIrTxGpios; i++) + if (txGpioTable[i] != kGpioUnused) return txGpioTable[i]; + return kGpioUnused; +} + +// Return a string containing the comma separated list of sending gpios. +String listOfTxGpios(void) { + bool found = false; + String result = ""; + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + if (i) result += ", "; + result += gpioToString(txGpioTable[i]); + if (!found && txGpioTable[i] == getDefaultTxGpio()) { + result += " (default)"; + found = true; + } + } + return result; +} + +String htmlMenu(void) { + String html = F("
"); + html += htmlButton(kUrlRoot, F("Home")); + html += htmlButton(kUrlAircon, F("Aircon")); + html += htmlButton(kUrlExamples, F("Examples")); + html += htmlButton(kUrlInfo, F("System Info")); + html += htmlButton(kUrlAdmin, F("Admin")); + html += F("

"); + return html; } // Root web page with example usage etc. -void handleRoot() { +void handleRoot(void) { #if HTML_PASSWORD_ENABLE - if (!server.authenticate(kHtmlUsername, kHtmlPassword)) { + if (!server.authenticate(HttpUsername, HttpPassword)) { debug("Basic HTTP authentication failure for /."); return server.requestAuthentication(); } #endif - String html = - "IR MQTT server" - "" - "

ESP8266 IR MQTT Server

" - "

" - "

Information

" - "

IP address: " + WiFi.localIP().toString() + "
" - "Booted: " + timeSince(1) + "
" + - "Version: " _MY_VERSION_ "
" - "Period Offset: " + String(offset) + "us
" - "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" - "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" - "IR Send GPIO: " + String(IR_LED) + "
" - "Total send requests: " + String(sendReqCounter) + "
" - "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") + - " (" + timeSince(lastSendTime) + ")
" -#ifdef IR_RX - "IR Recv GPIO: " + String(IR_RX) + -#if IR_RX_PULLUP - " (pullup)" -#endif // IR_RX_PULLUP - "
" - "Total IR Received: " + String(irRecvCounter) + "
" - "Last IR Received: " + lastIrReceived + - " (" + timeSince(lastIrReceivedTime) + ")
" -#endif // IR_RX - "

" -#if MQTT_ENABLE - "

MQTT Information

" - "

Server: " MQTT_SERVER ":" + String(kMqttPort) + " (" + - (mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime) - : "Disconnected " + timeSince(lastConnectedTime)) + - ")
" - "Disconnections: " + String(mqttDisconnectCounter - 1) + "
" - "Client id: " + mqtt_clientid + "
" - "Command topic: " MQTTcommand "
" - "Acknowledgements topic: " MQTTack "
" -#ifdef IR_RX - "IR Received topic: " MQTTrecv "
" -#endif // IR_RX - "Last MQTT command seen: " + - // lastMqttCmd is unescaped untrusted input. - // Avoid any possible HTML/XSS when displaying it. - (hasUnsafeHTMLChars(lastMqttCmd) ? - "Contains unsafe HTML characters" : lastMqttCmd) + - " (" + timeSince(lastMqttCmdTime) + ")

" -#endif // MQTT_ENABLE - "

" - "

Hardcoded examples

" - "

" - "Sherwood Amp On (GlobalCache)

" - "

" - "Sherwood Amp Off (Raw)

" - "

" - "Sherwood Amp Input TAPE (Pronto)

" - "

TV on (Samsung)

" - "

Power Off (Sony 12bit)

" - "

" + String html = htmlHeader(F("ESP IR MQTT Server")); + html += F("
" _MY_VERSION_ "
"); + html += htmlMenu(); + html += F( "

Send a simple IR message

" "

" "Type: " @@ -449,8 +675,11 @@ void handleRoot() { "" "" "" + "" + "" "" "" + "" "" "" "" @@ -503,6 +732,45 @@ void handleRoot() { " " "
" "

" + "

Send a complex (Air Conditioner) IR message

" + "

" + "Type: " + "" + " State code: 0x" + "" + " " + "
" + "

" "

Send an IRremote Raw IR message

" "

" "" @@ -540,87 +808,544 @@ void handleRoot() { "size='2' maxlength='2'>" " " "
" - "

" - "

Send an Air Conditioner IR message

" - "

" - "Type: " - "" - " State code: 0x" - "" - " " - "
" - "
"; + "
"); + html += htmlEnd(); + server.send(200, "text/html", html); +} + +String addJsReloadUrl(const String url, const uint16_t timeout_s, + const bool notify) { + String html = F( + "\n"); + return html; +} + +// Web page with hardcoded example usage etc. +void handleExamples(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /examples."); + return server.requestAuthentication(); + } +#endif + String html = htmlHeader(F("IR MQTT examples")); + html += htmlMenu(); + html += F( + "

Hardcoded examples

" + "

" + "Sherwood Amp On (GlobalCache)

" + "

" + "Sherwood Amp Off (Raw)

" + "

" + "Sherwood Amp Input TAPE (Pronto)

" + "

TV on (Samsung)

" + "

Power Off (Sony 12bit)

" + "

" + "Panasonic A/C LKE model, On, Auto mode, Min fan, 23C" + " (via HTTP aircon interface)

" + "

" + "Change just the temp to 27C (via HTTP aircon interface)

" + "

" + "Turn OFF the current A/C (via HTTP aircon interface)

" + "

"); + html += htmlEnd(); + server.send(200, "text/html", html); +} + +String htmlSelectBool(const String name, const bool def) { + String html = ""); + return html; +} + +String htmlSelectProtocol(const String name, const decode_type_t def) { + String html = ""); + return html; +} + +String htmlSelectModel(const String name, const int16_t def) { + String html = ""); + return html; +} + +String htmlSelectGpio(const String name, const int16_t def, + const int8_t list[], const int16_t length) { + String html = ": "); + return html; +} + +String htmlSelectMode(const String name, const stdAc::opmode_t def) { + String html = ""); + return html; +} + +String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) { + String html = ""); + return html; +} + +String htmlSelectSwingv(const String name, const stdAc::swingv_t def) { + String html = ""); + return html; +} + +String htmlSelectSwingh(const String name, const stdAc::swingh_t def) { + String html = ""); + return html; +} + +String htmlHeader(const String title, const String h1_text) { + String html = F(""); + html += title; + html += F("

"); + if (h1_text.length()) + html += h1_text; + else + html += title; + html += F("

"); + return html; +} + +String htmlEnd(void) { + return F(""); +} + +String htmlButton(const String url, const String button, const String text) { + String html = F(" "); + html += text; + return html; +} + +// Admin web page +void handleAirCon(void) { + String html = htmlHeader(F("Air Conditioner Control")); + html += htmlMenu(); + html += "

Current Settings

" + "
" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
Protocol" + + htmlSelectProtocol(KEY_PROTOCOL, climate.protocol) + "
Model" + htmlSelectModel(KEY_MODEL, climate.model) + + "
Power" + htmlSelectBool(KEY_POWER, climate.power) + + "
Mode" + htmlSelectMode(KEY_MODE, climate.mode) + + "
Temp" + "" + "
Fan Speed" + + htmlSelectFanspeed(KEY_FANSPEED, climate.fanspeed) + "
Swing (V)" + + htmlSelectSwingv(KEY_SWINGV, climate.swingv) + "
Swing (H)" + + htmlSelectSwingh(KEY_SWINGH, climate.swingh) + "
Quiet" + htmlSelectBool(KEY_QUIET, climate.quiet) + + "
Turbo" + htmlSelectBool(KEY_TURBO, climate.turbo) + + "
Econo" + htmlSelectBool(KEY_ECONO, climate.econo) + + "
Light" + htmlSelectBool(KEY_LIGHT, climate.light) + + "
Filter" + htmlSelectBool(KEY_FILTER, climate.filter) + + "
Clean" + htmlSelectBool(KEY_CLEAN, climate.clean) + + "
Beep" + htmlSelectBool(KEY_BEEP, climate.beep) + + "
" + "" + "
"; + html += htmlEnd(); + server.send(200, "text/html", html); +} + +// Parse the URL args to find the Common A/C arguments. +void handleAirConSet(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /aircon/set."); + return server.requestAuthentication(); + } +#endif + stdAc::state_t result = climate; + debug("New common a/c received via HTTP"); + for (uint16_t i = 0; i < server.args(); i++) + result = updateClimate(result, server.argName(i), "", server.arg(i)); + +#if MQTT_ENABLE + sendClimate(climate, result, MqttClimateStat, + true, false, false); +#else // MQTT_ENABLE + sendClimate(climate, result, "", false, false, false); +#endif // MQTT_ENABLE + lastClimateSource = F("HTTP"); + // Update the old climate state with the new one. + climate = result; + // Redirect back to the aircon page. + String html = htmlHeader(F("Aircon updated!")); + html += addJsReloadUrl(kUrlAircon, kQuickDisplayTime, false); + html += htmlEnd(); + server.send(200, "text/html", html); +} + +String htmlDisabled(void) { + String html = F( + "Updates disabled until you set a password. " + "You will need to wipe & reset to set one.

"); + return html; +} + +// Admin web page +void handleAdmin(void) { + String html = htmlHeader(F("Administration")); + html += htmlMenu(); + html += F("

Special commands

"); +#if MQTT_ENABLE + html += htmlButton( + kUrlSendDiscovery, F("Send MQTT Discovery"), + F("Send a Climate MQTT discovery message to Home Assistant.

")); +#endif // MQTT_ENABLE + html += htmlButton( + kUrlReboot, F("Reboot"), + F("A simple reboot of the ESP8266. ie. No changes
" + "
")); + html += htmlButton( + kUrlWipe, F("Wipe Settings"), + F("Warning: Resets the device back to original settings. " + "ie. Goes back to AP/Setup mode.

")); + html += htmlButton(kUrlGpio, F("GPIOs"), F("Change the IR GPIOs.
")); #if FIRMWARE_OTA - html += "

Update IR Server firmware

" - "Warning:
"; - if (!strcmp(kHtmlPassword, kDefaultPassword)) // Deny if password unchanged - html += "OTA firmware is disabled until you change the password. " - "(See 'kHtmlPassword' in the source code.)
"; + html += F("


Update firmware

" + "Warning:
"); + if (!strlen(HttpPassword)) // Deny if password not set + html += htmlDisabled(); else // default password has been changed, so allow it. - html += + html += F( "Updating your firmware may screw up your access to the device. " "If you are going to use this, know what you are doing first " "(and you probably do).
" "

" "Firmware to upload: " "" - "
"; - + ""); #endif // FIRMWARE_OTA - html += ""; + html += htmlEnd(); server.send(200, "text/html", html); } +// Info web page +void handleInfo(void) { + String html = htmlHeader(F("IR MQTT server info")); + html += htmlMenu(); + html += + "

General

" + "

Hostname: " + String(Hostname) + "
" + "IP address: " + WiFi.localIP().toString() + "
" + "Booted: " + timeSince(1) + "
" + + "Version: " _MY_VERSION_ "
" + "Built: " __DATE__ + " " __TIME__ "
" + "Period Offset: " + String(offset) + "us
" + "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" +#if defined(ESP8266) + "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" +#endif // ESP8266 +#if defined(ESP32) + "ESP32 SDK Version: " + ESP.getSdkVersion() + "
" +#endif // ESP32 + "Cpu Freq: " + String(ESP.getCpuFreqMHz()) + "MHz
" + "IR Send GPIO(s): " + listOfTxGpios() + "
" + "Total send requests: " + String(sendReqCounter) + "
" + "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") + + " (" + timeSince(lastSendTime) + ")
" +#if IR_RX + "IR Recv GPIO: " + gpioToString(rx_gpio) + +#if IR_RX_PULLUP + " (pullup)" +#endif // IR_RX_PULLUP + "
" + "Total IR Received: " + String(irRecvCounter) + "
" + "Last IR Received: " + lastIrReceived + + " (" + timeSince(lastIrReceivedTime) + ")
" +#endif // IR_RX + "Duplicate Wifi networks: " + + String(HIDE_DUPLIATE_NETWORKS ? "Hide" : "Show") + "
" + "Min Wifi signal required: " +#ifdef MIN_SIGNAL_STRENGTH + + String(static_cast(MIN_SIGNAL_STRENGTH)) + +#else // MIN_SIGNAL_STRENGTH + "8" +#endif // MIN_SIGNAL_STRENGTH + "%
" + "Serial debugging: " +#if DEBUG + + String(isSerialGpioUsedByIr() ? "Off" : "On") + +#else // DEBUG + "Off" +#endif // DEBUG + "
" + "

" +#if MQTT_ENABLE + "

MQTT Information

" + "

Server: " + String(MqttServer) + ":" + String(MqttPort) + " (" + + (mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime) + : "Disconnected " + timeSince(lastConnectedTime)) + + ")
" + "Disconnections: " + String(mqttDisconnectCounter - 1) + "
" + "Client id: " + MqttClientId + "
" + "Command topic(s): " + listOfCommandTopics() + "
" + "Acknowledgements topic: " + MqttAck + "
" +#if IR_RX + "IR Received topic: " + MqttRecv + "
" +#endif // IR_RX + "Log topic: " + MqttLog + "
" + "LWT topic: " + MqttLwt + "
" + "QoS: " + String(QOS) + "
" + // lastMqttCmd* is unescaped untrusted input. + // Avoid any possible HTML/XSS when displaying it. + "Last MQTT command seen: (topic) '" + htmlEscape(lastMqttCmdTopic) + + "' (payload) '" + htmlEscape(lastMqttCmd) + "' (" + + timeSince(lastMqttCmdTime) + ")
" + "Total published: " + String(mqttSentCounter) + "
" + "Total received: " + String(mqttRecvCounter) + "
" + "

" +#endif // MQTT_ENABLE + "

Climate Information

" + "

" + "IR Send GPIO: " + String(txGpioTable[0]) + "
" + "Last update source: " + lastClimateSource + "
" + "Total sent: " + String(irClimateCounter) + "
" + "Last send: " + String(hasClimateBeenSent ? + (String(lastClimateSucceeded ? "Ok" : "FAILED") + + " (" + timeElapsed(lastClimateIr.elapsed()) + ")") : + "Never") + "
" +#if MQTT_ENABLE + "State listen period: " + msToHumanString(kStatListenPeriodMs) + "
" + "State broadcast period: " + msToHumanString(kBroadcastPeriodMs) + "
" + "Last state broadcast: " + (hasBroadcastBeenSent ? + timeElapsed(lastBroadcast.elapsed()) : + String("Never")) + "
" + "Last discovery sent: " + (lockMqttBroadcast ? + String("Locked") : + (hasDiscoveryBeenSent ? + timeElapsed(lastDiscovery.elapsed()) : + String("Never"))) + + "
" + "Command topics: " + MqttClimateCmnd + kClimateTopics + + "State topics: " + MqttClimateStat + kClimateTopics + +#endif // MQTT_ENABLE + "

" + // Page footer + "

" + "(Note: Page will refresh every 60 seconds.)" + "

"; + html += addJsReloadUrl(kUrlInfo, 60, false); + html += htmlEnd(); + server.send(200, "text/html", html); +} // Reset web page -void handleReset() { +void handleReset(void) { #if HTML_PASSWORD_ENABLE - if (!server.authenticate(kHtmlUsername, kHtmlPassword)) { - debug("Basic HTTP authentication failure for /reset."); + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for " + kUrlWipe); return server.requestAuthentication(); } #endif server.send(200, "text/html", - "Reset Config" - "" - "

Resetting the WiFiManager config back to defaults.

" - "

Device restarting. Try connecting in a few seconds.

" - ""); - // Do the reset. + htmlHeader(F("Reset WiFi Config"), + F("Resetting the WiFiManager config back to defaults.")) + + "

Device restarting. Try connecting in a few seconds.

" + + addJsReloadUrl(kUrlRoot, 10, true) + + htmlEnd()); + // Do the reset. +#if MQTT_ENABLE + mqttLog("Wiping all saved config settings."); +#endif // MQTT_ENABLE + if (mountSpiffs()) { + debug("Removing JSON config file"); + SPIFFS.remove(kConfigFile); + SPIFFS.end(); + } + delay(1000); + debug("Reseting wifiManager's settings."); wifiManager.resetSettings(); - delay(10); + delay(1000); + debug("rebooting..."); + ESP.restart(); + delay(1000); +} + +// Reboot web page +void handleReboot() { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for " + kUrlReboot); + return server.requestAuthentication(); + } +#endif + server.send(200, "text/html", + htmlHeader(F("Device restarting.")) + + "

Try connecting in a few seconds.

" + + addJsReloadUrl(kUrlRoot, kRebootTime, true) + + htmlEnd()); +#if MQTT_ENABLE + mqttLog("Reboot requested"); +#endif // MQTT_ENABLE + // Do the reset. + delay(1000); ESP.restart(); delay(1000); } // Parse an Air Conditioner A/C Hex String/code and send it. // Args: +// irsend: A Ptr to the IRsend object to transmit via. // irType: Nr. of the protocol we need to send. // str: A hexadecimal string containing the state to be sent. // Returns: // bool: Successfully sent or not. -bool parseStringAndSendAirCon(const uint16_t irType, const String str) { +bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, + const String str) { uint8_t strOffset = 0; uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. uint16_t stateSize = 0; @@ -642,17 +1367,43 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String str) { stateSize = kToshibaACStateLength; break; case DAIKIN: - stateSize = kDaikinStateLength; + // Daikin has 2 different possible size states. + // (The correct size, and a legacy shorter size.) + // Guess which one we are being presented with based on the number of + // hexadecimal digits provided. i.e. Zero-pad if you need to to get + // the correct length/byte size. + // This should provide backward compatiblity with legacy messages. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, kDaikinStateLengthShort); + // If we think it isn't a "short" message. + if (stateSize > kDaikinStateLengthShort) + // Then it has to be at least the version of the "normal" size. + stateSize = std::max(stateSize, kDaikinStateLength); + // Lastly, it should never exceed the "normal" size. + stateSize = std::min(stateSize, kDaikinStateLength); + break; + case DAIKIN160: + stateSize = kDaikin160StateLength; break; case DAIKIN2: stateSize = kDaikin2StateLength; break; + case DAIKIN216: + stateSize = kDaikin216StateLength; + break; case ELECTRA_AC: stateSize = kElectraAcStateLength; break; case MITSUBISHI_AC: stateSize = kMitsubishiACStateLength; break; + case MITSUBISHI_HEAVY_88: + stateSize = kMitsubishiHeavy88StateLength; + break; + case MITSUBISHI_HEAVY_152: + stateSize = kMitsubishiHeavy152StateLength; + break; case PANASONIC_AC: stateSize = kPanasonicAcStateLength; break; @@ -715,6 +1466,9 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String str) { // Lastly, it should never exceed the maximum "extended" size. stateSize = std::min(stateSize, kSamsungAcExtendedStateLength); break; + case SHARP_AC: + stateSize = kSharpAcStateLength; + break; case MWM: // MWM has variable size states, so make a best guess // which one we are being presented with based on the number of @@ -751,7 +1505,8 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String str) { else c = c - 'a' + 10; } else { - debug("Aborting! Non-hexadecimal char found in AirCon state: " + str); + debug("Aborting! Non-hexadecimal char found in AirCon state:"); + debug(str.c_str()); return false; } if (i % 2 == 1) { // Odd: Upper half of the byte. @@ -766,102 +1521,125 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String str) { switch (irType) { #if SEND_KELVINATOR case KELVINATOR: - irsend.sendKelvinator(reinterpret_cast(state)); + irsend->sendKelvinator(reinterpret_cast(state)); break; #endif #if SEND_TOSHIBA_AC case TOSHIBA_AC: - irsend.sendToshibaAC(reinterpret_cast(state)); + irsend->sendToshibaAC(reinterpret_cast(state)); break; #endif #if SEND_DAIKIN case DAIKIN: - irsend.sendDaikin(reinterpret_cast(state)); + irsend->sendDaikin(reinterpret_cast(state)); break; #endif +#if SEND_DAIKIN160 + case DAIKIN160: // 65 + irsend->sendDaikin160(reinterpret_cast(state)); + break; +#endif // SEND_DAIKIN160 #if SEND_DAIKIN2 case DAIKIN2: - irsend.sendDaikin2(reinterpret_cast(state)); + irsend->sendDaikin2(reinterpret_cast(state)); break; #endif +#if SEND_DAIKIN216 + case DAIKIN216: // 61 + irsend->sendDaikin216(reinterpret_cast(state)); + break; +#endif // SEND_DAIKIN216 #if SEND_MITSUBISHI_AC case MITSUBISHI_AC: - irsend.sendMitsubishiAC(reinterpret_cast(state)); + irsend->sendMitsubishiAC(reinterpret_cast(state)); break; #endif +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: // 59 + irsend->sendMitsubishiHeavy88(reinterpret_cast(state)); + break; + case MITSUBISHI_HEAVY_152: // 60 + irsend->sendMitsubishiHeavy152(reinterpret_cast(state)); + break; +#endif // SEND_MITSUBISHIHEAVY #if SEND_TROTEC case TROTEC: - irsend.sendTrotec(reinterpret_cast(state)); + irsend->sendTrotec(reinterpret_cast(state)); break; #endif #if SEND_ARGO case ARGO: - irsend.sendArgo(reinterpret_cast(state)); + irsend->sendArgo(reinterpret_cast(state)); break; #endif #if SEND_GREE case GREE: - irsend.sendGree(reinterpret_cast(state)); + irsend->sendGree(reinterpret_cast(state)); break; #endif #if SEND_FUJITSU_AC case FUJITSU_AC: - irsend.sendFujitsuAC(reinterpret_cast(state), stateSize); + irsend->sendFujitsuAC(reinterpret_cast(state), stateSize); break; #endif #if SEND_HAIER_AC case HAIER_AC: - irsend.sendHaierAC(reinterpret_cast(state)); + irsend->sendHaierAC(reinterpret_cast(state)); break; #endif #if SEND_HAIER_AC_YRW02 case HAIER_AC_YRW02: - irsend.sendHaierACYRW02(reinterpret_cast(state)); + irsend->sendHaierACYRW02(reinterpret_cast(state)); break; #endif #if SEND_HITACHI_AC case HITACHI_AC: - irsend.sendHitachiAC(reinterpret_cast(state)); + irsend->sendHitachiAC(reinterpret_cast(state)); break; #endif #if SEND_HITACHI_AC1 case HITACHI_AC1: - irsend.sendHitachiAC1(reinterpret_cast(state)); + irsend->sendHitachiAC1(reinterpret_cast(state)); break; #endif #if SEND_HITACHI_AC2 case HITACHI_AC2: - irsend.sendHitachiAC2(reinterpret_cast(state)); + irsend->sendHitachiAC2(reinterpret_cast(state)); break; #endif #if SEND_WHIRLPOOL_AC case WHIRLPOOL_AC: - irsend.sendWhirlpoolAC(reinterpret_cast(state)); + irsend->sendWhirlpoolAC(reinterpret_cast(state)); break; #endif #if SEND_SAMSUNG_AC case SAMSUNG_AC: - irsend.sendSamsungAC(reinterpret_cast(state), stateSize); + irsend->sendSamsungAC(reinterpret_cast(state), stateSize); break; #endif +#if SEND_SHARP_AC + case SHARP_AC: // 62 + irsend->sendSharpAc(reinterpret_cast(state)); + break; +#endif // SEND_SHARP_AC #if SEND_ELECTRA_AC case ELECTRA_AC: - irsend.sendElectraAC(reinterpret_cast(state)); + irsend->sendElectraAC(reinterpret_cast(state)); break; #endif #if SEND_PANASONIC_AC case PANASONIC_AC: - irsend.sendPanasonicAC(reinterpret_cast(state)); + irsend->sendPanasonicAC(reinterpret_cast(state)); break; #endif #if SEND_MWM case MWM: - irsend.sendMWM(reinterpret_cast(state), stateSize); + irsend->sendMWM(reinterpret_cast(state), stateSize); break; #endif #if SEND_TCL112AC case TCL112AC: - irsend.sendTcl112Ac(reinterpret_cast(state)); + irsend->sendTcl112Ac(reinterpret_cast(state)); break; #endif default: @@ -891,16 +1669,16 @@ uint16_t countValuesInStr(const String str, char sep) { // Args: // size: Nr. of uint16_t's need to be in the new array. // Returns: -// A Ptr to the new array. Restarts the ESP8266 if it fails. +// A Ptr to the new array. Restarts the ESP if it fails. uint16_t * newCodeArray(const uint16_t size) { uint16_t *result; result = reinterpret_cast(malloc(size * sizeof(uint16_t))); // Check we malloc'ed successfully. if (result == NULL) { // malloc failed, so give up. - Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n", - size * sizeof(uint16_t), ESP.getFreeHeap()); - Serial.println("Giving up & forcing a reboot."); + debug("FATAL: Can't allocate memory for an array for a new message!"); + debug("Giving up & forcing a reboot."); + delay(5000); ESP.restart(); // Reboot. delay(500); // Wait for the restart to happen. return result; // Should never get here, but just in case. @@ -911,6 +1689,7 @@ uint16_t * newCodeArray(const uint16_t size) { #if SEND_GLOBALCACHE // Parse a GlobalCache String/code and send it. // Args: +// irsend: A ptr to the IRsend object to transmit via. // str: A GlobalCache formatted String of comma separated numbers. // e.g. "38000,1,1,170,170,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20, // 20,20,63,20,63,20,63,20,20,20,20,20,20,20,20,20,20,20,20,20,63, @@ -919,7 +1698,7 @@ uint16_t * newCodeArray(const uint16_t size) { // Note: The leading "1:1,1," of normal GC codes should be removed. // Returns: // bool: Successfully sent or not. -bool parseStringAndSendGC(const String str) { +bool parseStringAndSendGC(IRsend *irsend, const String str) { uint16_t count; uint16_t *code_array; String tmp_str; @@ -946,8 +1725,7 @@ bool parseStringAndSendGC(const String str) { start_from = index + 1; count++; } while (index != -1); - - irsend.sendGC(code_array, count); // All done. Send it. + irsend->sendGC(code_array, count); // All done. Send it. free(code_array); // Free up the memory allocated. if (count > 0) return true; // We sent something. @@ -958,6 +1736,7 @@ bool parseStringAndSendGC(const String str) { #if SEND_PRONTO // Parse a Pronto Hex String/code and send it. // Args: +// irsend: A ptr to the IRsend object to transmit via. // str: A comma-separated String of nr. of repeats, then hexadecimal numbers. // e.g. "R1,0000,0067,0000,0015,0060,0018,0018,0018,0030,0018,0030,0018, // 0030,0018,0018,0018,0030,0018,0018,0018,0018,0018,0030,0018, @@ -971,7 +1750,8 @@ bool parseStringAndSendGC(const String str) { // This value is ignored if an embeddd repeat is found in str. // Returns: // bool: Successfully sent or not. -bool parseStringAndSendPronto(const String str, uint16_t repeats) { +bool parseStringAndSendPronto(IRsend *irsend, const String str, + uint16_t repeats) { uint16_t count; uint16_t *code_array; int16_t index = -1; @@ -1007,7 +1787,7 @@ bool parseStringAndSendPronto(const String str, uint16_t repeats) { count++; } while (index != -1); - irsend.sendPronto(code_array, count, repeats); // All done. Send it. + irsend->sendPronto(code_array, count, repeats); // All done. Send it. free(code_array); // Free up the memory allocated. if (count > 0) return true; // We sent something. @@ -1018,13 +1798,14 @@ bool parseStringAndSendPronto(const String str, uint16_t repeats) { #if SEND_RAW // Parse an IRremote Raw Hex String/code and send it. // Args: +// irsend: A ptr to the IRsend object to transmit via. // str: A comma-separated String containing the freq and raw IR data. // e.g. "38000,9000,4500,600,1450,600,900,650,1500,..." // Requires at least two comma-separated values. // First value is the transmission frequency in Hz or kHz. // Returns: // bool: Successfully sent or not. -bool parseStringAndSendRaw(const String str) { +bool parseStringAndSendRaw(IRsend *irsend, const String str) { uint16_t count; uint16_t freq = 38000; // Default to 38kHz. uint16_t *raw_array; @@ -1054,7 +1835,7 @@ bool parseStringAndSendRaw(const String str) { count++; } while (index != -1); - irsend.sendRaw(raw_array, count, freq); // All done. Send it. + irsend->sendRaw(raw_array, count, freq); // All done. Send it. free(raw_array); // Free up the memory allocated. if (count > 0) return true; // We sent something. @@ -1062,39 +1843,150 @@ bool parseStringAndSendRaw(const String str) { } #endif // SEND_RAW +uint8_t getDefaultIrSendIdx(void) { + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) + if (IrSendTable[i] != NULL) return i; + return 0; +} + +IRsend* getDefaultIrSendPtr(void) { + return IrSendTable[getDefaultIrSendIdx()]; +} + // Parse the URL args to find the IR code. -void handleIr() { +void handleIr(void) { #if HTML_PASSWORD_ENABLE - if (!server.authenticate(kHtmlUsername, kHtmlPassword)) { + if (!server.authenticate(HttpUsername, HttpPassword)) { debug("Basic HTTP authentication failure for /ir."); return server.requestAuthentication(); } #endif uint64_t data = 0; String data_str = ""; - int ir_type = 3; // Default to NEC codes. + int16_t ir_type = decode_type_t::NEC; // Default to NEC codes. uint16_t nbits = 0; uint16_t repeat = 0; for (uint16_t i = 0; i < server.args(); i++) { - if (server.argName(i) == argType) - ir_type = atoi(server.arg(i).c_str()); - if (server.argName(i) == argData) { + if (server.argName(i).equals(KEY_TYPE) || + server.argName(i).equals(KEY_PROTOCOL)) { + ir_type = strToDecodeType(server.arg(i).c_str()); + } else if (server.argName(i).equals(KEY_CODE)) { data = getUInt64fromHex(server.arg(i).c_str()); data_str = server.arg(i); + } else if (server.argName(i).equals(KEY_BITS)) { + nbits = server.arg(i).toInt(); + } else if (server.argName(i).equals(KEY_REPEAT)) { + repeat = server.arg(i).toInt(); } - if (server.argName(i) == argBits) - nbits = atoi(server.arg(i).c_str()); - if (server.argName(i) == argRepeat) - repeat = atoi(server.arg(i).c_str()); } debug("New code received via HTTP"); - lastSendSucceeded = sendIRCode(ir_type, data, data_str.c_str(), nbits, - repeat); - handleRoot(); + lastSendSucceeded = sendIRCode(getDefaultIrSendPtr(), ir_type, data, + data_str.c_str(), nbits, repeat); + String html = htmlHeader(F("IR command sent!")); + html += addJsReloadUrl(kUrlRoot, kQuickDisplayTime, true); + html += htmlEnd(); + server.send(200, "text/html", html); } -void handleNotFound() { +// GPIO menu page +void handleGpio(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /gpios."); + return server.requestAuthentication(); + } +#endif + String html = htmlHeader(F("GPIO config")); + html += F( + "
"); + html += htmlMenu(); + html += F("

WARNING: Choose carefully! You can cause damage to your " + "hardware or make the device unresponsive.

"); + html += F("

Send

IR LED"); + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + if (kNrOfIrTxGpios > 1) { + html += F(" #"); + html += String(i); + } + html += htmlSelectGpio(KEY_TX_GPIO + String(i), txGpioTable[i], kTxGpios, + sizeof(kTxGpios)); + } +#if IR_RX + html += F("

Receive

IR RX Module"); + html += htmlSelectGpio(KEY_RX_GPIO, rx_gpio, kRxGpios, + sizeof(kRxGpios)); +#endif // IR_RX + html += F("


"); + if (strlen(HttpPassword)) // Allow if password set + html += F(""); + else + html += htmlDisabled(); + html += F("
"); + html += htmlEnd(); + server.send(200, "text/html", html); +} + +// GPIO setting page +void handleGpioSetting(void) { + bool changed = false; + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /gpios."); + return server.requestAuthentication(); + } + String html = htmlHeader(F("Update GPIOs")); + if (!strlen(HttpPassword)) { // Don't allow if password not set + html += htmlDisabled(); + } else { + debug("Attempt to change GPIOs"); + for (uint16_t arg = 0; arg < server.args(); arg++) { + int8_t num = std::max(static_cast(server.arg(arg).toInt()), + kGpioUnused); +#if IR_RX + if (server.argName(arg).equals(KEY_RX_GPIO)) { + if (rx_gpio != num) { + rx_gpio = num; + changed = true; + } + } else { +#endif // IR_RX + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + if (server.argName(arg).equals(KEY_TX_GPIO + String(i))) { + if (txGpioTable[i] != num) { + txGpioTable[i] = num; + changed = true; + } + } + } +#if IR_RX + } +#endif // IR_RX + } + if (!changed) { + html += F("

No changes detected!

"); + } else if (saveConfig()) { + html += F("

Saved changes & rebooting.

"); + } else { + html += F("

ERROR: Changes didn't save correctly! " + "Rebooting.

"); + } + } + html += addJsReloadUrl(changed ? kUrlRoot : kUrlGpio, + changed ? kRebootTime : kQuickDisplayTime, + true); + html += htmlEnd(); + server.send(200, "text/html", html); + if (changed) { +#if MQTT_ENABLE + mqttLog("GPIOs were changed. Rebooting!"); +#endif // MQTT_ENABLE + delay(1000); + ESP.restart(); + delay(2000); + } +} + +void handleNotFound(void) { String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); @@ -1108,102 +2000,293 @@ void handleNotFound() { server.send(404, "text/plain", message); } -void setup_wifi() { +void setup_wifi(void) { delay(10); + loadConfigFile(); // We start by connecting to a WiFi network - wifiManager.setTimeout(300); // Time out after 5 mins. + // Set up additional parameters for WiFiManager config menu page. + wifiManager.setSaveConfigCallback(saveWifiConfigCallback); + WiFiManagerParameter custom_hostname_text( + "
Hostname
"); + wifiManager.addParameter(&custom_hostname_text); + WiFiManagerParameter custom_hostname( + kHostnameKey, kHostnameKey, Hostname, kHostnameLength); + wifiManager.addParameter(&custom_hostname); + WiFiManagerParameter custom_authentication_text( + "

Web/OTA authentication
"); + wifiManager.addParameter(&custom_authentication_text); + WiFiManagerParameter custom_http_username( + kHttpUserKey, "username", HttpUsername, kUsernameLength); + wifiManager.addParameter(&custom_http_username); + WiFiManagerParameter custom_http_password( + kHttpPassKey, "password (No OTA if blank)", HttpPassword, kPasswordLength, + " type='password'"); + wifiManager.addParameter(&custom_http_password); +#if MQTT_ENABLE + WiFiManagerParameter custom_mqtt_text( + "

MQTT Broker details
"); + wifiManager.addParameter(&custom_mqtt_text); + WiFiManagerParameter custom_mqtt_server( + kMqttServerKey, "mqtt server", MqttServer, kHostnameLength); + wifiManager.addParameter(&custom_mqtt_server); + WiFiManagerParameter custom_mqtt_port( + kMqttPortKey, "mqtt port", MqttPort, kPortLength, + " input type='number' min='1' max='65535'"); + wifiManager.addParameter(&custom_mqtt_port); + WiFiManagerParameter custom_mqtt_user( + kMqttUserKey, "mqtt username", MqttUsername, kUsernameLength); + wifiManager.addParameter(&custom_mqtt_user); + WiFiManagerParameter custom_mqtt_pass( + kMqttPassKey, "mqtt password", MqttPassword, kPasswordLength, + " type='password'"); + wifiManager.addParameter(&custom_mqtt_pass); + WiFiManagerParameter custom_prefix_text( + "

MQTT Prefix
"); + wifiManager.addParameter(&custom_prefix_text); + WiFiManagerParameter custom_mqtt_prefix( + kMqttPrefixKey, "Leave empty to use Hostname", MqttPrefix, + kHostnameLength); + wifiManager.addParameter(&custom_mqtt_prefix); +#endif // MQTT_ENABLE #if USE_STATIC_IP // Use a static IP config rather than the one supplied via DHCP. wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask); #endif // USE_STATIC_IP +#if MIN_SIGNAL_STRENGTH + wifiManager.setMinimumSignalQuality(MIN_SIGNAL_STRENGTH); +#endif // MIN_SIGNAL_STRENGTH + wifiManager.setRemoveDuplicateAPs(HIDE_DUPLIATE_NETWORKS); + if (!wifiManager.autoConnect()) { - debug("Wifi failed to connect and hit timeout."); + debug("Wifi failed to connect and hit timeout. Rebooting..."); delay(3000); // Reboot. A.k.a. "Have you tried turning it Off and On again?" - ESP.reset(); + ESP.restart(); delay(5000); } - debug("WiFi connected. IP address: " + WiFi.localIP().toString()); +#if MQTT_ENABLE + strncpy(MqttServer, custom_mqtt_server.getValue(), kHostnameLength); + strncpy(MqttPort, custom_mqtt_port.getValue(), kPortLength); + strncpy(MqttUsername, custom_mqtt_user.getValue(), kUsernameLength); + strncpy(MqttPassword, custom_mqtt_pass.getValue(), kPasswordLength); + strncpy(MqttPrefix, custom_mqtt_prefix.getValue(), kHostnameLength); +#endif // MQTT_ENABLE + strncpy(Hostname, custom_hostname.getValue(), kHostnameLength); + strncpy(HttpUsername, custom_http_username.getValue(), kUsernameLength); + strncpy(HttpPassword, custom_http_password.getValue(), kPasswordLength); + if (flagSaveWifiConfig) { + saveConfig(); + } + debug("WiFi connected. IP address:"); + debug(WiFi.localIP().toString().c_str()); +} + +void init_vars(void) { +#if MQTT_ENABLE + // If we have a prefix already, use it. Otherwise use the hostname. + if (!strlen(MqttPrefix)) strncpy(MqttPrefix, Hostname, kHostnameLength); + // Topic we send back acknowledgements on. + MqttAck = String(MqttPrefix) + '/' + MQTT_ACK; + // Sub-topic we get new commands from. + MqttSend = String(MqttPrefix) + '/' + MQTT_SEND; + // Topic we send received IRs to. + MqttRecv = String(MqttPrefix) + '/' + MQTT_RECV; + // Topic we send log messages to. + MqttLog = String(MqttPrefix) + '/' + MQTT_LOG; + // Topic for the Last Will & Testament. + MqttLwt = String(MqttPrefix) + '/' + MQTT_LWT; + // Sub-topic for the climate topics. + MqttClimate = String(MqttPrefix) + '/' + MQTT_CLIMATE; + // Sub-topic for the climate command topics. + MqttClimateCmnd = MqttClimate + '/' + MQTT_CLIMATE_CMND + '/'; + // Sub-topic for the climate stat topics. + MqttClimateStat = MqttClimate + '/' + MQTT_CLIMATE_STAT + '/'; + MqttDiscovery = "homeassistant/climate/" + String(Hostname) + "/config"; + MqttHAName = String(Hostname) + "_aircon"; + // Create a unique MQTT client id. + MqttClientId = String(Hostname) + String(kChipId, HEX); +#endif // MQTT_ENABLE } void setup(void) { - irsend.begin(); - offset = irsend.calibrate(); -#ifdef IR_RX -#if IR_RX_PULLUP - pinMode(IR_RX, INPUT_PULLUP); -#endif // IR_RX_PULLUP -#if DECODE_HASH - // Ignore messages with less than minimum on or off pulses. - irrecv.setUnknownThreshold(kMinUnknownSize); -#endif // DECODE_HASH - irrecv.enableIRIn(); // Start the receiver -#endif // IR_RX + // Set the default climate settings. + climate.protocol = decode_type_t::UNKNOWN; + climate.model = -1; // Unknown. + climate.power = false; + climate.mode = stdAc::opmode_t::kAuto; + climate.celsius = true; + climate.degrees = 25; // 25C + climate.fanspeed = stdAc::fanspeed_t::kAuto; + climate.swingv = stdAc::swingv_t::kAuto; + climate.swingh = stdAc::swingh_t::kAuto; + climate.quiet = false; + climate.turbo = false; + climate.econo = false; + climate.light = false; + climate.filter = false; + climate.clean = false; + climate.beep = false; + climate.sleep = -1; // Off + climate.clock = -1; // Don't set. + climate_prev = climate; + lastClimateSource = F("None"); #if DEBUG - // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. - Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); - while (!Serial) // Wait for the serial connection to be establised. - delay(50); - Serial.println(); - debug("IRMQTTServer " _MY_VERSION_" has booted."); + if (!isSerialGpioUsedByIr()) { +#if defined(ESP8266) + // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. + Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(BAUD_RATE, SERIAL_8N1); +#endif // ESP8266 + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + debug("IRMQTTServer " _MY_VERSION_" has booted."); + } #endif // DEBUG setup_wifi(); +#if DEBUG + // After the config has been loaded, check again if we are using a Serial GPIO + if (isSerialGpioUsedByIr()) Serial.end(); +#endif // DEBUG + + // Initialise all the IR transmitters. + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + if (txGpioTable[i] == kGpioUnused) { + IrSendTable[i] = NULL; + } else { + IrSendTable[i] = new IRsend(txGpioTable[i]); + if (IrSendTable[i] == NULL) break; + IrSendTable[i]->begin(); + offset = IrSendTable[i]->calibrate(); + } + } +#if IR_RX + if (rx_gpio != kGpioUnused) + irrecv = new IRrecv(rx_gpio, kCaptureBufferSize, kCaptureTimeout, true); + if (irrecv != NULL) { +#if IR_RX_PULLUP + pinMode(rx_gpio, INPUT_PULLUP); +#endif // IR_RX_PULLUP +#if DECODE_HASH + // Ignore messages with less than minimum on or off pulses. + irrecv->setUnknownThreshold(kMinUnknownSize); +#endif // DECODE_HASH + irrecv->enableIRIn(); // Start the receiver + } +#endif // IR_RX + commonAc = new IRac(txGpioTable[0]); + // Wait a bit for things to settle. - delay(1500); + delay(500); lastReconnectAttempt = 0; - if (mdns.begin(HOSTNAME, WiFi.localIP())) { +#if MDNS_ENABLE +#if defined(ESP8266) + if (mdns.begin(Hostname, WiFi.localIP())) { +#else // ESP8266 + if (mdns.begin(Hostname)) { +#endif // ESP8266 debug("MDNS responder started"); } +#endif // MDNS_ENABLE // Setup the root web page. - server.on("/", handleRoot); + server.on(kUrlRoot, handleRoot); + // Setup the examples web page. + server.on(kUrlExamples, handleExamples); // Setup the page to handle web-based IR codes. server.on("/ir", handleIr); + // Setup the aircon page. + server.on(kUrlAircon, handleAirCon); + // Setup the aircon update page. + server.on("/aircon/set", handleAirConSet); + // Setup the info page. + server.on(kUrlInfo, handleInfo); + // Setup the admin page. + server.on(kUrlAdmin, handleAdmin); // Setup a reset page to cause WiFiManager information to be reset. - server.on("/reset", handleReset); + server.on(kUrlWipe, handleReset); + // Reboot url + server.on(kUrlReboot, handleReboot); + // Show & pick which gpios are used for what etc. + server.on(kUrlGpio, handleGpio); + // Parse and update the new gpios. + server.on(kUrlGpioSet, handleGpioSetting); +#if MQTT_ENABLE + // MQTT Discovery url + server.on(kUrlSendDiscovery, handleSendMqttDiscovery); + // Finish setup of the mqtt clent object. + mqtt_client.setServer(MqttServer, atoi(MqttPort)); + mqtt_client.setCallback(mqttCallback); + // Set various variables + init_vars(); +#endif // MQTT_ENABLE #if FIRMWARE_OTA // Setup the URL to allow Over-The-Air (OTA) firmware updates. - if (strcmp(kHtmlPassword, kDefaultPassword)) { // Allow if password changed. + if (strlen(HttpPassword)) { // Allow if password is set. server.on("/update", HTTP_POST, [](){ - server.sendHeader("Connection", "close"); - server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK"); +#if MQTT_ENABLE + mqttLog("Attempting firmware update & reboot"); + delay(1000); +#endif // MQTT_ENABLE + server.send(200, "text/html", + htmlHeader(F("Updating firmware")) + + "
" + "

Warning! Don't power off the device for 60 seconds!

" + "

The firmware is uploading and will try to flash itself. " + "It is important to not interrupt the process.

" + "

The firmware upload seems to have " + + String(Update.hasError() ? "FAILED!" : "SUCCEEDED!") + + " Rebooting!

" + + addJsReloadUrl(kUrlRoot, 20, true) + + htmlEnd()); + delay(1000); ESP.restart(); + delay(1000); }, [](){ - if (!server.authenticate(kHtmlUsername, kHtmlPassword)) { + if (!server.authenticate(HttpUsername, HttpPassword)) { debug("Basic HTTP authentication failure for /update."); return server.requestAuthentication(); } HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { + debug("Update:"); + debug(upload.filename.c_str()); +#if defined(ESP8266) WiFiUDP::stopAll(); - debug("Update: " + upload.filename); uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; +#else // ESP8266 + uint32_t maxSketchSpace = UPDATE_SIZE_UNKNOWN; +#endif // ESP8266 if (!Update.begin(maxSketchSpace)) { // start with max available size #if DEBUG - Update.printError(Serial); + if (!isSerialGpioUsedByIr()) + Update.printError(Serial); #endif // DEBUG } } else if (upload.status == UPLOAD_FILE_WRITE) { if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { #if DEBUG - Update.printError(Serial); + if (!isSerialGpioUsedByIr()) + Update.printError(Serial); #endif // DEBUG } } else if (upload.status == UPLOAD_FILE_END) { // true to set the size to the current progress if (Update.end(true)) { - debug("Update Success: " + (String) upload.totalSize + - "\nRebooting..."); + debug("Update Success:"); + debug(String(upload.totalSize).c_str()); + debug("Rebooting..."); } } yield(); @@ -1221,34 +2304,68 @@ void setup(void) { #if MQTT_ENABLE // MQTT subscribing to topic void subscribing(const String topic_name) { - // subscription to topic for receiving data - if (mqtt_client.subscribe(topic_name.c_str())) { - debug("Subscription OK to " + topic_name); - } + // subscription to topic for receiving data with QoS. + if (mqtt_client.subscribe(topic_name.c_str(), QOS)) + debug("Subscription OK to:"); + else + debug("Subscription FAILED to:"); + debug(topic_name.c_str()); } -bool reconnect() { +// Un-subscribe from a MQTT topic +void unsubscribing(const String topic_name) { + // subscription to topic for receiving data with QoS. + if (mqtt_client.unsubscribe(topic_name.c_str())) + debug("Unsubscribed OK from:"); + else + debug("FAILED to unsubscribe from:"); + debug(topic_name.c_str()); +} + +void mqttLog(const String mesg) { + debug(mesg.c_str()); + mqtt_client.publish(MqttLog.c_str(), mesg.c_str()); + mqttSentCounter++; +} + +bool reconnect(void) { // Loop a few times or until we're reconnected uint16_t tries = 1; while (!mqtt_client.connected() && tries <= 3) { int connected = false; // Attempt to connect - debug("Attempting MQTT connection to " MQTT_SERVER ":" + String(kMqttPort) + - "... "); - if (kMqttUsername && kMqttPassword) - connected = mqtt_client.connect(mqtt_clientid.c_str(), kMqttUsername, - kMqttPassword); - else - connected = mqtt_client.connect(mqtt_clientid.c_str()); + debug(("Attempting MQTT connection to " + String(MqttServer) + ":" + + String(MqttPort) + "... ").c_str()); + if (strcmp(MqttUsername, "") && strcmp(MqttPassword, "")) { + debug("Using mqtt username/password to connect."); + connected = mqtt_client.connect(MqttClientId.c_str(), + MqttUsername, MqttPassword, + MqttLwt.c_str(), + QOS, true, kLwtOffline); + + } else { + debug("Using password-less mqtt connection."); + connected = mqtt_client.connect(MqttClientId.c_str(), MqttLwt.c_str(), + QOS, true, kLwtOffline); + } if (connected) { // Once connected, publish an announcement... - mqtt_client.publish(MQTTack, "Connected"); - debug("connected."); + mqttLog("(Re)Connected."); + + // Update Last Will & Testament to say we are back online. + mqtt_client.publish(MqttLwt.c_str(), kLwtOnline, true); + mqttSentCounter++; + // Subscribing to topic(s) - subscribing(MQTTcommand); + subscribing(MqttSend); + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + subscribing(MqttSend + '_' + String(static_cast(i))); + } + // Climate command topics. + subscribing(MqttClimateCmnd + '+'); } else { - debug("failed, rc=" + String(mqtt_client.state()) + - " Try again in a bit."); + debug(("failed, rc=" + String(mqtt_client.state()) + + " Try again in a bit.").c_str()); // Wait for a bit before retrying delay(tries << 7); // Linear increasing back-off (x128) } @@ -1256,6 +2373,221 @@ bool reconnect() { } return mqtt_client.connected(); } + +// Return a string containing the comma separated list of MQTT command topics. +String listOfCommandTopics(void) { + String result = MqttSend; + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + result += ", " + MqttSend + '_' + String(i); + } + return result; +} + +// MQTT Discovery web page +void handleSendMqttDiscovery(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /send_discovery."); + return server.requestAuthentication(); + } +#endif // HTML_PASSWORD_ENABLE + server.send(200, "text/html", + htmlHeader(F("Sending MQTT Discovery message")) + + htmlMenu() + + "

The Home Assistant MQTT Discovery message is being sent to topic: " + + MqttDiscovery + ". It will show up in Home Assistant in a few seconds." + "

" + "

Warning!

" + "

Home Assistant's config for this device is reset each time this is " + " is sent.

" + + addJsReloadUrl(kUrlRoot, kRebootTime, true) + + htmlEnd()); + sendMQTTDiscovery(MqttDiscovery.c_str()); +} + +void doBroadcast(TimerMs *timer, const uint32_t interval, + const stdAc::state_t state, const bool retain, + const bool force) { + if (force || (!lockMqttBroadcast && timer->elapsed() > interval)) { + debug("Sending MQTT stat update broadcast."); + sendClimate(state, state, MqttClimateStat, + retain, true, false); + timer->reset(); // It's been sent, so reset the timer. + hasBroadcastBeenSent = true; + } +} + +void receivingMQTT(String const topic_name, String const callback_str) { + uint64_t code = 0; + uint16_t nbits = 0; + uint16_t repeat = 0; + uint8_t channel = getDefaultIrSendIdx(); // Default to first usable channel. + + debug("Receiving data by MQTT topic:"); + debug(topic_name.c_str()); + debug("with payload:"); + debug(callback_str.c_str()); + // Save the message as the last command seen (global). + lastMqttCmdTopic = topic_name; + lastMqttCmd = callback_str; + lastMqttCmdTime = millis(); + mqttRecvCounter++; + + if (topic_name.startsWith(MqttClimate)) { + if (topic_name.startsWith(MqttClimateCmnd)) { + debug("It's a climate command topic"); + stdAc::state_t updated = updateClimate( + climate, topic_name, MqttClimateCmnd, callback_str); + if (sendClimate(climate, updated, MqttClimateStat, + true, false, false)) + lastClimateSource = F("MQTT"); + climate = updated; + } else if (topic_name.startsWith(MqttClimateStat)) { + debug("It's a climate state topic. Update internal state and DON'T send"); + climate = updateClimate( + climate, topic_name, MqttClimateStat, callback_str); + } + return; // We are done for now. + } + // Check if a specific channel was requested by looking for a "*_[0-9]" suffix + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + debug(("Checking if " + topic_name + " ends with _" + String(i)).c_str()); + if (topic_name.endsWith("_" + String(i))) { + channel = i; + debug("It does!"); + break; + } + } + + debug(("Using transmit channel " + String(static_cast(channel)) + + " / GPIO " + String(static_cast(txGpioTable[channel]))).c_str()); + // Make a copy of the callback string as strtok destroys it. + char* callback_c_str = strdup(callback_str.c_str()); + debug("MQTT Payload (raw):"); + debug(callback_c_str); + + // Chop up the str into command chunks. + // i.e. commands in a sequence are delimitered by ';'. + char* sequence_tok_ptr; + for (char* sequence_item = strtok_r(callback_c_str, kSequenceDelimiter, + &sequence_tok_ptr); + sequence_item != NULL; + sequence_item = strtok_r(NULL, kSequenceDelimiter, &sequence_tok_ptr)) { + // Now, process each command individually. + char* tok_ptr; + // Make a copy of the sequence_item str as strtok_r stomps on it. + char* ircommand = strdup(sequence_item); + // Check if it is a pause command. + switch (ircommand[0]) { + case kPauseChar: + { // It's a pause. Everything after the 'P' should be a number. + int32_t msecs = std::min((int32_t) strtoul(ircommand + 1, NULL, 10), + kMaxPauseMs); + delay(msecs); + mqtt_client.publish(MqttAck.c_str(), + String(kPauseChar + String(msecs)).c_str()); + mqttSentCounter++; + break; + } + default: // It's an IR command. + { + // Get the numeric protocol type. + int32_t ir_type = strtoul(strtok_r(ircommand, kCommandDelimiter, + &tok_ptr), NULL, 10); + char* next = strtok_r(NULL, kCommandDelimiter, &tok_ptr); + // If there is unparsed string left, try to convert it assuming it's + // hex. + if (next != NULL) { + code = getUInt64fromHex(next); + next = strtok_r(NULL, kCommandDelimiter, &tok_ptr); + } else { + // We require at least two value in the string. Give up. + break; + } + // If there is still string left, assume it is the bit size. + if (next != NULL) { + nbits = atoi(next); + next = strtok_r(NULL, kCommandDelimiter, &tok_ptr); + } + // If there is still string left, assume it is the repeat count. + if (next != NULL) + repeat = atoi(next); + // send received MQTT value by IR signal + lastSendSucceeded = sendIRCode( + IrSendTable[channel], ir_type, code, + strchr(sequence_item, kCommandDelimiter[0]), nbits, repeat); + } + } + free(ircommand); + } + free(callback_c_str); +} + +// Callback function, when we receive an MQTT value on the topics +// subscribed this function is called +void mqttCallback(char* topic, byte* payload, unsigned int length) { + // In order to republish this payload, a copy must be made + // as the orignal payload buffer will be overwritten whilst + // constructing the PUBLISH packet. + // Allocate the correct amount of memory for the payload copy + byte* payload_copy = reinterpret_cast(malloc(length + 1)); + if (payload_copy == NULL) { + debug("Can't allocate memory for `payload_copy`. Skipping callback!"); + return; + } + // Copy the payload to the new buffer + memcpy(payload_copy, payload, length); + + // Conversion to a printable string + payload_copy[length] = '\0'; + String callback_string = String(reinterpret_cast(payload_copy)); + String topic_name = String(reinterpret_cast(topic)); + + // launch the function to treat received data + receivingMQTT(topic_name, callback_string); + + // Free the memory + free(payload_copy); +} + +void sendMQTTDiscovery(const char *topic) { + if (mqtt_client.publish( + topic, String( + "{" + "\"~\":\"" + MqttClimate + "\"," + "\"name\":\"" + MqttHAName + "\"," + "\"pow_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_POWER "\"," + "\"mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_MODE "\"," + "\"mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_MODE + "\"," + "\"modes\":[\"off\",\"auto\",\"cool\",\"heat\",\"dry\",\"fan_only\"]," + "\"temp_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" KEY_TEMP "\"," + "\"temp_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" KEY_TEMP + "\"," + "\"min_temp\":\"16\"," + "\"max_temp\":\"30\"," + "\"temp_step\":\"1\"," + "\"fan_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" + KEY_FANSPEED "\"," + "\"fan_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" + KEY_FANSPEED "\"," + "\"fan_modes\":[\"auto\",\"min\",\"low\",\"medium\",\"high\",\"max\"]," + "\"swing_mode_cmd_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_CMND "/" + KEY_SWINGV "\"," + "\"swing_mode_stat_t\":\"~" MQTT_CLIMATE "/" MQTT_CLIMATE_STAT "/" + KEY_SWINGV "\"," + "\"swing_modes\":[" + "\"off\",\"auto\",\"highest\",\"high\",\"middle\",\"low\",\"lowest\"" + "]" + "}").c_str())) { + mqttLog("MQTT climate discovery successful sent."); + hasDiscoveryBeenSent = true; + lastDiscovery.reset(); + mqttSentCounter++; + } else { + mqttLog("MQTT climate discovery FAILED to send."); + } +} #endif // MQTT_ENABLE void loop(void) { @@ -1279,32 +2611,53 @@ void loop(void) { lastReconnectAttempt = 0; wasConnected = true; if (boot) { - mqtt_client.publish(MQTTack, "IR Server just booted"); + mqttLog("IRMQTTServer " _MY_VERSION_ " just booted"); boot = false; } else { - String text = "IR Server just (re)connected to MQTT. " - "Lost connection about " + timeSince(lastConnectedTime); - mqtt_client.publish(MQTTack, text.c_str()); + mqttLog("IRMQTTServer just (re)connected to MQTT. " + "Lost connection about " + timeSince(lastConnectedTime)); } lastConnectedTime = now; debug("successful client mqtt connection"); + if (lockMqttBroadcast) { + // Attempt to fetch back any Climate state stored in MQTT retained + // messages on the MQTT broker. + mqttLog("Started listening for previous state."); + climate_prev = climate; // Make a copy so we can compare afterwards. + subscribing(MqttClimateStat + '+'); + statListenTime.reset(); + } } } } else { - lastConnectedTime = now; // MQTT loop + lastConnectedTime = now; mqtt_client.loop(); + if (lockMqttBroadcast && statListenTime.elapsed() > kStatListenPeriodMs) { + unsubscribing(MqttClimateStat + '+'); + mqttLog("Finished listening for previous state."); + if (cmpClimate(climate, climate_prev)) { // Something changed. + mqttLog("The state was recovered from MQTT broker. Updating."); + sendClimate(climate_prev, climate, MqttClimateStat, + true, false, false); + lastClimateSource = F("MQTT (via retain)"); + } + lockMqttBroadcast = false; // Release the lock so we can broadcast again. + } + // Periodically send all of the climate state via MQTT. + doBroadcast(&lastBroadcast, kBroadcastPeriodMs, climate, false, false); } #endif // MQTT_ENABLE -#ifdef IR_RX +#if IR_RX // Check if an IR code has been received via the IR RX module. #if REPORT_UNKNOWNS - if (irrecv.decode(&capture)) { -#else - if (irrecv.decode(&capture) && capture.decode_type != UNKNOWN) { + if (irrecv != NULL && irrecv->decode(&capture)) { +#else // REPORT_UNKNOWNS + if (irrecv != NULL && irrecv->decode(&capture) && + capture.decode_type != UNKNOWN) { #endif // REPORT_UNKNOWNS lastIrReceivedTime = millis(); - lastIrReceived = String(capture.decode_type) + "," + + lastIrReceived = String(capture.decode_type) + kCommandDelimiter[0] + resultToHexidecimal(&capture); #if REPORT_RAW_UNKNOWNS if (capture.decode_type == UNKNOWN) { @@ -1324,10 +2677,17 @@ void loop(void) { #endif // REPORT_RAW_UNKNOWNS // If it isn't an AC code, add the bits. if (!hasACState(capture.decode_type)) - lastIrReceived += "," + String(capture.bits); - mqtt_client.publish(MQTTrecv, lastIrReceived.c_str()); + lastIrReceived += kCommandDelimiter[0] + String(capture.bits); +#if MQTT_ENABLE + mqtt_client.publish(MqttRecv.c_str(), lastIrReceived.c_str()); + mqttSentCounter++; + debug("Incoming IR message sent to MQTT:"); + debug(lastIrReceived.c_str()); +#endif // MQTT_ENABLE irRecvCounter++; - debug("Incoming IR message sent to MQTT: " + lastIrReceived); +#if USE_DECODED_AC_SETTINGS + if (decodeCommonAc(&capture)) lastClimateSource = F("IR"); +#endif // USE_DECODED_AC_SETTINGS } #endif // IR_RX delay(100); @@ -1356,6 +2716,7 @@ uint64_t getUInt64fromHex(char const *str) { // Transmit the given IR message. // // Args: +// irsend: A pointer to a IRsend object to transmit via. // ir_type: enum of the protocol to be sent. // code: Numeric payload of the IR message. Most protocols use this. // code_str: The unparsed code to be sent. Used by complex protocol encodings. @@ -1363,36 +2724,42 @@ uint64_t getUInt64fromHex(char const *str) { // repeat: Nr. of times the message is to be repeated. (Not all protcols.) // Returns: // bool: Successfully sent or not. -bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, - uint16_t bits, uint16_t repeat) { +bool sendIRCode(IRsend *irsend, int const ir_type, + uint64_t const code, char const * code_str, uint16_t bits, + uint16_t repeat) { + if (irsend == NULL) return false; // Create a pseudo-lock so we don't try to send two codes at the same time. - while (ir_lock) + while (lockIr) delay(20); - ir_lock = true; + lockIr = true; bool success = true; // Assume success. + // Turn off IR capture if we need to. +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + if (irrecv != NULL) irrecv->disableIRIn(); // Stop the IR receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING // send the IR message. switch (ir_type) { #if SEND_RC5 case RC5: // 1 if (bits == 0) bits = kRC5Bits; - irsend.sendRC5(code, bits, repeat); + irsend->sendRC5(code, bits, repeat); break; #endif #if SEND_RC6 case RC6: // 2 if (bits == 0) bits = kRC6Mode0Bits; - irsend.sendRC6(code, bits, repeat); + irsend->sendRC6(code, bits, repeat); break; #endif #if SEND_NEC case NEC: // 3 if (bits == 0) bits = kNECBits; - irsend.sendNEC(code, bits, repeat); + irsend->sendNEC(code, bits, repeat); break; #endif #if SEND_SONY @@ -1400,42 +2767,50 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, if (bits == 0) bits = kSony12Bits; repeat = std::max(repeat, kSonyMinRepeat); - irsend.sendSony(code, bits, repeat); + irsend->sendSony(code, bits, repeat); break; #endif #if SEND_PANASONIC case PANASONIC: // 5 if (bits == 0) bits = kPanasonicBits; - irsend.sendPanasonic64(code, bits, repeat); + irsend->sendPanasonic64(code, bits, repeat); + break; +#endif +#if SEND_INAX + case INAX: // 64 + if (bits == 0) + bits = kInaxBits; + repeat = std::max(repeat, kInaxMinRepeat); + irsend->sendInax(code, bits, repeat); break; #endif #if SEND_JVC case JVC: // 6 if (bits == 0) bits = kJvcBits; - irsend.sendJVC(code, bits, repeat); + irsend->sendJVC(code, bits, repeat); break; #endif #if SEND_SAMSUNG case SAMSUNG: // 7 if (bits == 0) bits = kSamsungBits; - irsend.sendSAMSUNG(code, bits, repeat); + irsend->sendSAMSUNG(code, bits, repeat); break; #endif #if SEND_SAMSUNG36 case SAMSUNG36: // 56 if (bits == 0) bits = kSamsung36Bits; - irsend.sendSamsung36(code, bits, repeat); + irsend->sendSamsung36(code, bits, repeat); break; #endif #if SEND_WHYNTER case WHYNTER: // 8 if (bits == 0) bits = kWhynterBits; - irsend.sendWhynter(code, bits, repeat); + irsend->sendWhynter(code, bits, repeat); break; #endif #if SEND_AIWA_RC_T501 @@ -1443,14 +2818,14 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, if (bits == 0) bits = kAiwaRcT501Bits; repeat = std::max(repeat, kAiwaRcT501MinRepeats); - irsend.sendAiwaRCT501(code, bits, repeat); + irsend->sendAiwaRCT501(code, bits, repeat); break; #endif #if SEND_LG case LG: // 10 if (bits == 0) bits = kLgBits; - irsend.sendLG(code, bits, repeat); + irsend->sendLG(code, bits, repeat); break; #endif #if SEND_MITSUBISHI @@ -1458,7 +2833,7 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, if (bits == 0) bits = kMitsubishiBits; repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend.sendMitsubishi(code, bits, repeat); + irsend->sendMitsubishi(code, bits, repeat); break; #endif #if SEND_DISH @@ -1466,25 +2841,28 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, if (bits == 0) bits = kDishBits; repeat = std::max(repeat, kDishMinRepeat); - irsend.sendDISH(code, bits, repeat); + irsend->sendDISH(code, bits, repeat); break; #endif #if SEND_SHARP case SHARP: // 14 if (bits == 0) bits = kSharpBits; - irsend.sendSharpRaw(code, bits, repeat); + irsend->sendSharpRaw(code, bits, repeat); break; #endif #if SEND_COOLIX case COOLIX: // 15 if (bits == 0) bits = kCoolixBits; - irsend.sendCOOLIX(code, bits, repeat); + repeat = std::max(repeat, kCoolixDefaultRepeat); + irsend->sendCOOLIX(code, bits, repeat); break; #endif case DAIKIN: // 16 + case DAIKIN160: // 65 case DAIKIN2: // 53 + case DAIKIN216: // 61 case KELVINATOR: // 18 case MITSUBISHI_AC: // 20 case GREE: // 24 @@ -1499,16 +2877,17 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, case HITACHI_AC2: // 42 case WHIRLPOOL_AC: // 45 case SAMSUNG_AC: // 46 + case SHARP_AC: // 62 case ELECTRA_AC: // 48 case PANASONIC_AC: // 49 case MWM: // 52 - success = parseStringAndSendAirCon(ir_type, code_str); + success = parseStringAndSendAirCon(irsend, ir_type, code_str); break; #if SEND_DENON case DENON: // 17 if (bits == 0) - bits = DENON_BITS; - irsend.sendDenon(code, bits, repeat); + bits = kDenonBits; + irsend->sendDenon(code, bits, repeat); break; #endif #if SEND_SHERWOOD @@ -1516,78 +2895,78 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, if (bits == 0) bits = kSherwoodBits; repeat = std::max(repeat, kSherwoodMinRepeat); - irsend.sendSherwood(code, bits, repeat); + irsend->sendSherwood(code, bits, repeat); break; #endif #if SEND_RCMM case RCMM: // 21 if (bits == 0) bits = kRCMMBits; - irsend.sendRCMM(code, bits, repeat); + irsend->sendRCMM(code, bits, repeat); break; #endif #if SEND_SANYO case SANYO_LC7461: // 22 if (bits == 0) bits = kSanyoLC7461Bits; - irsend.sendSanyoLC7461(code, bits, repeat); + irsend->sendSanyoLC7461(code, bits, repeat); break; #endif #if SEND_RC5 case RC5X: // 23 if (bits == 0) bits = kRC5XBits; - irsend.sendRC5(code, bits, repeat); + irsend->sendRC5(code, bits, repeat); break; #endif #if SEND_PRONTO case PRONTO: // 25 - success = parseStringAndSendPronto(code_str, repeat); + success = parseStringAndSendPronto(irsend, code_str, repeat); break; #endif #if SEND_NIKAI case NIKAI: // 29 if (bits == 0) bits = kNikaiBits; - irsend.sendNikai(code, bits, repeat); + irsend->sendNikai(code, bits, repeat); break; #endif #if SEND_RAW case RAW: // 30 - success = parseStringAndSendRaw(code_str); + success = parseStringAndSendRaw(irsend, code_str); break; #endif #if SEND_GLOBALCACHE case GLOBALCACHE: // 31 - success = parseStringAndSendGC(code_str); + success = parseStringAndSendGC(irsend, code_str); break; #endif #if SEND_MIDEA case MIDEA: // 34 if (bits == 0) bits = kMideaBits; - irsend.sendMidea(code, bits, repeat); + irsend->sendMidea(code, bits, repeat); break; #endif #if SEND_MAGIQUEST case MAGIQUEST: // 35 if (bits == 0) bits = kMagiquestBits; - irsend.sendMagiQuest(code, bits, repeat); + irsend->sendMagiQuest(code, bits, repeat); break; #endif #if SEND_LASERTAG case LASERTAG: // 36 if (bits == 0) bits = kLasertagBits; - irsend.sendLasertag(code, bits, repeat); + irsend->sendLasertag(code, bits, repeat); break; #endif #if SEND_CARRIER_AC case CARRIER_AC: // 37 if (bits == 0) bits = kCarrierAcBits; - irsend.sendCarrierAC(code, bits, repeat); + irsend->sendCarrierAC(code, bits, repeat); break; #endif #if SEND_MITSUBISHI2 @@ -1595,7 +2974,7 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, if (bits == 0) bits = kMitsubishiBits; repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend.sendMitsubishi2(code, bits, repeat); + irsend->sendMitsubishi2(code, bits, repeat); break; #endif #if SEND_GICABLE @@ -1603,51 +2982,69 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, if (bits == 0) bits = kGicableBits; repeat = std::max(repeat, kGicableMinRepeat); - irsend.sendGICable(code, bits, repeat); + irsend->sendGICable(code, bits, repeat); break; #endif #if SEND_LUTRON case LUTRON: // 47 if (bits == 0) bits = kLutronBits; - irsend.sendLutron(code, bits, repeat); + irsend->sendLutron(code, bits, repeat); break; #endif #if SEND_PIONEER case PIONEER: // 50 if (bits == 0) bits = kPioneerBits; - irsend.sendPioneer(code, bits, repeat); + irsend->sendPioneer(code, bits, repeat); break; #endif #if SEND_LG case LG2: // 51 if (bits == 0) bits = kLgBits; - irsend.sendLG2(code, bits, repeat); + irsend->sendLG2(code, bits, repeat); break; #endif #if SEND_VESTEL_AC case VESTEL_AC: // 54 if (bits == 0) bits = kVestelAcBits; - irsend.sendVestelAc(code, bits, repeat); + irsend->sendVestelAc(code, bits, repeat); break; #endif #if SEND_TECO case TECO: // 55 if (bits == 0) bits = kTecoBits; - irsend.sendTeco(code, bits, repeat); + irsend->sendTeco(code, bits, repeat); break; #endif +#if SEND_LEGOPF + case LEGOPF: // 58 + if (bits == 0) + bits = kLegoPfBits; + irsend->sendLegoPf(code, bits, repeat); + break; +#endif +#if SEND_GOODWEATHER + case GOODWEATHER: // 63 + if (bits == 0) bits = kGoodweatherBits; + repeat = std::max(repeat, kGoodweatherMinRepeat); + irsend->sendGoodweather(code, bits, repeat); + break; +#endif // SEND_GOODWEATHER default: // If we got here, we didn't know how to send it. success = false; } +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture back on if we need to. + if (irrecv != NULL) irrecv->enableIRIn(); // Restart the receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING lastSendTime = millis(); // Release the lock. - ir_lock = false; + lockIr = false; // Indicate that we sent the message or not. if (success) { @@ -1656,7 +3053,8 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, } else { debug("Failed to send IR Message:"); } - debug("Type: " + String(ir_type)); + debug("Type:"); + debug(String(ir_type).c_str()); // For "long" codes we basically repeat what we got. if (hasACState((decode_type_t) ir_type) || ir_type == PRONTO || @@ -1668,95 +3066,474 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str, #if MQTT_ENABLE if (success) { if (ir_type == PRONTO && repeat > 0) - mqtt_client.publish(MQTTack, (String(ir_type) + ",R" + - String(repeat) + "," + - String(code_str)).c_str()); + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + + kCommandDelimiter[0] + 'R' + + String(repeat) + + kCommandDelimiter[0] + + String(code_str)).c_str()); else - mqtt_client.publish(MQTTack, (String(ir_type) + "," + - String(code_str)).c_str()); + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + + kCommandDelimiter[0] + + String(code_str)).c_str()); + mqttSentCounter++; } #endif // MQTT_ENABLE } else { // For "short" codes, we break it down a bit more before we report. - debug("Code: 0x" + uint64ToString(code, 16)); - debug("Bits: " + String(bits)); - debug("Repeats: " + String(repeat)); + debug(("Code: 0x" + uint64ToString(code, 16)).c_str()); + debug(("Bits: " + String(bits)).c_str()); + debug(("Repeats: " + String(repeat)).c_str()); #if MQTT_ENABLE - if (success) - mqtt_client.publish(MQTTack, (String(ir_type) + "," + - uint64ToString(code, 16) - + "," + String(bits) + "," + - String(repeat)).c_str()); + if (success) { + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + + kCommandDelimiter[0] + + uint64ToString(code, 16) + + kCommandDelimiter[0] + + String(bits) + + kCommandDelimiter[0] + + String(repeat)).c_str()); + mqttSentCounter++; + } #endif // MQTT_ENABLE } return success; } +bool sendInt(const String topic, const int32_t num, const bool retain) { #if MQTT_ENABLE -void receivingMQTT(String const topic_name, String const callback_str) { - char* tok_ptr; - uint64_t code = 0; - uint16_t nbits = 0; - uint16_t repeat = 0; - - debug("Receiving data by MQTT topic " + topic_name); - - // Make a copy of the callback string as strtok destroys it. - char* callback_c_str = strdup(callback_str.c_str()); - debug("MQTT Payload (raw): " + callback_str); - // Save the message as the last command seen (global). - lastMqttCmd = callback_str; - lastMqttCmdTime = millis(); - - // Get the numeric protocol type. - int ir_type = strtoul(strtok_r(callback_c_str, ",", &tok_ptr), NULL, 10); - char* next = strtok_r(NULL, ",", &tok_ptr); - // If there is unparsed string left, try to convert it assuming it's hex. - if (next != NULL) { - code = getUInt64fromHex(next); - next = strtok_r(NULL, ",", &tok_ptr); - } else { - // We require at least two value in the string. Give up. - return; - } - // If there is still string left, assume it is the bit size. - if (next != NULL) { - nbits = atoi(next); - next = strtok_r(NULL, ",", &tok_ptr); - } - // If there is still string left, assume it is the repeat count. - if (next != NULL) - repeat = atoi(next); - - free(callback_c_str); - - - // send received MQTT value by IR signal - lastSendSucceeded = sendIRCode( - ir_type, code, - callback_str.substring(callback_str.indexOf(",") + 1).c_str(), - nbits, repeat); -} - -// Callback function, when the gateway receive an MQTT value on the topics -// subscribed this function is called -void callback(char* topic, byte* payload, unsigned int length) { - // In order to republish this payload, a copy must be made - // as the orignal payload buffer will be overwritten whilst - // constructing the PUBLISH packet. - // Allocate the correct amount of memory for the payload copy - byte* payload_copy = reinterpret_cast(malloc(length + 1)); - // Copy the payload to the new buffer - memcpy(payload_copy, payload, length); - - // Conversion to a printable string - payload_copy[length] = '\0'; - String callback_string = String(reinterpret_cast(payload_copy)); - String topic_name = String(reinterpret_cast(topic)); - - // launch the function to treat received data - receivingMQTT(topic_name, callback_string); - - // Free the memory - free(payload_copy); -} + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), String(num).c_str(), retain); +#else // MQTT_ENABLE + return true; #endif // MQTT_ENABLE +} + +bool sendBool(const String topic, const bool on, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), (on ? "on" : "off"), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendString(const String topic, const String str, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), str.c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +bool sendFloat(const String topic, const float_t temp, const bool retain) { +#if MQTT_ENABLE + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), String(temp, 1).c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} + +stdAc::state_t updateClimate(stdAc::state_t current, const String str, + const String prefix, const String payload) { + stdAc::state_t result = current; + String value = payload; + value.toUpperCase(); + if (str.equals(prefix + KEY_PROTOCOL)) + result.protocol = strToDecodeType(value.c_str()); + else if (str.equals(prefix + KEY_MODEL)) + result.model = IRac::strToModel(value.c_str()); + else if (str.equals(prefix + KEY_POWER)) + result.power = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_MODE)) + result.mode = IRac::strToOpmode(value.c_str()); + else if (str.equals(prefix + KEY_TEMP)) + result.degrees = value.toFloat(); + else if (str.equals(prefix + KEY_FANSPEED)) + result.fanspeed = IRac::strToFanspeed(value.c_str()); + else if (str.equals(prefix + KEY_SWINGV)) + result.swingv = IRac::strToSwingV(value.c_str()); + else if (str.equals(prefix + KEY_SWINGH)) + result.swingh = IRac::strToSwingH(value.c_str()); + else if (str.equals(prefix + KEY_QUIET)) + result.quiet = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_TURBO)) + result.turbo = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_ECONO)) + result.econo = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_LIGHT)) + result.light = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_BEEP)) + result.beep = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_FILTER)) + result.filter = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_CLEAN)) + result.clean = IRac::strToBool(value.c_str()); + else if (str.equals(prefix + KEY_SLEEP)) + result.sleep = value.toInt(); + return result; +} + +// Compare two AirCon states (climates). +// Returns: True if they differ, False if they don't. +bool cmpClimate(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; +} + +bool sendClimate(const stdAc::state_t prev, const stdAc::state_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR, + const bool enableIR) { + bool diff = false; + bool success = true; + + if (prev.protocol != next.protocol || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_PROTOCOL, + typeToString(next.protocol), retain); + } + if (prev.model != next.model || forceMQTT) { + diff = true; + success &= sendInt(topic_prefix + KEY_MODEL, next.model, retain); + } + if (prev.power != next.power || prev.mode != next.mode || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_POWER, next.power, retain); + success &= sendString(topic_prefix + KEY_MODE, + (next.power ? IRac::opmodeToString(next.mode) + : F("off")), + retain); + } + if (prev.degrees != next.degrees || forceMQTT) { + diff = true; + success &= sendFloat(topic_prefix + KEY_TEMP, next.degrees, retain); + } + if (prev.celsius != next.celsius || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_CELSIUS, next.celsius, retain); + } + if (prev.fanspeed != next.fanspeed || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_FANSPEED, + IRac::fanspeedToString(next.fanspeed), retain); + } + if (prev.swingv != next.swingv || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGV, + IRac::swingvToString(next.swingv), retain); + } + if (prev.swingh != next.swingh || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGH, + IRac::swinghToString(next.swingh), retain); + } + if (prev.quiet != next.quiet || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_QUIET, next.quiet, retain); + } + if (prev.turbo != next.turbo || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_TURBO, next.turbo, retain); + } + if (prev.econo != next.econo || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_ECONO, next.econo, retain); + } + if (prev.light != next.light || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_LIGHT, next.light, retain); + } + if (prev.filter != next.filter || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_FILTER, next.filter, retain); + } + if (prev.clean != next.clean || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_CLEAN, next.clean, retain); + } + if (prev.beep != next.beep || forceMQTT) { + diff = true; + success &= sendBool(topic_prefix + KEY_BEEP, next.beep, retain); + } + if (prev.sleep != next.sleep || forceMQTT) { + diff = true; + success &= sendInt(topic_prefix + KEY_SLEEP, next.sleep, retain); + } + if (diff && !forceMQTT) + debug("Difference in common A/C state detected."); + else + debug("NO difference in common A/C state detected."); + // Only send an IR message if we need to. + if (enableIR && ((diff && !forceMQTT) || forceIR)) { + debug("Sending common A/C state via IR."); +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture off if we need to. + if (irrecv != NULL) irrecv->disableIRIn(); // Stop the IR receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + lastClimateSucceeded = commonAc->sendAc( + next.protocol, next.model, next.power, next.mode, + next.degrees, next.celsius, next.fanspeed, next.swingv, next.swingh, + next.quiet, next.turbo, next.econo, next.light, next.filter, next.clean, + next.beep, next.sleep, -1); +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture back on if we need to. + if (irrecv != NULL) irrecv->enableIRIn(); // Restart the receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + if (lastClimateSucceeded) hasClimateBeenSent = true; + success &= lastClimateSucceeded; + lastClimateIr.reset(); + irClimateCounter++; + sendReqCounter++; + } + return success; +} + +#if USE_DECODED_AC_SETTINGS && IR_RX +// Decode and use a valid IR A/C remote that we understand enough to convert +// to a Common A/C format. +// Args: +// decode: A successful raw IR decode object. +// Returns: +// A boolean indicating success or failure. +bool decodeCommonAc(const decode_results *decode) { + if (!IRac::isProtocolSupported(decode->decode_type)) { + debug("Inbound IR messages isn't a supported common A/C protocol"); + return false; + } + stdAc::state_t state = climate; + debug("Converting inbound IR A/C message to common A/C"); + int8_t txgpio = getDefaultTxGpio(); + switch (decode->decode_type) { +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_ARGO +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(txgpio); + ac.setRaw(decode->value); // Uses value instead of state. + state = ac.toCommon(); + break; + } +#endif // DECODE_COOLIX +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN216 +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(txgpio); + ac.setRaw(decode->state, decode->bits / 8); + state = ac.toCommon(); + break; + } +#endif // DECODE_FUJITSU_AC +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(txgpio); + ac.setRaw(decode->value); // Uses value instead of state. + state = ac.toCommon(); + break; + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC_YRW02 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_KELVINATOR +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(txgpio); + ac.setRaw(decode->value); // Uses value instead of state. + state = ac.toCommon(); + break; + } +#endif // DECODE_MIDEA +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + IRPanasonicAc ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_SHARP_AC +#if DECODE_TCL112AC + case decode_type_t::TCL112AC: { + IRTcl112Ac ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_TCL112AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(txgpio); + ac.setRaw(decode->value); // Uses value instead of state. + state = ac.toCommon(); + break; + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(txgpio); + ac.setRaw(decode->value); // Uses value instead of state. + state = ac.toCommon(); + break; + } +#endif // DECODE_VESTEL_AC +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(txgpio); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_WHIRLPOOL_AC + default: + debug("Failed to convert to common A/C."); // This shouldn't happen! + return false; + } +#if IGNORE_DECODED_AC_PROTOCOL + if (climate.protocol != decode_type_t::UNKNOWN) { + // Use the previous protcol/model if set. + state.protocol = climate.protocol; + state.model = climate.model; + } +#endif // IGNORE_DECODED_AC_PROTOCOL +// Continue to use the previously prefered temperature units. +// i.e. Keep using Celsius or Fahrenheit. +if (climate.celsius != state.celsius) { + // We've got a mismatch, so we need to convert. + state.degrees = climate.celsius ? fahrenheitToCelsius(state.degrees) + : celsiusToFahrenheit(state.degrees); + state.celsius = climate.celsius; +} +#if MQTT_ENABLE + sendClimate(climate, state, MqttClimateStat, true, false, false, + REPLAY_DECODED_AC_MESSAGE); +#else // MQTT_ENABLE + sendClimate(climate, state, "", false, false, false, + REPLAY_DECODED_AC_MESSAGE); +#endif // MQTT_ENABLE + climate = state; // Copy over the new climate state. + return true; +} +#endif // USE_DECODED_AC_SETTINGS && IR_RX diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini index 27b44dd..92d4a4a 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini @@ -3,26 +3,57 @@ lib_extra_dirs = ../../ src_dir=. [common] -build_flags = -DMQTT_MAX_PACKET_SIZE=512 +build_flags = -DMQTT_MAX_PACKET_SIZE=768 +lib_ldf_mode = chain+ lib_deps_builtin = lib_deps_external = PubSubClient - WifiManager@0.14 + ArduinoJson@<6.0 + +[common_esp8266] +lib_deps_external = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + WifiManager@>=0.14 + +[common_esp32] +lib_deps_external = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + https://github.com/tzapu/WiFiManager.git#development [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} + ${common_esp8266.lib_deps_external} [env:d1_mini] platform=espressif8266 framework=arduino board=d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} + ${common_esp8266.lib_deps_external} + +[env:d1_mini_no_mqtt] +platform=espressif8266 +framework=arduino +board=d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} -DMQTT_ENABLE=false +lib_deps = + ${common_esp8266.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common_esp32.lib_deps_external} diff --git a/lib/IRremoteESP8266/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266/examples/IRServer/IRServer.ino index b378d3b..1cadf8c 100644 --- a/lib/IRremoteESP8266/examples/IRServer/IRServer.ino +++ b/lib/IRremoteESP8266/examples/IRServer/IRServer.ino @@ -1,9 +1,11 @@ /* * IRremoteESP8266: IRServer - demonstrates sending IR codes controlled from a webserver + * Version 0.3 May, 2019 * Version 0.2 June, 2017 * Copyright 2015 Mark Szabo + * Copyright 2019 David Conran * - * An IR LED circuit *MUST* be connected to the ESP8266 on a pin + * An IR LED circuit *MUST* be connected to the ESP on a pin * as specified by kIrLed below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. @@ -26,12 +28,17 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif +#if defined(ESP8266) #include #include #include +#endif // ESP8266 +#if defined(ESP32) +#include +#include +#include +#endif // ESP32 #include #include #include @@ -40,18 +47,27 @@ const char* kSsid = "....."; const char* kPassword = "....."; MDNSResponder mdns; +#if defined(ESP8266) ESP8266WebServer server(80); +#undef HOSTNAME +#define HOSTNAME "esp8266" +#endif // ESP8266 +#if defined(ESP32) +WebServer server(80); +#undef HOSTNAME +#define HOSTNAME "esp32" +#endif // ESP32 -const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +const uint16_t kIrLed = 4; // ESP GPIO pin to use. Recommended: 4 (D2). IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message. void handleRoot() { server.send(200, "text/html", "" \ - "ESP8266 Demo" \ + "" HOSTNAME " Demo" \ "" \ - "

Hello from ESP8266, you can send NEC encoded IR" \ + "

Hello from " HOSTNAME ", you can send NEC encoded IR" \ "signals from here!

" \ "

Send 0xFFE01F

" \ "

Send 0xFAB123

" \ @@ -104,7 +120,11 @@ void setup(void) { Serial.print("IP address: "); Serial.println(WiFi.localIP().toString()); - if (mdns.begin("esp8266", WiFi.localIP())) { +#if defined(ESP8266) + if (mdns.begin(HOSTNAME, WiFi.localIP())) { +#else // ESP8266 + if (mdns.begin(HOSTNAME)) { +#endif // ESP8266 Serial.println("MDNS responder started"); } diff --git a/lib/IRremoteESP8266/examples/IRServer/platformio.ini b/lib/IRremoteESP8266/examples/IRServer/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRServer/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino index 09babe4..dd3197e 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino @@ -16,9 +16,7 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009 */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino index 34f10dc..2a65cb6 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDump/IRrecvDump.ino @@ -22,9 +22,7 @@ * LG added by Darryl Smith (based on the JVC protocol) */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino index 9e8c3e1..8224453 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -19,27 +19,30 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, */ -#ifndef UNIT_TEST #include -#endif #include #include #include // The following are only needed for extended decoding of A/C Messages +#include #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include +#include #include #include @@ -117,8 +120,15 @@ IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, true); decode_results results; // Somewhere to store the results // Display the human readable state of an A/C message if we can. -void dumpACInfo(decode_results *results) { +void dumpACInfo(const decode_results * const results) { String description = ""; +#if DECODE_ARGO + if (results->decode_type == ARGO) { + IRArgoAC ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_ARGO #if DECODE_DAIKIN if (results->decode_type == DAIKIN) { IRDaikinESP ac(0); @@ -133,6 +143,13 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + if (results->decode_type == DAIKIN216) { + IRDaikin216 ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_DAIKIN216 #if DECODE_FUJITSU_AC if (results->decode_type == FUJITSU_AC) { IRFujitsuAC ac(0); @@ -154,6 +171,18 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + if (results->decode_type == MITSUBISHI_HEAVY_88) { + IRMitsubishiHeavy88Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } + if (results->decode_type == MITSUBISHI_HEAVY_152) { + IRMitsubishiHeavy152Ac ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY #if DECODE_TOSHIBA_AC if (results->decode_type == TOSHIBA_AC) { IRToshibaAC ac(0); @@ -161,6 +190,20 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + if (results->decode_type == TROTEC) { + IRTrotecESP ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_TROTEC +#if DECODE_GOODWEATHER + if (results->decode_type == GOODWEATHER) { + IRGoodweatherAc ac(0); + ac.setRaw(results->value); // Goodweather uses value instead of state. + description = ac.toString(); + } +#endif // DECODE_GOODWEATHER #if DECODE_GREE if (results->decode_type == GREE) { IRGreeAC ac(0); @@ -196,6 +239,13 @@ void dumpACInfo(decode_results *results) { description = ac.toString(); } #endif // DECODE_SAMSUNG_AC +#if DECODE_SHARP_AC + if (results->decode_type == SHARP_AC) { + IRSharpAc ac(0); + ac.setRaw(results->state); + description = ac.toString(); + } +#endif // DECODE_SHARP_AC #if DECODE_COOLIX if (results->decode_type == COOLIX) { IRCoolixAC ac(0); @@ -250,9 +300,13 @@ void dumpACInfo(decode_results *results) { if (description != "") Serial.println("Mesg Desc.: " + description); } -// The section of code run only once at start-up. +// This section of code runs only once at start-up. void setup() { +#if defined(ESP8266) Serial.begin(kBaudRate, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(kBaudRate, SERIAL_8N1); +#endif // ESP8266 while (!Serial) // Wait for the serial connection to be establised. delay(50); Serial.println(); diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino index 19f1186..7084cf0 100644 --- a/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino +++ b/lib/IRremoteESP8266/examples/IRsendDemo/IRsendDemo.ino @@ -28,9 +28,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include @@ -53,7 +51,11 @@ uint8_t samsungState[kSamsungAcStateLength] = { void setup() { irsend.begin(); +#if ESP8266 Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(115200, SERIAL_8N1); +#endif // ESP8266 } void loop() { diff --git a/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino index 3bef217..e7a0f1b 100644 --- a/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino +++ b/lib/IRremoteESP8266/examples/IRsendProntoDemo/IRsendProntoDemo.ino @@ -29,9 +29,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino index ee24229..fc9699a 100644 --- a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino +++ b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino @@ -28,9 +28,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266/examples/LGACSend/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/LGACSend/platformio.ini +++ b/lib/IRremoteESP8266/examples/LGACSend/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino b/lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino new file mode 100644 index 0000000..4a7d56b --- /dev/null +++ b/lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino @@ -0,0 +1,143 @@ +/* + * IRremoteESP8266: SmartIRRepeater.ino - Record and playback IR codes. + * Copyright 2019 David Conran (crankyoldgit) + * + * This program will try to capture incoming IR messages and tries to + * intelligently replay them back. + * It uses the advanced detection features of the library, and the custom + * sending routines. Thus it will try to use the correct frequencies, + * duty cycles, and repeats as it thinks is required. + * Anything it doesn't understand, it will try to replay back as best it can, + * but at 38kHz. + * Note: + * That might NOT be the frequency of the incoming message, so some not + * recogised messages that are replayed may not work. The frequency & duty + * cycle of unknown incoming messages is lost at the point of the Hardware IR + * demodulator. The ESP can't see it. + * + * W A R N I N G + * This code is just for educational/example use only. No help will be given + * to you to make it do something else, or to make it work with some + * weird device or circuit, or to make it more usable or practical. + * If it works for you. Great. If not, Congratulations on changing/fixing it. + * + * An IR detector/demodulator must be connected to the input, kRecvPin. + * An IR LED circuit must be connected to the output, kIrLedPin. + * + * Example circuit diagrams (both are needed): + * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving + * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * + * Common mistakes & tips: + * * Don't just connect the IR LED directly to the pin, it won't + * have enough current to drive the IR LED effectively. + * * Make sure you have the IR LED polarity correct. + * See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity + * * Some digital camera/phones can be used to see if the IR LED is flashed. + * Replace the IR LED with a normal LED if you don't have a digital camera + * when debugging. + * * Avoid using the following pins unless you really know what you are doing: + * * Pin 0/D3: Can interfere with the boot/program mode & support circuits. + * * Pin 1/TX/TXD0: Any serial transmissions from the ESP will interfere. + * * Pin 3/RX/RXD0: Any serial transmissions to the ESP will interfere. + * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs + * for your first time. e.g. ESP-12 etc. + * + * Changes: + * Version 1.0: June, 2019 + * - Initial version. + */ + +#include +#include +#include +#include +#include + +// ==================== start of TUNEABLE PARAMETERS ==================== + +// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) +const uint16_t kRecvPin = 14; + +// GPIO to use to control the IR LED circuit. Recommended: 4 (D2). +const uint16_t kIrLedPin = 4; + +// The Serial connection baud rate. +// NOTE: Make sure you set your Serial Monitor to the same speed. +const uint32_t kBaudRate = 115200; + +// As this program is a special purpose capture/resender, let's use a larger +// than expected buffer so we can handle very large IR messages. +const uint16_t kCaptureBufferSize = 1024; // 1024 == ~511 bits + +// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a +// message ended. +const uint8_t kTimeout = 50; // Milli-Seconds + +// kFrequency is the modulation frequency all UNKNOWN messages will be sent at. +const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz. + +// ==================== end of TUNEABLE PARAMETERS ==================== + +// The IR transmitter. +IRsend irsend(kIrLedPin); +// The IR receiver. +IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false); +// Somewhere to store the captured message. +decode_results results; + +// This section of code runs only once at start-up. +void setup() { + irrecv.enableIRIn(); // Start up the IR receiver. + irsend.begin(); // Start up the IR sender. + + Serial.begin(kBaudRate, SERIAL_8N1); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + + Serial.print("SmartIRRepeater is now running and waiting for IR input " + "on Pin "); + Serial.println(kRecvPin); + Serial.print("and will retransmit it on Pin "); + Serial.println(kIrLedPin); +} + +// The repeating section of the code +void loop() { + // Check if an IR message has been received. + if (irrecv.decode(&results)) { // We have captured something. + // The capture has stopped at this point. + decode_type_t protocol = results.decode_type; + uint16_t size = results.bits; + bool success = true; + // Is it a protocol we don't understand? + if (protocol == decode_type_t::UNKNOWN) { // Yes. + // Convert the results into an array suitable for sendRaw(). + // resultToRawArray() allocates the memory we need for the array. + uint16_t *raw_array = resultToRawArray(&results); + // Find out how many elements are in the array. + size = getCorrectedRawLength(&results); + // Send it out via the IR LED circuit. + irsend.sendRaw(raw_array, size, kFrequency); + // Deallocate the memory allocated by resultToRawArray(). + delete [] raw_array; + } else if (hasACState(protocol)) { // Does the message require a state[]? + // It does, so send with bytes instead. + success = irsend.send(protocol, results.state, size / 8); + } else { // Anything else must be a simple message protocol. ie. <= 64 bits + success = irsend.send(protocol, results.value, size); + } + // Resume capturing IR messages. It was not restarted until after we sent + // the message so we didn't capture our own message. + irrecv.resume(); + + // Display a crude timestamp & notification. + uint32_t now = millis(); + Serial.printf( + "%06u.%03u: A %d-bit %s message was %ssuccessfully retransmitted.\n", + now / 1000, now % 1000, size, typeToString(protocol).c_str(), + success ? "" : "un"); + } + yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset. +} diff --git a/lib/IRremoteESP8266/examples/SmartIRRepeater/platformio.ini b/lib/IRremoteESP8266/examples/SmartIRRepeater/platformio.ini new file mode 100644 index 0000000..e11c48d --- /dev/null +++ b/lib/IRremoteESP8266/examples/SmartIRRepeater/platformio.ini @@ -0,0 +1,29 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino index 3993d11..49af68c 100644 --- a/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino @@ -23,9 +23,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -44,7 +42,7 @@ void loop() { // Set up what we want to send. See ir_Argo.cpp for all the options. ac.setPower(true); ac.setFan(kArgoFan1); - ac.setCoolMode(kArgoCoolAuto); + ac.setMode(kArgoAuto); ac.setTemp(25); #if SEND_ARGO diff --git a/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino index b3ab757..5f3dccc 100644 --- a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino @@ -24,9 +24,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino index 823a3f4..010d84c 100644 --- a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino @@ -1,7 +1,5 @@ // Copyright 2017 Jonny Graham, 2018 David Conran -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -30,11 +28,13 @@ void setup() { Serial.println("Default state of the remote."); printState(); Serial.println("Setting desired state for A/C."); - ac.setCmd(kFujitsuAcCmdTurnOn); - ac.setSwing(kFujitsuAcSwingBoth); + // See `fujitsu_ac_remote_model_t` in `ir_Fujitsu.h` for a list of models. + ac.setModel(ARRAH2E); + ac.setSwing(kFujitsuAcSwingOff); ac.setMode(kFujitsuAcModeCool); ac.setFanSpeed(kFujitsuAcFanHigh); ac.setTemp(24); // 24C + ac.setCmd(kFujitsuAcCmdTurnOn); } void loop() { diff --git a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino index b9b7007..4e9a417 100644 --- a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino index e719af6..ead4a39 100644 --- a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino new file mode 100644 index 0000000..3fb29a9 --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino @@ -0,0 +1,70 @@ +/* Copyright 2019 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#include +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRMitsubishiHeavy152Ac ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Mitsubishi Heavy A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); + // Display the encoded IR sequence. + unsigned char* ir_code = ac.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < kMitsubishiHeavy152StateLength; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_MitsubishiHeavy.(cpp|h) for all the + // options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + ac.setPower(true); // Turn it on. + ac.setFan(kMitsubishiHeavy152FanMed); // Medium Fan + ac.setMode(kMitsubishiHeavyCool); // Cool mode + ac.setTemp(26); // Celsius + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); // Swing vertically + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHMiddle); // Swing Horizontally +} + +void loop() { + // Now send the IR signal. + Serial.println("Sending IR command to A/C ..."); + ac.send(); + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/platformio.ini new file mode 100644 index 0000000..e11c48d --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/platformio.ini @@ -0,0 +1,29 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino new file mode 100644 index 0000000..ca3c165 --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino @@ -0,0 +1,72 @@ +/* Copyright 2017, 2018 David Conran +* +* An IR LED circuit *MUST* be connected to the ESP8266 on a pin +* as specified by kIrLed below. +* +* TL;DR: The IR LED needs to be driven by a transistor for a good result. +* +* Suggested circuit: +* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending +* +* Common mistakes & tips: +* * Don't just connect the IR LED directly to the pin, it won't +* have enough current to drive the IR LED effectively. +* * Make sure you have the IR LED polarity correct. +* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity +* * Typical digital camera/phones can be used to see if the IR LED is flashed. +* Replace the IR LED with a normal LED if you don't have a digital camera +* when debugging. +* * Avoid using the following pins unless you really know what you are doing: +* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. +* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. +* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. +* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs +* for your first time. e.g. ESP-12 etc. +*/ +#include +#include +#include +#include + +const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +IRPanasonicAc ac(kIrLed); // Set the GPIO used for sending messages. + +void printState() { + // Display the settings. + Serial.println("Panasonic A/C remote is in the following state:"); + Serial.printf(" %s\n", ac.toString().c_str()); + // Display the encoded IR sequence. + unsigned char* ir_code = ac.getRaw(); + Serial.print("IR Code: 0x"); + for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) + Serial.printf("%02X", ir_code[i]); + Serial.println(); +} + +void setup() { + ac.begin(); + Serial.begin(115200); + delay(200); + + // Set up what we want to send. See ir_Panasonic.cpp for all the options. + Serial.println("Default state of the remote."); + printState(); + Serial.println("Setting desired state for A/C."); + ac.setModel(kPanasonicRkr); + ac.on(); + ac.setFan(kPanasonicAcFanAuto); + ac.setMode(kPanasonicAcCool); + ac.setTemp(26); + ac.setSwingVertical(kPanasonicAcSwingVAuto); + ac.setSwingHorizontal(kPanasonicAcSwingHAuto); +} + +void loop() { + // Now send the IR signal. +#if SEND_PANASONIC_AC + Serial.println("Sending IR command to A/C ..."); + ac.send(); +#endif // SEND_PANASONIC_AC + printState(); + delay(5000); +} diff --git a/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/platformio.ini new file mode 100644 index 0000000..e11c48d --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/platformio.ini @@ -0,0 +1,29 @@ +[platformio] +lib_extra_dirs = ../../ +src_dir=. + +[common] +build_flags = +lib_deps_builtin = +lib_deps_external = +lib_ldf_mode = chain+ + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino index d781780..b1ff4aa 100644 --- a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino index b7881ee..27cbf96 100644 --- a/lib/IRremoteESP8266/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino @@ -23,9 +23,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini index eeb8d1f..e11c48d 100644 --- a/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini @@ -6,11 +6,23 @@ src_dir=. build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/keywords.txt b/lib/IRremoteESP8266/keywords.txt index f40cbdc..532d54c 100644 --- a/lib/IRremoteESP8266/keywords.txt +++ b/lib/IRremoteESP8266/keywords.txt @@ -22,9 +22,12 @@ IRArgoAC KEYWORD1 IRCoolixAC KEYWORD1 +IRDaikin160 KEYWORD1 IRDaikin2 KEYWORD1 +IRDaikin216 KEYWORD1 IRDaikinESP KEYWORD1 IRFujitsuAC KEYWORD1 +IRGoodweatherAc KEYWORD1 IRGreeAC KEYWORD1 IRHaierAC KEYWORD1 IRHaierACYRW02 KEYWORD1 @@ -32,17 +35,22 @@ IRHitachiAc KEYWORD1 IRKelvinatorAC KEYWORD1 IRMideaAC KEYWORD1 IRMitsubishiAC KEYWORD1 +IRMitsubishiHeavy152Ac KEYWORD1 +IRMitsubishiHeavy88Ac KEYWORD1 IRPanasonicAc KEYWORD1 IRSamsungAc KEYWORD1 +IRSharpAc KEYWORD1 IRTcl112Ac KEYWORD1 IRTecoAc KEYWORD1 IRToshibaAC KEYWORD1 IRTrotecESP KEYWORD1 IRVestelAc KEYWORD1 IRWhirlpoolAc KEYWORD1 +IRac KEYWORD1 IRrecv KEYWORD1 IRsend KEYWORD1 IRtimer KEYWORD1 +TimerMs KEYWORD1 decode_results KEYWORD1 ir_params_t KEYWORD1 match_result_t KEYWORD1 @@ -55,8 +63,9 @@ _delayMicroseconds KEYWORD2 _setMode KEYWORD2 _setTemp KEYWORD2 add KEYWORD2 -addbit KEYWORD2 +argo KEYWORD2 begin KEYWORD2 +boolToString KEYWORD2 buildFromState KEYWORD2 buildState KEYWORD2 calcBlockChecksum KEYWORD2 @@ -68,35 +77,45 @@ calibrate KEYWORD2 cancelOffTimer KEYWORD2 cancelOnTimer KEYWORD2 cancelTimers KEYWORD2 -checkheader KEYWORD2 +checkZjsSig KEYWORD2 +checkZmsSig KEYWORD2 checksum KEYWORD2 -clearBit KEYWORD2 clearOnTimerFlag KEYWORD2 clearSensorTemp KEYWORD2 clearSleepTimerFlag KEYWORD2 compare KEYWORD2 +coolix KEYWORD2 copyIrParams KEYWORD2 countBits KEYWORD2 +daikin KEYWORD2 +daikin2 KEYWORD2 +daikin216 KEYWORD2 decode KEYWORD2 decodeAiwaRCT501 KEYWORD2 +decodeArgo KEYWORD2 decodeCOOLIX KEYWORD2 decodeCarrierAC KEYWORD2 decodeDISH KEYWORD2 decodeDaikin KEYWORD2 +decodeDaikin160 KEYWORD2 decodeDaikin2 KEYWORD2 +decodeDaikin216 KEYWORD2 decodeDenon KEYWORD2 decodeElectraAC KEYWORD2 decodeFujitsuAC KEYWORD2 decodeGICable KEYWORD2 +decodeGoodweather KEYWORD2 decodeGree KEYWORD2 decodeHaierAC KEYWORD2 decodeHaierACYRW02 KEYWORD2 decodeHash KEYWORD2 decodeHitachiAC KEYWORD2 +decodeInax KEYWORD2 decodeJVC KEYWORD2 decodeKelvinator KEYWORD2 decodeLG KEYWORD2 decodeLasertag KEYWORD2 +decodeLegoPf KEYWORD2 decodeLutron KEYWORD2 decodeMWM KEYWORD2 decodeMagiQuest KEYWORD2 @@ -104,6 +123,7 @@ decodeMidea KEYWORD2 decodeMitsubishi KEYWORD2 decodeMitsubishi2 KEYWORD2 decodeMitsubishiAC KEYWORD2 +decodeMitsubishiHeavy KEYWORD2 decodeNEC KEYWORD2 decodeNikai KEYWORD2 decodePanasonic KEYWORD2 @@ -118,10 +138,12 @@ decodeSamsungAC KEYWORD2 decodeSanyo KEYWORD2 decodeSanyoLC7461 KEYWORD2 decodeSharp KEYWORD2 +decodeSharpAc KEYWORD2 decodeSony KEYWORD2 decodeTcl112Ac KEYWORD2 decodeTeco KEYWORD2 decodeToshibaAC KEYWORD2 +decodeTrotec KEYWORD2 decodeVestelAc KEYWORD2 decodeWhirlpoolAC KEYWORD2 decodeWhynter KEYWORD2 @@ -150,30 +172,33 @@ encodeSanyoLC7461 KEYWORD2 encodeSharp KEYWORD2 encodeSony KEYWORD2 encodeTime KEYWORD2 +fanspeed_t KEYWORD2 fixChecksum KEYWORD2 fixup KEYWORD2 +fujitsu KEYWORD2 +get3D KEYWORD2 getBeep KEYWORD2 -getBit KEYWORD2 getBufSize KEYWORD2 getButton KEYWORD2 getClean KEYWORD2 getClock KEYWORD2 getCmd KEYWORD2 +getComfort KEYWORD2 getCommand KEYWORD2 -getCoolMode KEYWORD2 getCorrectedRawLength KEYWORD2 getCurrTime KEYWORD2 +getCurrentDay KEYWORD2 getCurrentTime KEYWORD2 getEcono KEYWORD2 getEye KEYWORD2 getEyeAuto KEYWORD2 getFan KEYWORD2 getFanSpeed KEYWORD2 +getFilter KEYWORD2 getFlap KEYWORD2 getFreshAir KEYWORD2 getFreshAirHigh KEYWORD2 getHealth KEYWORD2 -getHeatMode KEYWORD2 getIon KEYWORD2 getIonFilter KEYWORD2 getLed KEYWORD2 @@ -189,6 +214,7 @@ getOffTimerEnabled KEYWORD2 getOnTime KEYWORD2 getOnTimer KEYWORD2 getOnTimerEnabled KEYWORD2 +getOutsideQuiet KEYWORD2 getPower KEYWORD2 getPowerToggle KEYWORD2 getPowerful KEYWORD2 @@ -196,8 +222,10 @@ getPurify KEYWORD2 getQuiet KEYWORD2 getRClevel KEYWORD2 getRaw KEYWORD2 +getRoomTemp KEYWORD2 getSensor KEYWORD2 getSensorTemp KEYWORD2 +getSilent KEYWORD2 getSleep KEYWORD2 getSleepTime KEYWORD2 getSleepTimerEnabled KEYWORD2 @@ -218,19 +246,28 @@ getTime KEYWORD2 getTimer KEYWORD2 getTurbo KEYWORD2 getVane KEYWORD2 +getWeeklyTimerEnable KEYWORD2 getXFan KEYWORD2 getZoneFollow KEYWORD2 getiFeel KEYWORD2 +goodweather KEYWORD2 +gree KEYWORD2 +haier KEYWORD2 +haierYrwo2 KEYWORD2 hasACState KEYWORD2 +hitachi KEYWORD2 +htmlEscape KEYWORD2 invertBits KEYWORD2 isOffTimerActive KEYWORD2 isOffTimerEnabled KEYWORD2 isOnTimerActive KEYWORD2 isOnTimerEnabled KEYWORD2 +isProtocolSupported KEYWORD2 isSpecialState KEYWORD2 isTimeCommand KEYWORD2 isTimerActive KEYWORD2 isTimerEnabled KEYWORD2 +kelvinator KEYWORD2 ledOff KEYWORD2 ledOn KEYWORD2 mark KEYWORD2 @@ -239,27 +276,36 @@ matchAtLeast KEYWORD2 matchData KEYWORD2 matchMark KEYWORD2 matchSpace KEYWORD2 +midea KEYWORD2 +mitsubishi KEYWORD2 +mitsubishiHeavy152 KEYWORD2 +mitsubishiHeavy88 KEYWORD2 off KEYWORD2 on KEYWORD2 -printState KEYWORD2 -readbits KEYWORD2 +opmode_t KEYWORD2 +panasonic KEYWORD2 recoverSavedState KEYWORD2 renderTime KEYWORD2 reset KEYWORD2 resultToHexidecimal KEYWORD2 resultToHumanReadableBasic KEYWORD2 +resultToRawArray KEYWORD2 resultToSourceCode KEYWORD2 resultToTimingInfo KEYWORD2 resume KEYWORD2 reverseBits KEYWORD2 +samsung KEYWORD2 send KEYWORD2 +sendAc KEYWORD2 sendAiwaRCT501 KEYWORD2 sendArgo KEYWORD2 sendCOOLIX KEYWORD2 sendCarrierAC KEYWORD2 sendDISH KEYWORD2 sendDaikin KEYWORD2 +sendDaikin160 KEYWORD2 sendDaikin2 KEYWORD2 +sendDaikin216 KEYWORD2 sendData KEYWORD2 sendDenon KEYWORD2 sendElectraAC KEYWORD2 @@ -268,17 +314,20 @@ sendFujitsuAC KEYWORD2 sendGC KEYWORD2 sendGICable KEYWORD2 sendGeneric KEYWORD2 +sendGoodweather KEYWORD2 sendGree KEYWORD2 sendHaierAC KEYWORD2 sendHaierACYRW02 KEYWORD2 sendHitachiAC KEYWORD2 sendHitachiAC1 KEYWORD2 sendHitachiAC2 KEYWORD2 +sendInax KEYWORD2 sendJVC KEYWORD2 sendKelvinator KEYWORD2 sendLG KEYWORD2 sendLG2 KEYWORD2 sendLasertag KEYWORD2 +sendLegoPf KEYWORD2 sendLutron KEYWORD2 sendMWM KEYWORD2 sendMagiQuest KEYWORD2 @@ -286,8 +335,12 @@ sendMidea KEYWORD2 sendMitsubishi KEYWORD2 sendMitsubishi2 KEYWORD2 sendMitsubishiAC KEYWORD2 +sendMitsubishiHeavy152 KEYWORD2 +sendMitsubishiHeavy88 KEYWORD2 sendNEC KEYWORD2 sendNikai KEYWORD2 +sendOff KEYWORD2 +sendOn KEYWORD2 sendPanasonic KEYWORD2 sendPanasonic64 KEYWORD2 sendPanasonicAC KEYWORD2 @@ -302,6 +355,7 @@ sendSamsung36 KEYWORD2 sendSamsungAC KEYWORD2 sendSanyoLC7461 KEYWORD2 sendSharp KEYWORD2 +sendSharpAc KEYWORD2 sendSharpRaw KEYWORD2 sendSherwood KEYWORD2 sendSony KEYWORD2 @@ -313,27 +367,28 @@ sendVestelAc KEYWORD2 sendWhirlpoolAC KEYWORD2 sendWhynter KEYWORD2 serialPrintUint64 KEYWORD2 +set3D KEYWORD2 setAuto KEYWORD2 setBeep KEYWORD2 -setBit KEYWORD2 setButton KEYWORD2 setClean KEYWORD2 setClock KEYWORD2 setCmd KEYWORD2 +setComfort KEYWORD2 setCommand KEYWORD2 -setCoolMode KEYWORD2 setCurrTime KEYWORD2 +setCurrentDay KEYWORD2 setCurrentTime KEYWORD2 setEcono KEYWORD2 setEye KEYWORD2 setEyeAuto KEYWORD2 setFan KEYWORD2 setFanSpeed KEYWORD2 +setFilter KEYWORD2 setFlap KEYWORD2 setFreshAir KEYWORD2 setFreshAirHigh KEYWORD2 setHealth KEYWORD2 -setHeatMode KEYWORD2 setIon KEYWORD2 setIonFilter KEYWORD2 setLed KEYWORD2 @@ -347,6 +402,7 @@ setOffTimer KEYWORD2 setOffTimerActive KEYWORD2 setOnTimer KEYWORD2 setOnTimerActive KEYWORD2 +setOutsideQuiet KEYWORD2 setPower KEYWORD2 setPowerToggle KEYWORD2 setPowerful KEYWORD2 @@ -357,6 +413,7 @@ setRoomTemp KEYWORD2 setSensor KEYWORD2 setSensorTemp KEYWORD2 setSensorTempRaw KEYWORD2 +setSilent KEYWORD2 setSleep KEYWORD2 setSpeed KEYWORD2 setStartClock KEYWORD2 @@ -373,30 +430,45 @@ setTimerActive KEYWORD2 setTurbo KEYWORD2 setUnknownThreshold KEYWORD2 setVane KEYWORD2 +setWeeklyTimerEnable KEYWORD2 setXFan KEYWORD2 setZoneFollow KEYWORD2 setiFeel KEYWORD2 +sharp KEYWORD2 space KEYWORD2 stateReset KEYWORD2 stepHoriz KEYWORD2 stepVert KEYWORD2 +strToBool KEYWORD2 +strToModel KEYWORD2 sumBytes KEYWORD2 +swingh_t KEYWORD2 +swingv_t KEYWORD2 +tcl112 KEYWORD2 +teco KEYWORD2 ticksHigh KEYWORD2 ticksLow KEYWORD2 timeToString KEYWORD2 toString KEYWORD2 toggleRC5 KEYWORD2 toggleRC6 KEYWORD2 +toggleSwingHoriz KEYWORD2 +toggleSwingVert KEYWORD2 +toshiba KEYWORD2 +trotec KEYWORD2 typeToString KEYWORD2 uint64ToString KEYWORD2 updateSavedState KEYWORD2 validChecksum KEYWORD2 +vestel KEYWORD2 +whirlpool KEYWORD2 xorBytes KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +// LITERAL1 AIWA_RC_T501 LITERAL1 AIWA_RC_T501_BITS LITERAL1 ALLOW_DELAY_CALLS LITERAL1 @@ -404,9 +476,9 @@ ARDB1 LITERAL1 ARGO LITERAL1 ARGO_COMMAND_LENGTH LITERAL1 ARGO_COOL_AUTO LITERAL1 +ARGO_COOL_HUM LITERAL1 ARGO_COOL_OFF LITERAL1 ARGO_COOL_ON LITERAL1 -ARGO_COOl_HUM LITERAL1 ARGO_FAN_1 LITERAL1 ARGO_FAN_2 LITERAL1 ARGO_FAN_3 LITERAL1 @@ -424,17 +496,20 @@ ARGO_HEAT_BLINK LITERAL1 ARGO_HEAT_ON LITERAL1 ARGO_MAX_TEMP LITERAL1 ARGO_MIN_TEMP LITERAL1 +ARJW2 LITERAL1 ARRAH2E LITERAL1 +ARREB1E LITERAL1 CARRIER_AC LITERAL1 CARRIER_AC_BITS LITERAL1 COOLIX LITERAL1 COOLIX_BITS LITERAL1 DAIKIN LITERAL1 +DAIKIN160 LITERAL1 DAIKIN2 LITERAL1 +DAIKIN216 LITERAL1 DAIKIN_AUTO LITERAL1 DAIKIN_COMMAND_LENGTH LITERAL1 DAIKIN_COOL LITERAL1 -DAIKIN_DEBUG LITERAL1 DAIKIN_DRY LITERAL1 DAIKIN_FAN LITERAL1 DAIKIN_FAN_AUTO LITERAL1 @@ -450,13 +525,16 @@ DECODE_ARGO LITERAL1 DECODE_CARRIER_AC LITERAL1 DECODE_COOLIX LITERAL1 DECODE_DAIKIN LITERAL1 +DECODE_DAIKIN160 LITERAL1 DECODE_DAIKIN2 LITERAL1 +DECODE_DAIKIN216 LITERAL1 DECODE_DENON LITERAL1 DECODE_DISH LITERAL1 DECODE_ELECTRA_AC LITERAL1 DECODE_FUJITSU_AC LITERAL1 DECODE_GICABLE LITERAL1 DECODE_GLOBALCACHE LITERAL1 +DECODE_GOODWEATHER LITERAL1 DECODE_GREE LITERAL1 DECODE_HAIER_AC LITERAL1 DECODE_HAIER_AC_YRW02 LITERAL1 @@ -464,15 +542,18 @@ DECODE_HASH LITERAL1 DECODE_HITACHI_AC LITERAL1 DECODE_HITACHI_AC1 LITERAL1 DECODE_HITACHI_AC2 LITERAL1 +DECODE_INAX LITERAL1 DECODE_JVC LITERAL1 DECODE_KELVINATOR LITERAL1 DECODE_LASERTAG LITERAL1 +DECODE_LEGOPF LITERAL1 DECODE_LG LITERAL1 DECODE_LUTRON LITERAL1 DECODE_MAGIQUEST LITERAL1 DECODE_MIDEA LITERAL1 DECODE_MITSUBISHI LITERAL1 DECODE_MITSUBISHI2 LITERAL1 +DECODE_MITSUBISHIHEAVY LITERAL1 DECODE_MITSUBISHI_AC LITERAL1 DECODE_MWM LITERAL1 DECODE_NEC LITERAL1 @@ -489,6 +570,7 @@ DECODE_SAMSUNG36 LITERAL1 DECODE_SAMSUNG_AC LITERAL1 DECODE_SANYO LITERAL1 DECODE_SHARP LITERAL1 +DECODE_SHARP_AC LITERAL1 DECODE_SHERWOOD LITERAL1 DECODE_SONY LITERAL1 DECODE_TCL112AC LITERAL1 @@ -537,6 +619,7 @@ FUJITSU_AC_SWING_VERT LITERAL1 GICABLE LITERAL1 GICABLE_BITS LITERAL1 GLOBALCACHE LITERAL1 +GOODWEATHER LITERAL1 GREE LITERAL1 GREE_AUTO LITERAL1 GREE_COOL LITERAL1 @@ -626,6 +709,7 @@ HITACHI_AC2_STATE_LENGTH LITERAL1 HITACHI_AC_BITS LITERAL1 HITACHI_AC_STATE_LENGTH LITERAL1 ICACHE_RAM_ATTR LITERAL1 +INAX LITERAL1 JVC LITERAL1 JVC_BITS LITERAL1 KELVINATOR LITERAL1 @@ -643,6 +727,7 @@ KELVINATOR_MIN_TEMP LITERAL1 KELVINATOR_STATE_LENGTH LITERAL1 LASERTAG LITERAL1 LASERTAG_BITS LITERAL1 +LEGOPF LITERAL1 LG LITERAL1 LG2 LITERAL1 LG32_BITS LITERAL1 @@ -686,6 +771,8 @@ MITSUBISHI_AC_STATE_LENGTH LITERAL1 MITSUBISHI_AC_VANE_AUTO LITERAL1 MITSUBISHI_AC_VANE_AUTO_MOVE LITERAL1 MITSUBISHI_BITS LITERAL1 +MITSUBISHI_HEAVY_152 LITERAL1 +MITSUBISHI_HEAVY_88 LITERAL1 MWM LITERAL1 NEC LITERAL1 NEC_BITS LITERAL1 @@ -722,28 +809,34 @@ SEND_ARGO LITERAL1 SEND_CARRIER_AC LITERAL1 SEND_COOLIX LITERAL1 SEND_DAIKIN LITERAL1 +SEND_DAIKIN160 LITERAL1 SEND_DAIKIN2 LITERAL1 +SEND_DAIKIN216 LITERAL1 SEND_DENON LITERAL1 SEND_DISH LITERAL1 SEND_ELECTRA_AC LITERAL1 SEND_FUJITSU_AC LITERAL1 SEND_GICABLE LITERAL1 SEND_GLOBALCACHE LITERAL1 +SEND_GOODWEATHER LITERAL1 SEND_GREE LITERAL1 SEND_HAIER_AC LITERAL1 SEND_HAIER_AC_YRW02 LITERAL1 SEND_HITACHI_AC LITERAL1 SEND_HITACHI_AC1 LITERAL1 SEND_HITACHI_AC2 LITERAL1 +SEND_INAX LITERAL1 SEND_JVC LITERAL1 SEND_KELVINATOR LITERAL1 SEND_LASERTAG LITERAL1 +SEND_LEGOPF LITERAL1 SEND_LG LITERAL1 SEND_LUTRON LITERAL1 SEND_MAGIQUEST LITERAL1 SEND_MIDEA LITERAL1 SEND_MITSUBISHI LITERAL1 SEND_MITSUBISHI2 LITERAL1 +SEND_MITSUBISHIHEAVY LITERAL1 SEND_MITSUBISHI_AC LITERAL1 SEND_MWM LITERAL1 SEND_NEC LITERAL1 @@ -761,6 +854,7 @@ SEND_SAMSUNG36 LITERAL1 SEND_SAMSUNG_AC LITERAL1 SEND_SANYO LITERAL1 SEND_SHARP LITERAL1 +SEND_SHARP_AC LITERAL1 SEND_SHERWOOD LITERAL1 SEND_SONY LITERAL1 SEND_TCL112AC LITERAL1 @@ -771,6 +865,7 @@ SEND_VESTEL_AC LITERAL1 SEND_WHIRLPOOL_AC LITERAL1 SEND_WHYNTER LITERAL1 SHARP LITERAL1 +SHARP_AC LITERAL1 SHARP_BITS LITERAL1 SHERWOOD LITERAL1 SHERWOOD_BITS LITERAL1 @@ -804,7 +899,6 @@ TROTEC_FAN_MED LITERAL1 TROTEC_MAX_TEMP LITERAL1 TROTEC_MAX_TIMER LITERAL1 TROTEC_MIN_TEMP LITERAL1 -TROTEC_MIN_TIMER LITERAL1 UNKNOWN LITERAL1 UNUSED LITERAL1 VESTEL_AC LITERAL1 @@ -817,16 +911,17 @@ kAiwaRcT501PostBits LITERAL1 kAiwaRcT501PostData LITERAL1 kAiwaRcT501PreBits LITERAL1 kAiwaRcT501PreData LITERAL1 +kArgoAuto LITERAL1 kArgoBitMark LITERAL1 -kArgoCoolAuto LITERAL1 -kArgoCoolHum LITERAL1 -kArgoCoolOff LITERAL1 -kArgoCoolOn LITERAL1 +kArgoBits LITERAL1 +kArgoCool LITERAL1 kArgoDefaultRepeat LITERAL1 +kArgoDry LITERAL1 kArgoFan1 LITERAL1 kArgoFan2 LITERAL1 kArgoFan3 LITERAL1 kArgoFanAuto LITERAL1 +kArgoFanMask LITERAL1 kArgoFlap1 LITERAL1 kArgoFlap2 LITERAL1 kArgoFlap3 LITERAL1 @@ -835,16 +930,31 @@ kArgoFlap5 LITERAL1 kArgoFlap6 LITERAL1 kArgoFlapAuto LITERAL1 kArgoFlapFull LITERAL1 +kArgoGap LITERAL1 kArgoHdrMark LITERAL1 kArgoHdrSpace LITERAL1 +kArgoHeat LITERAL1 kArgoHeatAuto LITERAL1 +kArgoHeatBit LITERAL1 kArgoHeatBlink LITERAL1 -kArgoHeatOn LITERAL1 +kArgoIFeelBit LITERAL1 +kArgoMaxBit LITERAL1 +kArgoMaxRoomTemp LITERAL1 kArgoMaxTemp LITERAL1 kArgoMinTemp LITERAL1 +kArgoModeMask LITERAL1 +kArgoNightBit LITERAL1 +kArgoOff LITERAL1 kArgoOneSpace LITERAL1 +kArgoPowerBit LITERAL1 +kArgoRoomTempHighMask LITERAL1 +kArgoRoomTempLowMask LITERAL1 kArgoStateLength LITERAL1 +kArgoTempHighMask LITERAL1 +kArgoTempLowMask LITERAL1 +kArgoTempOffset LITERAL1 kArgoZeroSpace LITERAL1 +kAuto LITERAL1 kCarrierAcBitMark LITERAL1 kCarrierAcBits LITERAL1 kCarrierAcGap LITERAL1 @@ -853,6 +963,7 @@ kCarrierAcHdrSpace LITERAL1 kCarrierAcMinRepeat LITERAL1 kCarrierAcOneSpace LITERAL1 kCarrierAcZeroSpace LITERAL1 +kCool LITERAL1 kCoolixAuto LITERAL1 kCoolixBitMark LITERAL1 kCoolixBitMarkTicks LITERAL1 @@ -902,6 +1013,44 @@ kCoolixUnknown LITERAL1 kCoolixZeroSpace LITERAL1 kCoolixZeroSpaceTicks LITERAL1 kCoolixZoneFollowMask LITERAL1 +kDaikin160BitMark LITERAL1 +kDaikin160Bits LITERAL1 +kDaikin160DefaultRepeat LITERAL1 +kDaikin160Freq LITERAL1 +kDaikin160Gap LITERAL1 +kDaikin160HdrMark LITERAL1 +kDaikin160HdrSpace LITERAL1 +kDaikin160OneSpace LITERAL1 +kDaikin160Section1Length LITERAL1 +kDaikin160Section2Length LITERAL1 +kDaikin160Sections LITERAL1 +kDaikin160StateLength LITERAL1 +kDaikin160ZeroSpace LITERAL1 +kDaikin216BitMark LITERAL1 +kDaikin216Bits LITERAL1 +kDaikin216ByteFan LITERAL1 +kDaikin216ByteMode LITERAL1 +kDaikin216BytePower LITERAL1 +kDaikin216BytePowerful LITERAL1 +kDaikin216ByteSwingH LITERAL1 +kDaikin216ByteSwingV LITERAL1 +kDaikin216ByteTemp LITERAL1 +kDaikin216DefaultRepeat LITERAL1 +kDaikin216Freq LITERAL1 +kDaikin216Gap LITERAL1 +kDaikin216HdrMark LITERAL1 +kDaikin216HdrSpace LITERAL1 +kDaikin216MaskFan LITERAL1 +kDaikin216MaskMode LITERAL1 +kDaikin216MaskSwingH LITERAL1 +kDaikin216MaskSwingV LITERAL1 +kDaikin216MaskTemp LITERAL1 +kDaikin216OneSpace LITERAL1 +kDaikin216Section1Length LITERAL1 +kDaikin216Section2Length LITERAL1 +kDaikin216Sections LITERAL1 +kDaikin216StateLength LITERAL1 +kDaikin216ZeroSpace LITERAL1 kDaikin2BeepMask LITERAL1 kDaikin2BitClean LITERAL1 kDaikin2BitEye LITERAL1 @@ -910,6 +1059,7 @@ kDaikin2BitFreshAir LITERAL1 kDaikin2BitFreshAirHigh LITERAL1 kDaikin2BitMark LITERAL1 kDaikin2BitMold LITERAL1 +kDaikin2BitPower LITERAL1 kDaikin2BitPurify LITERAL1 kDaikin2BitSleepTimer LITERAL1 kDaikin2Bits LITERAL1 @@ -921,6 +1071,7 @@ kDaikin2HdrSpace LITERAL1 kDaikin2LeaderMark LITERAL1 kDaikin2LeaderSpace LITERAL1 kDaikin2LightMask LITERAL1 +kDaikin2MinCoolTemp LITERAL1 kDaikin2OneSpace LITERAL1 kDaikin2Section1Length LITERAL1 kDaikin2Section2Length LITERAL1 @@ -939,6 +1090,7 @@ kDaikinAuto LITERAL1 kDaikinBeepLoud LITERAL1 kDaikinBeepOff LITERAL1 kDaikinBeepQuiet LITERAL1 +kDaikinBitComfort LITERAL1 kDaikinBitEcono LITERAL1 kDaikinBitEye LITERAL1 kDaikinBitMark LITERAL1 @@ -949,16 +1101,32 @@ kDaikinBitPower LITERAL1 kDaikinBitPowerful LITERAL1 kDaikinBitSensor LITERAL1 kDaikinBitSilent LITERAL1 +kDaikinBitWeeklyTimer LITERAL1 kDaikinBits LITERAL1 +kDaikinBitsShort LITERAL1 +kDaikinByteChecksum1 LITERAL1 +kDaikinByteChecksum2 LITERAL1 +kDaikinByteChecksum3 LITERAL1 +kDaikinByteClockMinsHigh LITERAL1 +kDaikinByteClockMinsLow LITERAL1 +kDaikinByteComfort LITERAL1 kDaikinByteEcono LITERAL1 kDaikinByteEye LITERAL1 +kDaikinByteFan LITERAL1 kDaikinByteMold LITERAL1 kDaikinByteOffTimer LITERAL1 +kDaikinByteOffTimerMinsHigh LITERAL1 +kDaikinByteOffTimerMinsLow LITERAL1 kDaikinByteOnTimer LITERAL1 +kDaikinByteOnTimerMinsHigh LITERAL1 +kDaikinByteOnTimerMinsLow LITERAL1 kDaikinBytePower LITERAL1 kDaikinBytePowerful LITERAL1 kDaikinByteSensor LITERAL1 kDaikinByteSilent LITERAL1 +kDaikinByteSwingH LITERAL1 +kDaikinByteTemp LITERAL1 +kDaikinByteWeeklyTimer LITERAL1 kDaikinCool LITERAL1 kDaikinCurBit LITERAL1 kDaikinCurIndex LITERAL1 @@ -973,6 +1141,7 @@ kDaikinFirstHeader64 LITERAL1 kDaikinGap LITERAL1 kDaikinHdrMark LITERAL1 kDaikinHdrSpace LITERAL1 +kDaikinHeaderLength LITERAL1 kDaikinHeat LITERAL1 kDaikinLightBright LITERAL1 kDaikinLightDim LITERAL1 @@ -981,12 +1150,18 @@ kDaikinMarkExcess LITERAL1 kDaikinMaxTemp LITERAL1 kDaikinMinTemp LITERAL1 kDaikinOneSpace LITERAL1 -kDaikinRawBits LITERAL1 +kDaikinSection1Length LITERAL1 +kDaikinSection2Length LITERAL1 +kDaikinSection3Length LITERAL1 +kDaikinSections LITERAL1 kDaikinStateLength LITERAL1 +kDaikinStateLengthShort LITERAL1 kDaikinTolerance LITERAL1 kDaikinUnusedTime LITERAL1 kDaikinZeroSpace LITERAL1 +kDefaultESP32Timer LITERAL1 kDefaultMessageGap LITERAL1 +kDenon48Bits LITERAL1 kDenonBitMark LITERAL1 kDenonBitMarkTicks LITERAL1 kDenonBits LITERAL1 @@ -1019,6 +1194,7 @@ kDishRptSpaceTicks LITERAL1 kDishTick LITERAL1 kDishZeroSpace LITERAL1 kDishZeroSpaceTicks LITERAL1 +kDry LITERAL1 kDutyDefault LITERAL1 kDutyMax LITERAL1 kElectraAcBitMark LITERAL1 @@ -1029,14 +1205,19 @@ kElectraAcMessageGap LITERAL1 kElectraAcOneSpace LITERAL1 kElectraAcStateLength LITERAL1 kElectraAcZeroSpace LITERAL1 +kFan LITERAL1 kFnvBasis32 LITERAL1 kFnvPrime32 LITERAL1 kFooter LITERAL1 kFujitsuAcBitMark LITERAL1 kFujitsuAcBits LITERAL1 +kFujitsuAcCmdEcono LITERAL1 +kFujitsuAcCmdPowerful LITERAL1 kFujitsuAcCmdStayOn LITERAL1 kFujitsuAcCmdStepHoriz LITERAL1 kFujitsuAcCmdStepVert LITERAL1 +kFujitsuAcCmdToggleSwingHoriz LITERAL1 +kFujitsuAcCmdToggleSwingVert LITERAL1 kFujitsuAcCmdTurnOff LITERAL1 kFujitsuAcCmdTurnOn LITERAL1 kFujitsuAcFanAuto LITERAL1 @@ -1080,6 +1261,57 @@ kGlobalCacheMinUsec LITERAL1 kGlobalCacheRptIndex LITERAL1 kGlobalCacheRptStartIndex LITERAL1 kGlobalCacheStartIndex LITERAL1 +kGoodweatherAuto LITERAL1 +kGoodweatherBitCommand LITERAL1 +kGoodweatherBitFan LITERAL1 +kGoodweatherBitLight LITERAL1 +kGoodweatherBitMark LITERAL1 +kGoodweatherBitMode LITERAL1 +kGoodweatherBitPower LITERAL1 +kGoodweatherBitSleep LITERAL1 +kGoodweatherBitSwing LITERAL1 +kGoodweatherBitTemp LITERAL1 +kGoodweatherBitTurbo LITERAL1 +kGoodweatherBits LITERAL1 +kGoodweatherCmdAirFlow LITERAL1 +kGoodweatherCmdDownTemp LITERAL1 +kGoodweatherCmdFan LITERAL1 +kGoodweatherCmdHold LITERAL1 +kGoodweatherCmdLight LITERAL1 +kGoodweatherCmdMode LITERAL1 +kGoodweatherCmdPower LITERAL1 +kGoodweatherCmdSleep LITERAL1 +kGoodweatherCmdSwing LITERAL1 +kGoodweatherCmdTimer LITERAL1 +kGoodweatherCmdTurbo LITERAL1 +kGoodweatherCmdUpTemp LITERAL1 +kGoodweatherCommandMask LITERAL1 +kGoodweatherCool LITERAL1 +kGoodweatherDry LITERAL1 +kGoodweatherFan LITERAL1 +kGoodweatherFanAuto LITERAL1 +kGoodweatherFanHigh LITERAL1 +kGoodweatherFanLow LITERAL1 +kGoodweatherFanMask LITERAL1 +kGoodweatherFanMed LITERAL1 +kGoodweatherHdrMark LITERAL1 +kGoodweatherHdrSpace LITERAL1 +kGoodweatherHeat LITERAL1 +kGoodweatherLightMask LITERAL1 +kGoodweatherMinRepeat LITERAL1 +kGoodweatherModeMask LITERAL1 +kGoodweatherOneSpace LITERAL1 +kGoodweatherPowerMask LITERAL1 +kGoodweatherSleepMask LITERAL1 +kGoodweatherSwingFast LITERAL1 +kGoodweatherSwingMask LITERAL1 +kGoodweatherSwingOff LITERAL1 +kGoodweatherSwingSlow LITERAL1 +kGoodweatherTempMask LITERAL1 +kGoodweatherTempMax LITERAL1 +kGoodweatherTempMin LITERAL1 +kGoodweatherTurboMask LITERAL1 +kGoodweatherZeroSpace LITERAL1 kGreeAuto LITERAL1 kGreeBitMark LITERAL1 kGreeBits LITERAL1 @@ -1089,8 +1321,10 @@ kGreeCool LITERAL1 kGreeDefaultRepeat LITERAL1 kGreeDry LITERAL1 kGreeFan LITERAL1 +kGreeFanAuto LITERAL1 kGreeFanMask LITERAL1 kGreeFanMax LITERAL1 +kGreeFanMin LITERAL1 kGreeHdrMark LITERAL1 kGreeHdrSpace LITERAL1 kGreeHeat LITERAL1 @@ -1152,8 +1386,10 @@ kHaierAcMaxTemp LITERAL1 kHaierAcMaxTime LITERAL1 kHaierAcMinGap LITERAL1 kHaierAcMinTemp LITERAL1 +kHaierAcModeMask LITERAL1 kHaierAcOneSpace LITERAL1 kHaierAcPrefix LITERAL1 +kHaierAcSleepBit LITERAL1 kHaierAcSwingChg LITERAL1 kHaierAcSwingDown LITERAL1 kHaierAcSwingOff LITERAL1 @@ -1191,6 +1427,9 @@ kHaierAcYrw02TurboLow LITERAL1 kHaierAcYrw02TurboOff LITERAL1 kHaierAcZeroSpace LITERAL1 kHeader LITERAL1 +kHeat LITERAL1 +kHigh LITERAL1 +kHighest LITERAL1 kHitachiAc1Bits LITERAL1 kHitachiAc1HdrMark LITERAL1 kHitachiAc1HdrSpace LITERAL1 @@ -1218,6 +1457,15 @@ kHitachiAcOneSpace LITERAL1 kHitachiAcStateLength LITERAL1 kHitachiAcZeroSpace LITERAL1 kIdleState LITERAL1 +kInaxBitMark LITERAL1 +kInaxBits LITERAL1 +kInaxHdrMark LITERAL1 +kInaxHdrSpace LITERAL1 +kInaxMinGap LITERAL1 +kInaxMinRepeat LITERAL1 +kInaxOneSpace LITERAL1 +kInaxTick LITERAL1 +kInaxZeroSpace LITERAL1 kJvcBitMark LITERAL1 kJvcBitMarkTicks LITERAL1 kJvcBits LITERAL1 @@ -1292,6 +1540,16 @@ kLasertagMinRepeat LITERAL1 kLasertagMinSamples LITERAL1 kLasertagTick LITERAL1 kLasertagTolerance LITERAL1 +kLastDecodeType LITERAL1 +kLeft LITERAL1 +kLeftMax LITERAL1 +kLegoPfBitMark LITERAL1 +kLegoPfBits LITERAL1 +kLegoPfHdrSpace LITERAL1 +kLegoPfMinCommandLength LITERAL1 +kLegoPfMinRepeat LITERAL1 +kLegoPfOneSpace LITERAL1 +kLegoPfZeroSpace LITERAL1 kLg2BitMark LITERAL1 kLg2BitMarkTicks LITERAL1 kLg2HdrMark LITERAL1 @@ -1323,6 +1581,8 @@ kLgRptSpaceTicks LITERAL1 kLgTick LITERAL1 kLgZeroSpace LITERAL1 kLgZeroSpaceTicks LITERAL1 +kLow LITERAL1 +kLowest LITERAL1 kLutronBits LITERAL1 kLutronDelta LITERAL1 kLutronGap LITERAL1 @@ -1346,8 +1606,11 @@ kMagiquestBits LITERAL1 kMark LITERAL1 kMarkExcess LITERAL1 kMarkState LITERAL1 +kMax LITERAL1 kMaxAccurateUsecDelay LITERAL1 kMaxTimeoutMs LITERAL1 +kMedium LITERAL1 +kMiddle LITERAL1 kMideaACAuto LITERAL1 kMideaACChecksumMask LITERAL1 kMideaACCool LITERAL1 @@ -1384,6 +1647,7 @@ kMideaTick LITERAL1 kMideaTolerance LITERAL1 kMideaZeroSpace LITERAL1 kMideaZeroSpaceTicks LITERAL1 +kMin LITERAL1 kMitsubishi2BitMark LITERAL1 kMitsubishi2HdrMark LITERAL1 kMitsubishi2HdrSpace LITERAL1 @@ -1420,6 +1684,91 @@ kMitsubishiAcZeroSpace LITERAL1 kMitsubishiBitMark LITERAL1 kMitsubishiBitMarkTicks LITERAL1 kMitsubishiBits LITERAL1 +kMitsubishiHeavy152Bits LITERAL1 +kMitsubishiHeavy152FanAuto LITERAL1 +kMitsubishiHeavy152FanEcono LITERAL1 +kMitsubishiHeavy152FanHigh LITERAL1 +kMitsubishiHeavy152FanLow LITERAL1 +kMitsubishiHeavy152FanMax LITERAL1 +kMitsubishiHeavy152FanMed LITERAL1 +kMitsubishiHeavy152FanTurbo LITERAL1 +kMitsubishiHeavy152MinRepeat LITERAL1 +kMitsubishiHeavy152StateLength LITERAL1 +kMitsubishiHeavy152SwingHAuto LITERAL1 +kMitsubishiHeavy152SwingHLeft LITERAL1 +kMitsubishiHeavy152SwingHLeftMax LITERAL1 +kMitsubishiHeavy152SwingHLeftRight LITERAL1 +kMitsubishiHeavy152SwingHMask LITERAL1 +kMitsubishiHeavy152SwingHMiddle LITERAL1 +kMitsubishiHeavy152SwingHOff LITERAL1 +kMitsubishiHeavy152SwingHRight LITERAL1 +kMitsubishiHeavy152SwingHRightLeft LITERAL1 +kMitsubishiHeavy152SwingHRightMax LITERAL1 +kMitsubishiHeavy152SwingVAuto LITERAL1 +kMitsubishiHeavy152SwingVHigh LITERAL1 +kMitsubishiHeavy152SwingVHighest LITERAL1 +kMitsubishiHeavy152SwingVLow LITERAL1 +kMitsubishiHeavy152SwingVLowest LITERAL1 +kMitsubishiHeavy152SwingVMask LITERAL1 +kMitsubishiHeavy152SwingVMiddle LITERAL1 +kMitsubishiHeavy152SwingVOff LITERAL1 +kMitsubishiHeavy3DMask LITERAL1 +kMitsubishiHeavy88Bits LITERAL1 +kMitsubishiHeavy88CleanBit LITERAL1 +kMitsubishiHeavy88FanAuto LITERAL1 +kMitsubishiHeavy88FanEcono LITERAL1 +kMitsubishiHeavy88FanHigh LITERAL1 +kMitsubishiHeavy88FanLow LITERAL1 +kMitsubishiHeavy88FanMask LITERAL1 +kMitsubishiHeavy88FanMed LITERAL1 +kMitsubishiHeavy88FanTurbo LITERAL1 +kMitsubishiHeavy88MinRepeat LITERAL1 +kMitsubishiHeavy88StateLength LITERAL1 +kMitsubishiHeavy88SwingH3D LITERAL1 +kMitsubishiHeavy88SwingHAuto LITERAL1 +kMitsubishiHeavy88SwingHLeft LITERAL1 +kMitsubishiHeavy88SwingHLeftMax LITERAL1 +kMitsubishiHeavy88SwingHLeftRight LITERAL1 +kMitsubishiHeavy88SwingHMask LITERAL1 +kMitsubishiHeavy88SwingHMiddle LITERAL1 +kMitsubishiHeavy88SwingHOff LITERAL1 +kMitsubishiHeavy88SwingHRight LITERAL1 +kMitsubishiHeavy88SwingHRightLeft LITERAL1 +kMitsubishiHeavy88SwingHRightMax LITERAL1 +kMitsubishiHeavy88SwingVAuto LITERAL1 +kMitsubishiHeavy88SwingVHigh LITERAL1 +kMitsubishiHeavy88SwingVHighest LITERAL1 +kMitsubishiHeavy88SwingVLow LITERAL1 +kMitsubishiHeavy88SwingVLowest LITERAL1 +kMitsubishiHeavy88SwingVMask LITERAL1 +kMitsubishiHeavy88SwingVMaskByte5 LITERAL1 +kMitsubishiHeavy88SwingVMaskByte7 LITERAL1 +kMitsubishiHeavy88SwingVMiddle LITERAL1 +kMitsubishiHeavy88SwingVOff LITERAL1 +kMitsubishiHeavyAuto LITERAL1 +kMitsubishiHeavyBitMark LITERAL1 +kMitsubishiHeavyCleanBit LITERAL1 +kMitsubishiHeavyCool LITERAL1 +kMitsubishiHeavyDry LITERAL1 +kMitsubishiHeavyFan LITERAL1 +kMitsubishiHeavyFanMask LITERAL1 +kMitsubishiHeavyFilterBit LITERAL1 +kMitsubishiHeavyGap LITERAL1 +kMitsubishiHeavyHdrMark LITERAL1 +kMitsubishiHeavyHdrSpace LITERAL1 +kMitsubishiHeavyHeat LITERAL1 +kMitsubishiHeavyMaxTemp LITERAL1 +kMitsubishiHeavyMinTemp LITERAL1 +kMitsubishiHeavyModeMask LITERAL1 +kMitsubishiHeavyNightBit LITERAL1 +kMitsubishiHeavyOneSpace LITERAL1 +kMitsubishiHeavyPowerBit LITERAL1 +kMitsubishiHeavySigLength LITERAL1 +kMitsubishiHeavySilentBit LITERAL1 +kMitsubishiHeavyTempMask LITERAL1 +kMitsubishiHeavyZeroSpace LITERAL1 +kMitsubishiHeavyZjsSig LITERAL1 +kMitsubishiHeavyZmsSig LITERAL1 kMitsubishiMinCommandLength LITERAL1 kMitsubishiMinCommandLengthTicks LITERAL1 kMitsubishiMinGap LITERAL1 @@ -1464,6 +1813,7 @@ kNikaiTick LITERAL1 kNikaiZeroSpace LITERAL1 kNikaiZeroSpaceTicks LITERAL1 kNoRepeat LITERAL1 +kOff LITERAL1 kPanasonicAcAuto LITERAL1 kPanasonicAcBits LITERAL1 kPanasonicAcChecksumInit LITERAL1 @@ -1527,6 +1877,7 @@ kPanasonicMinGapTicks LITERAL1 kPanasonicNke LITERAL1 kPanasonicOneSpace LITERAL1 kPanasonicOneSpaceTicks LITERAL1 +kPanasonicRkr LITERAL1 kPanasonicTick LITERAL1 kPanasonicUnknown LITERAL1 kPanasonicZeroSpace LITERAL1 @@ -1584,6 +1935,8 @@ kRcmmRptLengthTicks LITERAL1 kRcmmTick LITERAL1 kRcmmTolerance LITERAL1 kRepeat LITERAL1 +kRight LITERAL1 +kRightMax LITERAL1 kSamsung36Bits LITERAL1 kSamsungACSectionLength LITERAL1 kSamsungAcAuto LITERAL1 @@ -1614,9 +1967,12 @@ kSamsungAcMinTemp LITERAL1 kSamsungAcModeMask LITERAL1 kSamsungAcOneSpace LITERAL1 kSamsungAcPowerMask1 LITERAL1 -kSamsungAcPowerMask2 LITERAL1 +kSamsungAcPowerMask6 LITERAL1 kSamsungAcPowerSection LITERAL1 -kSamsungAcQuietMask11 LITERAL1 +kSamsungAcPowerfulMask10 LITERAL1 +kSamsungAcPowerfulMask8 LITERAL1 +kSamsungAcQuietMask1 LITERAL1 +kSamsungAcQuietMask5 LITERAL1 kSamsungAcSectionGap LITERAL1 kSamsungAcSectionMark LITERAL1 kSamsungAcSectionSpace LITERAL1 @@ -1664,6 +2020,37 @@ kSanyoSa8650bHdrSpace LITERAL1 kSanyoSa8650bOneMark LITERAL1 kSanyoSa8650bRptLength LITERAL1 kSanyoSa8650bZeroMark LITERAL1 +kSharpAcAuto LITERAL1 +kSharpAcBitFanManual LITERAL1 +kSharpAcBitMark LITERAL1 +kSharpAcBitPower LITERAL1 +kSharpAcBitTempManual LITERAL1 +kSharpAcBits LITERAL1 +kSharpAcByteFan LITERAL1 +kSharpAcByteManual LITERAL1 +kSharpAcByteMode LITERAL1 +kSharpAcBytePower LITERAL1 +kSharpAcByteTemp LITERAL1 +kSharpAcCool LITERAL1 +kSharpAcDefaultRepeat LITERAL1 +kSharpAcDry LITERAL1 +kSharpAcFanAuto LITERAL1 +kSharpAcFanHigh LITERAL1 +kSharpAcFanMax LITERAL1 +kSharpAcFanMed LITERAL1 +kSharpAcFanMin LITERAL1 +kSharpAcGap LITERAL1 +kSharpAcHdrMark LITERAL1 +kSharpAcHdrSpace LITERAL1 +kSharpAcHeat LITERAL1 +kSharpAcMaskFan LITERAL1 +kSharpAcMaskMode LITERAL1 +kSharpAcMaskTemp LITERAL1 +kSharpAcMaxTemp LITERAL1 +kSharpAcMinTemp LITERAL1 +kSharpAcOneSpace LITERAL1 +kSharpAcStateLength LITERAL1 +kSharpAcZeroSpace LITERAL1 kSharpAddressBits LITERAL1 kSharpAddressMask LITERAL1 kSharpBitMark LITERAL1 @@ -1706,12 +2093,23 @@ kStartOffset LITERAL1 kStateSizeMax LITERAL1 kStopState LITERAL1 kTcl112AcAuto LITERAL1 +kTcl112AcBitEcono LITERAL1 +kTcl112AcBitHealth LITERAL1 +kTcl112AcBitLight LITERAL1 kTcl112AcBitMark LITERAL1 +kTcl112AcBitSwingH LITERAL1 +kTcl112AcBitSwingV LITERAL1 +kTcl112AcBitTurbo LITERAL1 kTcl112AcBits LITERAL1 kTcl112AcCool LITERAL1 kTcl112AcDefaultRepeat LITERAL1 kTcl112AcDry LITERAL1 kTcl112AcFan LITERAL1 +kTcl112AcFanAuto LITERAL1 +kTcl112AcFanHigh LITERAL1 +kTcl112AcFanLow LITERAL1 +kTcl112AcFanMask LITERAL1 +kTcl112AcFanMed LITERAL1 kTcl112AcGap LITERAL1 kTcl112AcHalfDegree LITERAL1 kTcl112AcHdrMark LITERAL1 @@ -1722,6 +2120,7 @@ kTcl112AcPowerMask LITERAL1 kTcl112AcStateLength LITERAL1 kTcl112AcTempMax LITERAL1 kTcl112AcTempMin LITERAL1 +kTcl112AcTolerance LITERAL1 kTcl112AcZeroSpace LITERAL1 kTecoAuto LITERAL1 kTecoBitMark LITERAL1 @@ -1774,6 +2173,8 @@ kToshibaAcOneSpace LITERAL1 kToshibaAcPower LITERAL1 kToshibaAcZeroSpace LITERAL1 kTrotecAuto LITERAL1 +kTrotecBitMark LITERAL1 +kTrotecBits LITERAL1 kTrotecCool LITERAL1 kTrotecDefTemp LITERAL1 kTrotecDefaultRepeat LITERAL1 @@ -1791,15 +2192,11 @@ kTrotecIntro2 LITERAL1 kTrotecMaxTemp LITERAL1 kTrotecMaxTimer LITERAL1 kTrotecMinTemp LITERAL1 -kTrotecMinTimer LITERAL1 -kTrotecOff LITERAL1 -kTrotecOn LITERAL1 -kTrotecOneMark LITERAL1 kTrotecOneSpace LITERAL1 -kTrotecSleepOn LITERAL1 +kTrotecPowerBit LITERAL1 +kTrotecSleepBit LITERAL1 kTrotecStateLength LITERAL1 -kTrotecTimerOn LITERAL1 -kTrotecZeroMark LITERAL1 +kTrotecTimerBit LITERAL1 kTrotecZeroSpace LITERAL1 kUnknownThreshold LITERAL1 kVestelAcAuto LITERAL1 @@ -1836,9 +2233,11 @@ kVestelAcOnTimerFlagOffset LITERAL1 kVestelAcOneSpace LITERAL1 kVestelAcPowerOffset LITERAL1 kVestelAcSleep LITERAL1 +kVestelAcStateDefault LITERAL1 kVestelAcSwing LITERAL1 kVestelAcSwingOffset LITERAL1 kVestelAcTempOffset LITERAL1 +kVestelAcTimeStateDefault LITERAL1 kVestelAcTimerFlagOffset LITERAL1 kVestelAcTolerance LITERAL1 kVestelAcTurbo LITERAL1 diff --git a/lib/IRremoteESP8266/library.json b/lib/IRremoteESP8266/library.json index 7d5b7c2..34d6b40 100644 --- a/lib/IRremoteESP8266/library.json +++ b/lib/IRremoteESP8266/library.json @@ -1,7 +1,7 @@ { "name": "IRremoteESP8266", - "version": "2.5.6", - "keywords": "infrared, ir, remote, esp8266", + "version": "2.6.2", + "keywords": "infrared, ir, remote, esp8266, esp32", "description": "Send and receive infrared signals with multiple protocols (ESP8266)", "repository": { @@ -40,5 +40,5 @@ } ], "frameworks": "arduino", - "platforms": "espressif8266" + "platforms": ["espressif8266", "espressif32"] } diff --git a/lib/IRremoteESP8266/library.properties b/lib/IRremoteESP8266/library.properties index 4c7e413..2e8a21b 100644 --- a/lib/IRremoteESP8266/library.properties +++ b/lib/IRremoteESP8266/library.properties @@ -1,9 +1,9 @@ name=IRremoteESP8266 -version=2.5.6 +version=2.6.2 author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto -sentence=Send and receive infrared signals with multiple protocols (ESP8266) -paragraph=This library enables you to send and receive infra-red signals on an ESP8266. +sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32) +paragraph=This library enables you to send and receive infra-red signals on an ESP8266 or an ESP32. category=Device Control url=https://github.com/markszabo/IRremoteESP8266 -architectures=esp8266 +architectures=esp8266,esp32 diff --git a/lib/IRremoteESP8266/platformio.ini b/lib/IRremoteESP8266/platformio.ini index 63c3781..1df7da8 100644 --- a/lib/IRremoteESP8266/platformio.ini +++ b/lib/IRremoteESP8266/platformio.ini @@ -6,11 +6,13 @@ src_dir = examples/IRrecvDumpV2 build_flags = lib_deps_builtin = lib_deps_external = +lib_ldf_mode = chain+ [env:nodemcuv2] platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} @@ -20,6 +22,17 @@ lib_deps = platform = espressif8266 framework = arduino board = d1_mini +lib_ldf_mode = ${common.lib_ldf_mode} +build_flags = ${common.build_flags} +lib_deps = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp new file mode 100644 index 0000000..f20e068 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRac.cpp @@ -0,0 +1,1292 @@ +// 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 off most common functionallity across all supported devices. + +#include "IRac.h" +#ifndef UNIT_TEST +#include +#endif +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRremoteESP8266.h" +#include "IRutils.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" + +IRac::IRac(uint8_t pin) { _pin = pin; } + +// Is the given protocol supported by the IRac class? +bool IRac::isProtocolSupported(const decode_type_t protocol) { + switch (protocol) { +#if SEND_ARGO + case decode_type_t::ARGO: +#endif +#if SEND_COOLIX + case decode_type_t::COOLIX: +#endif +#if SEND_DAIKIN + case decode_type_t::DAIKIN: +#endif +#if SEND_DAIKIN2 + case decode_type_t::DAIKIN2: +#endif +#if SEND_DAIKIN216 + case decode_type_t::DAIKIN216: +#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_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: +#endif +#if SEND_HITACHI_AC + case decode_type_t::HITACHI_AC: +#endif +#if SEND_KELVINATOR + case decode_type_t::KELVINATOR: +#endif +#if SEND_MIDEA + case decode_type_t::MIDEA: +#endif +#if SEND_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: +#endif +#if SEND_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: + case decode_type_t::MITSUBISHI_HEAVY_152: +#endif +#if SEND_PANASONIC_AC + case decode_type_t::PANASONIC_AC: +#endif +#if SEND_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: +#endif +#if SEND_SHARP_AC + case decode_type_t::SHARP_AC: +#endif +#if SEND_TCL112AC + case decode_type_t::TCL112AC: +#endif +#if SEND_TECO + case decode_type_t::TECO: +#endif +#if SEND_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: +#endif +#if SEND_TROTEC + case decode_type_t::TROTEC: +#endif +#if SEND_VESTEL_AC + case decode_type_t::VESTEL_AC: +#endif +#if SEND_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: +#endif + return true; + default: + return false; + } +} + +#if SEND_ARGO +void IRac::argo(IRArgoAC *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 int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + 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(); +} +#endif // SEND_ARGO + +#if SEND_COOLIX +void IRac::coolix(IRCoolixAC *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 light, const bool clean, + const int16_t sleep) { + 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(); + } + 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(); + } + // Power gets done last, as off has a special command. + ac->setPower(on); + ac->send(); +} +#endif // SEND_COOLIX + +#if SEND_DAIKIN +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->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_DAIKIN2 +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->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->setSwingHorizontal((int8_t)swingh >= 0); + ac->setQuiet(quiet); + ac->setLight(light); + ac->setPowerful(turbo); + ac->setEcono(econo); + ac->setPurify(filter); + ac->setMold(clean); + ac->setBeep(beep); + if (sleep > 0) ac->enableSleepTimer(sleep); + if (clock >= 0) ac->setCurrentTime(clock); + ac->send(); +} +#endif // SEND_DAIKIN2 + +#if SEND_DAIKIN216 +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->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_FUJITSU_AC +void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_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 econo) { + 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); + 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. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + } 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 +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->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 +void IRac::gree(IRGreeAC *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 clean, + const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); + ac->setLight(light); + ac->setTurbo(turbo); + ac->setXFan(clean); + 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->send(); +} +#endif // SEND_GREE + +#if SEND_HAIER_AC +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->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(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_AC_YRW02 +void IRac::haierYrwo2(IRHaierACYRW02 *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) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(ac->convertSwingV(swingv)); + // No Horizontal Swing setting available. + // No Quiet setting available. + 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 +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->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_KELVINATOR +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->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan((uint8_t)fan); // No conversion needed. + ac->setSwingVertical((int8_t)swingv >= 0); + 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_MIDEA +void IRac::midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees, true); // true means use Celsius. + ac->setFan(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 on this A/C is either on or off. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MIDEA + +#if SEND_MITSUBISHI_AC +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 bool quiet, const int16_t clock) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setVane(ac->convertSwingV(swingv)); + // No Horizontal swing setting available. + if (quiet) ac->setFan(kMitsubishiAcFanSilent); + // 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_MITSUBISHIHEAVY +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->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(); +} + +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->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_PANASONIC_AC +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 int16_t clock) { + 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); + // 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_PANASONIC_AC + +#if SEND_SAMSUNG_AC +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 bool quiet, const bool turbo, const bool clean, + const bool beep, const bool dopower) { + // dopower is for unit testing only. It should only ever be false in tests. + if (dopower) 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. + ac->setQuiet(quiet); + ac->setPowerful(turbo); + // No Light setting available. + // No Econo setting available. + // No Filter setting available. + ac->setClean(clean); + ac->setBeep(beep); + // No Sleep setting available. + // 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_SHARP_AC +void IRac::sharp(IRSharpAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(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 Econo setting available. + // No Filter setting available. + // No Clean setting available. + // 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)); + ac->send(); +} +#endif // SEND_SHARP_AC + +#if SEND_TCL112AC +void IRac::tcl112(IRTcl112Ac *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 light, const bool econo, + const bool filter) { + 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. + 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_TECO +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 int16_t sleep) { + 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. + // 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_TECO + +#if SEND_TOSHIBA_AC +void IRac::toshiba(IRToshibaAC *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(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. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_TOSHIBA_AC + +#if SEND_TROTEC +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->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_VESTEL_AC +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->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_WHIRLPOOL_AC +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->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 + +// Send A/C message for a given device using common A/C settings. +// Args: +// vendor: The type of A/C protocol to use. +// model: The specific model of A/C if supported/applicable. +// on: Should the unit be powered on? (or in some cases, toggled) +// mode: What operating mode should the unit perform? e.g. Cool, Heat etc. +// degrees: What temperature should the unit be set to? +// celsius: Use degrees Celsius, otherwise Fahrenheit. +// fan: Fan speed. +// The following args are all "if supported" by the underlying A/C classes. +// swingv: Control the vertical swing of the vanes. +// swingh: Control the horizontal swing of the vanes. +// quiet: Set the unit to quiet (fan) operation mode. +// turbo: Set the unit to turbo operating mode. e.g. Max fan & cooling etc. +// econo: Set the unit to economical operating mode. +// light: Turn on the display/LEDs etc. +// filter: Turn on any particle/ion/allergy filter etc. +// clean: Turn on any settings to reduce mold etc. (Not self-clean mode.) +// beep: Control if the unit beeps upon receiving commands. +// sleep: Nr. of mins of sleep mode, or use sleep mode. (< 0 means off.) +// clock: Nr. of mins past midnight to set the clock to. (< 0 means off.) +// Returns: +// boolean: True, if accepted/converted/attempted. 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) { + // Convert the temperature to Celsius. + float degC; + if (celsius) + degC = degrees; + else + degC = fahrenheitToCelsius(degrees); + bool on = power; + // A hack for Home Assistant, it appears to need/want an Off opmode. + if (mode == stdAc::opmode_t::kOff) on = false; + // Per vendor settings & setup. + switch (vendor) { +#if SEND_ARGO + case ARGO: + { + IRArgoAC ac(_pin); + argo(&ac, on, mode, degC, fan, swingv, turbo, sleep); + break; + } +#endif // SEND_DAIKIN +#if SEND_COOLIX + case COOLIX: + { + IRCoolixAC ac(_pin); + coolix(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN + case DAIKIN: + { + IRDaikinESP ac(_pin); + daikin(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, clean); + break; + } +#endif // SEND_DAIKIN +#if SEND_DAIKIN2 + case DAIKIN2: + { + IRDaikin2 ac(_pin); + daikin2(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, econo, filter, clean, beep, sleep, clock); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN216 + case DAIKIN216: + { + IRDaikin216 ac(_pin); + daikin216(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo); + break; + } +#endif // SEND_DAIKIN216 +#if SEND_FUJITSU_AC + case FUJITSU_AC: + { + IRFujitsuAC ac(_pin); + ac.begin(); + fujitsu(&ac, (fujitsu_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet, turbo, econo); + break; + } +#endif // SEND_FUJITSU_AC +#if SEND_GOODWEATHER + case GOODWEATHER: + { + IRGoodweatherAc ac(_pin); + ac.begin(); + goodweather(&ac, on, mode, degC, fan, swingv, turbo, light, sleep); + break; + } +#endif // SEND_GOODWEATHER +#if SEND_GREE + case GREE: + { + IRGreeAC ac(_pin); + ac.begin(); + gree(&ac, on, mode, degC, fan, swingv, light, turbo, clean, sleep); + break; + } +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + { + IRHaierAC ac(_pin); + ac.begin(); + haier(&ac, on, mode, degC, fan, swingv, filter, sleep, clock); + break; + } +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + { + IRHaierACYRW02 ac(_pin); + ac.begin(); + haierYrwo2(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep); + break; + } +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + { + IRHitachiAc ac(_pin); + ac.begin(); + hitachi(&ac, on, mode, degC, fan, swingv, swingh); + break; + } +#endif // SEND_HITACHI_AC +#if SEND_KELVINATOR + case KELVINATOR: + { + IRKelvinatorAC ac(_pin); + ac.begin(); + kelvinator(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, + light, filter, clean); + break; + } +#endif // SEND_KELVINATOR +#if SEND_MIDEA + case MIDEA: + { + IRMideaAC ac(_pin); + ac.begin(); + midea(&ac, on, mode, degC, fan, sleep); + break; + } +#endif // SEND_MIDEA +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + { + IRMitsubishiAC ac(_pin); + ac.begin(); + mitsubishi(&ac, on, mode, degC, fan, swingv, quiet, clock); + break; + } +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + { + IRMitsubishiHeavy88Ac ac(_pin); + ac.begin(); + mitsubishiHeavy88(&ac, on, mode, degC, fan, swingv, swingh, + turbo, econo, clean); + break; + } + case MITSUBISHI_HEAVY_152: + { + IRMitsubishiHeavy152Ac ac(_pin); + ac.begin(); + mitsubishiHeavy152(&ac, on, mode, degC, fan, swingv, swingh, + quiet, turbo, econo, filter, clean, sleep); + break; + } +#endif // SEND_MITSUBISHIHEAVY +#if SEND_PANASONIC_AC + case PANASONIC_AC: + { + IRPanasonicAc ac(_pin); + ac.begin(); + panasonic(&ac, (panasonic_ac_remote_model_t)model, on, mode, degC, fan, + swingv, swingh, quiet, turbo, clock); + break; + } +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + { + IRSamsungAc ac(_pin); + ac.begin(); + samsung(&ac, on, mode, degC, fan, swingv, quiet, turbo, clean, beep); + break; + } +#endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + case SHARP_AC: + { + IRSharpAc ac(_pin); + ac.begin(); + sharp(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + case TCL112AC: + { + IRTcl112Ac ac(_pin); + ac.begin(); + tcl112(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, econo, + filter); + break; + } +#endif // SEND_TCL112AC +#if SEND_TECO + case TECO: + { + IRTecoAc ac(_pin); + ac.begin(); + teco(&ac, on, mode, degC, fan, swingv, sleep); + break; + } +#endif // SEND_TECO +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + { + IRToshibaAC ac(_pin); + ac.begin(); + toshiba(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + { + IRTrotecESP ac(_pin); + ac.begin(); + trotec(&ac, on, mode, degC, fan, sleep); + break; + } +#endif // SEND_TROTEC +#if SEND_VESTEL_AC + case VESTEL_AC: + { + IRVestelAc ac(_pin); + ac.begin(); + vestel(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep, clock); + break; + } +#endif // SEND_VESTEL_AC +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + { + IRWhirlpoolAc ac(_pin); + ac.begin(); + whirlpool(&ac, (whirlpool_ac_remote_model_t)model, on, mode, degC, fan, + swingv, turbo, light, sleep, clock); + break; + } +#endif // SEND_WHIRLPOOL_AC + default: + return false; // Fail, didn't match anything. + } + return true; // Success. +} + +stdAc::opmode_t IRac::strToOpmode(const char *str, + const stdAc::opmode_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + return stdAc::opmode_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::opmode_t::kOff; + else if (!strcmp(str, "COOL") || !strcmp(str, "COOLING")) + return stdAc::opmode_t::kCool; + else if (!strcmp(str, "HEAT") || !strcmp(str, "HEATING")) + return stdAc::opmode_t::kHeat; + else if (!strcmp(str, "DRY") || !strcmp(str, "DRYING") || + !strcmp(str, "DEHUMIDIFY")) + return stdAc::opmode_t::kDry; + else if (!strcmp(str, "FAN") || !strcmp(str, "FANONLY") || + !strcmp(str, "FAN_ONLY")) + return stdAc::opmode_t::kFan; + else + return def; +} + +stdAc::fanspeed_t IRac::strToFanspeed(const char *str, + const stdAc::fanspeed_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + return stdAc::fanspeed_t::kAuto; + else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || + !strcmp(str, "LOWEST")) + return stdAc::fanspeed_t::kMin; + else if (!strcmp(str, "LOW")) + return stdAc::fanspeed_t::kLow; + else if (!strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "MID")) + return stdAc::fanspeed_t::kMedium; + else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + return stdAc::fanspeed_t::kHigh; + else if (!strcmp(str, "MAX") || !strcmp(str, "MAXIMUM") || + !strcmp(str, "HIGHEST")) + return stdAc::fanspeed_t::kMax; + else + return def; +} + +stdAc::swingv_t IRac::strToSwingV(const char *str, + const stdAc::swingv_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || + !strcmp(str, "ON") || !strcmp(str, "SWING")) + return stdAc::swingv_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::swingv_t::kOff; + else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || + !strcmp(str, "LOWEST") || !strcmp(str, "BOTTOM") || + !strcmp(str, "DOWN")) + return stdAc::swingv_t::kLowest; + else if (!strcmp(str, "LOW")) + return stdAc::swingv_t::kLow; + else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || + !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) + return stdAc::swingv_t::kMiddle; + else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + return stdAc::swingv_t::kHigh; + else if (!strcmp(str, "HIGHEST") || !strcmp(str, "MAX") || + !strcmp(str, "MAXIMUM") || !strcmp(str, "TOP") || + !strcmp(str, "UP")) + return stdAc::swingv_t::kHighest; + else + return def; +} + +stdAc::swingh_t IRac::strToSwingH(const char *str, + const stdAc::swingh_t def) { + if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC") || + !strcmp(str, "ON") || !strcmp(str, "SWING")) + return stdAc::swingh_t::kAuto; + else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + return stdAc::swingh_t::kOff; + else if (!strcmp(str, "LEFTMAX") || !strcmp(str, "LEFT MAX") || + !strcmp(str, "MAXLEFT") || !strcmp(str, "MAX LEFT") || + !strcmp(str, "FARLEFT") || !strcmp(str, "FAR LEFT")) + return stdAc::swingh_t::kLeftMax; + else if (!strcmp(str, "LEFT")) + return stdAc::swingh_t::kLeft; + else if (!strcmp(str, "MID") || !strcmp(str, "MIDDLE") || + !strcmp(str, "MED") || !strcmp(str, "MEDIUM") || + !strcmp(str, "CENTRE") || !strcmp(str, "CENTER")) + return stdAc::swingh_t::kMiddle; + else if (!strcmp(str, "RIGHT")) + return stdAc::swingh_t::kRight; + else if (!strcmp(str, "RIGHTMAX") || !strcmp(str, "RIGHT MAX") || + !strcmp(str, "MAXRIGHT") || !strcmp(str, "MAX RIGHT") || + !strcmp(str, "FARRIGHT") || !strcmp(str, "FAR RIGHT")) + return stdAc::swingh_t::kRightMax; + else + return def; +} + +// Assumes str is upper case or an integer >= 1. +int16_t IRac::strToModel(const char *str, const int16_t def) { + // Fujitsu A/C models + if (!strcmp(str, "ARRAH2E")) { + return fujitsu_ac_remote_model_t::ARRAH2E; + } else if (!strcmp(str, "ARDB1")) { + return fujitsu_ac_remote_model_t::ARDB1; + // Panasonic A/C families + } else if (!strcmp(str, "LKE") || !strcmp(str, "PANASONICLKE")) { + return panasonic_ac_remote_model_t::kPanasonicLke; + } else if (!strcmp(str, "NKE") || !strcmp(str, "PANASONICNKE")) { + return panasonic_ac_remote_model_t::kPanasonicNke; + } else if (!strcmp(str, "DKE") || !strcmp(str, "PANASONICDKE")) { + return panasonic_ac_remote_model_t::kPanasonicDke; + } else if (!strcmp(str, "JKE") || !strcmp(str, "PANASONICJKE")) { + return panasonic_ac_remote_model_t::kPanasonicJke; + } else if (!strcmp(str, "CKP") || !strcmp(str, "PANASONICCKP")) { + return panasonic_ac_remote_model_t::kPanasonicCkp; + } else if (!strcmp(str, "RKR") || !strcmp(str, "PANASONICRKR")) { + return panasonic_ac_remote_model_t::kPanasonicRkr; + // Whirlpool A/C models + } else if (!strcmp(str, "DG11J13A") || !strcmp(str, "DG11J104") || + !strcmp(str, "DG11J1-04")) { + return whirlpool_ac_remote_model_t::DG11J13A; + } else if (!strcmp(str, "DG11J191")) { + return whirlpool_ac_remote_model_t::DG11J191; + } else { + int16_t number = atoi(str); + if (number > 0) + return number; + else + return def; + } +} + +// Assumes str is upper case. +bool IRac::strToBool(const char *str, const bool def) { + if (!strcmp(str, "ON") || !strcmp(str, "1") || !strcmp(str, "YES") || + !strcmp(str, "TRUE")) + return true; + else if (!strcmp(str, "OFF") || !strcmp(str, "0") || + !strcmp(str, "NO") || !strcmp(str, "FALSE")) + return false; + else + return def; +} + +String IRac::boolToString(const bool value) { + return value ? F("on") : F("off"); +} + +String IRac::opmodeToString(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kOff: + return F("off"); + case stdAc::opmode_t::kAuto: + return F("auto"); + case stdAc::opmode_t::kCool: + return F("cool"); + case stdAc::opmode_t::kHeat: + return F("heat"); + case stdAc::opmode_t::kDry: + return F("dry"); + case stdAc::opmode_t::kFan: + return F("fan_only"); + default: + return F("unknown"); + } +} + +String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: + return F("auto"); + case stdAc::fanspeed_t::kMax: + return F("max"); + case stdAc::fanspeed_t::kHigh: + return F("high"); + case stdAc::fanspeed_t::kMedium: + return F("medium"); + case stdAc::fanspeed_t::kLow: + return F("low"); + case stdAc::fanspeed_t::kMin: + return F("min"); + default: + return F("unknown"); + } +} + +String IRac::swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: + return F("off"); + case stdAc::swingv_t::kAuto: + return F("auto"); + case stdAc::swingv_t::kHighest: + return F("highest"); + case stdAc::swingv_t::kHigh: + return F("high"); + case stdAc::swingv_t::kMiddle: + return F("middle"); + case stdAc::swingv_t::kLow: + return F("low"); + case stdAc::swingv_t::kLowest: + return F("lowest"); + default: + return F("unknown"); + } +} + +String IRac::swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: + return F("off"); + case stdAc::swingh_t::kAuto: + return F("auto"); + case stdAc::swingh_t::kLeftMax: + return F("leftmax"); + case stdAc::swingh_t::kLeft: + return F("left"); + case stdAc::swingh_t::kMiddle: + return F("middle"); + case stdAc::swingh_t::kRight: + return F("right"); + case stdAc::swingh_t::kRightMax: + return F("rightmax"); + default: + return F("unknown"); + } +} diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h new file mode 100644 index 0000000..34fb370 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRac.h @@ -0,0 +1,244 @@ +#ifndef IRAC_H_ +#define IRAC_H_ + +// Copyright 2019 David Conran + +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Goodweather.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" + +class IRac { + public: + explicit IRac(uint8_t pin); + static bool isProtocolSupported(const decode_type_t protocol); + 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 strToBool(const char *str, const bool def = false); + static int16_t strToModel(const char *str, const int16_t def = -1); + 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 opmodeToString(const stdAc::opmode_t mode); + static String fanspeedToString(const stdAc::fanspeed_t speed); + static String swingvToString(const stdAc::swingv_t swingv); + static String swinghToString(const stdAc::swingh_t swingh); +#ifndef UNIT_TEST + + private: +#endif + uint8_t _pin; +#if SEND_ARGO + void argo(IRArgoAC *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 int16_t sleep = -1); +#endif // SEND_ARGO +#if SEND_COOLIX + void coolix(IRCoolixAC *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 light, const bool clean, + const int16_t sleep = -1); +#endif // SEND_COOLIX +#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_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_FUJITSU_AC + void fujitsu(IRFujitsuAC *ac, const fujitsu_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 econo); +#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 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 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_AC_YRW02 + void haierYrwo2(IRHaierACYRW02 *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); +#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_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_MIDEA + void midea(IRMideaAC *ac, + const bool on, const stdAc::opmode_t mode, const float degrees, + const stdAc::fanspeed_t fan, const int16_t sleep = -1); +#endif // SEND_MIDEA +#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 bool quiet, const int16_t clock = -1); +#endif // SEND_MITSUBISHI_AC +#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_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 int16_t clock = -1); +#endif // SEND_PANASONIC_AC +#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 bool quiet, const bool turbo, const bool clean, + const bool beep, const bool dopower = true); +#endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + void sharp(IRSharpAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + void tcl112(IRTcl112Ac *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 light, const bool econo, + const bool filter); +#endif // SEND_TCL112AC +#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 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); +#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_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_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 +}; // IRac class +#endif // IRAC_H_ diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp index c27222c..cc95033 100644 --- a/lib/IRremoteESP8266/src/IRrecv.cpp +++ b/lib/IRremoteESP8266/src/IRrecv.cpp @@ -1,15 +1,17 @@ // Copyright 2009 Ken Shirriff // Copyright 2015 Mark Szabo // Copyright 2015 Sebastien Warin -// Copyright 2017 David Conran +// Copyright 2017, 2019 David Conran #include "IRrecv.h" #include #ifndef UNIT_TEST +#if defined(ESP8266) extern "C" { #include #include } +#endif // ESP8266 #include #endif #include @@ -20,6 +22,20 @@ extern "C" { #undef ICACHE_RAM_ATTR #define ICACHE_RAM_ATTR #endif + +#ifndef USE_IRAM_ATTR +#if defined(ESP8266) +#define USE_IRAM_ATTR ICACHE_RAM_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/markszabo/IRremoteESP8266) for @@ -27,25 +43,47 @@ extern "C" { // Globals #ifndef UNIT_TEST +#if defined(ESP8266) static ETSTimer timer; -#endif +#endif // ESP8266 +#if defined(ESP32) +static hw_timer_t * timer = NULL; +#endif // ESP32 +#endif // UNIT_TEST + +#if defined(ESP32) +portMUX_TYPE irremote_mux = portMUX_INITIALIZER_UNLOCKED; +#endif // ESP32 volatile irparams_t irparams; irparams_t *irparams_save; // A copy of the interrupt state while decoding. #ifndef UNIT_TEST -static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) { +#if defined(ESP8266) +static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { os_intr_lock(); +#endif // ESP8266 +#if defined(ESP32) +static void USE_IRAM_ATTR read_timeout(void) { + portENTER_CRITICAL(&irremote_mux); +#endif // ESP32 if (irparams.rawlen) irparams.rcvstate = kStopState; +#if defined(ESP8266) os_intr_unlock(); +#endif // ESP8266 +#if defined(ESP32) + portEXIT_CRITICAL(&irremote_mux); +#endif // ESP32 } -static void ICACHE_RAM_ATTR gpio_intr() { - uint32_t now = system_get_time(); - uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); +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 @@ -74,8 +112,14 @@ static void ICACHE_RAM_ATTR gpio_intr() { irparams.rawlen++; start = now; -#define ONCE 0 + +#if defined(ESP8266) os_timer_arm(&timer, irparams.timeout, ONCE); +#endif // ESP8266 +#if defined(ESP32) + timerWrite(timer, 0); // Reset the timeout. + timerAlarmEnable(timer); +#endif // ESP32 } #endif // UNIT_TEST @@ -87,11 +131,21 @@ static void ICACHE_RAM_ATTR gpio_intr() { // bufsize: Nr. of entries to have in the capture buffer. (Default: kRawBuf) // timeout: Nr. of milli-Seconds of no signal before we stop capturing data. // (Default: kTimeoutMs) -// save_buffer: Use a second (save) buffer to decode from. (Def: false) +// save_buffer: Use a second (save) buffer to decode from. (Default: false) +// timer_num: Which ESP32 timer number to use? ESP32 only, otherwise unused. +// (Range: 0-3. Default: kDefaultESP32Timer) // Returns: // An IRrecv class object. -IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, - bool save_buffer) { +#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) { + // There are only 4 timers. 0 to 3. + _timer_num = std::min(timer_num, (uint8_t)3); +#else // ESP32 +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer) { +#endif // ESP32 irparams.recvpin = recvpin; irparams.bufsize = bufsize; // Ensure we are going to be able to store all possible values in the @@ -123,7 +177,7 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, irparams_save = NULL; } #if DECODE_HASH - unknown_threshold = kUnknownThreshold; + _unknown_threshold = kUnknownThreshold; #endif // DECODE_HASH } @@ -134,35 +188,57 @@ IRrecv::~IRrecv(void) { delete[] irparams_save->rawbuf; delete irparams_save; } + disableIRIn(); +#if defined(ESP32) + if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. +#endif // ESP32 } // initialization -void IRrecv::enableIRIn() { - // initialize state machine variables +void IRrecv::enableIRIn(void) { +#if defined(ESP32) + // Initialize the ESP32 timer. + timer = timerBegin(_timer_num, 80, true); // 80MHz / 80 = 1 uSec granularity. + // Set the timer so it only fires once, and set it's trigger in uSeconds. + timerAlarmWrite(timer, MS_TO_USEC(irparams.timeout), ONCE); + // Note: Interrupt needs to be attached before it can be enabled or disabled. + timerAttachInterrupt(timer, &read_timeout, true); +#endif // ESP32 + + // Initialize state machine variables resume(); #ifndef UNIT_TEST - // Initialize timer +#if defined(ESP8266) + // Initialize ESP8266 timer. os_timer_disarm(&timer); os_timer_setfn(&timer, reinterpret_cast(read_timeout), NULL); - +#endif // ESP8266 // Attach Interrupt attachInterrupt(irparams.recvpin, gpio_intr, CHANGE); -#endif +#endif // UNIT_TEST } -void IRrecv::disableIRIn() { +void IRrecv::disableIRIn(void) { #ifndef UNIT_TEST +#if defined(ESP8266) os_timer_disarm(&timer); +#endif // ESP8266 +#if defined(ESP32) + timerAlarmDisable(timer); +#endif // ESP32 detachInterrupt(irparams.recvpin); -#endif +#endif // UNIT_TEST } -void IRrecv::resume() { +void IRrecv::resume(void) { irparams.rcvstate = kIdleState; irparams.rawlen = 0; irparams.overflow = false; +#if defined(ESP32) + timerAlarmDisable(timer); +#endif // ESP32 } // Make a copy of the interrupt state & buffer data. @@ -196,12 +272,12 @@ void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { // Obtain the maximum number of entries possible in the capture buffer. // i.e. It's size. -uint16_t IRrecv::getBufSize() { return irparams.bufsize; } +uint16_t IRrecv::getBufSize(void) { return irparams.bufsize; } #if DECODE_HASH // Set the minimum length we will consider for reporting UNKNOWN message types. -void IRrecv::setUnknownThreshold(uint16_t length) { - unknown_threshold = length; +void IRrecv::setUnknownThreshold(const uint16_t length) { + _unknown_threshold = length; } #endif // DECODE_HASH @@ -335,7 +411,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { #if DECODE_DENON // Denon needs to precede Panasonic as it is a special case of Panasonic. DPRINTLN("Attempting Denon decode"); - if (decodeDenon(results, DENON_48_BITS) || decodeDenon(results, DENON_BITS) || + if (decodeDenon(results, kDenon48Bits) || decodeDenon(results, kDenonBits) || decodeDenon(results, kDenonLegacyBits)) return true; #endif @@ -402,6 +478,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting Daikin2 decode"); if (decodeDaikin2(results)) return true; #endif +#if DECODE_DAIKIN216 + DPRINTLN("Attempting Daikin216 decode"); + if (decodeDaikin216(results)) return true; +#endif #if DECODE_TOSHIBA_AC DPRINTLN("Attempting Toshiba AC decode"); if (decodeToshibaAC(results)) return true; @@ -509,6 +589,40 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting Teco decode"); if (decodeTeco(results)) return true; #endif +#if DECODE_LEGOPF + DPRINTLN("Attempting LEGOPF decode"); + if (decodeLegoPf(results)) return true; +#endif +#if DECODE_MITSUBISHIHEAVY + DPRINTLN("Attempting MITSUBISHIHEAVY (152 bit) decode"); + if (decodeMitsubishiHeavy(results, kMitsubishiHeavy152Bits)) return true; + DPRINTLN("Attempting MITSUBISHIHEAVY (88 bit) decode"); + if (decodeMitsubishiHeavy(results, kMitsubishiHeavy88Bits)) return true; +#endif +#if DECODE_ARGO + DPRINTLN("Attempting Argo decode"); + if (decodeArgo(results)) return true; +#endif // DECODE_ARGO +#if DECODE_SHARP_AC + DPRINTLN("Attempting SHARP_AC decode"); + if (decodeSharpAc(results)) return true; +#endif +#if DECODE_GOODWEATHER + DPRINTLN("Attempting GOODWEATHER decode"); + if (decodeGoodweather(results)) return true; +#endif // DECODE_GOODWEATHER +#if DECODE_INAX + DPRINTLN("Attempting Inax decode"); + if (decodeInax(results)) return true; +#endif // DECODE_INAX +#if DECODE_TROTEC + DPRINTLN("Attempting Trotec decode"); + if (decodeTrotec(results)) return true; +#endif // DECODE_TROTEC +#if DECODE_DAIKIN160 + DPRINTLN("Attempting Daikin160 decode"); + if (decodeDaikin160(results)) return true; +#endif // DECODE_DAIKIN160 #if DECODE_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. @@ -688,7 +802,7 @@ int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) { */ bool IRrecv::decodeHash(decode_results *results) { // Require at least some samples to prevent triggering on noise - if (results->rawlen < unknown_threshold) return false; + 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, diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 3d5b33f..d33dd96 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -51,6 +51,9 @@ const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1)); const uint32_t kFnvPrime32 = 16777619UL; const uint32_t kFnvBasis32 = 2166136261UL; +// Which of the ESP32 timers to use by default. (0-3) +const uint8_t kDefaultESP32Timer = 3; + #if DECODE_AC // Hitachi AC is the current largest state size. const uint16_t kStateSizeMax = kHitachiAc2StateLength; @@ -108,17 +111,24 @@ class decode_results { // main class for receiving IR class IRrecv { public: - explicit IRrecv(uint16_t recvpin, uint16_t bufsize = kRawBuf, - uint8_t timeout = kTimeoutMs, - bool save_buffer = false); // Constructor - ~IRrecv(); // Destructor +#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 bool decode(decode_results *results, irparams_t *save = NULL); - void enableIRIn(); - void disableIRIn(); - void resume(); - uint16_t getBufSize(); + void enableIRIn(void); + void disableIRIn(void); + void resume(void); + uint16_t getBufSize(void); #if DECODE_HASH - void setUnknownThreshold(uint16_t length); + void setUnknownThreshold(const uint16_t length); #endif static bool match(uint32_t measured, uint32_t desired, uint8_t tolerance = kTolerance, uint16_t delta = 0); @@ -133,8 +143,9 @@ class IRrecv { private: #endif irparams_t *irparams_save; + uint8_t _timer_num; #if DECODE_HASH - uint16_t unknown_threshold; + uint16_t _unknown_threshold; #endif // These are called by decode void copyIrParams(volatile irparams_t *src, irparams_t *dst); @@ -156,6 +167,10 @@ class IRrecv { bool decodeNEC(decode_results *results, uint16_t nbits = kNECBits, bool strict = true); #endif +#if DECODE_ARGO + bool decodeArgo(decode_results *results, const uint16_t nbits = kArgoBits, + const bool strict = true); +#endif // DECODE_ARGO #if DECODE_SONY bool decodeSony(decode_results *results, uint16_t nbits = kSonyMinBits, bool strict = false); @@ -181,6 +196,10 @@ class IRrecv { uint16_t nbits = kMitsubishiACBits, bool strict = false); #endif +#if DECODE_MITSUBISHIHEAVY + bool decodeMitsubishiHeavy(decode_results *results, const uint16_t nbits, + const bool strict = true); +#endif #if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG || DECODE_MWM) int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, uint16_t bitTime, uint8_t tolerance = kTolerance, @@ -200,21 +219,27 @@ class IRrecv { bool strict = false); #endif #if (DECODE_PANASONIC || DECODE_DENON) - bool decodePanasonic(decode_results *results, uint16_t nbits = kPanasonicBits, - bool strict = false, - uint32_t manufacturer = kPanasonicManufacturer); + bool decodePanasonic(decode_results *results, + 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 nbits = kLgBits, bool strict = false); #endif +#if DECODE_INAX + bool decodeInax(decode_results *results, const uint16_t nbits = kInaxBits, + const bool strict = true); +#endif // DECODE_INAX #if DECODE_JVC bool decodeJVC(decode_results *results, uint16_t nbits = kJvcBits, bool strict = true); #endif #if DECODE_SAMSUNG - bool decodeSAMSUNG(decode_results *results, uint16_t nbits = kSamsungBits, - bool strict = true); + bool decodeSAMSUNG(decode_results *results, + const uint16_t nbits = kSamsungBits, + const bool strict = true); #endif #if DECODE_SAMSUNG bool decodeSamsung36(decode_results *results, @@ -222,8 +247,9 @@ class IRrecv { const bool strict = true); #endif #if DECODE_SAMSUNG_AC - bool decodeSamsungAC(decode_results *results, uint16_t nbits = kSamsungAcBits, - bool strict = true); + bool decodeSamsungAC(decode_results *results, + const uint16_t nbits = kSamsungAcBits, + const bool strict = true); #endif #if DECODE_WHYNTER bool decodeWhynter(decode_results *results, uint16_t nbits = kWhynterBits, @@ -234,7 +260,7 @@ class IRrecv { bool strict = true); #endif #if DECODE_DENON - bool decodeDenon(decode_results *results, uint16_t nbits = DENON_BITS, + bool decodeDenon(decode_results *results, uint16_t nbits = kDenonBits, bool strict = true); #endif #if DECODE_DISH @@ -242,8 +268,13 @@ class IRrecv { bool strict = true); #endif #if (DECODE_SHARP || DECODE_DENON) - bool decodeSharp(decode_results *results, uint16_t nbits = kSharpBits, - bool strict = true, bool expansion = true); + bool decodeSharp(decode_results *results, const uint16_t nbits = kSharpBits, + const bool strict = true, const bool expansion = true); +#endif +#if DECODE_SHARP_AC + bool decodeSharpAc(decode_results *results, + const uint16_t nbits = kSharpAcBits, + const bool strict = true); #endif #if DECODE_AIWA_RC_T501 bool decodeAiwaRCT501(decode_results *results, @@ -262,17 +293,32 @@ class IRrecv { uint16_t nbits = kKelvinatorBits, bool strict = true); #endif #if DECODE_DAIKIN - bool decodeDaikin(decode_results *results, uint16_t nbits = kDaikinRawBits, - bool strict = true); + bool decodeDaikin(decode_results *results, const uint16_t nbits = kDaikinBits, + const bool strict = true); #endif +#if DECODE_DAIKIN160 + bool decodeDaikin160(decode_results *results, + const uint16_t nbits = kDaikin160Bits, + const bool strict = true); +#endif // DECODE_DAIKIN160 #if DECODE_DAIKIN2 bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits, bool strict = true); #endif +#if DECODE_DAIKIN216 + bool decodeDaikin216(decode_results *results, + const uint16_t nbits = kDaikin216Bits, + const bool strict = true); +#endif #if DECODE_TOSHIBA_AC bool decodeToshibaAC(decode_results *results, - uint16_t nbytes = kToshibaACBits, bool strict = true); + const uint16_t nbytes = kToshibaACBits, + const bool strict = true); #endif +#if DECODE_TROTEC + bool decodeTrotec(decode_results *results, const uint16_t nbits = kTrotecBits, + const bool strict = true); +#endif // DECODE_TROTEC #if DECODE_MIDEA bool decodeMidea(decode_results *results, uint16_t nbits = kMideaBits, bool strict = true); @@ -289,6 +335,11 @@ class IRrecv { bool decodeCarrierAC(decode_results *results, uint16_t nbits = kCarrierAcBits, bool strict = true); #endif +#if DECODE_GOODWEATHER + bool decodeGoodweather(decode_results *results, + const uint16_t nbits = kGoodweatherBits, + const bool strict = true); +#endif // DECODE_GOODWEATHER #if DECODE_GREE bool decodeGree(decode_results *results, uint16_t nbits = kGreeBits, bool strict = true); @@ -303,12 +354,14 @@ class IRrecv { bool strict = true); #endif #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) - bool decodeHitachiAC(decode_results *results, uint16_t nbits = kHitachiAcBits, - bool strict = true); + bool decodeHitachiAC(decode_results *results, + const uint16_t nbits = kHitachiAcBits, + const bool strict = true); #endif #if DECODE_HITACHI_AC1 bool decodeHitachiAC1(decode_results *results, - uint16_t nbits = kHitachiAc1Bits, bool strict = true); + const uint16_t nbits = kHitachiAc1Bits, + const bool strict = true); #endif #if DECODE_GICABLE bool decodeGICable(decode_results *results, uint16_t nbits = kGicableBits, @@ -316,7 +369,8 @@ class IRrecv { #endif #if DECODE_WHIRLPOOL_AC bool decodeWhirlpoolAC(decode_results *results, - uint16_t nbits = kWhirlpoolAcBits, bool strict = true); + const uint16_t nbits = kWhirlpoolAcBits, + const bool strict = true); #endif #if DECODE_LUTRON bool decodeLutron(decode_results *results, uint16_t nbits = kLutronBits, @@ -328,7 +382,8 @@ class IRrecv { #endif #if DECODE_PANASONIC_AC bool decodePanasonicAC(decode_results *results, - uint16_t nbits = kPanasonicAcBits, bool strict = true); + const uint16_t nbits = kPanasonicAcBits, + const bool strict = true); #endif #if DECODE_PIONEER bool decodePioneer(decode_results *results, @@ -340,16 +395,22 @@ class IRrecv { bool strict = true); #endif #if DECODE_VESTEL_AC - bool decodeVestelAc(decode_results *results, uint16_t nbits = kVestelAcBits, - bool strict = true); + bool decodeVestelAc(decode_results *results, + const uint16_t nbits = kVestelAcBits, + const bool strict = true); #endif #if DECODE_TCL112AC - bool decodeTcl112Ac(decode_results *results, uint16_t nbits = kTcl112AcBits, - bool strict = true); + bool decodeTcl112Ac(decode_results *results, + const uint16_t nbits = kTcl112AcBits, + const bool strict = true); #endif #if DECODE_TECO - bool decodeTeco(decode_results *results, uint16_t nbits = kTecoBits, - bool strict = false); + bool decodeTeco(decode_results *results, const uint16_t nbits = kTecoBits, + const bool strict = false); +#endif +#if DECODE_LEGOPF + bool decodeLegoPf(decode_results *results, const uint16_t nbits = kLegoPfBits, + const bool strict = true); #endif }; diff --git a/lib/IRremoteESP8266/src/IRremoteESP8266.h b/lib/IRremoteESP8266/src/IRremoteESP8266.h index b670ed7..8a7827d 100644 --- a/lib/IRremoteESP8266/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266/src/IRremoteESP8266.h @@ -47,10 +47,11 @@ #include #ifdef UNIT_TEST #include -#endif +#include +#endif // UNIT_TEST // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.5.6" +#define _IRREMOTEESP8266_VERSION_ "2.6.2" // 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! @@ -117,6 +118,9 @@ #define DECODE_SHARP true #define SEND_SHARP true +#define DECODE_SHARP_AC true +#define SEND_SHARP_AC true + #define DECODE_DENON true #define SEND_DENON true @@ -129,6 +133,9 @@ #define DECODE_FUJITSU_AC true #define SEND_FUJITSU_AC true +#define DECODE_INAX true +#define SEND_INAX true + #define DECODE_DAIKIN true #define SEND_DAIKIN true @@ -138,16 +145,19 @@ #define DECODE_GLOBALCACHE false // Not written. #define SEND_GLOBALCACHE true +#define DECODE_GOODWEATHER true +#define SEND_GOODWEATHER true + #define DECODE_GREE true #define SEND_GREE true #define DECODE_PRONTO false // Not written. #define SEND_PRONTO true -#define DECODE_ARGO false // Not written. +#define DECODE_ARGO true // Experimental #define SEND_ARGO true -#define DECODE_TROTEC false // Not implemented. +#define DECODE_TROTEC true #define SEND_TROTEC true #define DECODE_NIKAI true @@ -213,8 +223,20 @@ #define DECODE_TECO true #define SEND_TECO true -#define DECODE_TCL112AC true -#define SEND_TCL112AC true +#define DECODE_TCL112AC true +#define SEND_TCL112AC true + +#define DECODE_LEGOPF true +#define SEND_LEGOPF true + +#define DECODE_MITSUBISHIHEAVY true +#define SEND_MITSUBISHIHEAVY true + +#define DECODE_DAIKIN216 true +#define SEND_DAIKIN216 true + +#define DECODE_DAIKIN160 true +#define SEND_DAIKIN160 true #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ @@ -222,7 +244,8 @@ 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_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ + DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -299,6 +322,16 @@ enum decode_type_t { TECO, // (55) SAMSUNG36, TCL112AC, + LEGOPF, + MITSUBISHI_HEAVY_88, + MITSUBISHI_HEAVY_152, // 60 + DAIKIN216, + SHARP_AC, + GOODWEATHER, + INAX, + DAIKIN160, // 65 + // Add new entries before this one, and update it to point to the last entry. + kLastDecodeType = DAIKIN160, }; // Message lengths & required repeat values @@ -308,20 +341,28 @@ const uint16_t kSingleRepeat = 1; const uint16_t kAiwaRcT501Bits = 15; const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; const uint16_t kArgoStateLength = 12; +const uint16_t kArgoBits = kArgoStateLength * 8; const uint16_t kArgoDefaultRepeat = kNoRepeat; const uint16_t kCoolixBits = 24; -const uint16_t kCoolixDefaultRepeat = 1; +const uint16_t kCoolixDefaultRepeat = kSingleRepeat; const uint16_t kCarrierAcBits = 32; const uint16_t kCarrierAcMinRepeat = kNoRepeat; -// Daikin has a lot of static stuff that is discarded -const uint16_t kDaikinRawBits = 583; -const uint16_t kDaikinStateLength = 27; +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 kDaikin160StateLength = 20; +const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; +const uint16_t kDaikin160DefaultRepeat = kNoRepeat; +const uint16_t kDaikin216StateLength = 27; +const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; +const uint16_t kDaikin216DefaultRepeat = 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; @@ -334,6 +375,8 @@ 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 kGreeStateLength = 8; const uint16_t kGreeBits = kGreeStateLength * 8; const uint16_t kGreeDefaultRepeat = kNoRepeat; @@ -350,12 +393,16 @@ 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 kInaxBits = 24; +const uint16_t kInaxMinRepeat = kSingleRepeat; const uint16_t kJvcBits = 16; 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 kLutronBits = 35; @@ -369,6 +416,12 @@ const uint16_t kMitsubishiMinRepeat = kSingleRepeat; const uint16_t kMitsubishiACStateLength = 18; const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8; const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; +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 kNikaiBits = 24; const uint16_t kNECBits = 32; const uint16_t kPanasonicBits = 48; @@ -401,6 +454,9 @@ const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits + 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; @@ -417,12 +473,13 @@ const uint16_t kToshibaACStateLength = 9; const uint16_t kToshibaACBits = kToshibaACStateLength * 8; const uint16_t kToshibaACMinRepeat = kSingleRepeat; const uint16_t kTrotecStateLength = 9; +const uint16_t kTrotecBits = kTrotecStateLength * 8; const uint16_t kTrotecDefaultRepeat = kNoRepeat; const uint16_t kWhirlpoolAcStateLength = 21; const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; const uint16_t kWhynterBits = 32; -const uint8_t kVestelAcBits = 56; +const uint8_t kVestelAcBits = 56; // Legacy defines. (Deprecated) @@ -432,7 +489,7 @@ const uint8_t kVestelAcBits = 56; #define CARRIER_AC_BITS kCarrierAcBits #define DAIKIN_COMMAND_LENGTH kDaikinStateLength #define DENON_BITS kDenonBits -#define DENON_48_BITS kPanasonicBits +#define DENON_48_BITS kDenon48Bits #define DENON_LEGACY_BITS kDenonLegacyBits #define DISH_BITS kDishBits #define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat @@ -496,4 +553,15 @@ const uint8_t kVestelAcBits = 56; #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/markszabo/IRremoteESP8266/issues/667 +#define F(x) x +#endif // F +typedef std::string String; +#endif // UNIT_TEST + #endif // IRREMOTEESP8266_H_ diff --git a/lib/IRremoteESP8266/src/IRsend.cpp b/lib/IRremoteESP8266/src/IRsend.cpp index ad68fa0..987101c 100644 --- a/lib/IRremoteESP8266/src/IRsend.cpp +++ b/lib/IRremoteESP8266/src/IRsend.cpp @@ -110,6 +110,9 @@ void IRsend::enableIROut(uint32_t freq, uint8_t duty) { } 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; @@ -496,7 +499,8 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) { // nbits: How many bits long the message is to be. // Returns: // bool: True if it is a type we can attempt to send, false if not. -bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { +bool IRsend::send(const decode_type_t type, const uint64_t data, + const uint16_t nbits) { switch (type) { #if SEND_AIWA_RC_T501 case AIWA_RC_T501: @@ -533,6 +537,11 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { sendGree(data, nbits); break; #endif +#if SEND_INAX + case INAX: + sendInax(data, nbits); + break; +#endif // SEND_INAX #if SEND_JVC case JVC: sendJVC(data, nbits); @@ -543,6 +552,11 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { sendLasertag(data, nbits); break; #endif +#if SEND_LEGOPF + case LEGOPF: + sendLegoPf(data, nbits); + break; +#endif #if SEND_LG case LG: sendLG(data, nbits); @@ -662,3 +676,143 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { } return true; } + +// Send a complex (>= 64 bits) IR message of a given type. +// An unknown/unsupported type will do nothing. +// Args: +// type: Protocol number/type of the message you want to send. +// state: A pointer to the array of bytes that make up the state[]. +// nbytes: How many bytes are in the state. +// Returns: +// bool: True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const unsigned char *state, + const uint16_t nbytes) { + switch (type) { +#if SEND_ARGO + case ARGO: + sendArgo(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_DAIKIN + case DAIKIN: + sendDaikin(state, nbytes); + break; +#endif // SEND_DAIKIN +#if SEND_DAIKIN160 + case DAIKIN160: + sendDaikin160(state, nbytes); + break; +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN2 + case DAIKIN2: + sendDaikin2(state, nbytes); + break; +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + sendDaikin216(state, nbytes); + break; +#endif // SEND_DAIKIN216 +#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_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_KELVINATOR + case KELVINATOR: + sendKelvinator(state, nbytes); + break; +#endif // SEND_KELVINATOR +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + sendMitsubishiAC(state, nbytes); + break; +#endif // SEND_MITSUBISHI_AC +#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_PANASONIC_AC + case PANASONIC_AC: + sendPanasonicAC(state, nbytes); + break; +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + sendSamsungAC(state, nbytes); + break; +#endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + case SHARP_AC: + sendSharpAc(state, nbytes); + break; +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + case TCL112AC: + sendTcl112Ac(state, nbytes); + break; +#endif // SEND_TCL112AC +#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_WHIRLPOOL_AC + case WHIRLPOOL_AC: + sendWhirlpoolAC(state, nbytes); + break; +#endif // SEND_WHIRLPOOL_AC + default: + return false; + } + return true; +} diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index e446678..4ba9567 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -21,15 +21,87 @@ // Constants // Offset (in microseconds) to use in Period time calculations to account for // code excution time in producing the software PWM signal. -// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core +#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 = 1000000; +const uint32_t kDefaultMessageGap = 100000; + + +namespace stdAc { + enum class opmode_t { + kOff = -1, + kAuto = 0, + kCool = 1, + kHeat = 2, + kDry = 3, + kFan = 4, + }; + + enum class fanspeed_t { + kAuto = 0, + kMin = 1, + kLow = 2, + kMedium = 3, + kHigh = 4, + kMax = 5, + }; + + enum class swingv_t { + kOff = -1, + kAuto = 0, + kHighest = 1, + kHigh = 2, + kMiddle = 3, + kLow = 4, + kLowest = 5, + }; + + enum class swingh_t { + kOff = -1, + kAuto = 0, // a.k.a. On. + kLeftMax = 1, + kLeft = 2, + kMiddle = 3, + kRight = 4, + kRightMax = 5, + }; + + // Structure to hold a common A/C state. + typedef struct { + decode_type_t protocol; + int16_t model; + bool power; + stdAc::opmode_t mode; + float degrees; + bool celsius; + stdAc::fanspeed_t fanspeed; + stdAc::swingv_t swingv; + stdAc::swingh_t swingh; + bool quiet; + bool turbo; + bool econo; + bool light; + bool filter; + bool clean; + bool beep; + int16_t sleep; + int16_t clock; + } state_t; +}; // namespace stdAc // Classes class IRsend { @@ -68,7 +140,10 @@ class IRsend { const uint8_t *dataptr, const uint16_t nbytes, const uint16_t frequency, const bool MSBfirst, const uint16_t repeat, const uint8_t dutycycle); - bool send(decode_type_t type, uint64_t data, uint16_t nbits); + bool send(const decode_type_t type, const uint64_t data, + const uint16_t nbits); + 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) void sendNEC(uint64_t data, uint16_t nbits = kNECBits, uint16_t repeat = kNoRepeat); @@ -90,18 +165,18 @@ class IRsend { uint16_t repeat = kSherwoodMinRepeat); #endif #if SEND_SAMSUNG - void sendSAMSUNG(uint64_t data, uint16_t nbits = kSamsungBits, - uint16_t repeat = kNoRepeat); - uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command); + 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 #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(unsigned char data[], - uint16_t nbytes = kSamsungAcStateLength, - uint16_t repeat = kSamsungAcDefaultRepeat); + 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, @@ -111,21 +186,27 @@ class IRsend { uint32_t encodeLG(uint16_t address, uint16_t command); #endif #if (SEND_SHARP || SEND_DENON) - uint32_t encodeSharp(uint16_t address, uint16_t command, - uint16_t expansion = 1, uint16_t check = 0, - bool MSBfirst = false); - void sendSharp(uint16_t address, uint16_t command, - uint16_t nbits = kSharpBits, uint16_t repeat = kNoRepeat); - void sendSharpRaw(uint64_t data, uint16_t nbits = kSharpBits, - uint16_t repeat = kNoRepeat); + 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 = DENON_BITS, + void sendDenon(uint64_t data, uint16_t nbits = kDenonBits, uint16_t repeat = kNoRepeat); #endif #if SEND_SANYO @@ -142,13 +223,14 @@ class IRsend { uint16_t repeat = kDishMinRepeat); #endif #if (SEND_PANASONIC || SEND_DENON) - void sendPanasonic64(uint64_t data, uint16_t nbits = kPanasonicBits, - uint16_t repeat = kNoRepeat); - void sendPanasonic(uint16_t address, uint32_t data, - uint16_t nbits = kPanasonicBits, - uint16_t repeat = kNoRepeat); - uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device, - uint8_t subdevice, uint8_t function); + 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(uint64_t data, uint16_t nbits = kRC5XBits, @@ -187,59 +269,92 @@ class IRsend { uint16_t repeat = kMitsubishiMinRepeat); #endif #if SEND_MITSUBISHI_AC - void sendMitsubishiAC(unsigned char data[], - uint16_t nbytes = kMitsubishiACStateLength, - uint16_t repeat = kMitsubishiACMinRepeat); + 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(unsigned char data[], uint16_t nbytes, - uint16_t repeat = kFujitsuAcMinRepeat); + 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(unsigned char data[], - uint16_t nbytes = kKelvinatorStateLength, - uint16_t repeat = kKelvinatorDefaultRepeat); + void sendKelvinator(const unsigned char data[], + const uint16_t nbytes = kKelvinatorStateLength, + const uint16_t repeat = kKelvinatorDefaultRepeat); #endif #if SEND_DAIKIN - void sendDaikin(unsigned char data[], uint16_t nbytes = kDaikinStateLength, - uint16_t repeat = kDaikinDefaultRepeat); + void sendDaikin(const unsigned char data[], + const uint16_t nbytes = kDaikinStateLength, + const uint16_t repeat = kDaikinDefaultRepeat); #endif +#if SEND_DAIKIN160 + void sendDaikin160(const unsigned char data[], + const uint16_t nbytes = kDaikin160StateLength, + const uint16_t repeat = kDaikin160DefaultRepeat); +#endif // SEND_DAIKIN160 #if SEND_DAIKIN2 - void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength, - uint16_t repeat = kDaikin2DefaultRepeat); + void sendDaikin2(const unsigned char data[], + const uint16_t nbytes = kDaikin2StateLength, + const uint16_t repeat = kDaikin2DefaultRepeat); +#endif +#if SEND_DAIKIN216 + void sendDaikin216(const unsigned char data[], + const uint16_t nbytes = kDaikin216StateLength, + const uint16_t repeat = kDaikin216DefaultRepeat); #endif #if SEND_AIWA_RC_T501 void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, uint16_t repeat = kAiwaRcT501MinRepeats); #endif #if SEND_GREE - void sendGree(uint64_t data, uint16_t nbits = kGreeBits, - uint16_t repeat = kGreeDefaultRepeat); - void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength, - uint16_t repeat = kGreeDefaultRepeat); + 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_PRONTO void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); #endif #if SEND_ARGO - void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength, - uint16_t repeat = kArgoDefaultRepeat); + void sendArgo(const unsigned char data[], + const uint16_t nbytes = kArgoStateLength, + const uint16_t repeat = kArgoDefaultRepeat); #endif #if SEND_TROTEC - void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength, - uint16_t repeat = kTrotecDefaultRepeat); + void sendTrotec(const unsigned char data[], + const uint16_t nbytes = kTrotecStateLength, + const uint16_t repeat = kTrotecDefaultRepeat); #endif #if SEND_NIKAI void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, uint16_t repeat = kNoRepeat); #endif #if SEND_TOSHIBA_AC - void sendToshibaAC(unsigned char data[], - uint16_t nbytes = kToshibaACStateLength, - uint16_t repeat = kToshibaACMinRepeat); + void sendToshibaAC(const unsigned char data[], + const uint16_t nbytes = kToshibaACStateLength, + const uint16_t repeat = kToshibaACMinRepeat); #endif #if SEND_MIDEA void sendMidea(uint64_t data, uint16_t nbits = kMideaBits, @@ -259,51 +374,52 @@ class IRsend { uint16_t repeat = kCarrierAcMinRepeat); #endif #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) - void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength, - uint16_t repeat = kHaierAcDefaultRepeat); + void sendHaierAC(const unsigned char data[], + const uint16_t nbytes = kHaierACStateLength, + const uint16_t repeat = kHaierAcDefaultRepeat); #endif #if SEND_HAIER_AC_YRW02 - void sendHaierACYRW02(unsigned char data[], - uint16_t nbytes = kHaierACYRW02StateLength, - uint16_t repeat = kHaierAcYrw02DefaultRepeat); + void sendHaierACYRW02(const unsigned char data[], + const uint16_t nbytes = kHaierACYRW02StateLength, + const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif #if SEND_HITACHI_AC - void sendHitachiAC(unsigned char data[], - uint16_t nbytes = kHitachiAcStateLength, - uint16_t repeat = kHitachiAcDefaultRepeat); + void sendHitachiAC(const unsigned char data[], + const uint16_t nbytes = kHitachiAcStateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); #endif #if SEND_HITACHI_AC1 - void sendHitachiAC1(unsigned char data[], - uint16_t nbytes = kHitachiAc1StateLength, - uint16_t repeat = kNoRepeat); + void sendHitachiAC1(const unsigned char data[], + const uint16_t nbytes = kHitachiAc1StateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_HITACHI_AC2 - void sendHitachiAC2(unsigned char data[], - uint16_t nbytes = kHitachiAc2StateLength, - uint16_t repeat = kNoRepeat); + void sendHitachiAC2(const unsigned char data[], + const uint16_t nbytes = kHitachiAc2StateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_GICABLE void sendGICable(uint64_t data, uint16_t nbits = kGicableBits, uint16_t repeat = kGicableMinRepeat); #endif #if SEND_WHIRLPOOL_AC - void sendWhirlpoolAC(unsigned char data[], - uint16_t nbytes = kWhirlpoolAcStateLength, - uint16_t repeat = kWhirlpoolAcDefaultRepeat); + 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(unsigned char data[], - uint16_t nbytes = kElectraAcStateLength, - uint16_t repeat = kNoRepeat); + void sendElectraAC(const unsigned char data[], + const uint16_t nbytes = kElectraAcStateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_PANASONIC_AC - void sendPanasonicAC(unsigned char data[], - uint16_t nbytes = kPanasonicAcStateLength, - uint16_t repeat = kPanasonicAcDefaultRepeat); + void sendPanasonicAC(const unsigned char data[], + const uint16_t nbytes = kPanasonicAcStateLength, + const uint16_t repeat = kPanasonicAcDefaultRepeat); #endif #if SEND_PIONEER void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, @@ -311,8 +427,8 @@ class IRsend { uint64_t encodePioneer(uint16_t address, uint16_t command); #endif #if SEND_MWM - void sendMWM(unsigned char data[], uint16_t nbytes, - uint16_t repeat = kNoRepeat); + 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, @@ -324,8 +440,12 @@ class IRsend { const uint16_t repeat = kTcl112AcDefaultRepeat); #endif #if SEND_TECO - void sendTeco(uint64_t data, uint16_t nbits = kTecoBits, - uint16_t repeat = kNoRepeat); + 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 @@ -342,8 +462,12 @@ class IRsend { 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; diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp index 029637c..4173d76 100644 --- a/lib/IRremoteESP8266/src/IRtimer.cpp +++ b/lib/IRremoteESP8266/src/IRtimer.cpp @@ -7,12 +7,12 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. -extern uint32_t _IRtimer_unittest_now; +uint32_t _IRtimer_unittest_now = 0; +uint32_t _TimerMs_unittest_now = 0; #endif // UNIT_TEST // This class performs a simple time in useconds since instantiated. // Handles when the system timer wraps around (once). - IRtimer::IRtimer() { reset(); } void IRtimer::reset() { @@ -39,3 +39,32 @@ uint32_t IRtimer::elapsed() { #ifdef UNIT_TEST void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; } #endif // UNIT_TEST + +// This class performs a simple time in milli-seoncds since instantiated. +// Handles when the system timer wraps around (once). +TimerMs::TimerMs() { reset(); } + +void TimerMs::reset() { +#ifndef UNIT_TEST + start = millis(); +#else + start = _TimerMs_unittest_now; +#endif +} + +uint32_t TimerMs::elapsed() { +#ifndef UNIT_TEST + uint32_t now = millis(); +#else + uint32_t now = _TimerMs_unittest_now; +#endif + if (start <= now) // Check if the system timer has wrapped. + return now - start; // No wrap. + else + return UINT32_MAX - start + now; // Has wrapped. +} + +// Only used in unit testing. +#ifdef UNIT_TEST +void TimerMs::add(uint32_t msecs) { _IRtimer_unittest_now += msecs; } +#endif // UNIT_TEST diff --git a/lib/IRremoteESP8266/src/IRtimer.h b/lib/IRremoteESP8266/src/IRtimer.h index baca1cf..d00e1d0 100644 --- a/lib/IRremoteESP8266/src/IRtimer.h +++ b/lib/IRremoteESP8266/src/IRtimer.h @@ -20,4 +20,16 @@ class IRtimer { uint32_t start; }; +class TimerMs { + public: + TimerMs(); + void reset(); + uint32_t elapsed(); +#ifdef UNIT_TEST + static void add(uint32_t msecs); +#endif // UNIT_TEST + + private: + uint32_t start; +}; #endif // IRTIMER_H_ diff --git a/lib/IRremoteESP8266/src/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp index 3397b6b..e5c7ce7 100644 --- a/lib/IRremoteESP8266/src/IRutils.cpp +++ b/lib/IRremoteESP8266/src/IRutils.cpp @@ -7,6 +7,7 @@ #define __STDC_LIMIT_MACROS #include +#include #include #ifndef ARDUINO #include @@ -43,19 +44,19 @@ uint64_t reverseBits(uint64_t input, uint16_t nbits) { // Returns: // A string representation of the integer. // Note: Based on Arduino's Print::printNumber() -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String uint64ToString(uint64_t input, uint8_t base) { String result = ""; -#else -std::string uint64ToString(uint64_t input, uint8_t base) { - std::string result = ""; -#endif // 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; @@ -81,208 +82,437 @@ void serialPrintUint64(uint64_t input, uint8_t base) { } #endif +// Convert a c-style str to a decode_type_t +// Note: Assumes str is upper case. +// +// Args: +// str: An upper-case C-style string. +// Returns: +// A decode_type_t enum. +decode_type_t strToDecodeType(const char * const str) { + if (!strcmp(str, "UNKNOWN")) + return decode_type_t::UNKNOWN; + else if (!strcmp(str, "UNUSED")) + return decode_type_t::UNUSED; + else if (!strcmp(str, "AIWA_RC_T501")) + return decode_type_t::AIWA_RC_T501; + else if (!strcmp(str, "ARGO")) + return decode_type_t::ARGO; + else if (!strcmp(str, "CARRIER_AC")) + return decode_type_t::CARRIER_AC; + else if (!strcmp(str, "COOLIX")) + return decode_type_t::COOLIX; + else if (!strcmp(str, "DAIKIN")) + return decode_type_t::DAIKIN; + else if (!strcmp(str, "DAIKIN160")) + return decode_type_t::DAIKIN160; + else if (!strcmp(str, "DAIKIN2")) + return decode_type_t::DAIKIN2; + else if (!strcmp(str, "DAIKIN216")) + return decode_type_t::DAIKIN216; + else if (!strcmp(str, "DENON")) + return decode_type_t::DENON; + else if (!strcmp(str, "DISH")) + return decode_type_t::DISH; + else if (!strcmp(str, "ELECTRA_AC")) + return decode_type_t::ELECTRA_AC; + else if (!strcmp(str, "FUJITSU_AC")) + return decode_type_t::FUJITSU_AC; + else if (!strcmp(str, "GICABLE")) + return decode_type_t::GICABLE; + else if (!strcmp(str, "GLOBALCACHE")) + return decode_type_t::GLOBALCACHE; + else if (!strcmp(str, "GOODWEATHER")) + return decode_type_t::GOODWEATHER; + else if (!strcmp(str, "GREE")) + return decode_type_t::GREE; + else if (!strcmp(str, "HAIER_AC")) + return decode_type_t::HAIER_AC; + else if (!strcmp(str, "HAIER_AC_YRW02")) + return decode_type_t::HAIER_AC_YRW02; + else if (!strcmp(str, "HITACHI_AC")) + return decode_type_t::HITACHI_AC; + else if (!strcmp(str, "HITACHI_AC1")) + return decode_type_t::HITACHI_AC1; + else if (!strcmp(str, "HITACHI_AC2")) + return decode_type_t::HITACHI_AC2; + else if (!strcmp(str, "INAX")) + return decode_type_t::INAX; + else if (!strcmp(str, "JVC")) + return decode_type_t::JVC; + else if (!strcmp(str, "KELVINATOR")) + return decode_type_t::KELVINATOR; + else if (!strcmp(str, "LEGOPF")) + return decode_type_t::LEGOPF; + else if (!strcmp(str, "LG")) + return decode_type_t::LG; + else if (!strcmp(str, "LG2")) + return decode_type_t::LG2; + else if (!strcmp(str, "LASERTAG")) + return decode_type_t::LASERTAG; + else if (!strcmp(str, "LUTRON")) + return decode_type_t::LUTRON; + else if (!strcmp(str, "MAGIQUEST")) + return decode_type_t::MAGIQUEST; + else if (!strcmp(str, "MIDEA")) + return decode_type_t::MIDEA; + else if (!strcmp(str, "MITSUBISHI")) + return decode_type_t::MITSUBISHI; + else if (!strcmp(str, "MITSUBISHI2")) + return decode_type_t::MITSUBISHI2; + else if (!strcmp(str, "MITSUBISHI_AC")) + return decode_type_t::MITSUBISHI_AC; + else if (!strcmp(str, "MWM")) + return decode_type_t::MWM; + else if (!strcmp(str, "NEC") || !strcmp(str, "NEC (NON-STRICT")) + return decode_type_t::NEC; + else if (!strcmp(str, "NIKAI")) + return decode_type_t::NIKAI; + else if (!strcmp(str, "PANASONIC")) + return decode_type_t::PANASONIC; + else if (!strcmp(str, "PANASONIC_AC")) + return decode_type_t::PANASONIC_AC; + else if (!strcmp(str, "PIONEER")) + return decode_type_t::PIONEER; + else if (!strcmp(str, "PRONTO")) + return decode_type_t::PRONTO; + else if (!strcmp(str, "RAW")) + return decode_type_t::RAW; + else if (!strcmp(str, "RC5")) + return decode_type_t::RC5; + else if (!strcmp(str, "RC5X")) + return decode_type_t::RC5X; + else if (!strcmp(str, "RC6")) + return decode_type_t::RC6; + else if (!strcmp(str, "RCMM")) + return decode_type_t::RCMM; + else if (!strcmp(str, "SAMSUNG")) + return decode_type_t::SAMSUNG; + else if (!strcmp(str, "SAMSUNG36")) + return decode_type_t::SAMSUNG36; + else if (!strcmp(str, "SAMSUNG_AC")) + return decode_type_t::SAMSUNG_AC; + else if (!strcmp(str, "SANYO")) + return decode_type_t::SANYO; + else if (!strcmp(str, "SANYO_LC7461")) + return decode_type_t::SANYO_LC7461; + else if (!strcmp(str, "SHARP")) + return decode_type_t::SHARP; + else if (!strcmp(str, "SHARP_AC")) + return decode_type_t::SHARP_AC; + else if (!strcmp(str, "SHERWOOD")) + return decode_type_t::SHERWOOD; + else if (!strcmp(str, "SONY")) + return decode_type_t::SONY; + else if (!strcmp(str, "TCL112AC")) + return decode_type_t::TCL112AC; + else if (!strcmp(str, "TECO")) + return decode_type_t::TECO; + else if (!strcmp(str, "TOSHIBA_AC")) + return decode_type_t::TOSHIBA_AC; + else if (!strcmp(str, "TROTEC")) + return decode_type_t::TROTEC; + else if (!strcmp(str, "VESTEL_AC")) + return decode_type_t::VESTEL_AC; + else if (!strcmp(str, "WHIRLPOOL_AC")) + return decode_type_t::WHIRLPOOL_AC; + else if (!strcmp(str, "WHYNTER")) + return decode_type_t::WHYNTER; + // 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; + else + return decode_type_t::UNKNOWN; +} + +// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. +// Args: +// unescaped: A string containing text to make HTML safe. +// Returns: +// 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 protocol type (enum etc) to a human readable string. // Args: // protocol: Nr. (enum) of the protocol. // isRepeat: A flag indicating if it is a repeat message of the protocol. // Returns: // A string containing the protocol name. -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String typeToString(const decode_type_t protocol, const bool isRepeat) { String result = ""; -#else -std::string typeToString(const decode_type_t protocol, const bool isRepeat) { - std::string result = ""; -#endif switch (protocol) { - default: - case UNKNOWN: - result = "UNKNOWN"; - break; case UNUSED: - result = "UNUSED"; + result = F("UNUSED"); break; case AIWA_RC_T501: - result = "AIWA_RC_T501"; + result = F("AIWA_RC_T501"); break; case ARGO: - result = "ARGO"; + result = F("ARGO"); break; case CARRIER_AC: - result = "CARRIER_AC"; + result = F("CARRIER_AC"); break; case COOLIX: - result = "COOLIX"; + result = F("COOLIX"); break; case DAIKIN: - result = "DAIKIN"; + result = F("DAIKIN"); + break; + case DAIKIN160: + result = F("DAIKIN160"); break; case DAIKIN2: - result = "DAIKIN2"; + result = F("DAIKIN2"); + break; + case DAIKIN216: + result = F("DAIKIN216"); break; case DENON: - result = "DENON"; + result = F("DENON"); break; case DISH: - result = "DISH"; + result = F("DISH"); break; case ELECTRA_AC: - result = "ELECTRA_AC"; + result = F("ELECTRA_AC"); break; case FUJITSU_AC: - result = "FUJITSU_AC"; + result = F("FUJITSU_AC"); break; case GICABLE: - result = "GICABLE"; + result = F("GICABLE"); break; case GLOBALCACHE: - result = "GLOBALCACHE"; + result = F("GLOBALCACHE"); + break; + case GOODWEATHER: + result = F("GOODWEATHER"); break; case GREE: - result = "GREE"; + result = F("GREE"); break; case HAIER_AC: - result = "HAIER_AC"; + result = F("HAIER_AC"); break; case HAIER_AC_YRW02: - result = "HAIER_AC_YRW02"; + result = F("HAIER_AC_YRW02"); break; case HITACHI_AC: - result = "HITACHI_AC"; + result = F("HITACHI_AC"); break; case HITACHI_AC1: - result = "HITACHI_AC1"; + result = F("HITACHI_AC1"); break; case HITACHI_AC2: - result = "HITACHI_AC2"; + result = F("HITACHI_AC2"); + break; + case INAX: + result = F("INAX"); break; case JVC: - result = "JVC"; + result = F("JVC"); break; case KELVINATOR: - result = "KELVINATOR"; + result = F("KELVINATOR"); + break; + case LEGOPF: + result = F("LEGOPF"); break; case LG: - result = "LG"; + result = F("LG"); break; case LG2: - result = "LG2"; + result = F("LG2"); break; case LASERTAG: - result = "LASERTAG"; + result = F("LASERTAG"); break; case LUTRON: - result = "LUTRON"; + result = F("LUTRON"); break; case MAGIQUEST: - result = "MAGIQUEST"; + result = F("MAGIQUEST"); break; case MIDEA: - result = "MIDEA"; + result = F("MIDEA"); break; case MITSUBISHI: - result = "MITSUBISHI"; + result = F("MITSUBISHI"); break; case MITSUBISHI2: - result = "MITSUBISHI2"; + result = F("MITSUBISHI2"); break; case MITSUBISHI_AC: - result = "MITSUBISHI_AC"; + result = F("MITSUBISHI_AC"); + break; + case MITSUBISHI_HEAVY_88: + result = F("MITSUBISHI_HEAVY_88"); + break; + case MITSUBISHI_HEAVY_152: + result = F("MITSUBISHI_HEAVY_152"); break; case MWM: - result = "MWM"; + result = F("MWM"); break; case NEC: - result = "NEC"; + result = F("NEC"); break; case NEC_LIKE: - result = "NEC (non-strict)"; + result = F("NEC (non-strict)"); break; case NIKAI: - result = "NIKAI"; + result = F("NIKAI"); break; case PANASONIC: - result = "PANASONIC"; + result = F("PANASONIC"); break; case PANASONIC_AC: - result = "PANASONIC_AC"; + result = F("PANASONIC_AC"); break; case PIONEER: - result = "PIONEER"; + result = F("PIONEER"); break; case PRONTO: - result = "PRONTO"; + result = F("PRONTO"); break; case RAW: - result = "RAW"; + result = F("RAW"); break; case RC5: - result = "RC5"; + result = F("RC5"); break; case RC5X: - result = "RC5X"; + result = F("RC5X"); break; case RC6: - result = "RC6"; + result = F("RC6"); break; case RCMM: - result = "RCMM"; + result = F("RCMM"); break; case SAMSUNG: - result = "SAMSUNG"; + result = F("SAMSUNG"); break; case SAMSUNG36: - result = "SAMSUNG36"; + result = F("SAMSUNG36"); break; case SAMSUNG_AC: - result = "SAMSUNG_AC"; + result = F("SAMSUNG_AC"); break; case SANYO: - result = "SANYO"; + result = F("SANYO"); break; case SANYO_LC7461: - result = "SANYO_LC7461"; + result = F("SANYO_LC7461"); break; case SHARP: - result = "SHARP"; + result = F("SHARP"); + break; + case SHARP_AC: + result = F("SHARP_AC"); break; case SHERWOOD: - result = "SHERWOOD"; + result = F("SHERWOOD"); break; case SONY: - result = "SONY"; + result = F("SONY"); break; case TCL112AC: - result = "TCL112AC"; + result = F("TCL112AC"); break; case TECO: - result = "TECO"; + result = F("TECO"); break; case TOSHIBA_AC: - result = "TOSHIBA_AC"; + result = F("TOSHIBA_AC"); break; case TROTEC: - result = "TROTEC"; + result = F("TROTEC"); break; case VESTEL_AC: - result = "VESTEL_AC"; + result = F("VESTEL_AC"); break; case WHIRLPOOL_AC: - result = "WHIRLPOOL_AC"; + result = F("WHIRLPOOL_AC"); break; case WHYNTER: - result = "WHYNTER"; + result = F("WHYNTER"); + break; + case UNKNOWN: + default: + result = F("UNKNOWN"); break; } - if (isRepeat) result += " (Repeat)"; + if (isRepeat) result += F(" (Repeat)"); return result; } // Does the given protocol use a complex state as part of the decode? bool hasACState(const decode_type_t protocol) { switch (protocol) { + case ARGO: case DAIKIN: + case DAIKIN160: case DAIKIN2: + case DAIKIN216: case ELECTRA_AC: case FUJITSU_AC: case GREE: @@ -293,11 +523,15 @@ bool hasACState(const decode_type_t protocol) { case HITACHI_AC2: case KELVINATOR: case MITSUBISHI_AC: + case MITSUBISHI_HEAVY_88: + case MITSUBISHI_HEAVY_152: case MWM: case PANASONIC_AC: case SAMSUNG_AC: + case SHARP_AC: case TCL112AC: case TOSHIBA_AC: + case TROTEC: case WHIRLPOOL_AC: return true; default: @@ -311,7 +545,7 @@ bool hasACState(const decode_type_t protocol) { // results: A ptr to a decode result. // Returns: // A uint16_t containing the length. -uint16_t getCorrectedRawLength(const decode_results *results) { +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; @@ -323,19 +557,16 @@ uint16_t getCorrectedRawLength(const decode_results *results) { // Return a string containing the key values of a decode_results structure // in a C/C++ code style format. -#ifdef ARDUINO -String resultToSourceCode(const decode_results *results) { +String resultToSourceCode(const decode_results * const results) { String output = ""; -#else -std::string resultToSourceCode(const decode_results *results) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(1536); // 1.5KB should cover most cases. // Start declaration - output += "uint16_t "; // variable type - output += "rawData["; // array name + output += F("uint16_t "); // variable type + output += F("rawData["); // array name output += uint64ToString(getCorrectedRawLength(results), 10); // array size - output += "] = {"; // Start declaration + output += F("] = {"); // Start declaration // Dump data for (uint16_t i = 1; i < results->rawlen; i++) { @@ -344,39 +575,42 @@ std::string resultToSourceCode(const decode_results *results) { usecs -= UINT16_MAX) { output += uint64ToString(UINT16_MAX); if (i % 2) - output += ", 0, "; + output += F(", 0, "); else - output += ", 0, "; + output += F(", 0, "); } output += uint64ToString(usecs, 10); if (i < results->rawlen - 1) - output += ", "; // ',' not needed on the last one - if (i % 2 == 0) output += " "; // Extra if it was even. + output += F(", "); // ',' not needed on the last one + if (i % 2 == 0) output += ' '; // Extra if it was even. } // End declaration - output += "};"; + output += F("};"); // Comment - output += " // " + typeToString(results->decode_type, results->repeat); + 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 (!hasACState(results->decode_type)) - output += " " + uint64ToString(results->value, 16); - output += "\n"; + output += ' ' + uint64ToString(results->value, 16); + output += F("\n"); // Now dump "known" codes if (results->decode_type != UNKNOWN) { if (hasACState(results->decode_type)) { #if DECODE_AC uint16_t nbytes = results->bits / 8; - output += "uint8_t state[" + uint64ToString(nbytes) + "] = {"; + output += F("uint8_t state["); + output += uint64ToString(nbytes); + output += F("] = {"); for (uint16_t i = 0; i < nbytes; i++) { - output += "0x"; - if (results->state[i] < 0x10) output += "0"; + output += F("0x"); + if (results->state[i] < 0x10) output += '0'; output += uint64ToString(results->state[i], 16); - if (i < nbytes - 1) output += ", "; + if (i < nbytes - 1) output += F(", "); } - output += "};\n"; + output += F("};\n"); #endif // DECODE_AC } else { // Simple protocols @@ -384,14 +618,17 @@ std::string resultToSourceCode(const decode_results *results) { // 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 += "uint32_t address = 0x" + - uint64ToString(results->address, 16) + ";\n"; - output += "uint32_t command = 0x" + - uint64ToString(results->command, 16) + ";\n"; + 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 += - "uint64_t data = 0x" + uint64ToString(results->value, 16) + ";\n"; + output += F("uint64_t data = 0x"); + output += uint64ToString(results->value, 16); + output += F(";\n"); } } return output; @@ -399,46 +636,43 @@ std::string resultToSourceCode(const decode_results *results) { // Dump out the decode_results structure. // -#ifdef ARDUINO -String resultToTimingInfo(const decode_results *results) { +String resultToTimingInfo(const decode_results * const results) { String output = ""; String value = ""; -#else -std::string resultToTimingInfo(const decode_results *results) { - std::string output = ""; - std::string value = ""; -#endif - output += "Raw Timing[" + uint64ToString(results->rawlen - 1, 10) + "]:\n"; + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2048); // 2KB should cover most cases. + 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 += "-"; // even + output += '-'; // even else - output += " +"; // odd + 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; + while (value.length() < 6) value = ' ' + value; output += value; - if (i < results->rawlen - 1) output += ", "; // ',' not needed for last one - if (!(i % 8)) output += "\n"; // Newline every 8 entries. + if (i < results->rawlen - 1) + output += F(", "); // ',' not needed for last one + if (!(i % 8)) output += '\n'; // Newline every 8 entries. } - output += "\n"; + output += '\n'; return output; } // Convert the decode_results structure's value/state to simple hexadecimal. // -#ifdef ARDUINO -String resultToHexidecimal(const decode_results *result) { +String resultToHexidecimal(const decode_results * const result) { String output = ""; -#else -std::string resultToHexidecimal(const decode_results *result) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax); // 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 + if (result->state[i] < 0x10) output += '0'; // Zero pad output += uint64ToString(result->state[i], 16); } #endif // DECODE_AC @@ -450,35 +684,61 @@ std::string resultToHexidecimal(const decode_results *result) { // Dump out the decode_results structure. // -#ifdef ARDUINO -String resultToHumanReadableBasic(const decode_results *results) { +String resultToHumanReadableBasic(const decode_results * const results) { String output = ""; -#else -std::string resultToHumanReadableBasic(const decode_results *results) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax + 50); // Should cover most cases. // Show Encoding standard - output += - "Encoding : " + typeToString(results->decode_type, results->repeat) + - "\n"; + output += F("Encoding : "); + output += typeToString(results->decode_type, results->repeat); + output += '\n'; // Show Code & length - output += "Code : "; + output += F("Code : "); output += resultToHexidecimal(results); - output += " (" + uint64ToString(results->bits) + " bits)\n"; + output += F(" ("); + output += uint64ToString(results->bits); + output += F(" bits)\n"); return output; } -uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { +// Convert a decode_results into an array suitable for `sendRaw()`. +// Args: +// decode: A pointer to an IR decode_results structure that contains a mesg. +// Returns: +// A pointer to a dynamically allocated uint16_t sendRaw compatible array. +// Note: +// Result 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; +} + +uint8_t sumBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { uint8_t checksum = init; - uint8_t *ptr; + const uint8_t *ptr; for (ptr = start; ptr - start < length; ptr++) checksum += *ptr; return checksum; } -uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) { +uint8_t xorBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init) { uint8_t checksum = init; - uint8_t *ptr; + const uint8_t *ptr; for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr; return checksum; } @@ -491,8 +751,8 @@ uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) { // init: Start the counting from this value. // Returns: // Nr. of bits found. -uint16_t countBits(const uint8_t *start, const uint16_t length, const bool ones, - const uint16_t init) { +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); @@ -535,3 +795,7 @@ uint64_t invertBits(const uint64_t data, const uint16_t nbits) { // Mask off any unwanted bits and return the result. return (result & ((1ULL << nbits) - 1)); } + +float celsiusToFahrenheit(const float deg) { return (deg * 9.0) / 5.0 + 32.0; } + +float fahrenheitToCelsius(const float deg) { return (deg - 32.0) * 5.0 / 9.0; } diff --git a/lib/IRremoteESP8266/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index ff045e9..64a5b25 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -15,32 +15,28 @@ #include "IRrecv.h" uint64_t reverseBits(uint64_t input, uint16_t nbits); -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String uint64ToString(uint64_t input, uint8_t base = 10); String typeToString(const decode_type_t protocol, const bool isRepeat = false); void serialPrintUint64(uint64_t input, uint8_t base = 10); -String resultToSourceCode(const decode_results *results); -String resultToTimingInfo(const decode_results *results); -String resultToHumanReadableBasic(const decode_results *results); -String resultToHexidecimal(const decode_results *result); -#else -std::string uint64ToString(uint64_t input, uint8_t base = 10); -std::string typeToString(const decode_type_t protocol, - const bool isRepeat = false); -std::string resultToSourceCode(const decode_results *results); -std::string resultToTimingInfo(const decode_results *results); -std::string resultToHumanReadableBasic(const decode_results *results); -std::string resultToHexidecimal(const decode_results *result); -#endif +String resultToSourceCode(const decode_results * const results); +String resultToTimingInfo(const decode_results * const results); +String resultToHumanReadableBasic(const decode_results * const results); +String resultToHexidecimal(const decode_results * const result); +String htmlEscape(const String unescaped); bool hasACState(const decode_type_t protocol); -uint16_t getCorrectedRawLength(const decode_results *results); -uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); -uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); -uint16_t countBits(const uint8_t *start, const uint16_t length, +uint16_t getCorrectedRawLength(const decode_results * const results); +uint16_t * resultToRawArray(const decode_results * const decode); +uint8_t sumBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init = 0); +uint8_t xorBytes(const uint8_t * const start, const uint16_t length, + const uint8_t init = 0); +uint16_t countBits(const uint8_t * const start, const uint16_t length, const bool ones = true, const uint16_t init = 0); uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones = true, const uint16_t init = 0); uint64_t invertBits(const uint64_t data, const uint16_t nbits); - +decode_type_t strToDecodeType(const char *str); +float celsiusToFahrenheit(const float deg); +float fahrenheitToCelsius(const float deg); #endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266/src/ir_Aiwa.cpp b/lib/IRremoteESP8266/src/ir_Aiwa.cpp index 617711a..70dbc85 100644 --- a/lib/IRremoteESP8266/src/ir_Aiwa.cpp +++ b/lib/IRremoteESP8266/src/ir_Aiwa.cpp @@ -12,6 +12,8 @@ // Based off the RC-T501 RCU // Added by David Conran. (Inspired by IRremoteESP8266's implementation: // https://github.com/z3t0/Arduino-IRremote) +// Supports: +// Brand: Aiwa, Model: RC-T501 RCU const uint16_t kAiwaRcT501PreBits = 26; const uint16_t kAiwaRcT501PostBits = 1; diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp index 8246b26..285656e 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266/src/ir_Argo.cpp @@ -2,10 +2,14 @@ Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote Controls Argo Ulisse 13 DCI A/C Copyright 2017 Schmolders +Copyright 2019 crankyoldgit */ #include "ir_Argo.h" #include +#ifndef UNIT_TEST +#include +#endif // UNIT_TEST #include "IRremoteESP8266.h" #include "IRutils.h" @@ -16,6 +20,7 @@ const uint16_t kArgoHdrSpace = 3300; const uint16_t kArgoBitMark = 400; const uint16_t kArgoOneSpace = 2200; const uint16_t kArgoZeroSpace = 900; +const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess. #if SEND_ARGO // Send an Argo A/C message. @@ -25,7 +30,8 @@ const uint16_t kArgoZeroSpace = 900; // // Status: ALPHA / Untested. -void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendArgo(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { // Check if we have enough bytes to send a proper message. if (nbytes < kArgoStateLength) return; // TODO(kaschmo): validate @@ -35,26 +41,31 @@ void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) { } #endif // SEND_ARGO -IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRArgoAC::IRArgoAC(const uint16_t pin) : _irsend(pin) { this->stateReset(); } -void IRArgoAC::begin() { _irsend.begin(); } +void IRArgoAC::begin(void) { _irsend.begin(); } #if SEND_ARGO void IRArgoAC::send(const uint16_t repeat) { - checksum(); // Create valid checksum before sending + this->checksum(); // Create valid checksum before sending _irsend.sendArgo(argo, kArgoStateLength, repeat); } #endif // SEND_ARGO -void IRArgoAC::checksum() { - uint8_t sum = 2; // Corresponds to byte 11 being constant 0b01 - uint8_t i; - +uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) { + // Corresponds to byte 11 being constant 0b01 // Only add up bytes to 9. byte 10 is 0b01 constant anyway. // Assume that argo array is MSB first (left) - for (i = 0; i < 10; i++) sum += argo[i]; + return sumBytes(state, length - 2, 2); +} - sum = sum % 256; // modulo 256 +bool IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) { + return ((state[length - 2] >> 2) + (state[length - 1] << 6)) == + IRArgoAC::calcChecksum(state, length); +} + +void IRArgoAC::checksum(void) { + uint8_t sum = IRArgoAC::calcChecksum(argo, kArgoStateLength); // Append sum to end of array // Set const part of checksum bit 10 argo[10] = 0b00000010; @@ -62,7 +73,7 @@ void IRArgoAC::checksum() { argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11 } -void IRArgoAC::stateReset() { +void IRArgoAC::stateReset(void) { for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0; // Argo Message. Store MSB left. @@ -76,155 +87,374 @@ void IRArgoAC::stateReset() { this->off(); this->setTemp(20); this->setRoomTemp(25); - this->setCoolMode(kArgoCoolAuto); + this->setMode(kArgoAuto); this->setFan(kArgoFanAuto); } -uint8_t* IRArgoAC::getRaw() { - checksum(); // Ensure correct bit array before returning +uint8_t* IRArgoAC::getRaw(void) { + this->checksum(); // Ensure correct bit array before returning return argo; } -void IRArgoAC::on() { - // state = ON; - ac_state = 1; +void IRArgoAC::setRaw(const uint8_t state[]) { + for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = state[i]; +} + +void IRArgoAC::on(void) { // Bit 5 of byte 9 is on/off // in MSB first - argo[9] = argo[9] | 0b00100000; // Set ON/OFF bit to 1 + argo[9]|= kArgoPowerBit; // Set ON/OFF bit to 1 } -void IRArgoAC::off() { - // state = OFF; - ac_state = 0; +void IRArgoAC::off(void) { // in MSB first // bit 5 of byte 9 to off - argo[9] = argo[9] & 0b11011111; // Set on/off bit to 0 + argo[9] &= ~kArgoPowerBit; // Set on/off bit to 0 } -void IRArgoAC::setPower(bool state) { - if (state) - on(); +void IRArgoAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -uint8_t IRArgoAC::getPower() { return ac_state; } +bool IRArgoAC::getPower(void) { return argo[9] & kArgoPowerBit; } -void IRArgoAC::setMax(bool state) { - max_mode = state; - if (max_mode) - argo[9] |= 0b00001000; +void IRArgoAC::setMax(const bool on) { + if (on) + argo[9] |= kArgoMaxBit; else - argo[9] &= 0b11110111; + argo[9] &= ~kArgoMaxBit; } -bool IRArgoAC::getMax() { return max_mode; } +bool IRArgoAC::getMax(void) { return argo[9] & kArgoMaxBit; } // Set the temp in deg C // Sending 0 equals +4 -void IRArgoAC::setTemp(uint8_t temp) { - if (temp < kArgoMinTemp) - temp = kArgoMinTemp; - else if (temp > kArgoMaxTemp) - temp = kArgoMaxTemp; +void IRArgoAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kArgoMinTemp, degrees); + temp = std::min(kArgoMaxTemp, temp); - // Store in attributes - set_temp = temp; // offset 4 degrees. "If I want 12 degrees, I need to send 8" - temp -= 4; + temp -= kArgoTempOffset; // Settemp = Bit 6,7 of byte 2, and bit 0-2 of byte 3 // mask out bits // argo[13] & 0x00000100; // mask out ON/OFF Bit - argo[2] &= 0b00111111; - argo[3] &= 0b11111000; + argo[2] &= ~kArgoTempLowMask; + argo[3] &= ~kArgoTempHighMask; - argo[2] += temp << 6; // append to bit 6,7 - argo[3] += temp >> 2; // remove lowest to bits and append in 0-2 + // append to bit 6,7 + argo[2] += temp << 6; + // remove lowest to bits and append in 0-2 + argo[3] += ((temp >> 2) & kArgoTempHighMask); } -uint8_t IRArgoAC::getTemp() { return set_temp; } +uint8_t IRArgoAC::getTemp(void) { + return (((argo[3] & kArgoTempHighMask) << 2 ) | (argo[2] >> 6)) + + kArgoTempOffset; +} // Set the speed of the fan -void IRArgoAC::setFan(uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - fan_mode = fan; +void IRArgoAC::setFan(const uint8_t fan) { // Mask out bits - argo[3] &= 0b11100111; + argo[3] &= ~kArgoFanMask; // Set fan mode at bit positions - argo[3] += fan << 3; + argo[3] |= (std::min(fan, kArgoFan3) << 3); } -uint8_t IRArgoAC::getFan() { return fan_mode; } +uint8_t IRArgoAC::getFan(void) { return (argo[3] & kArgoFanMask) >> 3; } -void IRArgoAC::setFlap(uint8_t flap) { +void IRArgoAC::setFlap(const uint8_t flap) { flap_mode = flap; // TODO(kaschmo): set correct bits for flap mode } -uint8_t IRArgoAC::getFlap() { return flap_mode; } +uint8_t IRArgoAC::getFlap(void) { return flap_mode; } -uint8_t IRArgoAC::getMode() { - // return cooling 0, heating 1 - return ac_mode; +uint8_t IRArgoAC::getMode(void) { + return (argo[2] & kArgoModeMask) >> 3; } -void IRArgoAC::setCoolMode(uint8_t mode) { - ac_mode = 0; // Set ac mode to cooling - cool_mode = mode; - // Mask out bits, also leave bit 5 on 0 for cooling - argo[2] &= 0b11000111; - - // Set cool mode at bit positions - argo[2] += mode << 3; +void IRArgoAC::setMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: + case kArgoDry: + case kArgoAuto: + case kArgoOff: + case kArgoHeat: + case kArgoHeatAuto: + // Mask out bits + argo[2] &= ~kArgoModeMask; + // Set the mode at bit positions + argo[2] |= ((mode << 3) & kArgoModeMask); + return; + default: + this->setMode(kArgoAuto); + } } -uint8_t IRArgoAC::getCoolMode() { return cool_mode; } - -void IRArgoAC::setHeatMode(uint8_t mode) { - ac_mode = 1; // Set ac mode to heating - heat_mode = mode; - // Mask out bits - argo[2] &= 0b11000111; - // Set heating bit - argo[2] |= 0b00100000; - // Set cool mode at bit positions - argo[2] += mode << 3; -} - -uint8_t IRArgoAC::getHeatMode() { return heat_mode; } - -void IRArgoAC::setNight(bool state) { - night_mode = state; - if (night_mode) +void IRArgoAC::setNight(const bool on) { + if (on) // Set bit at night position: bit 2 - argo[9] |= 0b00000100; + argo[9] |= kArgoNightBit; else - argo[9] &= 0b11111011; + argo[9] &= ~kArgoNightBit; } -bool IRArgoAC::getNight() { return night_mode; } +bool IRArgoAC::getNight(void) { return argo[9] & kArgoNightBit; } -void IRArgoAC::setiFeel(bool state) { - ifeel_mode = state; - if (ifeel_mode) +void IRArgoAC::setiFeel(const bool on) { + if (on) // Set bit at iFeel position: bit 7 - argo[9] |= 0b10000000; + argo[9] |= kArgoIFeelBit; else - argo[9] &= 0b01111111; + argo[9] &= ~kArgoIFeelBit; } -bool IRArgoAC::getiFeel() { return ifeel_mode; } +bool IRArgoAC::getiFeel(void) { return argo[9] & kArgoIFeelBit; } -void IRArgoAC::setTime() { +void IRArgoAC::setTime(void) { // TODO(kaschmo): use function call from checksum to set time first } -void IRArgoAC::setRoomTemp(uint8_t temp) { - temp -= 4; +void IRArgoAC::setRoomTemp(const uint8_t degrees) { + uint8_t temp = std::min(degrees, kArgoMaxRoomTemp); + temp = std::max(temp, kArgoTempOffset); + temp -= kArgoTempOffset;; // Mask out bits - argo[3] &= 0b00011111; - argo[4] &= 0b11111100; + argo[3] &= ~kArgoRoomTempLowMask; + argo[4] &= ~kArgoRoomTempHighMask; argo[3] += temp << 5; // Append to bit 5,6,7 argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 } + +uint8_t IRArgoAC::getRoomTemp(void) { + return ((argo[4] & kArgoRoomTempHighMask) << 3 | (argo[3] >> 5)) + + kArgoTempOffset; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRArgoAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kArgoCool; + case stdAc::opmode_t::kHeat: + return kArgoHeat; + case stdAc::opmode_t::kDry: + return kArgoDry; + case stdAc::opmode_t::kOff: + return kArgoOff; + // No fan mode. + default: + return kArgoAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kArgoFan1; + case stdAc::fanspeed_t::kMedium: + return kArgoFan2; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kArgoFan3; + default: + return kArgoFanAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + return kArgoFlapFull; + case stdAc::swingv_t::kHigh: + return kArgoFlap5; + case stdAc::swingv_t::kMiddle: + return kArgoFlap4; + case stdAc::swingv_t::kLow: + return kArgoFlap3; + case stdAc::swingv_t::kLowest: + return kArgoFlap1; + default: + return kArgoFlapAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRArgoAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: return stdAc::opmode_t::kCool; + case kArgoHeat: return stdAc::opmode_t::kHeat; + case kArgoDry: return stdAc::opmode_t::kDry; + // No fan mode. + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRArgoAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kArgoFan3: return stdAc::fanspeed_t::kMax; + case kArgoFan2: return stdAc::fanspeed_t::kMedium; + case kArgoFan1: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRArgoAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::ARGO; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.turbo = this->getMax(); + result.sleep = this->getNight() ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRArgoAC::toString() { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (getMode()) { + case kArgoAuto: + result += F(" (AUTO)"); + break; + case kArgoCool: + result += F(" (COOL)"); + break; + case kArgoHeat: + result += F(" (HEAT)"); + break; + case kArgoDry: + result += F(" (DRY)"); + break; + case kArgoHeatAuto: + result += F(" (HEATAUTO)"); + break; + case kArgoOff: + result += F(" (OFF)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kArgoFanAuto: + result += F(" (AUTO)"); + break; + case kArgoFan3: + result += F(" (MAX)"); + break; + case kArgoFan1: + result += F(" (MIN)"); + break; + case kArgoFan2: + result += F(" (MED)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += 'C'; + result += F(", Room Temp: "); + result += uint64ToString(getRoomTemp()); + result += 'C'; + result += F(", Max: "); + result += getMax() ? F("On") : F("Off"); + result += F(", iFeel: "); + result += getiFeel() ? F("On") : F("Off"); + result += F(", Night: "); + result += getNight() ? F("On") : F("Off"); + return result; +} + +#if DECODE_ARGO +// Decode the supplied Argo message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kArgoBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: ALPHA / Probably doesn't work. +// +// Note: +// This decoder is based soley off sendArgo(). We have no actual captures +// to test this against. If you have one of these units, please let us know. +bool IRrecv::decodeArgo(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) + return false; // Can't possibly be a valid Samsung A/C message. + if (strict && nbits != kArgoBits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + match_result_t data_result; + + // Message Header + if (!matchMark(results->rawbuf[offset++], kArgoHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kArgoHdrSpace)) return false; + + // Data + // Keep reading bytes until we either run out of data or state to fill. + for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, kArgoBitMark, + kArgoOneSpace, kArgoBitMark, + kArgoZeroSpace, kTolerance, 0, false); + if (data_result.success == false) { + DPRINT("DEBUG: offset = "); + DPRINTLN(offset + data_result.used); + return false; // Fail + } + results->state[i] = data_result.data; + DPRINT("DEBUG: Matched state["); + DPRINT(i); + DPRINTLN("]"); + } + + // Footer (None, allegedly. This seems very wrong.) + + // Compliance + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != nbits) return false; + // Verify we got a valid checksum. + if (strict && !IRArgoAC::validChecksum(results->state)) return false; + // Success + results->decode_type = decode_type_t::ARGO; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_ARGO diff --git a/lib/IRremoteESP8266/src/ir_Argo.h b/lib/IRremoteESP8266/src/ir_Argo.h index 0174d61..fddf516 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -1,11 +1,21 @@ -/* Copyright 2017 Schmolders +// Copyright 2017 Schmolders // Adds support for Argo Ulisse 13 DCI Mobile Split ACs. -*/ + +// Supports: +// Brand: Argo, Model: Ulisse 13 DCI Mobile Split A/C + #ifndef IR_ARGO_H_ #define IR_ARGO_H_ +#ifndef UNIT_TEST +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + // ARGO Ulisse DCI @@ -29,19 +39,42 @@ // Constants. Store MSB left. -const uint8_t kArgoCoolOn = 0; // 0b000 -const uint8_t kArgoCoolOff = 3; // 0b110 -const uint8_t kArgoCoolAuto = 2; // 0b010 -const uint8_t kArgoCoolHum = 1; // 0b100 -const uint8_t kArgoHeatOn = 0; // 0b001 -const uint8_t kArgoHeatAuto = 1; // 0b101 -const uint8_t kArgoHeatBlink = 2; // 0b011 // ??no idea what mode that is -const uint8_t kArgoMinTemp = 10; // Celsius offset +4 -const uint8_t kArgoMaxTemp = 32; // Celsius +// byte[2] +const uint8_t kArgoHeatBit = 0b00100000; +const uint8_t kArgoModeMask = 0b00111000; +const uint8_t kArgoTempLowMask = 0b11000000; +const uint8_t kArgoCool = 0b000; // 0b000 (LSB) 0 +const uint8_t kArgoDry = 0b001; // 0b100 (LSB) 1 +const uint8_t kArgoAuto = 0b010; // 0b010 (LSB) 2 +const uint8_t kArgoOff = 0b011; // 0b110 (LSB) 3 +const uint8_t kArgoHeat = 0b100; // 0b001 (LSB) 4 +const uint8_t kArgoHeatAuto = 0b101; // 0b101 (LSB) 5 +// ?no idea what mode that is +const uint8_t kArgoHeatBlink = 0b110; // 0b011 (LSB) 6 + +// byte[3] +const uint8_t kArgoTempHighMask = 0b00000111; +const uint8_t kArgoFanMask = 0b00011000; +const uint8_t kArgoRoomTempLowMask = 0b11100000; const uint8_t kArgoFanAuto = 0; // 0b00 const uint8_t kArgoFan3 = 3; // 0b11 const uint8_t kArgoFan2 = 2; // 0b01 const uint8_t kArgoFan1 = 1; // 0b10 + +// byte[4] +const uint8_t kArgoRoomTempHighMask = 0b00000011; +const uint8_t kArgoTempOffset = 4; +const uint8_t kArgoMaxRoomTemp = ((1 << 5) - 1) + kArgoTempOffset; // 35C + +// byte[9] +const uint8_t kArgoPowerBit = 0b00100000; +const uint8_t kArgoMaxBit = 0b00001000; +const uint8_t kArgoNightBit = 0b00000100; +const uint8_t kArgoIFeelBit = 0b10000000; + +const uint8_t kArgoMinTemp = 10; // Celsius offset +4 +const uint8_t kArgoMaxTemp = 32; // Celsius + const uint8_t kArgoFlapAuto = 0; // 0b000 const uint8_t kArgoFlap1 = 1; // 0b100 const uint8_t kArgoFlap2 = 2; // 0b010 @@ -55,7 +88,7 @@ const uint8_t kArgoFlapFull = 7; // 0b111 #define ARGO_COOL_ON kArgoCoolOn #define ARGO_COOL_OFF kArgoCoolOff #define ARGO_COOL_AUTO kArgoCoolAuto -#define ARGO_COOl_HUM kArgoCoolHum +#define ARGO_COOL_HUM kArgoCoolHum #define ARGO_HEAT_ON kArgoHeatOn #define ARGO_HEAT_AUTO kArgoHeatAuto #define ARGO_HEAT_BLINK kArgoHeatBlink @@ -77,66 +110,73 @@ const uint8_t kArgoFlapFull = 7; // 0b111 class IRArgoAC { public: - explicit IRArgoAC(uint16_t pin); + explicit IRArgoAC(const uint16_t pin); #if SEND_ARGO void send(const uint16_t repeat = kArgoDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_ARGO - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); - void setPower(bool state); - uint8_t getPower(); + void setPower(const bool on); + bool getPower(void); - void setTemp(uint8_t temp); - uint8_t getTemp(); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); - void setFan(uint8_t fan); - uint8_t getFan(); + void setFan(const uint8_t fan); + uint8_t getFan(void); - void setFlap(uint8_t flap); - uint8_t getFlap(); + void setFlap(const uint8_t flap); + uint8_t getFlap(void); - void setCoolMode(uint8_t mode); - uint8_t getCoolMode(); + void setMode(const uint8_t mode); + uint8_t getMode(void); - void setHeatMode(uint8_t mode); - uint8_t getHeatMode(); - uint8_t getMode(); + void setMax(const bool on); + bool getMax(void); - void setMax(bool state); - bool getMax(); + void setNight(const bool on); + bool getNight(void); - void setNight(bool state); - bool getNight(); + void setiFeel(const bool on); + bool getiFeel(void); - void setiFeel(bool state); - bool getiFeel(); + void setTime(void); + void setRoomTemp(const uint8_t degrees); + uint8_t getRoomTemp(void); - void setTime(); - void setRoomTemp(uint8_t temp); - - uint8_t* getRaw(); + uint8_t* getRaw(void); + void setRaw(const uint8_t state[]); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kArgoStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kArgoStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); +#ifndef UNIT_TEST private: + IRsend _irsend; // instance of the IR send class +#else + IRsendTest _irsend; // instance of the testing IR send class +#endif // # of bytes per command uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h - void stateReset(); - void checksum(); - IRsend _irsend; // instance of the IR send class + void stateReset(void); + void checksum(void); // Attributes - uint8_t set_temp; - uint8_t fan_mode; uint8_t flap_mode; - uint8_t ac_state; - uint8_t ac_mode; // heat 1, cool 0 uint8_t heat_mode; uint8_t cool_mode; - uint8_t night_mode; // on/off - uint8_t max_mode; // on/off - uint8_t ifeel_mode; // on/off }; #endif // IR_ARGO_H_ diff --git a/lib/IRremoteESP8266/src/ir_Carrier.cpp b/lib/IRremoteESP8266/src/ir_Carrier.cpp index 350d61c..6492e70 100644 --- a/lib/IRremoteESP8266/src/ir_Carrier.cpp +++ b/lib/IRremoteESP8266/src/ir_Carrier.cpp @@ -1,20 +1,17 @@ // Copyright 2018 David Conran +// Supports: +// Brand: Carrier/Surrey, Model: 42QG5A55970 remote +// Brand: Carrier/Surrey, Model: 619EGX0090E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0120E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0180E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C +// Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter + #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// CCCCC AAA RRRRRR RRRRRR IIIII EEEEEEE RRRRRR -// CC C AAAAA RR RR RR RR III EE RR RR -// CC AA AA RRRRRR RRRRRR III EEEEE RRRRRR -// CC C AAAAAAA RR RR RR RR III EE RR RR -// CCCCC AA AA RR RR RR RR IIIII EEEEEEE RR RR - -// Suits Carrier/Surrey HVAC models: -// 42QG5A55970 (remote) -// 619EGX0090E0 / 619EGX0120E0 / 619EGX0180E0 / 619EGX0220E0 (indoor units) -// 53NGK009/012 (inverter) - // Constants // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/385 diff --git a/lib/IRremoteESP8266/src/ir_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp index 38f1b69..accc269 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266/src/ir_Coolix.cpp @@ -1,5 +1,5 @@ // Copyright bakrus -// Copyright 2017 David Conran +// Copyright 2017,2019 David Conran #include "ir_Coolix.h" #include @@ -19,7 +19,11 @@ // Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit // // Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. +// Brand: Beko, Model: RG57K7(B)/BGEF Remote +// Brand: Beko, Model: BINR 070/071 split-type A/C +// Brand: Midea, Model: RG52D/BGE Remote +// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C +// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016) // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/484 @@ -200,6 +204,14 @@ void IRCoolixAC::setPower(const bool power) { } } +void IRCoolixAC::on(void) { + this->setPower(true); +} + +void IRCoolixAC::off(void) { + this->setPower(false); +} + bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; } void IRCoolixAC::setSwing() { @@ -260,18 +272,30 @@ void IRCoolixAC::clearSensorTemp() { void IRCoolixAC::setMode(const uint8_t mode) { uint32_t actualmode = mode; + switch (actualmode) { + case kCoolixAuto: + case kCoolixDry: + if (this->getFan() == kCoolixFanAuto) + // No kCoolixFanAuto in Dry/Auto mode. + this->setFan(kCoolixFanAuto0, false); + break; + case kCoolixCool: + case kCoolixHeat: + case kCoolixFan: + if (this->getFan() == kCoolixFanAuto0) + // kCoolixFanAuto0 only in Dry/Auto mode. + this->setFan(kCoolixFanAuto, false); + break; + default: // Anything else, go with Auto mode. + this->setMode(kCoolixAuto); + return; + } // Fan mode is a special case of Dry. if (mode == kCoolixFan) actualmode = kCoolixDry; - switch (actualmode) { - case kCoolixCool: - case kCoolixAuto: - case kCoolixHeat: - case kCoolixDry: - recoverSavedState(); - remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); - // Force the temp into a known-good state. - setTemp(getTemp()); - } + recoverSavedState(); + remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); + // Force the temp into a known-good state. + setTemp(getTemp()); if (mode == kCoolixFan) setTempRaw(kCoolixFanTempCode); } @@ -286,15 +310,33 @@ uint8_t IRCoolixAC::getFan() { return (getNormalState() & kCoolixFanMask) >> 13; } -void IRCoolixAC::setFan(const uint8_t speed) { +void IRCoolixAC::setFan(const uint8_t speed, const bool modecheck) { recoverSavedState(); uint8_t newspeed = speed; switch (speed) { + case kCoolixFanAuto: // Dry & Auto mode can't have this speed. + if (modecheck) { + switch (this->getMode()) { + case kCoolixAuto: + case kCoolixDry: + newspeed = kCoolixFanAuto0; + } + } + break; + case kCoolixFanAuto0: // Only Dry & Auto mode can have this speed. + if (modecheck) { + switch (this->getMode()) { + case kCoolixAuto: + case kCoolixDry: + break; + default: + newspeed = kCoolixFanAuto; + } + } + break; case kCoolixFanMin: case kCoolixFanMed: case kCoolixFanMax: - case kCoolixFanAuto: - case kCoolixFanAuto0: case kCoolixFanZoneFollow: case kCoolixFanFixed: break; @@ -305,100 +347,181 @@ void IRCoolixAC::setFan(const uint8_t speed) { remote_state |= ((newspeed << 13) & kCoolixFanMask); } +// Convert a standard A/C mode into its native mode. +uint8_t IRCoolixAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kCoolixCool; + case stdAc::opmode_t::kHeat: + return kCoolixHeat; + case stdAc::opmode_t::kDry: + return kCoolixDry; + case stdAc::opmode_t::kFan: + return kCoolixFan; + default: + return kCoolixAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRCoolixAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kCoolixFanMin; + case stdAc::fanspeed_t::kMedium: + return kCoolixFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kCoolixFanMax; + default: + return kCoolixFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRCoolixAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kCoolixCool: return stdAc::opmode_t::kCool; + case kCoolixHeat: return stdAc::opmode_t::kHeat; + case kCoolixDry: return stdAc::opmode_t::kDry; + case kCoolixFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRCoolixAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kCoolixFanMax: return stdAc::fanspeed_t::kMax; + case kCoolixFanMed: return stdAc::fanspeed_t::kMedium; + case kCoolixFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRCoolixAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::COOLIX; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwing() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.turbo = this->getTurbo(); + result.sleep = this->getSleep() ? 0 : -1; + result.light = this->getLed(); + result.clean = this->getClean(); + // Not supported. + result.quiet = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRCoolixAC::toString() { String result = ""; -#else -std::string IRCoolixAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); if (getPower()) { - result += "On"; + result += F("On"); } else { - result += "Off"; + result += F("Off"); return result; // If it's off, there is no other info. } // Special modes. if (getSwing()) { - result += ", Swing: Toggle"; + result += F(", Swing: Toggle"); return result; } if (getSleep()) { - result += ", Sleep: Toggle"; + result += F(", Sleep: Toggle"); return result; } if (getTurbo()) { - result += ", Turbo: Toggle"; + result += F(", Turbo: Toggle"); return result; } if (getLed()) { - result += ", Led: Toggle"; + result += F(", Led: Toggle"); return result; } if (getClean()) { - result += ", Clean: Toggle"; + result += F(", Clean: Toggle"); return result; } - result += ", Mode: " + uint64ToString(getMode()); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kCoolixAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kCoolixCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kCoolixHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kCoolixDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kCoolixFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kCoolixFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kCoolixFanAuto0: - result += " (AUTO0)"; + result += F(" (AUTO0)"); break; case kCoolixFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; case kCoolixFanMin: - result += " (MIN)"; + result += F(" (MIN)"); break; case kCoolixFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kCoolixFanZoneFollow: - result += " (ZONEFOLLOW)"; + result += F(" (ZONEFOLLOW)"); break; case kCoolixFanFixed: - result += " (FIXED)"; + result += F(" (FIXED)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - if (getMode() != kCoolixFan) // Fan mode doesn't have a temperature. - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Zone Follow: "; + if (getMode() != kCoolixFan) { // Fan mode doesn't have a temperature. + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += 'C'; + } + result += F(", Zone Follow: "); if (getZoneFollow()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Sensor Temp: "; + result += F("Off"); + result += F(", Sensor Temp: "); if (getSensorTemp() > kCoolixSensorTempMax) - result += "Ignored"; + result += F("Ignored"); else - result += uint64ToString(getSensorTemp()) + "C"; + result += uint64ToString(getSensorTemp()) + F("C"); return result; } diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index fdc060a..22906cf 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -9,11 +9,12 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // CCCCC OOOOO OOOOO LL IIIII XX XX // CC C OO OO OO OO LL III XX XX @@ -22,7 +23,11 @@ // CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX // Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. +// Brand: Beko, Model: RG57K7(B)/BGEF Remote +// Brand: Beko, Model: BINR 070/071 split-type A/C +// Brand: Midea, Model: RG52D/BGE Remote +// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C +// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016) // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/484 // Kudos: @@ -30,11 +35,11 @@ // Constants // Modes -const uint8_t kCoolixCool = 0b00; -const uint8_t kCoolixDry = 0b01; -const uint8_t kCoolixAuto = 0b10; -const uint8_t kCoolixHeat = 0b11; -const uint8_t kCoolixFan = 4; // Synthetic. +const uint8_t kCoolixCool = 0b000; +const uint8_t kCoolixDry = 0b001; +const uint8_t kCoolixAuto = 0b010; +const uint8_t kCoolixHeat = 0b011; +const uint8_t kCoolixFan = 0b100; // Synthetic. const uint32_t kCoolixModeMask = 0b000000000000000000001100; // 0xC const uint32_t kCoolixZoneFollowMask = 0b000010000000000000000000; // 0x80000 // Fan Control @@ -92,6 +97,7 @@ class IRCoolixAC { void stateReset(); #if SEND_COOLIX void send(const uint16_t repeat = kCoolixDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_COOLIX void begin(); void on(); @@ -103,7 +109,7 @@ class IRCoolixAC { void setSensorTemp(const uint8_t desired); uint8_t getSensorTemp(); void clearSensorTemp(); - void setFan(const uint8_t fan); + void setFan(const uint8_t speed, const bool modecheck = true); uint8_t getFan(); void setMode(const uint8_t mode); uint8_t getMode(); @@ -120,17 +126,21 @@ class IRCoolixAC { bool getZoneFollow(); uint32_t getRaw(); void setRaw(const uint32_t new_code); - -#ifdef ARDUINO + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); String toString(); -#else - std::string toString(); -#endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint32_t remote_state; // The state of the IR remote in IR code form. uint32_t saved_state; // Copy of the state if we required a special mode. - IRsend _irsend; void setTempRaw(const uint8_t code); uint8_t getTempRaw(); void setSensorTempRaw(const uint8_t code); diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp index 946ab8f..1e6c3db 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.cpp +++ b/lib/IRremoteESP8266/src/ir_Daikin.cpp @@ -16,14 +16,11 @@ Copyright 2018-2019 crankyoldgit #include "IRrecv.h" #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif #include "IRutils.h" -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - // Constants // Ref: // https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote @@ -31,17 +28,6 @@ Copyright 2018-2019 crankyoldgit // https://github.com/markszabo/IRremoteESP8266/issues/582 #if SEND_DAIKIN -// Original header -// static uint8_t header1[DAIKIN_HEADER1_LENGTH]; -// header1[0] = 0b00010001; -// header1[1] = 0b11011010; -// header1[2] = 0b00100111; -// header1[3] = 0b00000000; -// header1[4] = 0b11000101; -// header1[5] = 0b00000000; -// header1[6] = 0b00000000; -// header1[7] = 0b11010111; - // Send a Daikin A/C message. // // Args: @@ -52,132 +38,163 @@ Copyright 2018-2019 crankyoldgit // Ref: // IRDaikinESP.cpp // https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -void IRsend::sendDaikin(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kDaikinStateLength) +// https://github.com/blafois/Daikin-IR-Reverse +void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikinStateLengthShort) return; // Not enough bytes to send a proper message. for (uint16_t r = 0; r <= repeat; r++) { + uint16_t offset = 0; // Send the header, 0b00000 sendGeneric(0, 0, // No header for the header kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - (uint64_t)0b00000, 5, 38, false, 0, 50); - // Leading header - // Do this as a constant to save RAM and keep in flash memory - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - kDaikinFirstHeader64, 64, 38, false, 0, 50); + (uint64_t)0b00000, kDaikinHeaderLength, 38, false, 0, 50); // Data #1 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data, 8, 38, - false, 0, 50); + if (nbytes < kDaikinStateLength) { // Are we using the legacy size? + // Do this as a constant to save RAM and keep in flash memory + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + kDaikinFirstHeader64, 64, 38, false, 0, 50); + } else { // We are using the newer/more correct size. + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data, kDaikinSection1Length, 38, false, 0, 50); + offset += kDaikinSection1Length; + } // Data #2 sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data + 8, - nbytes - 8, 38, false, 0, 50); + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, kDaikinSection2Length, 38, false, 0, 50); + offset += kDaikinSection2Length; + // Data #3 + sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, + kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + data + offset, nbytes - offset, 38, false, 0, 50); } } #endif // SEND_DAIKIN IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } -void IRDaikinESP::begin() { _irsend.begin(); } +void IRDaikinESP::begin(void) { _irsend.begin(); } #if SEND_DAIKIN void IRDaikinESP::send(const uint16_t repeat) { - checksum(); - _irsend.sendDaikin(daikin, kDaikinStateLength, repeat); + this->checksum(); + _irsend.sendDaikin(remote, kDaikinStateLength, repeat); } #endif // SEND_DAIKIN -// Verify the checksum is valid for a given state. +// Verify the checksums are valid for a given state. // Args: -// state: The array to verify the checksum of. +// state: The array to verify the checksums of. // length: The size of the state. // Returns: // A boolean. bool IRDaikinESP::validChecksum(uint8_t state[], const uint16_t length) { - if (length < 8 || state[7] != sumBytes(state, 7)) return false; - if (length < 10 || - state[length - 1] != sumBytes(state + 8, length - 9)) + // Data #1 + if (length < kDaikinSection1Length || + state[kDaikinByteChecksum1] != sumBytes(state, kDaikinSection1Length - 1)) + return false; + // Data #2 + if (length < kDaikinSection1Length + kDaikinSection2Length || + state[kDaikinByteChecksum2] != sumBytes(state + kDaikinSection1Length, + kDaikinSection2Length - 1)) + return false; + // Data #3 + if (length < kDaikinSection1Length + kDaikinSection2Length + 2 || + state[length - 1] != sumBytes(state + kDaikinSection1Length + + kDaikinSection2Length, + length - (kDaikinSection1Length + + kDaikinSection2Length) - 1)) return false; return true; } // Calculate and set the checksum values for the internal state. -void IRDaikinESP::checksum() { - daikin[7] = sumBytes(daikin, 7); - daikin[26] = sumBytes(daikin + 8, 17); +void IRDaikinESP::checksum(void) { + remote[kDaikinByteChecksum1] = sumBytes(remote, kDaikinSection1Length - 1); + remote[kDaikinByteChecksum2] = sumBytes(remote + kDaikinSection1Length, + kDaikinSection2Length - 1); + remote[kDaikinByteChecksum3] = sumBytes(remote + kDaikinSection1Length + + kDaikinSection2Length, + kDaikinSection3Length - 1); } -void IRDaikinESP::stateReset() { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = 0x0; +void IRDaikinESP::stateReset(void) { + for (uint8_t i = 0; i < kDaikinStateLength; i++) remote[i] = 0x0; - daikin[0] = 0x11; - daikin[1] = 0xDA; - daikin[2] = 0x27; - daikin[4] = 0x42; - // daikin[7] is a checksum byte, it will be set by checksum(). - daikin[8] = 0x11; - daikin[9] = 0xDA; - daikin[10] = 0x27; - daikin[13] = 0x49; - daikin[14] = 0x1E; - daikin[16] = 0xB0; - daikin[19] = 0x06; - daikin[20] = 0x60; - daikin[23] = 0xC0; - // daikin[26] is a checksum byte, it will be set by checksum(). - checksum(); + remote[0] = 0x11; + remote[1] = 0xDA; + remote[2] = 0x27; + remote[4] = 0xC5; + // remote[7] is a checksum byte, it will be set by checksum(). + + remote[8] = 0x11; + remote[9] = 0xDA; + remote[10] = 0x27; + remote[12] = 0x42; + // remote[15] is a checksum byte, it will be set by checksum(). + remote[16] = 0x11; + remote[17] = 0xDA; + remote[18] = 0x27; + remote[21] = 0x49; + remote[22] = 0x1E; + remote[24] = 0xB0; + remote[27] = 0x06; + remote[28] = 0x60; + remote[31] = 0xC0; + // remote[34] is a checksum byte, it will be set by checksum(). + this->checksum(); } -uint8_t *IRDaikinESP::getRaw() { - checksum(); // Ensure correct settings before sending. - return daikin; +uint8_t *IRDaikinESP::getRaw(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; } -void IRDaikinESP::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = new_code[i]; +void IRDaikinESP::setRaw(const uint8_t new_code[], const uint16_t length) { + uint8_t offset = 0; + if (length == kDaikinStateLengthShort) { // Handle the "short" length case. + offset = kDaikinStateLength - kDaikinStateLengthShort; + this->stateReset(); + } + for (uint8_t i = 0; i < length && i < kDaikinStateLength; i++) + remote[i + offset] = new_code[i]; } -void IRDaikinESP::on() { - // state = ON; - setBit(kDaikinBytePower, kDaikinBitPower); -} +void IRDaikinESP::on(void) { remote[kDaikinBytePower] |= kDaikinBitPower; } -void IRDaikinESP::off() { - // state = OFF; - clearBit(kDaikinBytePower, kDaikinBitPower); -} +void IRDaikinESP::off(void) { remote[kDaikinBytePower] &= ~kDaikinBitPower; } -void IRDaikinESP::setPower(bool state) { - if (state) - on(); +void IRDaikinESP::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -bool IRDaikinESP::getPower() { - return (getBit(kDaikinBytePower, kDaikinBitPower) > 0); +bool IRDaikinESP::getPower(void) { + return remote[kDaikinBytePower] & kDaikinBitPower; } // Set the temp in deg C -void IRDaikinESP::setTemp(uint8_t temp) { - if (temp < kDaikinMinTemp) - temp = kDaikinMinTemp; - else if (temp > kDaikinMaxTemp) - temp = kDaikinMaxTemp; - daikin[14] = temp * 2; +void IRDaikinESP::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote[kDaikinByteTemp] = degrees << 1; } -uint8_t IRDaikinESP::getTemp() { return daikin[14] / 2; } +uint8_t IRDaikinESP::getTemp(void) { return remote[kDaikinByteTemp] >> 1; } // Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikinESP::setFan(uint8_t fan) { +void IRDaikinESP::setFan(const uint8_t fan) { // Set the fan speed bits, leave low 4 bits alone uint8_t fanset; if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) @@ -186,525 +203,470 @@ void IRDaikinESP::setFan(uint8_t fan) { fanset = kDaikinFanAuto; else fanset = 2 + fan; - daikin[16] &= 0x0F; - daikin[16] |= (fanset << 4); + remote[kDaikinByteFan] &= 0x0F; + remote[kDaikinByteFan] |= (fanset << 4); } -uint8_t IRDaikinESP::getFan() { - uint8_t fan = daikin[16] >> 4; +uint8_t IRDaikinESP::getFan(void) { + uint8_t fan = remote[kDaikinByteFan] >> 4; if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; return fan; } -uint8_t IRDaikinESP::getMode() { - /* - kDaikinCool - kDaikinHeat - kDaikinFan - kDaikinAuto - kDaikinDry - */ - return daikin[13] >> 4; -} +uint8_t IRDaikinESP::getMode(void) { return remote[kDaikinBytePower] >> 4; } -void IRDaikinESP::setMode(uint8_t mode) { +void IRDaikinESP::setMode(const uint8_t mode) { switch (mode) { + case kDaikinAuto: case kDaikinCool: case kDaikinHeat: case kDaikinFan: case kDaikinDry: + remote[kDaikinBytePower] &= 0b10001111; + remote[kDaikinBytePower] |= (mode << 4); break; default: - mode = kDaikinAuto; + this->setMode(kDaikinAuto); } - mode <<= 4; - daikin[13] &= 0b10001111; - daikin[13] |= mode; } -void IRDaikinESP::setSwingVertical(bool state) { - if (state) - daikin[16] |= 0x0F; +void IRDaikinESP::setSwingVertical(const bool on) { + if (on) + remote[kDaikinByteFan] |= 0x0F; else - daikin[16] &= 0xF0; + remote[kDaikinByteFan] &= 0xF0; } -bool IRDaikinESP::getSwingVertical() { return daikin[16] & 0x01; } +bool IRDaikinESP::getSwingVertical(void) { + return remote[kDaikinByteFan] & 0x0F; +} -void IRDaikinESP::setSwingHorizontal(bool state) { - if (state) - daikin[17] |= 0x0F; +void IRDaikinESP::setSwingHorizontal(const bool on) { + if (on) + remote[kDaikinByteSwingH] |= 0x0F; else - daikin[17] &= 0xF0; + remote[kDaikinByteSwingH] &= 0xF0; } -bool IRDaikinESP::getSwingHorizontal() { return daikin[17] & 0x01; } +bool IRDaikinESP::getSwingHorizontal(void) { + return remote[kDaikinByteSwingH] & 0x0F; +} -void IRDaikinESP::setQuiet(bool state) { - if (state) { - setBit(kDaikinByteSilent, kDaikinBitSilent); +void IRDaikinESP::setQuiet(const bool on) { + if (on) { + remote[kDaikinByteSilent] |= kDaikinBitSilent; // Powerful & Quiet mode being on are mutually exclusive. - setPowerful(false); + this->setPowerful(false); } else { - clearBit(kDaikinByteSilent, kDaikinBitSilent); + remote[kDaikinByteSilent] &= ~kDaikinBitSilent; } } -bool IRDaikinESP::getQuiet() { - return (getBit(kDaikinByteSilent, kDaikinBitSilent) > 0); +bool IRDaikinESP::getQuiet(void) { + return remote[kDaikinByteSilent] & kDaikinBitSilent; } -void IRDaikinESP::setPowerful(bool state) { - if (state) { - setBit(kDaikinBytePowerful, kDaikinBitPowerful); +void IRDaikinESP::setPowerful(const bool on) { + if (on) { + remote[kDaikinBytePowerful] |= kDaikinBitPowerful; // Powerful, Quiet, & Econo mode being on are mutually exclusive. - setQuiet(false); - setEcono(false); + this->setQuiet(false); + this->setEcono(false); } else { - clearBit(kDaikinBytePowerful, kDaikinBitPowerful); + remote[kDaikinBytePowerful] &= ~kDaikinBitPowerful; } } -bool IRDaikinESP::getPowerful() { - return (getBit(kDaikinBytePowerful, kDaikinBitPowerful) > 0); +bool IRDaikinESP::getPowerful(void) { + return remote[kDaikinBytePowerful] & kDaikinBitPowerful; } -void IRDaikinESP::setSensor(bool state) { - if (state) - setBit(kDaikinByteSensor, kDaikinBitSensor); +void IRDaikinESP::setSensor(const bool on) { + if (on) + remote[kDaikinByteSensor] |= kDaikinBitSensor; else - clearBit(kDaikinByteSensor, kDaikinBitSensor); + remote[kDaikinByteSensor] &= ~kDaikinBitSensor; } -bool IRDaikinESP::getSensor() { - return (getBit(kDaikinByteSensor, kDaikinBitSensor) > 0); +bool IRDaikinESP::getSensor(void) { + return remote[kDaikinByteSensor] & kDaikinBitSensor; } -void IRDaikinESP::setEcono(bool state) { - if (state) { - setBit(kDaikinByteEcono, kDaikinBitEcono); +void IRDaikinESP::setEcono(const bool on) { + if (on) { + remote[kDaikinByteEcono] |= kDaikinBitEcono; // Powerful & Econo mode being on are mutually exclusive. - setPowerful(false); + this->setPowerful(false); } else { - clearBit(kDaikinByteEcono, kDaikinBitEcono); + remote[kDaikinByteEcono] &= ~kDaikinBitEcono; } } -bool IRDaikinESP::getEcono() { - return (getBit(kDaikinByteEcono, kDaikinBitEcono) > 0); +bool IRDaikinESP::getEcono(void) { + return remote[kDaikinByteEcono] & kDaikinBitEcono; } -void IRDaikinESP::setEye(bool state) { - if (state) - setBit(kDaikinByteEye, kDaikinBitEye); +void IRDaikinESP::setMold(const bool on) { + if (on) + remote[kDaikinByteMold] |= kDaikinBitMold; else - clearBit(kDaikinByteEye, kDaikinBitEye); + remote[kDaikinByteMold] &= ~kDaikinBitMold; } -bool IRDaikinESP::getEye() { - return (getBit(kDaikinByteEye, kDaikinBitEye) > 0); +bool IRDaikinESP::getMold(void) { + return remote[kDaikinByteMold] & kDaikinBitMold; } -void IRDaikinESP::setMold(bool state) { - if (state) - setBit(kDaikinByteMold, kDaikinBitMold); +void IRDaikinESP::setComfort(const bool on) { + if (on) + remote[kDaikinByteComfort] |= kDaikinBitComfort; else - clearBit(kDaikinByteMold, kDaikinBitMold); + remote[kDaikinByteComfort] &= ~kDaikinBitComfort; } -bool IRDaikinESP::getMold() { - return (getBit(kDaikinByteMold, kDaikinBitMold) > 0); -} - -void IRDaikinESP::setBit(uint8_t byte, uint8_t bitmask) { - daikin[byte] |= bitmask; -} - -void IRDaikinESP::clearBit(uint8_t byte, uint8_t bitmask) { - bitmask = ~bitmask; - daikin[byte] &= bitmask; -} - -uint8_t IRDaikinESP::getBit(uint8_t byte, uint8_t bitmask) { - return daikin[byte] & bitmask; +bool IRDaikinESP::getComfort(void) { + return remote[kDaikinByteComfort] & kDaikinBitComfort; } // starttime: Number of minutes after midnight. -void IRDaikinESP::enableOnTimer(uint16_t starttime) { - setBit(kDaikinByteOnTimer, kDaikinBitOnTimer); - daikin[18] = (uint8_t)(starttime & 0x00FF); +void IRDaikinESP::enableOnTimer(const uint16_t starttime) { + remote[kDaikinByteOnTimer] |= kDaikinBitOnTimer; + remote[kDaikinByteOnTimerMinsLow] = starttime; // only keep 4 bits - daikin[19] &= 0xF0; - daikin[19] |= (uint8_t)((starttime >> 8) & 0x0F); + remote[kDaikinByteOnTimerMinsHigh] &= 0xF0; + remote[kDaikinByteOnTimerMinsHigh] |= ((starttime >> 8) & 0x0F); } -void IRDaikinESP::disableOnTimer() { - enableOnTimer(kDaikinUnusedTime); - clearBit(kDaikinByteOnTimer, kDaikinBitOnTimer); +void IRDaikinESP::disableOnTimer(void) { + this->enableOnTimer(kDaikinUnusedTime); + remote[kDaikinByteOnTimer] &= ~kDaikinBitOnTimer; } -uint16_t IRDaikinESP::getOnTime() { - uint16_t ret; - ret = daikin[19] & 0x0F; - ret = ret << 8; - ret += daikin[18]; - return ret; +uint16_t IRDaikinESP::getOnTime(void) { + return ((remote[kDaikinByteOnTimerMinsHigh] & 0x0F) << 8) + + remote[kDaikinByteOnTimerMinsLow]; } -bool IRDaikinESP::getOnTimerEnabled() { - return getBit(kDaikinByteOnTimer, kDaikinBitOnTimer); +bool IRDaikinESP::getOnTimerEnabled(void) { + return remote[kDaikinByteOnTimer] & kDaikinBitOnTimer; } // endtime: Number of minutes after midnight. -void IRDaikinESP::enableOffTimer(uint16_t endtime) { - setBit(kDaikinByteOffTimer, kDaikinBitOffTimer); - daikin[20] = (uint8_t)((endtime >> 4) & 0xFF); - daikin[19] &= 0x0F; - daikin[19] |= (uint8_t)((endtime & 0x000F) << 4); +void IRDaikinESP::enableOffTimer(const uint16_t endtime) { + remote[kDaikinByteOffTimer] |= kDaikinBitOffTimer; + remote[kDaikinByteOffTimerMinsHigh] = endtime >> 4; + remote[kDaikinByteOffTimerMinsLow] &= 0x0F; + remote[kDaikinByteOffTimerMinsLow] |= ((endtime & 0x0F) << 4); } -void IRDaikinESP::disableOffTimer() { - enableOffTimer(kDaikinUnusedTime); - clearBit(kDaikinByteOffTimer, kDaikinBitOffTimer); +void IRDaikinESP::disableOffTimer(void) { + this->enableOffTimer(kDaikinUnusedTime); + remote[kDaikinByteOffTimer] &= ~kDaikinBitOffTimer; } -uint16_t IRDaikinESP::getOffTime() { - uint16_t ret, tmp; - ret = daikin[20]; - ret <<= 4; - tmp = daikin[19] & 0xF0; - tmp >>= 4; - ret += tmp; - return ret; +uint16_t IRDaikinESP::getOffTime(void) { + return (remote[kDaikinByteOffTimerMinsHigh] << 4) + + ((remote[kDaikinByteOffTimerMinsLow] & 0xF0) >> 4); } -bool IRDaikinESP::getOffTimerEnabled() { - return getBit(kDaikinByteOffTimer, kDaikinBitOffTimer); +bool IRDaikinESP::getOffTimerEnabled(void) { + return remote[kDaikinByteOffTimer] & kDaikinBitOffTimer; } -void IRDaikinESP::setCurrentTime(uint16_t numMins) { - if (numMins > 24 * 60) numMins = 0; // If > 23:59, set to 00:00 - daikin[5] = (uint8_t)(numMins & 0x00FF); - // only keep 4 bits - daikin[6] &= 0xF0; - daikin[6] |= (uint8_t)((numMins >> 8) & 0x0F); +void IRDaikinESP::setCurrentTime(const uint16_t mins_since_midnight) { + uint16_t mins = mins_since_midnight; + if (mins > 24 * 60) mins = 0; // If > 23:59, set to 00:00 + remote[kDaikinByteClockMinsLow] = mins; + // only keep 3 bits + remote[kDaikinByteClockMinsHigh] &= 0xF8; + remote[kDaikinByteClockMinsHigh] |= ((mins >> 8) & 0x07); } -uint16_t IRDaikinESP::getCurrentTime() { - uint16_t ret; - ret = daikin[6] & 0x0F; - ret <<= 8; - ret += daikin[5]; - return ret; +uint16_t IRDaikinESP::getCurrentTime(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x07) << 8) + + remote[kDaikinByteClockMinsLow]; } -#ifdef ARDUINO -String IRDaikinESP::renderTime(uint16_t timemins) { +void IRDaikinESP::setCurrentDay(const uint8_t day_of_week) { + // 1 is SUN, 2 is MON, ..., 7 is SAT + uint8_t days = day_of_week; + if (days > 7) days = 0; // Enforce the limit + // Update bits 5-3 + remote[kDaikinByteClockMinsHigh] &= 0xc7; + remote[kDaikinByteClockMinsHigh] |= days << 3; +} + +uint8_t IRDaikinESP::getCurrentDay(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x38) >> 3); +} + +void IRDaikinESP::setWeeklyTimerEnable(const bool on) { + if (on) + remote[kDaikinByteWeeklyTimer] &= ~kDaikinBitWeeklyTimer; // Clear the bit. + else + remote[kDaikinByteWeeklyTimer] |= kDaikinBitWeeklyTimer; // Set the bit. +} + +bool IRDaikinESP::getWeeklyTimerEnable(void) { + return !(remote[kDaikinByteWeeklyTimer] & kDaikinBitWeeklyTimer); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikinESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kDaikinCool; + case stdAc::opmode_t::kHeat: + return kDaikinHeat; + case stdAc::opmode_t::kDry: + return kDaikinDry; + case stdAc::opmode_t::kFan: + return kDaikinFan; + default: + return kDaikinAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kDaikinFanQuiet; + case stdAc::fanspeed_t::kLow: + return kDaikinFanMin; + case stdAc::fanspeed_t::kMedium: + return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kHigh: + return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: + return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRDaikinESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kDaikinCool: return stdAc::opmode_t::kCool; + case kDaikinHeat: return stdAc::opmode_t::kHeat; + case kDaikinDry: return stdAc::opmode_t::kDry; + case kDaikinFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRDaikinESP::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kDaikinFanMax: return stdAc::fanspeed_t::kMax; + case kDaikinFanMax - 1: return stdAc::fanspeed_t::kHigh; + case kDaikinFanMin + 1: return stdAc::fanspeed_t::kMedium; + case kDaikinFanMin: return stdAc::fanspeed_t::kLow; + case kDaikinFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikinESP::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + result.clean = this->getMold(); + result.econo = this->getEcono(); + // Not supported. + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +String IRDaikinESP::renderTime(const uint16_t timemins) { String ret; -#else // ARDUINO -std::string IRDaikinESP::renderTime(uint16_t timemins) { - std::string ret; -#endif // ARDUINO - uint16_t hours, mins; - hours = timemins / 60; - ret = uint64ToString(hours) + ":"; - mins = timemins - (hours * 60); - if (mins < 10) ret += "0"; + ret = uint64ToString(timemins / 60) + ':'; + uint8_t mins = timemins % 60; + if (mins < 10) ret += '0'; ret += uint64ToString(mins); return ret; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikinESP::toString() { +String IRDaikinESP::toString(void) { String result = ""; -#else // ARDUINO -std::string IRDaikinESP::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { + result.reserve(230); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += this->getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kDaikinAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kDaikinCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kDaikinHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kDaikinDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kDaikinFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { case kDaikinFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kDaikinFanQuiet: - result += " (QUIET)"; + result += F(" (QUIET)"); break; case kDaikinFanMin: - result += " (MIN)"; + result += F(" (MIN)"); break; case kDaikinFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Powerful: "; - if (getPowerful()) - result += "On"; + result += F(", Powerful: "); + result += this->getPowerful() ? F("On") : F("Off"); + result += F(", Quiet: "); + result += this->getQuiet() ? F("On") : F("Off"); + result += F(", Sensor: "); + result += this->getSensor() ? F("On") : F("Off"); + result += F(", Mold: "); + result += this->getMold() ? F("On") : F("Off"); + result += F(", Comfort: "); + result += this->getComfort() ? F("On") : F("Off"); + result += F(", Swing (Horizontal): "); + result += this->getSwingHorizontal() ? F("On") : F("Off"); + result += F(", Swing (Vertical): "); + result += this->getSwingVertical() ? F("On") : F("Off"); + result += F(", Current Time: "); + result += this->renderTime(this->getCurrentTime()); + result += F(", Current Day: "); + switch (this->getCurrentDay()) { + case 1: + result +=F("SUN"); break; + case 2: + result +=F("MON"); break; + case 3: + result +=F("TUE"); break; + case 4: + result +=F("WED"); break; + case 5: + result +=F("THU"); break; + case 6: + result +=F("FRI"); break; + case 7: + result +=F("SAT"); break; + default: + result +=F("(UNKNOWN)"); break; + } + result += F(", On Time: "); + if (this->getOnTimerEnabled()) + result += this->renderTime(this->getOnTime()); else - result += "Off"; - result += ", Quiet: "; - if (getQuiet()) - result += "On"; + result += F("Off"); + result += F(", Off Time: "); + if (this->getOffTimerEnabled()) + result += this->renderTime(this->getOffTime()); else - result += "Off"; - result += ", Sensor: "; - if (getSensor()) - result += "On"; - else - result += "Off"; - result += ", Eye: "; - if (getEye()) - result += "On"; - else - result += "Off"; - result += ", Mold: "; - if (getMold()) - result += "On"; - else - result += "Off"; - result += ", Swing (Horizontal): "; - if (getSwingHorizontal()) - result += "On"; - else - result += "Off"; - result += ", Swing (Vertical): "; - if (getSwingVertical()) - result += "On"; - else - result += "Off"; - result += ", Current Time: " + renderTime(getCurrentTime()); - result += ", On Time: "; - if (getOnTimerEnabled()) - result += renderTime(getOnTime()); - else - result += "Off"; - result += ", Off Time: "; - if (getOffTimerEnabled()) - result += renderTime(getOffTime()); - else - result += "Off"; - + result += F("Off"); + result += F(", Weekly Timer: "); + result += this->getWeeklyTimerEnable() ? F("On") : F("Off"); return result; } -#if DAIKIN_DEBUG -// Print what we have -void IRDaikinESP::printState() { -#ifdef ARDUINO - String strbits; -#else // ARDUINO - std::string strbits; -#endif // ARDUINO - DPRINTLN("Raw Bits:"); - for (uint8_t i = 0; i < kDaikinStateLength; i++) { - strbits = uint64ToString(daikin[i], BIN); - while (strbits.length() < 8) strbits = "0" + strbits; - DPRINT(strbits); - DPRINT(" "); - } - DPRINTLN(""); - DPRINTLN(toString()); -} -#endif // DAIKIN_DEBUG - -/* - * Return most important bits to allow replay - * layout is: - * 0: Power - * 1-3: Mode - * 4-7: Fan speed/mode - * 8-14: Target Temperature - * 15: Econo - * 16: Powerful - * 17: Quiet - * 18: Sensor - * 19: Swing Vertical - * 20-31: Current time (mins since midnight) - * */ -uint32_t IRDaikinESP::getCommand() { - uint32_t ret = 0; - uint32_t tmp = 0; - if (getPower()) ret |= 0b00000000000000000000000000000001; - tmp = getMode(); - tmp = tmp << 1; - ret |= tmp; - - tmp = getFan(); - tmp <<= 4; - ret |= tmp; - - tmp = getTemp(); - tmp <<= 8; - ret |= tmp; - - if (getEcono()) ret |= 0b00000000000000001000000000000000; - if (getPowerful()) ret |= 0b00000000000000010000000000000000; - if (getQuiet()) ret |= 0b00000000000000100000000000000000; - if (getSensor()) ret |= 0b00000000000001000000000000000000; - if (getSwingVertical()) ret |= 0b00000000000010000000000000000000; - ret |= (getCurrentTime() << 20); - return ret; -} - -void IRDaikinESP::setCommand(uint32_t value) { - uint32_t tmp = 0; - if (value & 0b00000000000000000000000000000001) setPower(true); - tmp = value & 0b00000000000000000000000000001110; - tmp >>= 1; - setMode(tmp); - - tmp = value & 0b00000000000000000000000011110000; - tmp >>= 4; - setFan(tmp); - - tmp = value & 0b00000000000000000111111100000000; - tmp >>= 8; - setTemp(tmp); - - if (value & 0b00000000000000001000000000000000) setEcono(true); - if (value & 0b00000000000000010000000000000000) setPowerful(true); - if (value & 0b00000000000000100000000000000000) setQuiet(true); - if (value & 0b00000000000001000000000000000000) setSensor(true); - if (value & 0b00000000000010000000000000000000) setSwingVertical(true); - - value >>= 20; - setCurrentTime(value); -} - #if DECODE_DAIKIN - -void addbit(bool val, unsigned char data[]) { - uint8_t curbit = data[kDaikinCurBit]; - uint8_t curindex = data[kDaikinCurIndex]; - if (val) { - unsigned char bit = 1; - bit = bit << curbit; - data[curindex] |= bit; - } - curbit++; - if (curbit == 8) { - curbit = 0; - curindex++; - } - data[kDaikinCurBit] = curbit; - data[kDaikinCurIndex] = curindex; -} - -bool checkheader(decode_results *results, uint16_t *offset) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], - kDaikinZeroSpace + kDaikinGap, kDaikinTolerance, - kDaikinMarkExcess)) - return false; - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinHdrMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], kDaikinHdrSpace, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - - return true; -} - -bool readbits(decode_results *results, uint16_t *offset, - unsigned char daikin_code[], uint16_t countbits) { - for (uint16_t i = 0; i < countbits && *offset < results->rawlen - 1; - i++, (*offset)++) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinOneSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(1, daikin_code); - else if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(0, daikin_code); - else - return false; - } - return true; -} - // Decode the supplied Daikin A/C message. // Args: // results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikinRawBits) +// nbits: Nr. of bits to expect in the data portion. (kDaikinBits) // strict: Flag to indicate if we strictly adhere to the specification. // Returns: // boolean: True if it can decode it, false if it can't. // // Status: BETA / Should be working. // -// Notes: -// If DAIKIN_DEBUG enabled, will print all the set options and values. -// // Ref: // https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < kDaikinRawBits) return false; +bool IRrecv::decodeDaikin(decode_results *results, const uint16_t nbits, + const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < (2 * (nbits + kDaikinHeaderLength) + + kDaikinSections * (kHeader + kFooter) + kFooter - 1)) + return false; // Compliance - if (strict && nbits != kDaikinRawBits) return false; + if (strict && nbits != kDaikinBits) return false; uint16_t offset = kStartOffset; - unsigned char daikin_code[kDaikinStateLength + 2]; - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; + match_result_t data_result; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; - // Header (#1) - for (uint8_t i = 0; i < 10; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; + // Header #1 - Doesn't count as data. + data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + offset += data_result.used; + if (data_result.success == false) return false; // Fail + if (data_result.data) return false; // The header bits should be zero. + + // Read the Data sections. + // Keep reading bytes until we either run out of section or state to fill. + const uint8_t kSectionSize[kDaikinSections] = { + kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; + for (uint8_t section = 0, pos = 0; section < kDaikinSections; + section++) { + pos += kSectionSize[section]; + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, + kDaikinTolerance, kDaikinMarkExcess)) return false; + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikinHdrMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinHdrSpace, + kDaikinTolerance, kDaikinMarkExcess)) return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinTolerance, kDaikinMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } } - if (!checkheader(results, &offset)) return false; - - // Data (#1) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Ignore everything that has just been captured as it is not needed. - // Some remotes may not send this portion, my remote did, but it's not - // required. - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; - - // Header (#2) - if (!checkheader(results, &offset)) return false; - - // Data (#2) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Header (#3) - if (!checkheader(results, &offset)) return false; - - // Data (#3), read up everything else - if (!readbits(results, &offset, daikin_code, kDaikinBits - (8 * 8))) - return false; // Footer if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; @@ -714,24 +676,18 @@ bool IRrecv::decodeDaikin(decode_results *results, uint16_t nbits, // Compliance if (strict) { - if (!IRDaikinESP::validChecksum(daikin_code)) return false; + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikinBits) return false; + // Validate the checksum. + if (!IRDaikinESP::validChecksum(results->state)) return false; } // Success -#if DAIKIN_DEBUG - IRDaikinESP dako = IRDaikinESP(0); - dako.setRaw(daikin_code); -#ifdef ARDUINO - yield(); -#endif // ARDUINO - dako.printState(); -#endif // DAIKIN_DEBUG - - // Copy across the bits to state - for (uint8_t i = 0; i < kDaikinStateLength; i++) - results->state[i] = daikin_code[i]; - results->bits = kDaikinStateLength * 8; results->decode_type = DAIKIN; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. return true; } #endif // DECODE_DAIKIN @@ -742,12 +698,12 @@ bool IRrecv::decodeDaikin(decode_results *results, uint16_t nbits, // Args: // data: An array of kDaikin2StateLength bytes containing the IR command. // -// Status: Alpha/Untested. +// Status: BETA/Appears to work. // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/582 -void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kDaikin2Section1Length) return; // Not enough bytes to send a partial message. @@ -866,10 +822,12 @@ void IRDaikin2::setRaw(const uint8_t new_code[]) { void IRDaikin2::on() { remote_state[25] |= kDaikinBitPower; + remote_state[6] &= ~kDaikin2BitPower; } void IRDaikin2::off() { remote_state[25] &= ~kDaikinBitPower; + remote_state[6] |= kDaikin2BitPower; } void IRDaikin2::setPower(const bool state) { @@ -880,7 +838,8 @@ void IRDaikin2::setPower(const bool state) { } bool IRDaikin2::getPower() { - return (remote_state[25] & kDaikinBitPower); + return (remote_state[25] & kDaikinBitPower) && + !(remote_state[6] & kDaikin2BitPower); } uint8_t IRDaikin2::getMode() { return remote_state[25] >> 4; } @@ -898,11 +857,16 @@ void IRDaikin2::setMode(const uint8_t desired_mode) { } remote_state[25] &= 0b10001111; remote_state[25] |= (mode << 4); + // Redo the temp setting as Cool mode has a different min temp. + if (mode == kDaikinCool) this->setTemp(this->getTemp()); } // Set the temp in deg C void IRDaikin2::setTemp(const uint8_t desired) { - uint8_t temp = std::max(kDaikinMinTemp, desired); + // The A/C has a different min temp if in cool mode. + uint8_t temp = std::max( + (this->getMode() == kDaikinCool) ? kDaikin2MinCoolTemp : kDaikinMinTemp, + desired); temp = std::min(kDaikinMaxTemp, temp); remote_state[26] = temp * 2; } @@ -1170,59 +1134,130 @@ void IRDaikin2::setPurify(const bool on) { bool IRDaikin2::getPurify() { return remote_state[36] & kDaikin2BitPurify; } +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin2::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin2::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +// Convert a standard A/C vertical swing into its native version. +uint8_t IRDaikin2::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return (uint8_t)position + kDaikin2SwingVHigh; + default: + return kDaikin2SwingVAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRDaikin2::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingVHigh: return stdAc::swingv_t::kHighest; + case kDaikin2SwingVHigh + 1: return stdAc::swingv_t::kHigh; + case kDaikin2SwingVHigh + 2: + case kDaikin2SwingVHigh + 3: return stdAc::swingv_t::kMiddle; + case kDaikin2SwingVLow - 1: return stdAc::swingv_t::kLow; + case kDaikin2SwingVLow: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert a native horizontal swing to it's common equivalent. +stdAc::swingh_t IRDaikin2::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin2SwingHSwing: + case kDaikin2SwingHAuto: return stdAc::swingh_t::kAuto; + default: return stdAc::swingh_t::kOff; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin2::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN2; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRDaikinESP::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = IRDaikinESP::toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.quiet = this->getQuiet(); + result.light = this->getLight(); + result.turbo = this->getPowerful(); + result.clean = this->getMold(); + result.econo = this->getEcono(); + result.filter = this->getPurify(); + result.beep = this->getBeep(); + result.sleep = this->getSleepTimerEnabled() ? this->getSleepTime() : -1; + // Not supported. + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRDaikin2::toString() { String result = ""; -#else // ARDUINO -std::string IRDaikin2::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; + result.reserve(310); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kDaikinAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kDaikinCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kDaikinHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kDaikinDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kDaikinFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kDaikinFanAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kDaikinFanQuiet: - result += " (Quiet)"; + result += F(" (Quiet)"); break; case kDaikinFanMin: - result += " (Min)"; + result += F(" (Min)"); break; case kDaikinFanMax: - result += " (Max)"; + result += F(" (Max)"); break; } - result += ", Swing (V): " + uint64ToString(getSwingVertical()); + result += F(", Swing (V): "); + result += uint64ToString(getSwingVertical()); switch (getSwingVertical()) { case kDaikin2SwingVHigh: - result += " (Highest)"; + result += F(" (Highest)"); break; case 2: case 3: @@ -1230,94 +1265,98 @@ std::string IRDaikin2::toString() { case 5: break; case kDaikin2SwingVLow: - result += " (Lowest)"; + result += F(" (Lowest)"); break; case kDaikin2SwingVBreeze: - result += " (Breeze)"; + result += F(" (Breeze)"); break; case kDaikin2SwingVCirculate: - result += " (Circulate)"; + result += F(" (Circulate)"); break; case kDaikin2SwingVAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; default: - result += " (Unknown)"; + result += F(" (Unknown)"); } - result += ", Swing (H): " + uint64ToString(getSwingHorizontal()); + result += F(", Swing (H): "); + result += uint64ToString(getSwingHorizontal()); switch (getSwingHorizontal()) { case kDaikin2SwingHAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kDaikin2SwingHSwing: - result += " (Swing)"; + result += F(" (Swing)"); break; } - result += ", Clock: " + IRDaikinESP::renderTime(getCurrentTime()); - result += ", On Time: "; + result += F(", Clock: "); + result += IRDaikinESP::renderTime(getCurrentTime()); + result += F(", On Time: "); if (getOnTimerEnabled()) result += IRDaikinESP::renderTime(getOnTime()); else - result += "Off"; - result += ", Off Time: "; + result += F("Off"); + result += F(", Off Time: "); if (getOffTimerEnabled()) result += IRDaikinESP::renderTime(getOffTime()); else - result += "Off"; - result += ", Sleep Time: "; + result += F("Off"); + result += F(", Sleep Time: "); if (getSleepTimerEnabled()) result += IRDaikinESP::renderTime(getSleepTime()); else - result += "Off"; - result += ", Beep: " + uint64ToString(getBeep()); + result += F("Off"); + result += F(", Beep: "); + result += uint64ToString(getBeep()); switch (getBeep()) { case kDaikinBeepLoud: - result += " (Loud)"; + result += F(" (Loud)"); break; case kDaikinBeepQuiet: - result += " (Quiet)"; + result += F(" (Quiet)"); break; case kDaikinBeepOff: - result += " (Off)"; + result += F(" (Off)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Light: " + uint64ToString(getLight()); + result += F(", Light: "); + result += uint64ToString(getLight()); switch (getLight()) { case kDaikinLightBright: - result += " (Bright)"; + result += F(" (Bright)"); break; case kDaikinLightDim: - result += " (Dim)"; + result += F(" (Dim)"); break; case kDaikinLightOff: - result += " (Off)"; + result += F(" (Off)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Mold: "; - result += (getMold() ? "On" : "Off"); - result += ", Clean: "; - result += (getClean() ? "On" : "Off"); - result += ", Fresh Air: "; + result += F(", Mold: "); + result += (getMold() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (getClean() ? F("On") : F("Off")); + result += F(", Fresh Air: "); if (getFreshAir()) result += (getFreshAirHigh() ? "High" : "On"); else - result += "Off"; - result += ", Eye: "; - result += (getEye() ? "On" : "Off"); - result += ", Eye Auto: "; - result += (getEyeAuto() ? "On" : "Off"); - result += ", Quiet: "; - result += (getQuiet() ? "On" : "Off"); - result += ", Powerful: "; - result += (getPowerful() ? "On" : "Off"); + result += F("Off"); + result += F(", Eye: "); + result += (getEye() ? F("On") : F("Off")); + result += F(", Eye Auto: "); + result += (getEyeAuto() ? F("On") : F("Off")); + result += F(", Quiet: "); + result += (getQuiet() ? F("On") : F("Off")); + result += F(", Powerful: "); + result += (getPowerful() ? F("On") : F("Off")); result += ", Purify: "; - result += (getPurify() ? "On" : "Off"); - result += ", Econo: "; - result += (getEcono() ? "On" : "Off"); + result += (getPurify() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (getEcono() ? F("On") : F("Off")); return result; } @@ -1413,3 +1452,606 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, return true; } #endif // DECODE_DAIKIN2 + +#if SEND_DAIKIN216 +// Send a Daikin 216 bit A/C message. +// +// Args: +// data: An array of kDaikin216StateLength bytes containing the IR command. +// +// Status: Alpha/Untested on a real device. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin216Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, data, + kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin216HdrMark, kDaikin216HdrSpace, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + data + kDaikin216Section1Length, + nbytes - kDaikin216Section1Length, + kDaikin216Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN216 + +// Class for handling Daikin 216 bit / 27 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC433B69 remote +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +IRDaikin216::IRDaikin216(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikin216::begin() { _irsend.begin(); } + +#if SEND_DAIKIN216 +void IRDaikin216::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin216(remote_state, kDaikin216StateLength, repeat); +} +#endif // SEND_DAIKIN216 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin216::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin216Section1Length - 1 || + state[kDaikin216Section1Length - 1] != sumBytes( + state, kDaikin216Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin216Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin216Section1Length, + length - kDaikin216Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin216::checksum() { + remote_state[kDaikin216Section1Length - 1] = sumBytes( + remote_state, kDaikin216Section1Length - 1); + remote_state[kDaikin216StateLength - 1] = sumBytes( + remote_state + kDaikin216Section1Length, kDaikin216Section2Length - 1); +} + +void IRDaikin216::stateReset() { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[3] = 0xF0; + // remote_state[7] is a checksum byte, it will be set by checksum(). + remote_state[8] = 0x11; + remote_state[9] = 0xDA; + remote_state[10] = 0x27; + remote_state[23] = 0xC0; + // remote_state[26] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin216::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin216::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin216StateLength; i++) + remote_state[i] = new_code[i]; +} + + +void IRDaikin216::on() { + remote_state[kDaikin216BytePower] |= kDaikinBitPower; +} + +void IRDaikin216::off() { + remote_state[kDaikin216BytePower] &= ~kDaikinBitPower; +} + +void IRDaikin216::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin216::getPower() { + return remote_state[kDaikin216BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin216::getMode() { + return (remote_state[kDaikin216ByteMode] & kDaikin216MaskMode) >> 4; +} + +void IRDaikin216::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote_state[kDaikin216ByteMode] &= ~kDaikin216MaskMode; + remote_state[kDaikin216ByteMode] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin216::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Set the temp in deg C +void IRDaikin216::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp); + remote_state[kDaikin216ByteTemp] &= ~kDaikin216MaskTemp; + remote_state[kDaikin216ByteTemp] |= (degrees << 1); +} + +uint8_t IRDaikin216::getTemp(void) { + return (remote_state[kDaikin216ByteTemp] & kDaikin216MaskTemp) >> 1; +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin216::setFan(const uint8_t fan) { + // Set the fan speed bits, leave low 4 bits alone + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + remote_state[kDaikin216ByteFan] &= ~kDaikin216MaskFan; + remote_state[kDaikin216ByteFan] |= (fanset << 4); +} + +uint8_t IRDaikin216::getFan() { + uint8_t fan = remote_state[kDaikin216ByteFan] >> 4; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin216::convertFan(const stdAc::fanspeed_t speed) { + return IRDaikinESP::convertFan(speed); +} + +void IRDaikin216::setSwingVertical(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingV] |= kDaikin216MaskSwingV; + else + remote_state[kDaikin216ByteSwingV] &= ~kDaikin216MaskSwingV; +} + +bool IRDaikin216::getSwingVertical(void) { + return remote_state[kDaikin216ByteSwingV] & kDaikin216MaskSwingV; +} + +void IRDaikin216::setSwingHorizontal(const bool on) { + if (on) + remote_state[kDaikin216ByteSwingH] |= kDaikin216MaskSwingH; + else + remote_state[kDaikin216ByteSwingH] &= ~kDaikin216MaskSwingH; +} + +bool IRDaikin216::getSwingHorizontal(void) { + return remote_state[kDaikin216ByteSwingH] & kDaikin216MaskSwingH; +} + +// This is a horrible hack till someone works out the quiet mode bit. +void IRDaikin216::setQuiet(const bool on) { + if (on) { + this->setFan(kDaikinFanQuiet); + // Powerful & Quiet mode being on are mutually exclusive. + this->setPowerful(false); + } else if (this->getFan() == kDaikinFanQuiet) { + this->setFan(kDaikinFanAuto); + } +} + +// This is a horrible hack till someone works out the quiet mode bit. +bool IRDaikin216::getQuiet(void) { + return this->getFan() == kDaikinFanQuiet; +} + +void IRDaikin216::setPowerful(const bool on) { + if (on) { + remote_state[kDaikin216BytePowerful] |= kDaikinBitPowerful; + // Powerful & Quiet mode being on are mutually exclusive. + this->setQuiet(false); + } else { + remote_state[kDaikin216BytePowerful] &= ~kDaikinBitPowerful; + } +} + +bool IRDaikin216::getPowerful() { + return remote_state[kDaikin216BytePowerful] & kDaikinBitPowerful; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin216::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN216; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRDaikinESP::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = IRDaikinESP::toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + // Not supported. + result.light = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikin216::toString() { + String result = ""; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + if (this->getPower()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (getMode()) { + case kDaikinAuto: + result += F(" (AUTO)"); + break; + case kDaikinCool: + result += F(" (COOL)"); + break; + case kDaikinHeat: + result += F(" (HEAT)"); + break; + case kDaikinDry: + result += F(" (DRY)"); + break; + case kDaikinFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kDaikinFanAuto: + result += F(" (AUTO)"); + break; + case kDaikinFanQuiet: + result += F(" (QUIET)"); + break; + case kDaikinFanMin: + result += F(" (MIN)"); + break; + case kDaikinFanMax: + result += F(" (MAX)"); + break; + } + result += F(", Swing (Horizontal): "); + result += this->getSwingHorizontal() ? F("On") : F("Off"); + result += F(", Swing (Vertical): "); + result += this->getSwingVertical() ? F("On") : F("Off"); + result += F(", Quiet: "); + result += (this->getQuiet() ? F("On") : F("Off")); + result += F(", Powerful: "); + result += (this->getPowerful() ? F("On") : F("Off")); + return result; +} + +#if DECODE_DAIKIN216 +// Decode the supplied Daikin 216 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin216Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC433B69 remote. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/danny-source/Arduino_DY_IRDaikin +bool IRrecv::decodeDaikin216(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin216Bits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + uint8_t sectionSize[kDaikin216Sections] = {kDaikin216Section1Length, + kDaikin216Section2Length}; + + // Sections + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kDaikin216Sections; + section++) { + pos += sectionSize[section]; + + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikin216HdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin216HdrSpace)) + return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, kDaikin216BitMark, + kDaikin216OneSpace, kDaikin216BitMark, + kDaikin216ZeroSpace, kDaikinTolerance, kDaikinMarkExcess, + false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikin216BitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (section < kDaikin216Sections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kDaikin216Gap)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kDaikin216Gap)) return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikin216Bits) return false; + // Validate the checksum. + if (!IRDaikin216::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN216; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN216 + +#if SEND_DAIKIN160 +// Send a Daikin 160 bit A/C message. +// +// Args: +// data: An array of kDaikin160StateLength bytes containing the IR command. +// +// Status: Alpha/Untested on a real device. +// +// Supported devices: +// - Daikin ARC423A5 remote. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/731 +void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin160Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, data, + kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + data + kDaikin160Section1Length, + nbytes - kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN160 + +// Class for handling Daikin 160 bit / 20 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC423A5 remote +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/731 +IRDaikin160::IRDaikin160(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikin160::begin() { _irsend.begin(); } + +#if SEND_DAIKIN160 +void IRDaikin160::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin160(remote_state, kDaikin160StateLength, repeat); +} +#endif // SEND_DAIKIN160 + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin160Section1Length - 1 || + state[kDaikin160Section1Length - 1] != sumBytes( + state, kDaikin160Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin160Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin160Section1Length, + length - kDaikin160Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin160::checksum() { + remote_state[kDaikin160Section1Length - 1] = sumBytes( + remote_state, kDaikin160Section1Length - 1); + remote_state[kDaikin160StateLength - 1] = sumBytes( + remote_state + kDaikin160Section1Length, kDaikin160Section2Length - 1); +} + +void IRDaikin160::stateReset() { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[3] = 0xF0; + // remote_state[6] is a checksum byte, it will be set by checksum(). + remote_state[7] = 0x11; + remote_state[8] = 0xDA; + remote_state[9] = 0x27; + // remote_state[19] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin160::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin160::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) + remote_state[i] = new_code[i]; +} + +#if DECODE_DAIKIN160 +// Decode the supplied Daikin 160 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin160Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC423A5 remote. +// +// Status: BETA / Probably works. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/731 +bool IRrecv::decodeDaikin160(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin160Bits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; + match_result_t data_result; + uint8_t sectionSize[kDaikin160Sections] = {kDaikin160Section1Length, + kDaikin160Section2Length}; + + // Sections + // Keep reading bytes until we either run out of section or state to fill. + for (uint8_t section = 0, pos = 0; section < kDaikin160Sections; + section++) { + pos += sectionSize[section]; + + // Section Header + if (!matchMark(results->rawbuf[offset++], kDaikin160HdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikin160HdrSpace)) + return false; + + // Section Data + for (; offset <= results->rawlen - 16 && i < pos; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, + kDaikin160ZeroSpace, kDaikinTolerance, kDaikinMarkExcess, + false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + + // Section Footer + if (!matchMark(results->rawbuf[offset++], kDaikin160BitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (section < kDaikin160Sections - 1) { // Inter-section gaps. + if (!matchSpace(results->rawbuf[offset++], kDaikin160Gap)) return false; + } else { // Last section / End of message gap. + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kDaikin160Gap)) return false; + } + } + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kDaikin160Bits) return false; + // Validate the checksum. + if (!IRDaikin160::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN160; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN160 diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 4423d47..d35b929 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -1,34 +1,40 @@ // Copyright 2016 sillyfrog // Copyright 2017 sillyfrog, crankyoldgit // Copyright 2018-2019 crankyoldgit + +// Supports: +// Brand: Daikin, Model: ARC433** remote +// Brand: Daikin, Model: ARC477A1 remote +// Brand: Daikin, Model: FTXZ25NV1B A/C +// Brand: Daikin, Model: FTXZ35NV1B A/C +// Brand: Daikin, Model: FTXZ50NV1B A/C +// Brand: Daikin, Model: ARC433B69 remote +// Brand: Daikin, Model: ARC423A5 remote + #ifndef IR_DAIKIN_H_ #define IR_DAIKIN_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" #include "IRsend.h" - -// Option to disable the additional Daikin debug info to conserve memory -#define DAIKIN_DEBUG false - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif /* Daikin AC map - byte 5=Current time, mins past midnight, low bits - byte 6 - b0-b3=Current time, mins past midnight, high bits + byte 6= + b4:Comfort byte 7= checksum of the first part (and last byte before a 29ms pause) - byte 13=mode + byte 13=Current time, mins past midnight, low bits + byte 14 + b5-b3=Day of the week (SUN=1, MON=2, ..., SAT=7) + b2-b0=Current time, mins past midnight, high bits + byte 15= checksum of the second part (and last byte before a 29ms pause) + byte 21=mode b7 = 0 b6+b5+b4 = Mode Modes: b6+b5+b4 @@ -41,8 +47,8 @@ b2 = OFF timer set b1 = ON timer set b0 = Air Conditioner ON - byte 14=temp*2 (Temp should be between 10 - 32) - byte 16=Fan + byte 22=temp*2 (Temp should be between 10 - 32) + byte 24=Fan FAN control b7+b6+b5+b4 = Fan speed Fan: b7+b6+b5+b4 @@ -57,23 +63,23 @@ Swing control up/down: 0000 = Swing up/down off 1111 = Swing up/down on - byte 17 + byte 25 Swing control left/right: 0000 = Swing left/right off 1111 = Swing left/right on - byte 18=On timer mins past midnight, low bits - byte 19 + byte 26=On timer mins past midnight, low bits + byte 27 b0-b3=On timer mins past midnight, high bits b4-b7=Off timer mins past midnight, low bits - byte 20=Off timer mins past midnight, high bits - byte 21=Aux -> Powerful (bit 1), Silent (bit 5) - byte 24=Aux2 + byte 28=Off timer mins past midnight, high bits + byte 29=Aux -> Powerful (bit 1), Silent (bit 5) + byte 32=Aux2 b1: Sensor b2: Econo mode b7: Intelligent eye on - byte 25=Aux3 + byte 33=Aux3 b1: Mold Proof - byte 26= checksum of the second part + byte 34= checksum of the third part */ // Constants @@ -88,24 +94,46 @@ const uint8_t kDaikinFanMin = 1; const uint8_t kDaikinFanMax = 5; const uint8_t kDaikinFanAuto = 0b1010; const uint8_t kDaikinFanQuiet = 0b1011; -const uint8_t kDaikinBytePower = 13; +const uint16_t kDaikinHeaderLength = 5; +const uint8_t kDaikinSections = 3; +const uint8_t kDaikinSection1Length = 8; +const uint8_t kDaikinSection2Length = 8; +const uint8_t kDaikinSection3Length = + kDaikinStateLength - kDaikinSection1Length - kDaikinSection2Length; +const uint8_t kDaikinByteComfort = 6; +const uint8_t kDaikinByteChecksum1 = 7; +const uint8_t kDaikinBitComfort = 0b00010000; +const uint8_t kDaikinByteClockMinsLow = 13; +const uint8_t kDaikinByteClockMinsHigh = 14; +const uint8_t kDaikinByteChecksum2 = 15; +const uint8_t kDaikinBytePower = 21; const uint8_t kDaikinBitPower = 0b00000001; -const uint8_t kDaikinBytePowerful = 21; +const uint8_t kDaikinByteTemp = 22; +const uint8_t kDaikinByteFan = 24; +const uint8_t kDaikinByteSwingH = 25; +const uint8_t kDaikinByteOnTimerMinsLow = 26; +const uint8_t kDaikinByteOnTimerMinsHigh = 27; +const uint8_t kDaikinByteOffTimerMinsLow = kDaikinByteOnTimerMinsHigh; +const uint8_t kDaikinByteOffTimerMinsHigh = 28; +const uint8_t kDaikinBytePowerful = 29; const uint8_t kDaikinBitPowerful = 0b00000001; -const uint8_t kDaikinByteSilent = 21; +const uint8_t kDaikinByteSilent = kDaikinBytePowerful; const uint8_t kDaikinBitSilent = 0b00100000; -const uint8_t kDaikinByteSensor = 24; +const uint8_t kDaikinByteSensor = 32; const uint8_t kDaikinBitSensor = 0b00000010; -const uint8_t kDaikinByteEcono = 24; +const uint8_t kDaikinByteEcono = kDaikinByteSensor; const uint8_t kDaikinBitEcono = 0b00000100; -const uint8_t kDaikinByteEye = 24; +const uint8_t kDaikinByteEye = kDaikinByteSensor; const uint8_t kDaikinBitEye = 0b10000000; -const uint8_t kDaikinByteMold = 25; +const uint8_t kDaikinByteWeeklyTimer = kDaikinByteSensor; +const uint8_t kDaikinBitWeeklyTimer = 0b10000000; +const uint8_t kDaikinByteMold = 33; const uint8_t kDaikinBitMold = 0b00000010; -const uint8_t kDaikinByteOffTimer = 13; +const uint8_t kDaikinByteOffTimer = kDaikinBytePower; const uint8_t kDaikinBitOffTimer = 0b00000100; -const uint8_t kDaikinByteOnTimer = 13; +const uint8_t kDaikinByteOnTimer = kDaikinByteOffTimer; const uint8_t kDaikinBitOnTimer = 0b00000010; +const uint8_t kDaikinByteChecksum3 = kDaikinStateLength - 1; const uint16_t kDaikinUnusedTime = 0x600; const uint8_t kDaikinBeepQuiet = 1; const uint8_t kDaikinBeepLoud = 2; @@ -150,6 +178,7 @@ const uint8_t kDaikin2BitMold = 0b00001000; const uint8_t kDaikin2BitClean = 0b00100000; const uint8_t kDaikin2BitFreshAir = 0b00000001; const uint8_t kDaikin2BitFreshAirHigh = 0b10000000; +const uint8_t kDaikin2BitPower = 0b10000000; const uint8_t kDaikin2LightMask = 0b00110000; const uint8_t kDaikin2BeepMask = 0b11000000; const uint8_t kDaikin2SwingVHigh = 0x1; @@ -159,8 +188,45 @@ const uint8_t kDaikin2SwingVCirculate = 0xD; const uint8_t kDaikin2SwingVAuto = 0xE; const uint8_t kDaikin2SwingHAuto = 0xBE; const uint8_t kDaikin2SwingHSwing = 0xBF; +const uint8_t kDaikin2MinCoolTemp = 18; // Min temp (in C) when in Cool mode. +// Another variant of the protocol for the Daikin ARC433B69 remote. +const uint16_t kDaikin216Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin216HdrMark = 3440; +const uint16_t kDaikin216HdrSpace = 1750; +const uint16_t kDaikin216BitMark = 420; +const uint16_t kDaikin216OneSpace = 1300; +const uint16_t kDaikin216ZeroSpace = 450; +const uint16_t kDaikin216Gap = 29650; +const uint16_t kDaikin216Sections = 2; +const uint16_t kDaikin216Section1Length = 8; +const uint16_t kDaikin216Section2Length = kDaikin216StateLength - + kDaikin216Section1Length; +const uint8_t kDaikin216BytePower = 13; +const uint8_t kDaikin216ByteMode = kDaikin216BytePower; +const uint8_t kDaikin216MaskMode = 0b01110000; +const uint8_t kDaikin216ByteTemp = 14; +const uint8_t kDaikin216MaskTemp = 0b01111110; +const uint8_t kDaikin216ByteFan = 16; +const uint8_t kDaikin216MaskFan = 0b11110000; +const uint8_t kDaikin216ByteSwingV = 16; +const uint8_t kDaikin216MaskSwingV = 0b00001111; +const uint8_t kDaikin216ByteSwingH = 17; +const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV; +const uint8_t kDaikin216BytePowerful = 21; +// Another variant of the protocol for the Daikin ARC423A5 remote. +const uint16_t kDaikin160Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin160HdrMark = 5000; +const uint16_t kDaikin160HdrSpace = 2145; +const uint16_t kDaikin160BitMark = 342; +const uint16_t kDaikin160OneSpace = 1786; +const uint16_t kDaikin160ZeroSpace = 700; +const uint16_t kDaikin160Gap = 29650; +const uint16_t kDaikin160Sections = 2; +const uint16_t kDaikin160Section1Length = 7; +const uint16_t kDaikin160Section2Length = kDaikin160StateLength - + kDaikin160Section1Length; // Legacy defines. #define DAIKIN_COOL kDaikinCool #define DAIKIN_HEAT kDaikinHeat @@ -180,70 +246,72 @@ class IRDaikinESP { #if SEND_DAIKIN void send(const uint16_t repeat = kDaikinDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - uint8_t getMode(); - void setMode(uint8_t mode); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - bool getQuiet(); - void setQuiet(bool state); - bool getPowerful(); - void setPowerful(bool state); - void setSensor(bool state); - bool getSensor(); - void setEcono(bool state); - bool getEcono(); - void setEye(bool state); - bool getEye(); - void setMold(bool state); - bool getMold(); - void enableOnTimer(uint16_t starttime); - void disableOnTimer(); - uint16_t getOnTime(); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + bool getQuiet(void); + void setQuiet(const bool on); + bool getPowerful(void); + void setPowerful(const bool on); + void setSensor(const bool on); + bool getSensor(void); + void setEcono(const bool on); + bool getEcono(void); + void setMold(const bool on); + bool getMold(void); + void setComfort(const bool on); + bool getComfort(void); + void enableOnTimer(const uint16_t starttime); + void disableOnTimer(void); + uint16_t getOnTime(void); bool getOnTimerEnabled(); - void enableOffTimer(uint16_t endtime); - void disableOffTimer(); - uint16_t getOffTime(); - bool getOffTimerEnabled(); - void setCurrentTime(uint16_t time); - uint16_t getCurrentTime(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); -#if DAIKIN_DEBUG - void printState(); -#endif // DAIKIN_DEBUG - uint32_t getCommand(); - void setCommand(uint32_t value); + void enableOffTimer(const uint16_t endtime); + void disableOffTimer(void); + uint16_t getOffTime(void); + bool getOffTimerEnabled(void); + void setCurrentTime(const uint16_t mins_since_midnight); + uint16_t getCurrentTime(void); + void setCurrentDay(const uint8_t day_of_week); + uint8_t getCurrentDay(void); + void setWeeklyTimerEnable(const bool on); + bool getWeeklyTimerEnable(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kDaikinStateLength); static bool validChecksum(uint8_t state[], const uint16_t length = kDaikinStateLength); -#ifdef ARDUINO - String toString(); - static String renderTime(uint16_t timemins); -#else - std::string toString(); - static std::string renderTime(uint16_t timemins); -#endif + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); + static String renderTime(const uint16_t timemins); +#ifndef UNIT_TEST private: - // # of bytes per command - uint8_t daikin[kDaikinStateLength]; - void stateReset(); - void checksum(); - void setBit(uint8_t byte, uint8_t bitmask); - void clearBit(uint8_t byte, uint8_t bitmask); - uint8_t getBit(uint8_t byte, uint8_t bitmask); IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote[kDaikinStateLength]; + void stateReset(void); + void checksum(void); }; // Class to emulate a Daikin ARC477A1 remote. @@ -253,6 +321,7 @@ class IRDaikin2 { #if SEND_DAIKIN2 void send(const uint16_t repeat = kDaikin2DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif void begin(); void on(); @@ -315,22 +384,104 @@ class IRDaikin2 { void setCommand(uint32_t value); static bool validChecksum(uint8_t state[], const uint16_t length = kDaikin2StateLength); -#ifdef ARDUINO + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::swingv_t toCommonSwingV(const uint8_t setting); + static stdAc::swingh_t toCommonSwingH(const uint8_t setting); + stdAc::state_t toCommon(void); String toString(); static String renderTime(uint16_t timemins); -#else - std::string toString(); - static std::string renderTime(uint16_t timemins); -#endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // # of bytes per command uint8_t remote_state[kDaikin2StateLength]; void stateReset(); void checksum(); void clearOnTimerFlag(); void clearSleepTimerFlag(); +}; + +// Class to emulate a Daikin ARC433B69 remote. +class IRDaikin216 { + public: + explicit IRDaikin216(uint16_t pin); + +#if SEND_DAIKIN216 + void send(const uint16_t repeat = kDaikin216DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin216StateLength); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(); + void setMode(const uint8_t mode); + uint8_t getMode(void); + static uint8_t convertMode(const stdAc::opmode_t mode); + void setFan(const uint8_t fan); + uint8_t getFan(void); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); + stdAc::state_t toCommon(void); + String toString(void); + static String renderTime(const uint16_t timemins); +#ifndef UNIT_TEST + + private: IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin216StateLength]; + void stateReset(); + void checksum(); +}; + +// Class to emulate a Daikin ARC423A5 remote. +class IRDaikin160 { + public: + explicit IRDaikin160(uint16_t pin); + +#if SEND_DAIKIN160 + void send(const uint16_t repeat = kDaikin160DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin160StateLength); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin160StateLength]; + void stateReset(); + void checksum(); }; #endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266/src/ir_Denon.cpp b/lib/IRremoteESP8266/src/ir_Denon.cpp index 6798e02..e7dc12f 100644 --- a/lib/IRremoteESP8266/src/ir_Denon.cpp +++ b/lib/IRremoteESP8266/src/ir_Denon.cpp @@ -6,12 +6,6 @@ #include "IRsend.h" #include "IRutils.h" -// DDDD EEEEE N N OOO N N -// D D E NN N O O NN N -// D D EEE N N N O O N N N -// D D E N NN O O N NN -// DDDD EEEEE N N OOO N N - // Original Denon support added by https://github.com/csBlueChip // Ported over by Massimiliano Pinto @@ -43,7 +37,7 @@ const uint64_t kDenonManufacturer = 0x2A4CULL; // // Args: // data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically DENON_BITS. +// nbits: Nr. of bits of data to be sent. Typically kDenonBits. // repeat: Nr. of additional times the message is to be sent. // // Status: BETA / Should be working. @@ -70,7 +64,7 @@ void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) { // // Args: // results: Ptr to the data to decode and where to store the decode result. -// nbits: Expected nr. of data bits. (Typically DENON_BITS) +// nbits: Expected nr. of data bits. (Typically kDenonBits) // Returns: // boolean: True if it can decode it, false if it can't. // @@ -82,8 +76,8 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) { // Compliance if (strict) { switch (nbits) { - case DENON_BITS: - case DENON_48_BITS: + case kDenonBits: + case kDenon48Bits: case kDenonLegacyBits: break; default: diff --git a/lib/IRremoteESP8266/src/ir_Dish.cpp b/lib/IRremoteESP8266/src/ir_Dish.cpp index 040aa3b..621a67e 100644 --- a/lib/IRremoteESP8266/src/ir_Dish.cpp +++ b/lib/IRremoteESP8266/src/ir_Dish.cpp @@ -5,15 +5,12 @@ #include "IRsend.h" #include "IRutils.h" -// DDDD IIIII SSSS H H -// D D I S H H -// D D I SSS HHHHH -// D D I S H H -// DDDD IIIII SSSS H H - // DISH support originally by Todd Treece // http://unionbridge.org/design/ircommand +// Supports: +// Brand: DISH NETWORK, Model: echostar 301 + // Constants // Ref: // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp diff --git a/lib/IRremoteESP8266/src/ir_Electra.cpp b/lib/IRremoteESP8266/src/ir_Electra.cpp index df69be7..9b51edc 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.cpp +++ b/lib/IRremoteESP8266/src/ir_Electra.cpp @@ -1,15 +1,9 @@ -// Copyright 2018 David Conran +// Copyright 2018, 2019 David Conran #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// EEEEEEE LL EEEEEEE CCCCC TTTTTTT RRRRRR AAA -// EE LL EE CC C TTT RR RR AAAAA -// EEEEE LL EEEEE CC TTT RRRRRR AA AA -// EE LL EE CC C TTT RR RR AAAAAAA -// EEEEEEE LLLLLLL EEEEEEE CCCCC TTT RR RR AA AA - // Electra A/C added by crankyoldgit // // Equipment it seems compatible with: @@ -17,6 +11,7 @@ // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/527 +// https://github.com/markszabo/IRremoteESP8266/issues/642 // Constants const uint16_t kElectraAcHdrMark = 9166; @@ -24,7 +19,7 @@ const uint16_t kElectraAcBitMark = 646; const uint16_t kElectraAcHdrSpace = 4470; const uint16_t kElectraAcOneSpace = 1647; const uint16_t kElectraAcZeroSpace = 547; -const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess. +const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess. #if SEND_ELECTRA_AC // Send a Electra message @@ -36,13 +31,15 @@ const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess. // // Status: Alpha / Needs testing against a real device. // -void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendElectraAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { for (uint16_t r = 0; r <= repeat; r++) sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark, kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, 38000, // Complete guess of the modulation frequency. - true, 0, 50); + false, // Send data in LSB order per byte + 0, 50); } #endif @@ -56,7 +53,7 @@ void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: Alpha / Needs testing against a real device. +// Status: Beta / Probably works. // bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, bool strict) { @@ -68,8 +65,6 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, return false; // Not strictly a ELECTRA_AC message. } - // The protocol sends the data normal + inverted, alternating on - // each byte. Hence twice the number of expected data bits. if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid ELECTRA_AC message. @@ -87,7 +82,7 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, i++, dataBitsSoFar += 8, offset += data_result.used) { data_result = matchData(&(results->rawbuf[offset]), 8, kElectraAcBitMark, kElectraAcOneSpace, kElectraAcBitMark, - kElectraAcZeroSpace, kTolerance, 0, true); + kElectraAcZeroSpace, kTolerance, 0, false); if (data_result.success == false) return false; // Fail results->state[i] = data_result.data; } @@ -99,7 +94,12 @@ bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, return false; // Compliance - if (strict && dataBitsSoFar != nbits) return false; + if (strict) { + if (dataBitsSoFar != nbits) return false; + // Verify the checksum. + if (sumBytes(results->state, (dataBitsSoFar / 8) - 1) != + results->state[(dataBitsSoFar / 8) - 1]) return false; + } // Success results->decode_type = ELECTRA_AC; diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp index 60bb96e..c1b9dae 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp @@ -1,4 +1,5 @@ -// Copyright 2017 Jonny Graham, David Conran +// Copyright 2017 Jonny Graham +// Copyright 2017-2019 David Conran #include "ir_Fujitsu.h" #include #ifndef ARDUINO @@ -12,6 +13,9 @@ // Equipment it seems compatible with: // * Fujitsu ASYG30LFCA with remote AR-RAH2E // * Fujitsu AST9RSGCW with remote AR-DB1 +// * Fujitsu ASYG7LMCA with remote AR-REB1E +// * Fujitsu AR-RAE1E remote. +// * Fujitsu General with remote AR-JW2 // * // Ref: @@ -36,10 +40,10 @@ const uint16_t kFujitsuAcMinGap = 8100; // repeat: Nr. of times the message is to be repeated. // (Default = kFujitsuAcMinRepeat). // -// Status: BETA / Appears to be working. +// Status: STABLE / Known Good. // -void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +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, @@ -50,47 +54,53 @@ void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes, // Code to emulate Fujitsu A/C IR remote control unit. // Initialise the object. -IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model) +IRFujitsuAC::IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model) : _irsend(pin) { - setModel(model); - stateReset(); + this->setModel(model); + this->stateReset(); } -void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) { +void IRFujitsuAC::setModel(const fujitsu_ac_remote_model_t model) { _model = model; switch (model) { case ARDB1: + case ARJW2: _state_length = kFujitsuAcStateLength - 1; _state_length_short = kFujitsuAcStateLengthShort - 1; break; + case ARRAH2E: + case ARREB1E: default: _state_length = kFujitsuAcStateLength; _state_length_short = kFujitsuAcStateLengthShort; } } +fujitsu_ac_remote_model_t IRFujitsuAC::getModel(void) { return _model; } + // Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset() { +void IRFujitsuAC::stateReset(void) { _temp = 24; _fanSpeed = kFujitsuAcFanHigh; _mode = kFujitsuAcModeCool; _swingMode = kFujitsuAcSwingBoth; _cmd = kFujitsuAcCmdTurnOn; - buildState(); + this->buildState(); } // Configure the pin for output. -void IRFujitsuAC::begin() { _irsend.begin(); } +void IRFujitsuAC::begin(void) { _irsend.begin(); } #if SEND_FUJITSU_AC // Send the current desired state to the IR LED. void IRFujitsuAC::send(const uint16_t repeat) { - getRaw(); + this->buildState(); _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); } #endif // SEND_FUJITSU_AC -void IRFujitsuAC::buildState() { +void IRFujitsuAC::buildState(void) { remote_state[0] = 0x14; remote_state[1] = 0x63; remote_state[2] = 0x00; @@ -98,21 +108,23 @@ void IRFujitsuAC::buildState() { remote_state[4] = 0x10; bool fullCmd = false; switch (_cmd) { - case kFujitsuAcCmdTurnOff: - remote_state[5] = 0x02; - break; - case kFujitsuAcCmdStepHoriz: - remote_state[5] = 0x79; - break; - case kFujitsuAcCmdStepVert: - remote_state[5] = 0x6C; + case kFujitsuAcCmdTurnOff: // 0x02 + case kFujitsuAcCmdEcono: // 0x09 + case kFujitsuAcCmdPowerful: // 0x39 + case kFujitsuAcCmdStepVert: // 0x6C + case kFujitsuAcCmdToggleSwingVert: // 0x6D + case kFujitsuAcCmdStepHoriz: // 0x79 + case kFujitsuAcCmdToggleSwingHoriz: // 0x7A + remote_state[5] = _cmd; break; default: switch (_model) { case ARRAH2E: + case ARREB1E: remote_state[5] = 0xFE; break; case ARDB1: + case ARJW2: remote_state[5] = 0xFC; break; } @@ -127,49 +139,61 @@ void IRFujitsuAC::buildState() { remote_state[7] = 0x30; remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4); remote_state[9] = _mode | 0 << 4; // timer off - remote_state[10] = _fanSpeed | _swingMode << 4; + remote_state[10] = _fanSpeed; remote_state[11] = 0; // timerOff values remote_state[12] = 0; // timerOff/On values remote_state[13] = 0; // timerOn values - if (_model == ARRAH2E) - remote_state[14] = 0x20; - else - remote_state[14] = 0x00; - + remote_state[14] = 0; uint8_t checksum = 0; uint8_t checksum_complement = 0; - if (_model == ARRAH2E) { - checksum = sumBytes(remote_state + _state_length_short, - _state_length - _state_length_short - 1); - } else if (_model == ARDB1) { - checksum = sumBytes(remote_state, _state_length - 1); - checksum_complement = 0x9B; + switch (_model) { + case ARDB1: + case ARJW2: + checksum = sumBytes(remote_state, _state_length - 1); + checksum_complement = 0x9B; + break; + case ARREB1E: + remote_state[14] |= (_outsideQuiet << 7); + // FALL THRU + case ARRAH2E: + remote_state[14] |= 0x20; + remote_state[10] |= _swingMode << 4; + // FALL THRU + default: + checksum = sumBytes(remote_state + _state_length_short, + _state_length - _state_length_short - 1); } // and negate the checksum and store it in the last byte. remote_state[_state_length - 1] = checksum_complement - checksum; } else { // short codes - if (_model == ARRAH2E) - // The last byte is the inverse of penultimate byte - remote_state[_state_length_short - 1] = - ~remote_state[_state_length_short - 2]; + switch (_model) { + case ARRAH2E: + case ARREB1E: + // The last byte is the inverse of penultimate byte + remote_state[_state_length_short - 1] = + ~remote_state[_state_length_short - 2]; + break; + default: + {}; // We don't need to do anything for the others. + } // Zero the rest of the state. for (uint8_t i = _state_length_short; i < kFujitsuAcStateLength; i++) remote_state[i] = 0; } } -uint8_t IRFujitsuAC::getStateLength() { - buildState(); // Force an update of the internal state. - if ((_model == ARRAH2E && remote_state[5] != 0xFE) || - (_model == ARDB1 && remote_state[5] != 0xFC)) +uint8_t IRFujitsuAC::getStateLength(void) { + this->buildState(); // Force an update of the internal state. + if (((_model == ARRAH2E || _model == ARREB1E) && remote_state[5] != 0xFE) || + ((_model == ARDB1 || _model == ARJW2) && remote_state[5] != 0xFC)) return _state_length_short; else return _state_length; } // Return a pointer to the internal state date of the remote. -uint8_t* IRFujitsuAC::getRaw() { - buildState(); +uint8_t* IRFujitsuAC::getRaw(void) { + this->buildState(); return remote_state; } @@ -177,17 +201,28 @@ void IRFujitsuAC::buildFromState(const uint16_t length) { switch (length) { case kFujitsuAcStateLength - 1: case kFujitsuAcStateLengthShort - 1: - setModel(ARDB1); + this->setModel(ARDB1); + // ARJW2 has horizontal swing. + if (this->getSwing(true) > kFujitsuAcSwingVert) this->setModel(ARJW2); break; default: - setModel(ARRAH2E); + switch (this->getCmd(true)) { + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + this->setModel(fujitsu_ac_remote_model_t::ARREB1E); + break; + default: + this->setModel(fujitsu_ac_remote_model_t::ARRAH2E); + } } switch (remote_state[6]) { case 8: - setModel(ARDB1); + if (this->getModel() != fujitsu_ac_remote_model_t::ARJW2) + this->setModel(ARDB1); break; case 9: - setModel(ARRAH2E); + if (this->getModel() != fujitsu_ac_remote_model_t::ARREB1E) + this->setModel(ARRAH2E); break; } setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp); @@ -201,10 +236,15 @@ void IRFujitsuAC::buildFromState(const uint16_t length) { switch (remote_state[5]) { case kFujitsuAcCmdTurnOff: case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: setCmd(remote_state[5]); break; } + _outsideQuiet = this->getOutsideQuiet(true); } bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { @@ -220,184 +260,378 @@ bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { } // Set the requested power state of the A/C to off. -void IRFujitsuAC::off() { _cmd = kFujitsuAcCmdTurnOff; } +void IRFujitsuAC::off(void) { this->setCmd(kFujitsuAcCmdTurnOff); } -void IRFujitsuAC::stepHoriz() { - switch (_model) { - case ARDB1: - break; // This remote doesn't have a horizontal option. - default: - _cmd = kFujitsuAcCmdStepHoriz; - } +void IRFujitsuAC::stepHoriz(void) { this->setCmd(kFujitsuAcCmdStepHoriz); } + +void IRFujitsuAC::toggleSwingHoriz(const bool update) { + // Toggle the current setting. + if (update) this->setSwing(this->getSwing() ^ kFujitsuAcSwingHoriz); + // and set the appropriate special command. + this->setCmd(kFujitsuAcCmdToggleSwingHoriz); } -void IRFujitsuAC::stepVert() { _cmd = kFujitsuAcCmdStepVert; } +void IRFujitsuAC::stepVert(void) { this->setCmd(kFujitsuAcCmdStepVert); } + +void IRFujitsuAC::toggleSwingVert(const bool update) { + // Toggle the current setting. + if (update) this->setSwing(this->getSwing() ^ kFujitsuAcSwingVert); + // and set the appropriate special command. + this->setCmd(kFujitsuAcCmdToggleSwingVert); +} // Set the requested command of the A/C. -void IRFujitsuAC::setCmd(uint8_t cmd) { +void IRFujitsuAC::setCmd(const uint8_t cmd) { switch (cmd) { case kFujitsuAcCmdTurnOff: case kFujitsuAcCmdTurnOn: case kFujitsuAcCmdStayOn: case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: _cmd = cmd; break; case kFujitsuAcCmdStepHoriz: - if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal. + case kFujitsuAcCmdToggleSwingHoriz: + switch (_model) { + // Only these remotes have step horizontal. + case ARRAH2E: + case ARJW2: + _cmd = cmd; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + case kFujitsuAcCmdEcono: + case kFujitsuAcCmdPowerful: + switch (_model) { + // Only these remotes have these commands. + case ARREB1E: _cmd = cmd; - // FALLTHRU + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; default: _cmd = kFujitsuAcCmdStayOn; - break; } } -uint8_t IRFujitsuAC::getCmd() { return _cmd; } +// Get the special command part of the message. +// Args: +// raw: Do we need to get it from first principles from the raw data? +// Returns: +// A uint8_t containing the contents of the special command byte. +uint8_t IRFujitsuAC::getCmd(const bool raw) { + if (raw) return remote_state[5]; + return _cmd; +} -bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; } +bool IRFujitsuAC::getPower(void) { return _cmd != kFujitsuAcCmdTurnOff; } + +void IRFujitsuAC::setOutsideQuiet(const bool on) { + _outsideQuiet = on; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +// Get the status of the Outside Quiet setting. +// Args: +// raw: Do we get the result from base data? +// Returns: +// A boolean for if it is set or not. +bool IRFujitsuAC::getOutsideQuiet(const bool raw) { + if (_state_length == kFujitsuAcStateLength && raw) { + _outsideQuiet = remote_state[14] & 0b10000000; + // Only ARREB1E seems to have this mode. + if (_outsideQuiet) this->setModel(fujitsu_ac_remote_model_t::ARREB1E); + } + return _outsideQuiet; +} // Set the temp. in deg C -void IRFujitsuAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kFujitsuAcMinTemp, temp); - temp = std::min((uint8_t)kFujitsuAcMaxTemp, temp); - _temp = temp; +void IRFujitsuAC::setTemp(const uint8_t temp) { + _temp = std::max((uint8_t)kFujitsuAcMinTemp, temp); + _temp = std::min((uint8_t)kFujitsuAcMaxTemp, _temp); + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. } -uint8_t IRFujitsuAC::getTemp() { return _temp; } +uint8_t IRFujitsuAC::getTemp(void) { return _temp; } // Set the speed of the fan -void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) { +void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { if (fanSpeed > kFujitsuAcFanQuiet) - fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - _fanSpeed = fanSpeed; + _fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. + else + _fanSpeed = fanSpeed; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. } -uint8_t IRFujitsuAC::getFanSpeed() { return _fanSpeed; } +uint8_t IRFujitsuAC::getFanSpeed(void) { return _fanSpeed; } // Set the requested climate operation mode of the a/c unit. -void IRFujitsuAC::setMode(uint8_t mode) { +void IRFujitsuAC::setMode(const uint8_t mode) { if (mode > kFujitsuAcModeHeat) - mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - _mode = mode; + _mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. + else + _mode = mode; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. } -uint8_t IRFujitsuAC::getMode() { return _mode; } +uint8_t IRFujitsuAC::getMode(void) { return _mode; } + // Set the requested swing operation mode of the a/c unit. -void IRFujitsuAC::setSwing(uint8_t swingMode) { +void IRFujitsuAC::setSwing(const uint8_t swingMode) { + _swingMode = swingMode; switch (_model) { + // No Horizontal support. case ARDB1: + case ARREB1E: // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) swingMode = kFujitsuAcSwingVert; + if (swingMode > kFujitsuAcSwingVert) _swingMode = kFujitsuAcSwingVert; break; + // Has Horizontal support. case ARRAH2E: + case ARJW2: default: // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) swingMode = kFujitsuAcSwingBoth; + if (swingMode > kFujitsuAcSwingBoth) _swingMode = kFujitsuAcSwingBoth; } - _swingMode = swingMode; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. } -uint8_t IRFujitsuAC::getSwing() { return _swingMode; } +// Get what the swing part of the message should be. +// Args: +// raw: Do we need to get it from first principles from the raw data? +// Returns: +// A uint8_t containing the contents of the swing state. +uint8_t IRFujitsuAC::getSwing(const bool raw) { + if (raw) _swingMode = remote_state[10] >> 4; + return _swingMode; +} -bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { +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 + case kFujitsuAcStateLengthShort: // ARRAH2E & ARREB1E return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 + case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 sum = sumBytes(state, length - 1); sum_complement = 0x9B; break; - case kFujitsuAcStateLength: // ARRAH2E + case kFujitsuAcStateLength: // ARRAH2E & ARREB1E sum = sumBytes(state + kFujitsuAcStateLengthShort, length - 1 - kFujitsuAcStateLengthShort); break; - default: // Includes ARDB1 short. + 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? } +// Convert a standard A/C mode into its native mode. +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 standard A/C Fan speed into its native fan speed. +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 to it's common equivalent. +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 to it's common equivalent. +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 A/C state to it's common equivalent. +stdAc::state_t IRFujitsuAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::FUJITSU_AC; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFanSpeed()); + uint8_t swing = this->getSwing(); + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + 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 = (this->getFanSpeed() == kFujitsuAcFanQuiet); + result.turbo = this->getCmd() == kFujitsuAcCmdPowerful; + result.econo = this->getCmd() == 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 internal state into a human readable string. -#ifdef ARDUINO -String IRFujitsuAC::toString() { +String IRFujitsuAC::toString(void) { String result = ""; -#else -std::string IRFujitsuAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += F("Model: "); + fujitsu_ac_remote_model_t model = this->getModel(); + result += uint64ToString(model); + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: result += F(" (ARRAH2E)"); break; + case fujitsu_ac_remote_model_t::ARDB1: result += F(" (ARDB1)"); break; + case fujitsu_ac_remote_model_t::ARREB1E: result += F(" (ARREB1E)"); break; + case fujitsu_ac_remote_model_t::ARJW2: result += F(" (ARJW2)"); break; + default: result += F(" (UNKNOWN)"); + } + result += F(", Power: "); + result += this->getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kFujitsuAcModeAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kFujitsuAcModeCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kFujitsuAcModeHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kFujitsuAcModeDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kFujitsuAcModeFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFanSpeed()); + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFanSpeed()); switch (getFanSpeed()) { case kFujitsuAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kFujitsuAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kFujitsuAcFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kFujitsuAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kFujitsuAcFanQuiet: - result += " (QUIET)"; + result += F(" (QUIET)"); break; } - result += ", Swing: "; - switch (getSwing()) { - case kFujitsuAcSwingOff: - result += "Off"; + switch (model) { + // These models have no internal swing state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: break; - case kFujitsuAcSwingVert: - result += "Vert"; - break; - case kFujitsuAcSwingHoriz: - result += "Horiz"; - break; - case kFujitsuAcSwingBoth: - result += "Vert + Horiz"; - break; - default: - result += "UNKNOWN"; + default: // Assume everything else does. + result += F(", Swing: "); + switch (this->getSwing()) { + case kFujitsuAcSwingOff: + result += F("Off"); + break; + case kFujitsuAcSwingVert: + result += F("Vert"); + break; + case kFujitsuAcSwingHoriz: + result += F("Horiz"); + break; + case kFujitsuAcSwingBoth: + result += F("Vert + Horiz"); + break; + default: + result += F("UNKNOWN"); + } } - result += ", Command: "; - switch (getCmd()) { + result += F(", Command: "); + switch (this->getCmd()) { case kFujitsuAcCmdStepHoriz: - result += "Step vane horizontally"; + result += F("Step vane horizontally"); break; case kFujitsuAcCmdStepVert: - result += "Step vane vertically"; + result += F("Step vane vertically"); + break; + case kFujitsuAcCmdToggleSwingHoriz: + result += F("Toggle horizontal swing"); + break; + case kFujitsuAcCmdToggleSwingVert: + result += F("Toggle vertically swing"); + break; + case kFujitsuAcCmdEcono: + result += F("Economy"); + break; + case kFujitsuAcCmdPowerful: + result += F("Powerful"); break; default: - result += "N/A"; + result += F("N/A"); + } + if (this->getModel() == fujitsu_ac_remote_model_t::ARREB1E) { + result += F(", Outside Quiet: "); + result += this->getOutsideQuiet() ? F("On") : F("Off"); } return result; } diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index d5dc837..06f57e2 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -1,5 +1,16 @@ // Copyright 2017 Jonny Graham -// Copyright 2018 David Conran +// Copyright 2018-2019 David Conran + +// Supports: +// Brand: Fujitsu, Model: AR-RAH2E remote +// Brand: Fujitsu, Model: ASYG30LFCA A/C +// Brand: Fujitsu, Model: AR-DB1 remote +// Brand: Fujitsu, Model: AST9RSGCW A/C +// Brand: Fujitsu, Model: AR-REB1E remote +// Brand: Fujitsu, Model: ASYG7LMCA A/C +// Brand: Fujitsu, Model: AR-RAE1E remote +// Brand: Fujitsu General, Model: AR-JW2 remote + #ifndef IR_FUJITSU_H_ #define IR_FUJITSU_H_ @@ -7,12 +18,13 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // FUJITSU A/C support added by Jonny Graham @@ -23,11 +35,15 @@ const uint8_t kFujitsuAcModeDry = 0x02; const uint8_t kFujitsuAcModeFan = 0x03; const uint8_t kFujitsuAcModeHeat = 0x04; -const uint8_t kFujitsuAcCmdStayOn = 0x00; -const uint8_t kFujitsuAcCmdTurnOn = 0x01; -const uint8_t kFujitsuAcCmdTurnOff = 0x02; -const uint8_t kFujitsuAcCmdStepHoriz = 0x79; -const uint8_t kFujitsuAcCmdStepVert = 0x6C; +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; @@ -67,47 +83,63 @@ const uint8_t kFujitsuAcSwingBoth = 0x03; #define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth enum fujitsu_ac_remote_model_t { - ARRAH2E = 1, - ARDB1, + ARRAH2E = 1, // (1) AR-RAH2E, AR-RAE1E (Default) + ARDB1, // (2) AR-DB1 + ARREB1E, // (3) AR-REB1E + ARJW2, // (4) AR-JW2 (Same as ARDB1 but with horiz control) }; class IRFujitsuAC { public: - explicit IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model = ARRAH2E); + explicit IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model = ARRAH2E); - void setModel(fujitsu_ac_remote_model_t model); - void stateReset(); + void setModel(const fujitsu_ac_remote_model_t model); + fujitsu_ac_remote_model_t getModel(void); + void stateReset(void); #if SEND_FUJITSU_AC void send(const uint16_t repeat = kFujitsuAcMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_FUJITSU_AC - void begin(); - void off(); - void stepHoriz(); - void stepVert(); - void setCmd(uint8_t cmd); - uint8_t getCmd(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFanSpeed(uint8_t fan); - uint8_t getFanSpeed(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwing(uint8_t mode); - uint8_t getSwing(); - uint8_t* getRaw(); + void begin(void); + void off(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(const bool raw = false); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFanSpeed(const uint8_t fan); + uint8_t getFanSpeed(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwing(const uint8_t mode); + uint8_t getSwing(const bool raw = false); + uint8_t* getRaw(void); bool setRaw(const uint8_t newState[], const uint16_t length); - uint8_t getStateLength(); - static bool validChecksum(uint8_t* state, uint16_t length); - bool getPower(); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + uint8_t getStateLength(void); + static bool validChecksum(uint8_t* state, const uint16_t length); + bool getPower(void); + void setOutsideQuiet(const bool on); + + bool getOutsideQuiet(const bool raw = false); + + uint8_t convertMode(const stdAc::opmode_t mode); + 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(void); + String toString(void); +#ifndef UNIT_TEST private: - uint8_t remote_state[kFujitsuAcStateLength]; IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kFujitsuAcStateLength]; uint8_t _temp; uint8_t _fanSpeed; uint8_t _mode; @@ -116,7 +148,8 @@ class IRFujitsuAC { fujitsu_ac_remote_model_t _model; uint8_t _state_length; uint8_t _state_length_short; - void buildState(); + bool _outsideQuiet; + void buildState(void); void buildFromState(const uint16_t length); }; diff --git a/lib/IRremoteESP8266/src/ir_GICable.cpp b/lib/IRremoteESP8266/src/ir_GICable.cpp index 229e4e5..15bb0a3 100644 --- a/lib/IRremoteESP8266/src/ir_GICable.cpp +++ b/lib/IRremoteESP8266/src/ir_GICable.cpp @@ -1,4 +1,5 @@ // Copyright 2018 David Conran +// G.I. Cable #define __STDC_LIMIT_MACROS #include @@ -7,12 +8,6 @@ #include "IRsend.h" #include "IRutils.h" -// GGGG IIIII CCCCC AAA BBBBB LL EEEEEEE -// GG GG III CC C AAAAA BB B LL EE -// GG III CC AA AA BBBBBB LL EEEEE -// GG GG ... III ... CC C AAAAAAA BB BB LL EE -// GGGGGG ... IIIII ... CCCCC AA AA BBBBBB LLLLLLL EEEEEEE -// // Ref: // https://github.com/cyborg5/IRLib2/blob/master/IRLibProtocols/IRLib_P09_GICable.h // https://github.com/markszabo/IRremoteESP8266/issues/447 diff --git a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp index daa9dd2..8c96469 100644 --- a/lib/IRremoteESP8266/src/ir_GlobalCache.cpp +++ b/lib/IRremoteESP8266/src/ir_GlobalCache.cpp @@ -1,18 +1,12 @@ // Copyright 2016 Hisham Khalifa // Copyright 2017 David Conran -#include -#include "IRsend.h" - -// GGG L OOOO BBBB AA L CCCC AA CCCC H H EEEEEE -// G G L O O B B AAAA L C C AAAA C C H H E -// G L O O BBBBB A A L C A A C HHHHHH EEEE -// G GG L O O B BB AAAAAA L C C AAAAAA C C H H E -// GGGGG LLLLLL OOOO BBBBB A A LLLLLL CCCC A A CCCC H H EEEEEE - // Global Cache IR format sender originally added by Hisham Khalifa // (http://www.hishamkhalifa.com) +#include +#include "IRsend.h" + // Constants const uint16_t kGlobalCacheMaxRepeat = 50; const uint32_t kGlobalCacheMinUsec = 80; diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.cpp b/lib/IRremoteESP8266/src/ir_Goodweather.cpp new file mode 100644 index 0000000..83fd2ea --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Goodweather.cpp @@ -0,0 +1,494 @@ +// Copyright 2019 ribeirodanielf +// Copyright 2019 David Conran +// +// Code to emulate Goodweather protocol compatible HVAC devices. +// Should be compatible with: +// * ZH/JT-03 remote control +// + +#include "ir_Goodweather.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRutils.h" + +#if SEND_GOODWEATHER +// Send a Goodweather message. +// +// Args: +// data: The raw message to be sent. +// nbits: Nr. of bits of data in the message. (Default is kGoodweatherBits) +// repeat: Nr. of times the message is to be repeated. (Default = 0). +// +// Status: ALPHA / Untested. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/697 +void IRsend::sendGoodweather(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits != kGoodweatherBits) + return; // Wrong nr. of bits to send a proper message. + // Set IR carrier frequency + enableIROut(38); + + for (uint16_t r = 0; r <= repeat; r++) { + // Header + mark(kGoodweatherHdrMark); + space(kGoodweatherHdrSpace); + + // Data + for (int16_t i = 0; i < nbits; i += 8) { + uint16_t chunk = (data >> i) & 0xFF; // Grab a byte at a time. + chunk = (~chunk) << 8 | chunk; // Prepend a inverted copy of the byte. + sendData(kGoodweatherBitMark, kGoodweatherOneSpace, + kGoodweatherBitMark, kGoodweatherZeroSpace, + chunk, 16, false); + } + // Footer + mark(kGoodweatherBitMark); + space(kGoodweatherHdrSpace); + mark(kGoodweatherBitMark); + space(kDefaultMessageGap); + } +} +#endif // SEND_GOODWEATHER + +IRGoodweatherAc::IRGoodweatherAc(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRGoodweatherAc::stateReset(void) { +} + +void IRGoodweatherAc::begin(void) { _irsend.begin(); } + +#if SEND_GOODWEATHER +void IRGoodweatherAc::send(const uint16_t repeat) { + _irsend.sendGoodweather(remote, kGoodweatherBits, repeat); +} +#endif // SEND_GOODWEATHER + +uint64_t IRGoodweatherAc::getRaw(void) { return remote; } + +void IRGoodweatherAc::setRaw(const uint64_t state) { remote = state; } + +void IRGoodweatherAc::on(void) { this->setPower(true); } + +void IRGoodweatherAc::off(void) { this->setPower(false); } + +void IRGoodweatherAc::setPower(const bool on) { + this->setCommand(kGoodweatherCmdPower); + if (on) + remote |= kGoodweatherPowerMask; + else + remote &= ~kGoodweatherPowerMask; +} + +bool IRGoodweatherAc::getPower(void) { return remote & kGoodweatherPowerMask; } + +// Set the temp. in deg C +void IRGoodweatherAc::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max(kGoodweatherTempMin, temp); + new_temp = std::min(kGoodweatherTempMax, new_temp); + if (new_temp > this->getTemp()) this->setCommand(kGoodweatherCmdUpTemp); + if (new_temp < this->getTemp()) this->setCommand(kGoodweatherCmdDownTemp); + remote &= ~kGoodweatherTempMask; + remote |= (uint64_t)(new_temp - kGoodweatherTempMin) << kGoodweatherBitTemp; +} + +// Return the set temp. in deg C +uint8_t IRGoodweatherAc::getTemp(void) { + return ((remote & kGoodweatherTempMask) >> kGoodweatherBitTemp) + + kGoodweatherTempMin; +} + +// Set the speed of the fan +void IRGoodweatherAc::setFan(const uint8_t speed) { + switch (speed) { + case kGoodweatherFanAuto: + case kGoodweatherFanLow: + case kGoodweatherFanMed: + case kGoodweatherFanHigh: + this->setCommand(kGoodweatherCmdFan); + remote &= ~kGoodweatherFanMask; + remote |= ((uint64_t)speed << kGoodweatherBitFan); + break; + default: + this->setFan(kGoodweatherFanAuto); + } +} + +uint8_t IRGoodweatherAc::getFan() { + return (remote & kGoodweatherFanMask) >> kGoodweatherBitFan; +} + +void IRGoodweatherAc::setMode(const uint8_t mode) { + switch (mode) { + case kGoodweatherAuto: + case kGoodweatherDry: + case kGoodweatherCool: + case kGoodweatherFan: + case kGoodweatherHeat: + this->setCommand(kGoodweatherCmdMode); + remote &= ~kGoodweatherModeMask; + remote |= (uint64_t)mode << kGoodweatherBitMode; + break; + default: + // If we get an unexpected mode, default to AUTO. + this->setMode(kGoodweatherAuto); + } +} + +uint8_t IRGoodweatherAc::getMode() { + return (remote & kGoodweatherModeMask) >> kGoodweatherBitMode; +} + +void IRGoodweatherAc::setLight(const bool toggle) { + this->setCommand(kGoodweatherCmdLight); + if (toggle) + remote |= kGoodweatherLightMask; + else + remote &= ~kGoodweatherLightMask; +} + +bool IRGoodweatherAc::getLight() { return remote & kGoodweatherLightMask; } + +void IRGoodweatherAc::setSleep(const bool toggle) { + this->setCommand(kGoodweatherCmdSleep); + if (toggle) + remote |= kGoodweatherSleepMask; + else + remote &= ~kGoodweatherSleepMask; +} + +bool IRGoodweatherAc::getSleep() { return remote & kGoodweatherSleepMask; } + +void IRGoodweatherAc::setTurbo(const bool toggle) { + this->setCommand(kGoodweatherCmdTurbo); + if (toggle) + remote |= kGoodweatherTurboMask; + else + remote &= ~kGoodweatherTurboMask; +} + +bool IRGoodweatherAc::getTurbo() { return remote & kGoodweatherTurboMask; } + +void IRGoodweatherAc::setSwing(const uint8_t speed) { + switch (speed) { + case kGoodweatherSwingOff: + case kGoodweatherSwingSlow: + case kGoodweatherSwingFast: + this->setCommand(kGoodweatherCmdSwing); + remote &= ~kGoodweatherSwingMask; + remote |= ((uint64_t)speed << kGoodweatherBitSwing); + break; + default: + this->setSwing(kGoodweatherSwingOff); + } +} + +uint8_t IRGoodweatherAc::getSwing() { + return (remote & kGoodweatherSwingMask) >> kGoodweatherBitSwing; +} + +void IRGoodweatherAc::setCommand(const uint8_t cmd) { + if (cmd <= kGoodweatherCmdLight) { + remote &= ~kGoodweatherCommandMask; + remote |= (cmd << kGoodweatherBitCommand); + } +} + +uint8_t IRGoodweatherAc::getCommand() { + return (remote & kGoodweatherCommandMask) >> kGoodweatherBitCommand; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRGoodweatherAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kGoodweatherCool; + case stdAc::opmode_t::kHeat: + return kGoodweatherHeat; + case stdAc::opmode_t::kDry: + return kGoodweatherDry; + case stdAc::opmode_t::kFan: + return kGoodweatherFan; + default: + return kGoodweatherAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRGoodweatherAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kGoodweatherFanLow; + case stdAc::fanspeed_t::kMedium: + return kGoodweatherFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kGoodweatherFanHigh; + default: + return kGoodweatherFanAuto; + } +} + +// Convert a standard A/C Vertical Swing into its native version. +uint8_t IRGoodweatherAc::convertSwingV(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + return kGoodweatherSwingFast; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + case stdAc::swingv_t::kAuto: + return kGoodweatherSwingSlow; + default: + return kGoodweatherSwingOff; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRGoodweatherAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kGoodweatherCool: return stdAc::opmode_t::kCool; + case kGoodweatherHeat: return stdAc::opmode_t::kHeat; + case kGoodweatherDry: return stdAc::opmode_t::kDry; + case kGoodweatherFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRGoodweatherAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kGoodweatherFanHigh: return stdAc::fanspeed_t::kMax; + case kGoodweatherFanMed: return stdAc::fanspeed_t::kMedium; + case kGoodweatherFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRGoodweatherAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::GOODWEATHER; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() == kGoodweatherSwingOff ? + stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.sleep = this->getSleep() ? 0: -1; + // Not supported. + result.model = -1; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = false; + result.filter = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRGoodweatherAc::toString() { + String result = ""; + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += this->getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kGoodweatherAuto: + result += F(" (AUTO)"); + break; + case kGoodweatherCool: + result += F(" (COOL)"); + break; + case kGoodweatherHeat: + result += F(" (HEAT)"); + break; + case kGoodweatherDry: + result += F(" (DRY)"); + break; + case kGoodweatherFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kGoodweatherFanAuto: + result += F(" (AUTO)"); + break; + case kGoodweatherFanHigh: + result += F(" (HIGH)"); + break; + case kGoodweatherFanMed: + result += F(" (MED)"); + break; + case kGoodweatherFanLow: + result += F(" (LOW)"); + break; + } + result += F(", Turbo: "); + result += this->getTurbo() ? F("Toggle") : F("-"); + result += F(", Light: "); + result += this->getLight() ? F("Toggle") : F("-"); + result += F(", Sleep: "); + result += this->getSleep() ? F("Toggle") : F("-"); + result += F(", Swing: "); + result += uint64ToString(this->getSwing()); + switch (this->getSwing()) { + case kGoodweatherSwingFast: + result += F(" (Fast)"); + break; + case kGoodweatherSwingSlow: + result += F(" (Slow)"); + break; + case kGoodweatherSwingOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Command: "); + result += uint64ToString(this->getCommand()); + switch (this->getCommand()) { + case kGoodweatherCmdPower: + result += F(" (Power)"); + break; + case kGoodweatherCmdMode: + result += F(" (Mode)"); + break; + case kGoodweatherCmdUpTemp: + result += F(" (Temp Up)"); + break; + case kGoodweatherCmdDownTemp: + result += F(" (Temp Down)"); + break; + case kGoodweatherCmdSwing: + result += F(" (Swing)"); + break; + case kGoodweatherCmdFan: + result += F(" (Fan)"); + break; + case kGoodweatherCmdTimer: + result += F(" (Timer)"); + break; + case kGoodweatherCmdAirFlow: + result += F(" (Air Flow)"); + break; + case kGoodweatherCmdHold: + result += F(" (Hold)"); + break; + case kGoodweatherCmdSleep: + result += F(" (Sleep)"); + break; + case kGoodweatherCmdTurbo: + result += F(" (Turbo)"); + break; + case kGoodweatherCmdLight: + result += F(" (Light)"); + break; + default: + result += F(" (UNKNOWN)"); + } + return result; +} + +#if DECODE_GOODWEATHER +// Decode the supplied Goodweather message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kGoodweatherBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: ALPHA / Untested. +bool IRrecv::decodeGoodweather(decode_results* results, + const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (2 * nbits) + kHeader + 2 * kFooter - 1) + return false; // Can't possibly be a valid Goodweather message. + if (strict && nbits != kGoodweatherBits) + return false; // Not strictly a Goodweather message. + + uint64_t dataSoFar = 0; + uint16_t dataBitsSoFar = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kGoodweatherHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace)) + return false; + + // Data + for (; offset <= results->rawlen - 32 && dataBitsSoFar < nbits; + dataBitsSoFar += 8) { + DPRINT("DEBUG: Attempting Byte #"); + DPRINTLN(dataBitsSoFar / 8); + // Read in a byte at a time. + // Normal first. + data_result = matchData(&(results->rawbuf[offset]), 8, + kGoodweatherBitMark, kGoodweatherOneSpace, + kGoodweatherBitMark, kGoodweatherZeroSpace, + kTolerance, kMarkExcess, false); + if (data_result.success == false) return false; + DPRINTLN("DEBUG: Normal byte read okay."); + offset += data_result.used; + uint8_t data = (uint8_t)data_result.data; + // Then inverted. + data_result = matchData(&(results->rawbuf[offset]), 8, + kGoodweatherBitMark, kGoodweatherOneSpace, + kGoodweatherBitMark, kGoodweatherZeroSpace, + kTolerance, kMarkExcess, false); + if (data_result.success == false) return false; + DPRINTLN("DEBUG: Inverted byte read okay."); + offset += data_result.used; + uint8_t inverted = (uint8_t)data_result.data; + DPRINT("DEBUG: data = "); + DPRINTLN((uint16_t)data); + DPRINT("DEBUG: inverted = "); + DPRINTLN((uint16_t)inverted); + if (data != (inverted ^ 0xFF)) return false; // Data integrity failed. + dataSoFar |= (uint64_t)data << dataBitsSoFar; + } + + // Footer. + if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace)) + return false; + if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset], kGoodweatherHdrSpace)) + return false; + + // Compliance + if (strict && (dataBitsSoFar != kGoodweatherBits)) return false; + + // Success + results->decode_type = decode_type_t::GOODWEATHER; + results->bits = dataBitsSoFar; + results->value = dataSoFar; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_GOODWEATHER diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.h b/lib/IRremoteESP8266/src/ir_Goodweather.h new file mode 100644 index 0000000..95788ab --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Goodweather.h @@ -0,0 +1,136 @@ +// Goodweather A/C +// +// Copyright 2019 ribeirodanielf +// Copyright 2019 David Conran + +// Supports: +// Brand: Goodweather, Model: ZH/JT-03 remote + +#ifndef IR_GOODWEATHER_H_ +#define IR_GOODWEATHER_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/697 + +// Constants + +// Timing +const uint16_t kGoodweatherBitMark = 640; +const uint16_t kGoodweatherOneSpace = 580; +const uint16_t kGoodweatherZeroSpace = 1600; +const uint16_t kGoodweatherHdrMark = 6800; +const uint16_t kGoodweatherHdrSpace = 6800; + +// Masks +const uint8_t kGoodweatherBitLight = 8; +const uint64_t kGoodweatherLightMask = 0x1ULL << kGoodweatherBitLight; +const uint8_t kGoodweatherBitTurbo = kGoodweatherBitLight + 3; // 11 +const uint64_t kGoodweatherTurboMask = 0x1ULL << kGoodweatherBitTurbo; +const uint8_t kGoodweatherBitCommand = kGoodweatherBitTurbo + 5; // 16 +const uint64_t kGoodweatherCommandMask = 0xFULL << kGoodweatherBitCommand; +const uint8_t kGoodweatherBitSleep = kGoodweatherBitCommand + 8; // 24 +const uint64_t kGoodweatherSleepMask = 0x1ULL << kGoodweatherBitSleep; +const uint8_t kGoodweatherBitPower = kGoodweatherBitSleep + 1; // 25 +const uint64_t kGoodweatherPowerMask = 0x1ULL << kGoodweatherBitPower; +const uint8_t kGoodweatherBitSwing = kGoodweatherBitPower + 1; // 26 +const uint64_t kGoodweatherSwingMask = 0x3ULL << kGoodweatherBitSwing; +const uint8_t kGoodweatherBitFan = kGoodweatherBitSwing + 3; // 29 +const uint64_t kGoodweatherFanMask = 0x3ULL << kGoodweatherBitFan; +const uint8_t kGoodweatherBitTemp = kGoodweatherBitFan + 3; // 32 +const uint64_t kGoodweatherTempMask = 0xFULL << kGoodweatherBitTemp; +const uint8_t kGoodweatherBitMode = kGoodweatherBitTemp + 5; // 37 +const uint64_t kGoodweatherModeMask = 0x7ULL << kGoodweatherBitMode; + +// Modes +const uint8_t kGoodweatherAuto = 0b000; +const uint8_t kGoodweatherCool = 0b001; +const uint8_t kGoodweatherDry = 0b010; +const uint8_t kGoodweatherFan = 0b011; +const uint8_t kGoodweatherHeat = 0b100; +const uint8_t kGoodweatherSwingFast = 0b00; +const uint8_t kGoodweatherSwingSlow = 0b01; +const uint8_t kGoodweatherSwingOff = 0b10; +// Fan Control +const uint8_t kGoodweatherFanAuto = 0b00; +const uint8_t kGoodweatherFanHigh = 0b01; +const uint8_t kGoodweatherFanMed = 0b10; +const uint8_t kGoodweatherFanLow = 0b11; +// Temperature +const uint8_t kGoodweatherTempMin = 16; // Celsius +const uint8_t kGoodweatherTempMax = 31; // Celsius +// Commands +const uint8_t kGoodweatherCmdPower = 0x00; +const uint8_t kGoodweatherCmdMode = 0x01; +const uint8_t kGoodweatherCmdUpTemp = 0x02; +const uint8_t kGoodweatherCmdDownTemp = 0x03; +const uint8_t kGoodweatherCmdSwing = 0x04; +const uint8_t kGoodweatherCmdFan = 0x05; +const uint8_t kGoodweatherCmdTimer = 0x06; +const uint8_t kGoodweatherCmdAirFlow = 0x07; +const uint8_t kGoodweatherCmdHold = 0x08; +const uint8_t kGoodweatherCmdSleep = 0x09; +const uint8_t kGoodweatherCmdTurbo = 0x0A; +const uint8_t kGoodweatherCmdLight = 0x0B; + + +// Classes +class IRGoodweatherAc { + public: + explicit IRGoodweatherAc(uint16_t pin); + + void stateReset(void); +#if SEND_GOODWEATHER + void send(const uint16_t repeat = kGoodweatherMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_GOODWEATHER + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(); + void setSwing(const uint8_t speed); + uint8_t getSwing(void); + void setSleep(const bool toggle); + bool getSleep(void); + void setTurbo(const bool toggle); + bool getTurbo(void); + void setLight(const bool toggle); + bool getLight(void); + void setCommand(const uint8_t cmd); + uint8_t getCommand(void); + uint64_t getRaw(void); + void setRaw(const uint64_t state); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint64_t remote; // The state of the IR remote in IR code form. +}; +#endif // IR_GOODWEATHER_H_ diff --git a/lib/IRremoteESP8266/src/ir_Gree.cpp b/lib/IRremoteESP8266/src/ir_Gree.cpp index 0740d5f..116fda0 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266/src/ir_Gree.cpp @@ -18,16 +18,10 @@ #include "IRutils.h" #include "ir_Kelvinator.h" -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE - // Constants // Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h const uint16_t kGreeHdrMark = 9000; -const uint16_t kGreeHdrSpace = 4000; +const uint16_t kGreeHdrSpace = 4500; // See #684 and real example in unit tests const uint16_t kGreeBitMark = 620; const uint16_t kGreeOneSpace = 1600; const uint16_t kGreeZeroSpace = 540; @@ -47,7 +41,8 @@ const uint8_t kGreeBlockFooterBits = 3; // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendGree(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kGreeStateLength) return; // Not enough bytes to send a proper message. @@ -59,7 +54,7 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { // Footer #1 sendGeneric(0, 0, // No Header kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, - kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, true, 0, false); + kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, false, 0, 50); // Block #2 sendGeneric(0, 0, // No Header for Block #2 @@ -80,7 +75,8 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendGree(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { if (nbits != kGreeBits) return; // Wrong nr. of bits to send a proper message. // Set IR carrier frequency @@ -112,7 +108,7 @@ void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { stateReset(); } -void IRGreeAC::stateReset() { +void IRGreeAC::stateReset(void) { // This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C. for (uint8_t i = 0; i < kGreeStateLength; i++) remote_state[i] = 0x0; remote_state[1] = 0x09; @@ -122,11 +118,11 @@ void IRGreeAC::stateReset() { remote_state[7] = 0x50; } -void IRGreeAC::fixup() { +void IRGreeAC::fixup(void) { checksum(); // Calculate the checksums } -void IRGreeAC::begin() { _irsend.begin(); } +void IRGreeAC::begin(void) { _irsend.begin(); } #if SEND_GREE void IRGreeAC::send(const uint16_t repeat) { @@ -135,12 +131,12 @@ void IRGreeAC::send(const uint16_t repeat) { } #endif // SEND_GREE -uint8_t* IRGreeAC::getRaw() { +uint8_t* IRGreeAC::getRaw(void) { fixup(); // Ensure correct settings before sending. return remote_state; } -void IRGreeAC::setRaw(uint8_t new_code[]) { +void IRGreeAC::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kGreeStateLength; i++) { remote_state[i] = new_code[i]; } @@ -167,24 +163,24 @@ bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) { return false; } -void IRGreeAC::on() { +void IRGreeAC::on(void) { remote_state[0] |= kGreePower1Mask; remote_state[2] |= kGreePower2Mask; } -void IRGreeAC::off() { +void IRGreeAC::off(void) { remote_state[0] &= ~kGreePower1Mask; remote_state[2] &= ~kGreePower2Mask; } -void IRGreeAC::setPower(const bool state) { - if (state) - on(); +void IRGreeAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -bool IRGreeAC::getPower() { +bool IRGreeAC::getPower(void) { return (remote_state[0] & kGreePower1Mask) && (remote_state[2] & kGreePower2Mask); } @@ -198,7 +194,7 @@ void IRGreeAC::setTemp(const uint8_t temp) { } // Return the set temp. in deg C -uint8_t IRGreeAC::getTemp() { +uint8_t IRGreeAC::getTemp(void) { return ((remote_state[1] & 0xFU) + kGreeMinTemp); } @@ -212,7 +208,7 @@ void IRGreeAC::setFan(const uint8_t speed) { remote_state[0] |= (fan << 4); } -uint8_t IRGreeAC::getFan() { return ((remote_state[0] & kGreeFanMask) >> 4); } +uint8_t IRGreeAC::getFan(void) { return (remote_state[0] & kGreeFanMask) >> 4; } void IRGreeAC::setMode(const uint8_t new_mode) { uint8_t mode = new_mode; @@ -237,35 +233,35 @@ void IRGreeAC::setMode(const uint8_t new_mode) { remote_state[0] |= mode; } -uint8_t IRGreeAC::getMode() { return (remote_state[0] & kGreeModeMask); } +uint8_t IRGreeAC::getMode(void) { return (remote_state[0] & kGreeModeMask); } -void IRGreeAC::setLight(const bool state) { +void IRGreeAC::setLight(const bool on) { remote_state[2] &= ~kGreeLightMask; - remote_state[2] |= (state << 5); + remote_state[2] |= (on << 5); } -bool IRGreeAC::getLight() { return remote_state[2] & kGreeLightMask; } +bool IRGreeAC::getLight(void) { return remote_state[2] & kGreeLightMask; } -void IRGreeAC::setXFan(const bool state) { +void IRGreeAC::setXFan(const bool on) { remote_state[2] &= ~kGreeXfanMask; - remote_state[2] |= (state << 7); + remote_state[2] |= (on << 7); } -bool IRGreeAC::getXFan() { return remote_state[2] & kGreeXfanMask; } +bool IRGreeAC::getXFan(void) { return remote_state[2] & kGreeXfanMask; } -void IRGreeAC::setSleep(const bool state) { +void IRGreeAC::setSleep(const bool on) { remote_state[0] &= ~kGreeSleepMask; - remote_state[0] |= (state << 7); + remote_state[0] |= (on << 7); } -bool IRGreeAC::getSleep() { return remote_state[0] & kGreeSleepMask; } +bool IRGreeAC::getSleep(void) { return remote_state[0] & kGreeSleepMask; } -void IRGreeAC::setTurbo(const bool state) { +void IRGreeAC::setTurbo(const bool on) { remote_state[2] &= ~kGreeTurboMask; - remote_state[2] |= (state << 4); + remote_state[2] |= (on << 4); } -bool IRGreeAC::getTurbo() { return remote_state[2] & kGreeTurboMask; } +bool IRGreeAC::getTurbo(void) { return remote_state[2] & kGreeTurboMask; } void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { remote_state[0] &= ~kGreeSwingAutoMask; @@ -297,90 +293,201 @@ void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { remote_state[4] |= new_position; } -bool IRGreeAC::getSwingVerticalAuto() { +bool IRGreeAC::getSwingVerticalAuto(void) { return remote_state[0] & kGreeSwingAutoMask; } -uint8_t IRGreeAC::getSwingVerticalPosition() { +uint8_t IRGreeAC::getSwingVerticalPosition(void) { return remote_state[4] & kGreeSwingPosMask; } -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRGreeAC::toString() { - String result = ""; -#else -std::string IRGreeAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; + +// Convert a standard A/C mode into its native mode. +uint8_t IRGreeAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kGreeCool; + case stdAc::opmode_t::kHeat: + return kGreeHeat; + case stdAc::opmode_t::kDry: + return kGreeDry; + case stdAc::opmode_t::kFan: + return kGreeFan; + default: + return kGreeAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRGreeAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kGreeFanMin; + case stdAc::fanspeed_t::kLow: + case stdAc::fanspeed_t::kMedium: + return kGreeFanMax - 1; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kGreeFanMax; + default: + return kGreeFanAuto; + } +} + +// Convert a standard A/C Vertical Swing into its native version. +uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kHighest: + return kGreeSwingUp; + case stdAc::swingv_t::kHigh: + return kGreeSwingMiddleUp; + case stdAc::swingv_t::kMiddle: + return kGreeSwingMiddle; + case stdAc::swingv_t::kLow: + return kGreeSwingMiddleDown; + case stdAc::swingv_t::kLowest: + return kGreeSwingDown; + default: + return kGreeSwingAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kGreeCool: return stdAc::opmode_t::kCool; + case kGreeHeat: return stdAc::opmode_t::kHeat; + case kGreeDry: return stdAc::opmode_t::kDry; + case kGreeFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kGreeFanMax: return stdAc::fanspeed_t::kMax; + case kGreeFanMax - 1: return stdAc::fanspeed_t::kMedium; + case kGreeFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kGreeSwingUp: return stdAc::swingv_t::kHighest; + case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh; + case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle; + case kGreeSwingMiddleDown: return stdAc::swingv_t::kLow; + case kGreeSwingDown: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRGreeAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::GREE; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + if (this->getSwingVerticalAuto()) + result.swingv = stdAc::swingv_t::kAuto; else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result.swingv = this->toCommonSwingV(this->getSwingVerticalPosition()); + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.clean = this->getXFan(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRGreeAC::toString(void) { + String result = ""; + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + if (getPower()) + result += F("On"); + else + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kGreeAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kGreeCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kGreeHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kGreeDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kGreeFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case 0: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kGreeFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Turbo: "; + result += F(", Turbo: "); if (getTurbo()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", XFan: "; + result += F("Off"); + result += F(", XFan: "); if (getXFan()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Light: "; + result += F("Off"); + result += F(", Light: "); if (getLight()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Sleep: "; + result += F("Off"); + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing Vertical Mode: "; + result += F("Off"); + result += F(", Swing Vertical Mode: "); if (getSwingVerticalAuto()) - result += "Auto"; + result += F("Auto"); else - result += "Manual"; - result += - ", Swing Vertical Pos: " + uint64ToString(getSwingVerticalPosition()); + result += F("Manual"); + result += F(", Swing Vertical Pos: "); + result += uint64ToString(getSwingVerticalPosition()); switch (getSwingVerticalPosition()) { case kGreeSwingLastPos: - result += " (Last Pos)"; + result += F(" (Last Pos)"); break; case kGreeSwingAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; } return result; diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index 489cefb..504ba94 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -1,6 +1,9 @@ -// Kelvinator A/C -// // Copyright 2016 David Conran +// Gree A/C +// +// Supports: +// Brand: Ultimate, Model: Heat Pump +// Brand: EKOKAI, Model: A/C #ifndef IR_GREE_H_ #define IR_GREE_H_ @@ -9,17 +12,12 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint8_t kGreeAuto = 0; @@ -44,6 +42,8 @@ const uint8_t kGreeSwingPosMask = 0b00001111; const uint8_t kGreeMinTemp = 16; // Celsius const uint8_t kGreeMaxTemp = 30; // Celsius +const uint8_t kGreeFanAuto = 0; +const uint8_t kGreeFanMin = 1; const uint8_t kGreeFanMax = 3; const uint8_t kGreeSwingLastPos = 0b00000000; @@ -82,49 +82,56 @@ class IRGreeAC { public: explicit IRGreeAC(uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_GREE void send(const uint16_t repeat = kGreeDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_GREE - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t new_mode); - uint8_t getMode(); - void setLight(const bool state); - bool getLight(); - void setXFan(const bool state); - bool getXFan(); - void setSleep(const bool state); - bool getSleep(); - void setTurbo(const bool state); - bool getTurbo(); + uint8_t getMode(void); + void setLight(const bool on); + bool getLight(void); + void setXFan(const bool on); + bool getXFan(void); + void setSleep(const bool on); + bool getSleep(void); + void setTurbo(const bool on); + bool getTurbo(void); void setSwingVertical(const bool automatic, const uint8_t position); - bool getSwingVerticalAuto(); - uint8_t getSwingVerticalPosition(); - - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + bool getSwingVerticalAuto(void); + uint8_t getSwingVerticalPosition(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static bool validChecksum(const uint8_t state[], const uint16_t length = kGreeStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + String toString(void); +#ifndef UNIT_TEST private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST // The state of the IR remote in IR code form. uint8_t remote_state[kGreeStateLength]; void checksum(const uint16_t length = kGreeStateLength); - void fixup(); - IRsend _irsend; + void fixup(void); }; #endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp index 719e8c5..a89ad32 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -1,4 +1,5 @@ // Copyright 2018 crankyoldgit +// Code to emulate Haier protocol compatible devices. // The specifics of reverse engineering the protocols details: // * HSU07-HEA03 by kuzin2006. // * YR-W02/HSU-09HMC203 by non7top. @@ -6,18 +7,10 @@ #include "ir_Haier.h" #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRutils.h" -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR - // Supported devices: // * Haier HSU07-HEA03 Remote control. // * Haier YR-W02 Remote control @@ -45,10 +38,10 @@ const uint32_t kHaierAcMinGap = 150000; // Completely made up value. // nbytes: Nr. of bytes of data in the array. (>=kHaierACStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: Beta / Probably working. +// Status: STABLE / Known to be working. // -void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHaierAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHaierACStateLength) return; for (uint16_t r = 0; r <= repeat; r++) { @@ -74,16 +67,16 @@ void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, // // Status: Alpha / Untested on a real device. // -void IRsend::sendHaierACYRW02(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHaierACYRW02(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes >= kHaierACYRW02StateLength) sendHaierAC(data, nbytes, repeat); } #endif // SEND_HAIER_AC_YRW02 // Class for emulating a Haier HSU07-HEA03 remote -IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRHaierAC::IRHaierAC(const uint16_t pin) : _irsend(pin) { stateReset(); } -void IRHaierAC::begin() { _irsend.begin(); } +void IRHaierAC::begin(void) { _irsend.begin(); } #if SEND_HAIER_AC void IRHaierAC::send(const uint16_t repeat) { @@ -92,7 +85,7 @@ void IRHaierAC::send(const uint16_t repeat) { } #endif // SEND_HAIER_AC -void IRHaierAC::checksum() { +void IRHaierAC::checksum(void) { remote_state[8] = sumBytes(remote_state, kHaierACStateLength - 1); } @@ -101,10 +94,12 @@ bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { return (state[length - 1] == sumBytes(state, length - 1)); } -void IRHaierAC::stateReset() { +void IRHaierAC::stateReset(void) { for (uint8_t i = 1; i < kHaierACStateLength; i++) remote_state[i] = 0x0; remote_state[0] = kHaierAcPrefix; - remote_state[2] = 0b00100000; + remote_state[2] = 0x20; + remote_state[4] = 0x0C; + remote_state[5] = 0xC0; setTemp(kHaierAcDefTemp); setFan(kHaierAcFanAuto); @@ -112,18 +107,18 @@ void IRHaierAC::stateReset() { setCommand(kHaierAcCmdOn); } -uint8_t* IRHaierAC::getRaw() { +uint8_t* IRHaierAC::getRaw(void) { checksum(); return remote_state; } -void IRHaierAC::setRaw(uint8_t new_code[]) { +void IRHaierAC::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kHaierACStateLength; i++) { remote_state[i] = new_code[i]; } } -void IRHaierAC::setCommand(uint8_t state) { +void IRHaierAC::setCommand(const uint8_t state) { remote_state[1] &= 0b11110000; switch (state) { case kHaierAcCmdOff: @@ -141,9 +136,9 @@ void IRHaierAC::setCommand(uint8_t state) { } } -uint8_t IRHaierAC::getCommand() { return remote_state[1] & (0b00001111); } +uint8_t IRHaierAC::getCommand(void) { return remote_state[1] & (0b00001111); } -void IRHaierAC::setFan(uint8_t speed) { +void IRHaierAC::setFan(const uint8_t speed) { uint8_t new_speed = kHaierAcFanAuto; switch (speed) { case kHaierAcFanLow: @@ -164,7 +159,7 @@ void IRHaierAC::setFan(uint8_t speed) { remote_state[5] |= new_speed; } -uint8_t IRHaierAC::getFan() { +uint8_t IRHaierAC::getFan(void) { switch (remote_state[5] & 0b00000011) { case 1: return kHaierAcFanMed; @@ -182,11 +177,13 @@ void IRHaierAC::setMode(uint8_t mode) { setCommand(kHaierAcCmdMode); if (mode > kHaierAcFan) // If out of range, default to auto mode. new_mode = kHaierAcAuto; - remote_state[7] &= 0b00011111; - remote_state[7] |= (new_mode << 5); + remote_state[6] &= ~kHaierAcModeMask; + remote_state[6] |= (new_mode << 5); } -uint8_t IRHaierAC::getMode() { return (remote_state[7] & 0b11100000) >> 5; } +uint8_t IRHaierAC::getMode(void) { + return (remote_state[6] & kHaierAcModeMask) >> 5; +} void IRHaierAC::setTemp(const uint8_t celsius) { uint8_t temp = celsius; @@ -206,45 +203,47 @@ void IRHaierAC::setTemp(const uint8_t celsius) { remote_state[1] |= ((temp - kHaierAcMinTemp) << 4); } -uint8_t IRHaierAC::getTemp() { +uint8_t IRHaierAC::getTemp(void) { return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp; } -void IRHaierAC::setHealth(bool state) { +void IRHaierAC::setHealth(const bool on) { setCommand(kHaierAcCmdHealth); remote_state[4] &= 0b11011111; - remote_state[4] |= (state << 5); + remote_state[4] |= (on << 5); } bool IRHaierAC::getHealth(void) { return remote_state[4] & (1 << 5); } -void IRHaierAC::setSleep(bool state) { +void IRHaierAC::setSleep(const bool on) { setCommand(kHaierAcCmdSleep); - remote_state[7] &= 0b10111111; - remote_state[7] |= (state << 6); + if (on) + remote_state[7] |= kHaierAcSleepBit; + else + remote_state[7] &= ~kHaierAcSleepBit; } -bool IRHaierAC::getSleep(void) { return remote_state[7] & 0b01000000; } +bool IRHaierAC::getSleep(void) { return remote_state[7] & kHaierAcSleepBit; } uint16_t IRHaierAC::getTime(const uint8_t ptr[]) { return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111); } -int16_t IRHaierAC::getOnTimer() { +int16_t IRHaierAC::getOnTimer(void) { if (remote_state[3] & 0b10000000) // Check if the timer is turned on. return getTime(remote_state + 6); else return -1; } -int16_t IRHaierAC::getOffTimer() { +int16_t IRHaierAC::getOffTimer(void) { if (remote_state[3] & 0b01000000) // Check if the timer is turned on. return getTime(remote_state + 4); else return -1; } -uint16_t IRHaierAC::getCurrTime() { return getTime(remote_state + 2); } +uint16_t IRHaierAC::getCurrTime(void) { return getTime(remote_state + 2); } void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) { uint16_t mins = nr_mins; @@ -270,7 +269,7 @@ void IRHaierAC::setOffTimer(const uint16_t nr_mins) { setTime(remote_state + 4, nr_mins); } -void IRHaierAC::cancelTimers() { +void IRHaierAC::cancelTimers(void) { setCommand(kHaierAcCmdTimerCancel); remote_state[3] &= 0b00111111; } @@ -279,7 +278,9 @@ void IRHaierAC::setCurrTime(const uint16_t nr_mins) { setTime(remote_state + 2, nr_mins); } -uint8_t IRHaierAC::getSwing() { return (remote_state[2] & 0b11000000) >> 6; } +uint8_t IRHaierAC::getSwing(void) { + return (remote_state[2] & 0b11000000) >> 6; +} void IRHaierAC::setSwing(const uint8_t state) { if (state == getSwing()) return; // Nothing to do. @@ -296,139 +297,243 @@ void IRHaierAC::setSwing(const uint8_t state) { } // Convert a Haier time into a human readable string. -#ifdef ARDUINO String IRHaierAC::timeToString(const uint16_t nr_mins) { String result = ""; -#else -std::string IRHaierAC::timeToString(const uint16_t nr_mins) { - std::string result = ""; -#endif // ARDUINO - - if (nr_mins / 24 < 10) result += "0"; // Zero pad. + if (nr_mins / 24 < 10) result += '0'; // Zero pad. result += uint64ToString(nr_mins / 60); - result += ":"; - if (nr_mins % 60 < 10) result += "0"; // Zero pad. + result += ':'; + if (nr_mins % 60 < 10) result += '0'; // Zero pad. result += uint64ToString(nr_mins % 60); return result; } +// Convert a standard A/C mode into its native mode. +uint8_t IRHaierAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kHaierAcCool; + case stdAc::opmode_t::kHeat: + return kHaierAcHeat; + case stdAc::opmode_t::kDry: + return kHaierAcDry; + case stdAc::opmode_t::kFan: + return kHaierAcFan; + default: + return kHaierAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRHaierAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kHaierAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kHaierAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kHaierAcFanHigh; + default: + return kHaierAcFanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRHaierAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + return kHaierAcSwingUp; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kHaierAcSwingDown; + case stdAc::swingv_t::kOff: + return kHaierAcSwingOff; + default: + return kHaierAcSwingChg; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRHaierAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHaierAcCool: return stdAc::opmode_t::kCool; + case kHaierAcHeat: return stdAc::opmode_t::kHeat; + case kHaierAcDry: return stdAc::opmode_t::kDry; + case kHaierAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRHaierAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHaierAcFanHigh: return stdAc::fanspeed_t::kMax; + case kHaierAcFanMed: return stdAc::fanspeed_t::kMedium; + case kHaierAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRHaierAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kHaierAcSwingUp: return stdAc::swingv_t::kHighest; + case kHaierAcSwingDown: return stdAc::swingv_t::kLowest; + case kHaierAcSwingOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRHaierAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::HAIER_AC; + result.model = -1; // No models used. + result.power = true; + if (this->getCommand() == kHaierAcCmdOff) result.power = false; + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwing()); + result.filter = this->getHealth(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.econo = false; + result.light = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHaierAC::toString() { +String IRHaierAC::toString(void) { String result = ""; -#else -std::string IRHaierAC::toString() { - std::string result = ""; -#endif // ARDUINO + result.reserve(150); // Reserve some heap for the string to reduce fragging. uint8_t cmd = getCommand(); - result += "Command: " + uint64ToString(cmd) + " ("; + result += F("Command: "); + result += uint64ToString(cmd); + result += F(" ("); switch (cmd) { case kHaierAcCmdOff: - result += "Off"; + result += F("Off"); break; case kHaierAcCmdOn: - result += "On"; + result += F("On"); break; case kHaierAcCmdMode: - result += "Mode"; + result += F("Mode"); break; case kHaierAcCmdFan: - result += "Fan"; + result += F("Fan"); break; case kHaierAcCmdTempUp: - result += "Temp Up"; + result += F("Temp Up"); break; case kHaierAcCmdTempDown: - result += "Temp Down"; + result += F("Temp Down"); break; case kHaierAcCmdSleep: - result += "Sleep"; + result += F("Sleep"); break; case kHaierAcCmdTimerSet: - result += "Timer Set"; + result += F("Timer Set"); break; case kHaierAcCmdTimerCancel: - result += "Timer Cancel"; + result += F("Timer Cancel"); break; case kHaierAcCmdHealth: - result += "Health"; + result += F("Health"); break; case kHaierAcCmdSwing: - result += "Swing"; + result += F("Swing"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); + result += ')'; + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHaierAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHaierAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kHaierAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kHaierAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kHaierAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHaierAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHaierAcFanHigh: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Swing: " + uint64ToString(getSwing()) + " ("; + result += F(", Swing: "); + result += uint64ToString(getSwing()); + result += F(" ("); switch (getSwing()) { case kHaierAcSwingOff: - result += "Off"; + result += F("Off"); break; case kHaierAcSwingUp: - result += "Up"; + result += F("Up"); break; case kHaierAcSwingDown: - result += "Down"; + result += F("Down"); break; case kHaierAcSwingChg: - result += "Chg"; + result += F("Chg"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Sleep: "; + result += ')'; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Health: "; + result += F("Off"); + result += F(", Health: "); if (getHealth()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Current Time: " + timeToString(getCurrTime()); - result += ", On Timer: "; + result += F("Off"); + result += F(", Current Time: "); + result += timeToString(getCurrTime()); + result += F(", On Timer: "); if (getOnTimer() >= 0) result += timeToString(getOnTimer()); else - result += "Off"; - result += ", Off Timer: "; + result += F("Off"); + result += F(", Off Timer: "); if (getOffTimer() >= 0) result += timeToString(getOffTimer()); else - result += "Off"; + result += F("Off"); return result; } @@ -437,7 +542,7 @@ std::string IRHaierAC::toString() { // Class for emulating a Haier YRW02 remote IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); } -void IRHaierACYRW02::begin() { _irsend.begin(); } +void IRHaierACYRW02::begin(void) { _irsend.begin(); } #if SEND_HAIER_AC_YRW02 void IRHaierACYRW02::send(const uint16_t repeat) { @@ -446,7 +551,7 @@ void IRHaierACYRW02::send(const uint16_t repeat) { } #endif // SEND_HAIER_AC_YRW02 -void IRHaierACYRW02::checksum() { +void IRHaierACYRW02::checksum(void) { remote_state[kHaierACYRW02StateLength - 1] = sumBytes(remote_state, kHaierACYRW02StateLength - 1); } @@ -456,7 +561,7 @@ bool IRHaierACYRW02::validChecksum(uint8_t state[], const uint16_t length) { return (state[length - 1] == sumBytes(state, length - 1)); } -void IRHaierACYRW02::stateReset() { +void IRHaierACYRW02::stateReset(void) { for (uint8_t i = 1; i < kHaierACYRW02StateLength; i++) remote_state[i] = 0x0; remote_state[0] = kHaierAcYrw02Prefix; @@ -470,12 +575,12 @@ void IRHaierACYRW02::stateReset() { setPower(true); } -uint8_t* IRHaierACYRW02::getRaw() { +uint8_t* IRHaierACYRW02::getRaw(void) { checksum(); return remote_state; } -void IRHaierACYRW02::setRaw(uint8_t new_code[]) { +void IRHaierACYRW02::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kHaierACYRW02StateLength; i++) { remote_state[i] = new_code[i]; } @@ -497,7 +602,9 @@ void IRHaierACYRW02::setButton(uint8_t button) { } } -uint8_t IRHaierACYRW02::getButton() { return remote_state[12] & (0b00001111); } +uint8_t IRHaierACYRW02::getButton(void) { + return remote_state[12] & 0b00001111; +} void IRHaierACYRW02::setMode(uint8_t mode) { uint8_t new_mode = mode; @@ -516,10 +623,10 @@ void IRHaierACYRW02::setMode(uint8_t mode) { remote_state[7] |= (new_mode << 4); } -uint8_t IRHaierACYRW02::getMode() { return remote_state[7] >> 4; } +uint8_t IRHaierACYRW02::getMode(void) { return remote_state[7] >> 4; } -void IRHaierACYRW02::setTemp(const uint8_t celcius) { - uint8_t temp = celcius; +void IRHaierACYRW02::setTemp(const uint8_t celsius) { + uint8_t temp = celsius; if (temp < kHaierAcMinTemp) temp = kHaierAcMinTemp; else if (temp > kHaierAcMaxTemp) @@ -536,43 +643,47 @@ void IRHaierACYRW02::setTemp(const uint8_t celcius) { remote_state[1] |= ((temp - kHaierAcMinTemp) << 4); } -uint8_t IRHaierACYRW02::getTemp() { +uint8_t IRHaierACYRW02::getTemp(void) { return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp; } -void IRHaierACYRW02::setHealth(bool state) { +void IRHaierACYRW02::setHealth(const bool on) { setButton(kHaierAcYrw02ButtonHealth); remote_state[3] &= 0b11111101; - remote_state[3] |= (state << 1); + remote_state[3] |= (on << 1); } bool IRHaierACYRW02::getHealth(void) { return remote_state[3] & 0b00000010; } -bool IRHaierACYRW02::getPower() { return remote_state[4] & kHaierAcYrw02Power; } +bool IRHaierACYRW02::getPower(void) { + return remote_state[4] & kHaierAcYrw02Power; +} -void IRHaierACYRW02::setPower(bool state) { +void IRHaierACYRW02::setPower(const bool on) { setButton(kHaierAcYrw02ButtonPower); - if (state) + if (on) remote_state[4] |= kHaierAcYrw02Power; else remote_state[4] &= ~kHaierAcYrw02Power; } -void IRHaierACYRW02::on() { setPower(true); } +void IRHaierACYRW02::on(void) { setPower(true); } -void IRHaierACYRW02::off() { setPower(false); } +void IRHaierACYRW02::off(void) { setPower(false); } -bool IRHaierACYRW02::getSleep() { return remote_state[8] & kHaierAcYrw02Sleep; } +bool IRHaierACYRW02::getSleep(void) { + return remote_state[8] & kHaierAcYrw02Sleep; +} -void IRHaierACYRW02::setSleep(bool state) { +void IRHaierACYRW02::setSleep(const bool on) { setButton(kHaierAcYrw02ButtonSleep); - if (state) + if (on) remote_state[8] |= kHaierAcYrw02Sleep; else remote_state[8] &= ~kHaierAcYrw02Sleep; } -uint8_t IRHaierACYRW02::getTurbo() { return remote_state[6] >> 6; } +uint8_t IRHaierACYRW02::getTurbo(void) { return remote_state[6] >> 6; } void IRHaierACYRW02::setTurbo(uint8_t speed) { switch (speed) { @@ -585,7 +696,7 @@ void IRHaierACYRW02::setTurbo(uint8_t speed) { } } -uint8_t IRHaierACYRW02::getFan() { return remote_state[5] >> 4; } +uint8_t IRHaierACYRW02::getFan(void) { return remote_state[5] >> 4; } void IRHaierACYRW02::setFan(uint8_t speed) { switch (speed) { @@ -599,7 +710,7 @@ void IRHaierACYRW02::setFan(uint8_t speed) { } } -uint8_t IRHaierACYRW02::getSwing() { return remote_state[1] & 0b00001111; } +uint8_t IRHaierACYRW02::getSwing(void) { return remote_state[1] & 0b00001111; } void IRHaierACYRW02::setSwing(uint8_t state) { uint8_t newstate = state; @@ -628,140 +739,254 @@ void IRHaierACYRW02::setSwing(uint8_t state) { remote_state[1] |= newstate; } +// Convert a standard A/C mode into its native mode. +uint8_t IRHaierACYRW02::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kHaierAcYrw02Cool; + case stdAc::opmode_t::kHeat: + return kHaierAcYrw02Heat; + case stdAc::opmode_t::kDry: + return kHaierAcYrw02Dry; + case stdAc::opmode_t::kFan: + return kHaierAcYrw02Fan; + default: + return kHaierAcYrw02Auto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRHaierACYRW02::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kHaierAcYrw02FanLow; + case stdAc::fanspeed_t::kMedium: + return kHaierAcYrw02FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kHaierAcYrw02FanHigh; + default: + return kHaierAcYrw02FanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRHaierACYRW02::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + return kHaierAcYrw02SwingTop; + case stdAc::swingv_t::kMiddle: + return kHaierAcYrw02SwingMiddle; + case stdAc::swingv_t::kLow: + return kHaierAcYrw02SwingDown; + case stdAc::swingv_t::kLowest: + return kHaierAcYrw02SwingBottom; + case stdAc::swingv_t::kOff: + return kHaierAcYrw02SwingOff; + default: + return kHaierAcYrw02SwingAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRHaierACYRW02::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHaierAcYrw02Cool: return stdAc::opmode_t::kCool; + case kHaierAcYrw02Heat: return stdAc::opmode_t::kHeat; + case kHaierAcYrw02Dry: return stdAc::opmode_t::kDry; + case kHaierAcYrw02Fan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRHaierACYRW02::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHaierAcYrw02FanHigh: return stdAc::fanspeed_t::kMax; + case kHaierAcYrw02FanMed: return stdAc::fanspeed_t::kMedium; + case kHaierAcYrw02FanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRHaierACYRW02::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kHaierAcYrw02SwingTop: return stdAc::swingv_t::kHighest; + case kHaierAcYrw02SwingMiddle: return stdAc::swingv_t::kMiddle; + case kHaierAcYrw02SwingDown: return stdAc::swingv_t::kLow; + case kHaierAcYrw02SwingBottom: return stdAc::swingv_t::kLowest; + case kHaierAcYrw02SwingOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRHaierACYRW02::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::HAIER_AC_YRW02; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwing()); + result.filter = this->getHealth(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.econo = false; + result.light = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHaierACYRW02::toString() { +String IRHaierACYRW02::toString(void) { String result = ""; -#else -std::string IRHaierACYRW02::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; + result.reserve(130); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); uint8_t cmd = getButton(); - result += ", Button: " + uint64ToString(cmd) + " ("; + result += F(", Button: "); + result += uint64ToString(cmd); + result += F(" ("); switch (cmd) { case kHaierAcYrw02ButtonPower: - result += "Power"; + result += F("Power"); break; case kHaierAcYrw02ButtonMode: - result += "Mode"; + result += F("Mode"); break; case kHaierAcYrw02ButtonFan: - result += "Fan"; + result += F("Fan"); break; case kHaierAcYrw02ButtonTempUp: - result += "Temp Up"; + result += F("Temp Up"); break; case kHaierAcYrw02ButtonTempDown: - result += "Temp Down"; + result += F("Temp Down"); break; case kHaierAcYrw02ButtonSleep: - result += "Sleep"; + result += F("Sleep"); break; case kHaierAcYrw02ButtonHealth: result += "Health"; break; case kHaierAcYrw02ButtonSwing: - result += "Swing"; + result += F("Swing"); break; case kHaierAcYrw02ButtonTurbo: - result += "Turbo"; + result += F("Turbo"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); + result += ')'; + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHaierAcYrw02Auto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kHaierAcYrw02Cool: - result += " (Cool)"; + result += F(" (Cool)"); break; case kHaierAcYrw02Heat: - result += " (Heat)"; + result += F(" (Heat)"); break; case kHaierAcYrw02Dry: - result += " (Dry)"; + result += F(" (Dry)"); break; case kHaierAcYrw02Fan: - result += " (Fan)"; + result += F(" (Fan)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHaierAcYrw02FanAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kHaierAcYrw02FanHigh: - result += " (High)"; + result += F(" (High)"); break; case kHaierAcYrw02FanLow: - result += " (Low)"; + result += F(" (Low)"); break; case kHaierAcYrw02FanMed: - result += " (Med)"; + result += F(" (Med)"); break; default: - result += " (Unknown)"; + result += F(" (Unknown)"); } - result += ", Turbo: " + uint64ToString(getTurbo()) + " ("; + result += F(", Turbo: "); + result += uint64ToString(getTurbo()); + result += F(" ("); switch (getTurbo()) { case kHaierAcYrw02TurboOff: - result += "Off"; + result += F("Off"); break; case kHaierAcYrw02TurboLow: - result += "Low"; + result += F("Low"); break; case kHaierAcYrw02TurboHigh: - result += "High"; + result += F("High"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Swing: " + uint64ToString(getSwing()) + " ("; + result += ')'; + result += F(", Swing: "); + result += uint64ToString(getSwing()); + result += F(" ("); switch (getSwing()) { case kHaierAcYrw02SwingOff: - result += "Off"; + result += F("Off"); break; case kHaierAcYrw02SwingAuto: - result += "Auto"; + result += F("Auto"); break; case kHaierAcYrw02SwingBottom: - result += "Bottom"; + result += F("Bottom"); break; case kHaierAcYrw02SwingDown: - result += "Down"; + result += F("Down"); break; case kHaierAcYrw02SwingTop: - result += "Top"; + result += F("Top"); break; case kHaierAcYrw02SwingMiddle: - result += "Middle"; + result += F("Middle"); break; default: - result += "Unknown"; + result += F("Unknown"); } - result += ")"; - result += ", Sleep: "; + result += ')'; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Health: "; + result += F("Off"); + result += F(", Health: "); if (getHealth()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } @@ -777,7 +1002,7 @@ std::string IRHaierACYRW02::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Appears to be working. +// Status: STABLE / Known to be working. // bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, bool strict) { diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index f4a0d32..d162f74 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -1,22 +1,22 @@ // Copyright 2018 crankyoldgit // The specifics of reverse engineering the protocol details by kuzin2006 +// Supports: +// Brand: Haier, Model: HSU07-HEA03 remote +// Brand: Haier, Model: YR-W02 remote +// Brand: Haier, Model: HSU-09HMC203 A/C + #ifndef IR_HAIER_H_ #define IR_HAIER_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/404 @@ -53,6 +53,7 @@ const uint8_t kHaierAcSwingDown = 0b00000010; const uint8_t kHaierAcSwingChg = 0b00000011; // Byte 6 +const uint8_t kHaierAcModeMask = 0b11100000; const uint8_t kHaierAcAuto = 0; const uint8_t kHaierAcCool = 1; const uint8_t kHaierAcDry = 2; @@ -66,6 +67,9 @@ const uint8_t kHaierAcFanHigh = 3; const uint16_t kHaierAcMaxTime = (23 * 60) + 59; +// Byte 7 +const uint8_t kHaierAcSleepBit = 0b01000000; + // Legacy Haier AC defines. #define HAIER_AC_MIN_TEMP kHaierAcMinTemp #define HAIER_AC_DEF_TEMP kHaierAcDefTemp @@ -183,61 +187,68 @@ const uint8_t kHaierAcYrw02ButtonSleep = 0xB; class IRHaierAC { public: - explicit IRHaierAC(uint16_t pin); + explicit IRHaierAC(const uint16_t pin); #if SEND_HAIER_AC void send(const uint16_t repeat = kHaierAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_HAIER_AC - void begin(); + void begin(void); void setCommand(const uint8_t command); - uint8_t getCommand(); + uint8_t getCommand(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); - uint8_t getMode(); + uint8_t getMode(void); void setMode(const uint8_t mode); - bool getSleep(); - void setSleep(const bool state); - bool getHealth(); - void setHealth(const bool state); + bool getSleep(void); + void setSleep(const bool on); + bool getHealth(void); + void setHealth(const bool on); - int16_t getOnTimer(); + int16_t getOnTimer(void); void setOnTimer(const uint16_t mins); - int16_t getOffTimer(); + int16_t getOffTimer(void); void setOffTimer(const uint16_t mins); - void cancelTimers(); + void cancelTimers(void); - uint16_t getCurrTime(); + uint16_t getCurrTime(void); void setCurrTime(const uint16_t mins); - uint8_t getSwing(); + uint8_t getSwing(void); void setSwing(const uint8_t state); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACStateLength); -#ifdef ARDUINO - String toString(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); static String timeToString(const uint16_t nr_mins); -#else - std::string toString(); - static std::string timeToString(const uint16_t nr_mins); -#endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint8_t remote_state[kHaierACStateLength]; - void stateReset(); - void checksum(); + void stateReset(void); + void checksum(void); static uint16_t getTime(const uint8_t ptr[]); static void setTime(uint8_t ptr[], const uint16_t nr_mins); - IRsend _irsend; }; class IRHaierACYRW02 { @@ -247,51 +258,58 @@ class IRHaierACYRW02 { #if SEND_HAIER_AC_YRW02 void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif // SEND_HAIER_AC_YRW02 - void begin(); + void begin(void); void setButton(const uint8_t button); - uint8_t getButton(); + uint8_t getButton(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); - uint8_t getMode(); + uint8_t getMode(void); void setMode(const uint8_t mode); - bool getPower(); - void setPower(const bool state); - void on(); - void off(); + bool getPower(void); + void setPower(const bool on); + void on(void); + void off(void); - bool getSleep(); - void setSleep(const bool state); - bool getHealth(); - void setHealth(const bool state); + bool getSleep(void); + void setSleep(const bool on); + bool getHealth(void); + void setHealth(const bool on); - uint8_t getTurbo(); + uint8_t getTurbo(void); void setTurbo(const uint8_t speed); - uint8_t getSwing(); + uint8_t getSwing(void); void setSwing(const uint8_t state); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACYRW02StateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: - uint8_t remote_state[kHaierACYRW02StateLength]; - void stateReset(); - void checksum(); IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kHaierACYRW02StateLength]; + void stateReset(void); + void checksum(void); }; #endif // IR_HAIER_H_ diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp index 248f2d5..454e2c2 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266/src/ir_Hitachi.cpp @@ -15,12 +15,6 @@ #include "IRsend.h" #include "IRutils.h" -// HH HH IIIII TTTTTTT AAA CCCCC HH HH IIIII -// HH HH III TTT AAAAA CC C HH HH III -// HHHHHHH III TTT AA AA CC HHHHHHH III -// HH HH III TTT AAAAAAA CC C HH HH III -// HH HH IIIII TTT AA AA CCCCC HH HH IIIII - // Constants // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 const uint16_t kHitachiAcHdrMark = 3300; @@ -30,7 +24,7 @@ const uint16_t kHitachiAc1HdrSpace = 3400; const uint16_t kHitachiAcBitMark = 400; const uint16_t kHitachiAcOneSpace = 1250; const uint16_t kHitachiAcZeroSpace = 500; -const uint32_t kHitachiAcMinGap = 100000; // Completely made up value. +const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. #if (SEND_HITACHI_AC || SEND_HITACHI_AC2) // Send a Hitachi A/C message. @@ -44,8 +38,8 @@ const uint32_t kHitachiAcMinGap = 100000; // Completely made up value. // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/417 -void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAcStateLength) return; // Not enough bytes to send a proper message. sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, @@ -71,8 +65,8 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/453 // Basically the same as sendHitatchiAC() except different size and header. -void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAc1StateLength) return; // Not enough bytes to send a proper message. sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark, @@ -98,21 +92,21 @@ void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/417 // Basically the same as sendHitatchiAC() except different size. -void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAc2StateLength) return; // Not enough bytes to send a proper message. sendHitachiAC(data, nbytes, repeat); } #endif // SEND_HITACHI_AC2 -// Class for handling the remote control oh a Hitachi 28 byte A/C message. +// Class for handling the remote control on a Hitachi 28 byte A/C message. // Inspired by: // https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp -IRHitachiAc::IRHitachiAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRHitachiAc::IRHitachiAc(const uint16_t pin) : _irsend(pin) { stateReset(); } -void IRHitachiAc::stateReset() { +void IRHitachiAc::stateReset(void) { remote_state[0] = 0x80; remote_state[1] = 0x08; remote_state[2] = 0x0C; @@ -130,7 +124,7 @@ void IRHitachiAc::stateReset() { setTemp(23); } -void IRHitachiAc::begin() { _irsend.begin(); } +void IRHitachiAc::begin(void) { _irsend.begin(); } uint8_t IRHitachiAc::calcChecksum(const uint8_t state[], const uint16_t length) { @@ -148,7 +142,7 @@ bool IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) { return (state[length - 1] == calcChecksum(state, length)); } -uint8_t *IRHitachiAc::getRaw() { +uint8_t *IRHitachiAc::getRaw(void) { checksum(); return remote_state; } @@ -165,7 +159,7 @@ void IRHitachiAc::send(const uint16_t repeat) { } #endif // SEND_HITACHI_AC -bool IRHitachiAc::getPower() { return (remote_state[17] & 0x01); } +bool IRHitachiAc::getPower(void) { return (remote_state[17] & 0x01); } void IRHitachiAc::setPower(const bool on) { if (on) @@ -174,11 +168,11 @@ void IRHitachiAc::setPower(const bool on) { remote_state[17] &= 0xFE; } -void IRHitachiAc::on() { setPower(true); } +void IRHitachiAc::on(void) { setPower(true); } -void IRHitachiAc::off() { setPower(false); } +void IRHitachiAc::off(void) { setPower(false); } -uint8_t IRHitachiAc::getMode() { return reverseBits(remote_state[10], 8); } +uint8_t IRHitachiAc::getMode(void) { return reverseBits(remote_state[10], 8); } void IRHitachiAc::setMode(const uint8_t mode) { uint8_t newmode = mode; @@ -200,7 +194,9 @@ void IRHitachiAc::setMode(const uint8_t mode) { setFan(getFan()); // Reset the fan speed after the mode change. } -uint8_t IRHitachiAc::getTemp() { return reverseBits(remote_state[11], 8) >> 1; } +uint8_t IRHitachiAc::getTemp(void) { + return reverseBits(remote_state[11], 8) >> 1; +} void IRHitachiAc::setTemp(const uint8_t celsius) { uint8_t temp; @@ -220,7 +216,7 @@ void IRHitachiAc::setTemp(const uint8_t celsius) { remote_state[9] = 0x10; } -uint8_t IRHitachiAc::getFan() { return reverseBits(remote_state[13], 8); } +uint8_t IRHitachiAc::getFan(void) { return reverseBits(remote_state[13], 8); } void IRHitachiAc::setFan(const uint8_t speed) { uint8_t fanmin = kHitachiAcFanAuto; @@ -239,7 +235,7 @@ void IRHitachiAc::setFan(const uint8_t speed) { remote_state[13] = reverseBits(newspeed, 8); } -bool IRHitachiAc::getSwingVertical() { return remote_state[14] & 0x80; } +bool IRHitachiAc::getSwingVertical(void) { return remote_state[14] & 0x80; } void IRHitachiAc::setSwingVertical(const bool on) { if (on) @@ -248,7 +244,7 @@ void IRHitachiAc::setSwingVertical(const bool on) { remote_state[14] &= 0x7F; } -bool IRHitachiAc::getSwingHorizontal() { return remote_state[15] & 0x80; } +bool IRHitachiAc::getSwingHorizontal(void) { return remote_state[15] & 0x80; } void IRHitachiAc::setSwingHorizontal(const bool on) { if (on) @@ -257,65 +253,147 @@ void IRHitachiAc::setSwingHorizontal(const bool on) { remote_state[15] &= 0x7F; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRHitachiAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kHitachiAcCool; + case stdAc::opmode_t::kHeat: + return kHitachiAcHeat; + case stdAc::opmode_t::kDry: + return kHitachiAcDry; + case stdAc::opmode_t::kFan: + return kHitachiAcFan; + default: + return kHitachiAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kHitachiAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kHitachiAcFanLow + 1; + case stdAc::fanspeed_t::kHigh: + return kHitachiAcFanHigh - 1; + case stdAc::fanspeed_t::kMax: + return kHitachiAcFanHigh; + default: + return kHitachiAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRHitachiAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAcCool: return stdAc::opmode_t::kCool; + case kHitachiAcHeat: return stdAc::opmode_t::kHeat; + case kHitachiAcDry: return stdAc::opmode_t::kDry; + case kHitachiAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRHitachiAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAcFanHigh: return stdAc::fanspeed_t::kMax; + case kHitachiAcFanHigh - 1: return stdAc::fanspeed_t::kHigh; + case kHitachiAcFanLow + 1: return stdAc::fanspeed_t::kMedium; + case kHitachiAcFanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRHitachiAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::HITACHI_AC; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + // Not supported. + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHitachiAc::toString() { +String IRHitachiAc::toString(void) { String result = ""; -#else -std::string IRHitachiAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kHitachiAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHitachiAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kHitachiAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kHitachiAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kHitachiAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kHitachiAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kHitachiAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kHitachiAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing (Vertical): "; + result += F(", Swing (Vertical): "); if (getSwingVertical()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Swing (Horizontal): "; + result += F("Off"); + result += F(", Swing (Horizontal): "); if (getSwingHorizontal()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } @@ -338,8 +416,8 @@ std::string IRHitachiAc::toString() { // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/417 // https://github.com/markszabo/IRremoteESP8266/issues/453 -bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeHitachiAC(decode_results *results, const uint16_t nbits, + const bool strict) { const uint8_t kTolerance = 30; if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid HitachiAC message. diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.h b/lib/IRremoteESP8266/src/ir_Hitachi.h index efc775e..4f591d9 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -2,6 +2,11 @@ // // Copyright 2018 David Conran +// Supports: +// Brand: Hitachi, Model: RAS-35THA6 remote +// Brand: Hitachi, Model: LT0541-HTA remote +// Brand: Hitachi, Model: Series VI A/C (Circa 2007) + #ifndef IR_HITACHI_H_ #define IR_HITACHI_H_ @@ -9,11 +14,12 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint8_t kHitachiAcAuto = 2; @@ -31,45 +37,51 @@ const uint8_t kHitachiAcAutoTemp = 23; // 23C // Classes class IRHitachiAc { public: - explicit IRHitachiAc(uint16_t pin); + explicit IRHitachiAc(const uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_HITACHI_AC void send(const uint16_t repeat = kHitachiAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_HITACHI_AC - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); void setPower(const bool on); - bool getPower(); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setSwingVertical(const bool on); - bool getSwingVertical(); + bool getSwingVertical(void); void setSwingHorizontal(const bool on); - bool getSwingHorizontal(); - uint8_t* getRaw(); + bool getSwingHorizontal(void); + uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kHitachiAcStateLength); static bool validChecksum(const uint8_t state[], const uint16_t length = kHitachiAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kHitachiAcStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kHitachiAcStateLength]; void checksum(const uint16_t length = kHitachiAcStateLength); - IRsend _irsend; uint8_t _previoustemp; }; diff --git a/lib/IRremoteESP8266/src/ir_Inax.cpp b/lib/IRremoteESP8266/src/ir_Inax.cpp new file mode 100644 index 0000000..0c95b5b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Inax.cpp @@ -0,0 +1,96 @@ +// Copyright 2019 David Conran (crankyoldgit) +// Support for an IR controlled Robot Toilet + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Supports: +// Brand: Lixil, Model: Inax DT-BA283 Toilet + +// Documentation: +// https://www.lixil-manual.com/GCW-1365-16050/GCW-1365-16050.pdf + +// Constants +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/706 +const uint16_t kInaxTick = 500; +const uint16_t kInaxHdrMark = 9000; +const uint16_t kInaxHdrSpace = 4500; +const uint16_t kInaxBitMark = 560; +const uint16_t kInaxOneSpace = 1675; +const uint16_t kInaxZeroSpace = kInaxBitMark; +const uint16_t kInaxMinGap = 40000; + +#if SEND_INAX +// Send a Inax Toilet formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The bit size of the message being sent. typically kInaxBits. +// repeat: The number of times the message is to be repeated. +// +// Status: BETA / Should be working. +// +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/706 +void IRsend::sendInax(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kInaxHdrMark, kInaxHdrSpace, + kInaxBitMark, kInaxOneSpace, + kInaxBitMark, kInaxZeroSpace, + kInaxBitMark, kInaxMinGap, + data, nbits, 38, true, repeat, kDutyDefault); +} +#endif + +#if DECODE_INAX +// Decode the supplied Inax Toilet message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. +// Typically kInaxBits. +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Should be Working. +// +bool IRrecv::decodeInax(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) + return false; // Can't possibly be a valid Inax message. + if (strict && nbits != kInaxBits) + return false; // We expect Inax to be a certain sized message. + + uint64_t data = 0; + uint16_t offset = kStartOffset; + + // Header + if (!matchMark(results->rawbuf[offset++], kInaxHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kInaxHdrSpace)) return false; + // Data + match_result_t data_result = + matchData(&(results->rawbuf[offset]), nbits, kInaxBitMark, + kInaxOneSpace, kInaxBitMark, kInaxZeroSpace); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + // Footer + if (!matchMark(results->rawbuf[offset++], kInaxBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kInaxMinGap)) + return false; + + // Compliance + + // Success + results->bits = nbits; + results->value = data; + results->decode_type = INAX; + results->command = 0; + results->address = 0; + return true; +} +#endif diff --git a/lib/IRremoteESP8266/src/ir_JVC.cpp b/lib/IRremoteESP8266/src/ir_JVC.cpp index 47df29d..91777e1 100644 --- a/lib/IRremoteESP8266/src/ir_JVC.cpp +++ b/lib/IRremoteESP8266/src/ir_JVC.cpp @@ -7,12 +7,6 @@ #include "IRtimer.h" #include "IRutils.h" -// JJJJJ V V CCCC -// J V V C -// J V V C -// J J V V C -// J V CCCC - // JVC originally added by Kristian Lauszus // (Thanks to zenwheel and other people at the original blog post) diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp index e172c81..b1b5ef6 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp @@ -19,16 +19,11 @@ #ifndef ARDUINO #include #endif +#include "IRac.h" #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR -// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR - // Constants const uint16_t kKelvinatorTick = 85; @@ -81,8 +76,8 @@ const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset; // // Status: STABLE / Known working. // -void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendKelvinator(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kKelvinatorStateLength) return; // Not enough bytes to send a proper message. @@ -123,36 +118,38 @@ void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, } #endif // SEND_KELVINATOR -IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { + this->stateReset(); +} -void IRKelvinatorAC::stateReset() { +void IRKelvinatorAC::stateReset(void) { for (uint8_t i = 0; i < kKelvinatorStateLength; i++) remote_state[i] = 0x0; remote_state[3] = 0x50; remote_state[11] = 0x70; } -void IRKelvinatorAC::begin() { _irsend.begin(); } +void IRKelvinatorAC::begin(void) { _irsend.begin(); } -void IRKelvinatorAC::fixup() { +void IRKelvinatorAC::fixup(void) { // X-Fan mode is only valid in COOL or DRY modes. - if (getMode() != kKelvinatorCool && getMode() != kKelvinatorDry) - setXFan(false); - checksum(); // Calculate the checksums + if (this->getMode() != kKelvinatorCool && this->getMode() != kKelvinatorDry) + this->setXFan(false); + this->checksum(); // Calculate the checksums } #if SEND_KELVINATOR void IRKelvinatorAC::send(const uint16_t repeat) { - fixup(); // Ensure correct settings before sending. + this->fixup(); // Ensure correct settings before sending. _irsend.sendKelvinator(remote_state, kKelvinatorStateLength, repeat); } #endif // SEND_KELVINATOR -uint8_t *IRKelvinatorAC::getRaw() { - fixup(); // Ensure correct settings before sending. +uint8_t *IRKelvinatorAC::getRaw(void) { + this->fixup(); // Ensure correct settings before sending. return remote_state; } -void IRKelvinatorAC::setRaw(uint8_t new_code[]) { +void IRKelvinatorAC::setRaw(const uint8_t new_code[]) { for (uint8_t i = 0; i < kKelvinatorStateLength; i++) { remote_state[i] = new_code[i]; } @@ -195,46 +192,46 @@ bool IRKelvinatorAC::validChecksum(const uint8_t state[], return true; } -void IRKelvinatorAC::on() { +void IRKelvinatorAC::on(void) { remote_state[0] |= kKelvinatorPower; remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -void IRKelvinatorAC::off() { +void IRKelvinatorAC::off(void) { remote_state[0] &= ~kKelvinatorPower; remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -void IRKelvinatorAC::setPower(bool state) { - if (state) - on(); +void IRKelvinatorAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -bool IRKelvinatorAC::getPower() { - return ((remote_state[0] & kKelvinatorPower) != 0); +bool IRKelvinatorAC::getPower(void) { + return remote_state[0] & kKelvinatorPower; } // Set the temp. in deg C -void IRKelvinatorAC::setTemp(uint8_t temp) { - temp = std::max(kKelvinatorMinTemp, temp); +void IRKelvinatorAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kKelvinatorMinTemp, degrees); temp = std::min(kKelvinatorMaxTemp, temp); remote_state[1] = (remote_state[1] & 0xF0U) | (temp - kKelvinatorMinTemp); remote_state[9] = remote_state[1]; // Duplicate to the 2nd command chunk. } // Return the set temp. in deg C -uint8_t IRKelvinatorAC::getTemp() { +uint8_t IRKelvinatorAC::getTemp(void) { return ((remote_state[1] & 0xFU) + kKelvinatorMinTemp); } // Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed -void IRKelvinatorAC::setFan(uint8_t fan) { - fan = std::min(kKelvinatorFanMax, fan); // Bounds check +void IRKelvinatorAC::setFan(const uint8_t speed) { + uint8_t fan = std::min(kKelvinatorFanMax, speed); // Bounds check // Only change things if we need to. - if (fan != getFan()) { + if (fan != this->getFan()) { // Set the basic fan values. uint8_t fan_basic = std::min(kKelvinatorBasicFanMax, fan); remote_state[0] = (remote_state[0] & kKelvinatorBasicFanMask) | @@ -243,188 +240,231 @@ void IRKelvinatorAC::setFan(uint8_t fan) { // Set the advanced(?) fan value. remote_state[14] = (remote_state[14] & kKelvinatorFanMask) | (fan << kKelvinatorFanOffset); - setTurbo(false); // Turbo mode is turned off if we change the fan settings. + // Turbo mode is turned off if we change the fan settings. + this->setTurbo(false); } } -uint8_t IRKelvinatorAC::getFan() { +uint8_t IRKelvinatorAC::getFan(void) { return ((remote_state[14] & ~kKelvinatorFanMask) >> kKelvinatorFanOffset); } -uint8_t IRKelvinatorAC::getMode() { +uint8_t IRKelvinatorAC::getMode(void) { return (remote_state[0] & ~kKelvinatorModeMask); } -void IRKelvinatorAC::setMode(uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - if (mode > kKelvinatorHeat) mode = kKelvinatorAuto; - remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode; - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. - if (mode == kKelvinatorAuto || kKelvinatorDry) - // When the remote is set to Auto or Dry, it defaults to 25C and doesn't - // show it. - setTemp(kKelvinatorAutoTemp); +void IRKelvinatorAC::setMode(const uint8_t mode) { + switch (mode) { + case kKelvinatorAuto: + case kKelvinatorDry: + // When the remote is set to Auto or Dry, it defaults to 25C and doesn't + // show it. + this->setTemp(kKelvinatorAutoTemp); + // FALL-THRU + case kKelvinatorHeat: + case kKelvinatorCool: + case kKelvinatorFan: + remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode; + remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. + break; + default: // If we get an unexpected mode, default to AUTO. + this->setMode(kKelvinatorAuto); + } } -void IRKelvinatorAC::setSwingVertical(bool state) { - if (state) { +void IRKelvinatorAC::setSwingVertical(const bool on) { + if (on) { remote_state[0] |= kKelvinatorVentSwing; remote_state[4] |= kKelvinatorVentSwingV; } else { remote_state[4] &= ~kKelvinatorVentSwingV; - if (!getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing; + if (!this->getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing; } remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getSwingVertical() { - return ((remote_state[4] & kKelvinatorVentSwingV) != 0); +bool IRKelvinatorAC::getSwingVertical(void) { + return remote_state[4] & kKelvinatorVentSwingV; } -void IRKelvinatorAC::setSwingHorizontal(bool state) { - if (state) { +void IRKelvinatorAC::setSwingHorizontal(const bool on) { + if (on) { remote_state[0] |= kKelvinatorVentSwing; remote_state[4] |= kKelvinatorVentSwingH; } else { remote_state[4] &= ~kKelvinatorVentSwingH; - if (!getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing; + if (!this->getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing; } remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getSwingHorizontal() { - return ((remote_state[4] & kKelvinatorVentSwingH) != 0); +bool IRKelvinatorAC::getSwingHorizontal(void) { + return remote_state[4] & kKelvinatorVentSwingH; } -void IRKelvinatorAC::setQuiet(bool state) { +void IRKelvinatorAC::setQuiet(const bool on) { remote_state[12] &= ~kKelvinatorQuiet; - remote_state[12] |= (state << kKelvinatorQuietOffset); + remote_state[12] |= (on << kKelvinatorQuietOffset); } -bool IRKelvinatorAC::getQuiet() { - return ((remote_state[12] & kKelvinatorQuiet) != 0); +bool IRKelvinatorAC::getQuiet(void) { + return remote_state[12] & kKelvinatorQuiet; } -void IRKelvinatorAC::setIonFilter(bool state) { +void IRKelvinatorAC::setIonFilter(const bool on) { remote_state[2] &= ~kKelvinatorIonFilter; - remote_state[2] |= (state << kKelvinatorIonFilterOffset); + remote_state[2] |= (on << kKelvinatorIonFilterOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getIonFilter() { - return ((remote_state[2] & kKelvinatorIonFilter) != 0); +bool IRKelvinatorAC::getIonFilter(void) { + return remote_state[2] & kKelvinatorIonFilter; } -void IRKelvinatorAC::setLight(bool state) { +void IRKelvinatorAC::setLight(const bool on) { remote_state[2] &= ~kKelvinatorLight; - remote_state[2] |= (state << kKelvinatorLightOffset); + remote_state[2] |= (on << kKelvinatorLightOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getLight() { - return ((remote_state[2] & kKelvinatorLight) != 0); +bool IRKelvinatorAC::getLight(void) { + return remote_state[2] & kKelvinatorLight; } // Note: XFan mode is only valid in Cool or Dry mode. -void IRKelvinatorAC::setXFan(bool state) { +void IRKelvinatorAC::setXFan(const bool on) { remote_state[2] &= ~kKelvinatorXfan; - remote_state[2] |= (state << kKelvinatorXfanOffset); + remote_state[2] |= (on << kKelvinatorXfanOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getXFan() { - return ((remote_state[2] & kKelvinatorXfan) != 0); +bool IRKelvinatorAC::getXFan(void) { + return remote_state[2] & kKelvinatorXfan; } // Note: Turbo mode is turned off if the fan speed is changed. -void IRKelvinatorAC::setTurbo(bool state) { +void IRKelvinatorAC::setTurbo(const bool on) { remote_state[2] &= ~kKelvinatorTurbo; - remote_state[2] |= (state << kKelvinatorTurboOffset); + remote_state[2] |= (on << kKelvinatorTurboOffset); remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. } -bool IRKelvinatorAC::getTurbo() { - return ((remote_state[2] & kKelvinatorTurbo) != 0); +bool IRKelvinatorAC::getTurbo(void) { + return remote_state[2] & kKelvinatorTurbo; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kKelvinatorCool; + case stdAc::opmode_t::kHeat: + return kKelvinatorHeat; + case stdAc::opmode_t::kDry: + return kKelvinatorDry; + case stdAc::opmode_t::kFan: + return kKelvinatorFan; + default: + return kKelvinatorAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRKelvinatorAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kKelvinatorCool: return stdAc::opmode_t::kCool; + case kKelvinatorHeat: return stdAc::opmode_t::kHeat; + case kKelvinatorDry: return stdAc::opmode_t::kDry; + case kKelvinatorFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRKelvinatorAC::toCommonFanSpeed(const uint8_t speed) { + return (stdAc::fanspeed_t)speed; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRKelvinatorAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::KELVINATOR; + result.model = -1; // Unused. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.filter = this->getIonFilter(); + result.clean = this->getXFan(); + // Not supported. + result.econo = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRKelvinatorAC::toString() { +String IRKelvinatorAC::toString(void) { String result = ""; -#else -std::string IRKelvinatorAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result.reserve(160); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kKelvinatorAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kKelvinatorCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kKelvinatorHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kKelvinatorDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kKelvinatorFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kKelvinatorFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kKelvinatorFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } - result += ", Turbo: "; - if (getTurbo()) - result += "On"; - else - result += "Off"; - result += ", Quiet: "; - if (getQuiet()) - result += "On"; - else - result += "Off"; - result += ", XFan: "; - if (getXFan()) - result += "On"; - else - result += "Off"; - result += ", IonFilter: "; - if (getIonFilter()) - result += "On"; - else - result += "Off"; - result += ", Light: "; - if (getLight()) - result += "On"; - else - result += "Off"; - result += ", Swing (Horizontal): "; - if (getSwingHorizontal()) - result += "On"; - else - result += "Off"; - result += ", Swing (Vertical): "; - if (getSwingVertical()) - result += "On"; - else - result += "Off"; + result += F(", Turbo: "); + result += getTurbo() ? F("On") : F("Off"); + result += F(", Quiet: "); + result += getQuiet() ? F("On") : F("Off"); + result += F(", XFan: "); + result += getXFan() ? F("On") : F("Off"); + result += F(", IonFilter: "); + result += getIonFilter() ? F("On") : F("Off"); + result += F(", Light: "); + result += getLight() ? F("On") : F("Off"); + result += F(", Swing (Horizontal): "); + result += getSwingHorizontal() ? F("On") : F("Off"); + result += F(", Swing (Vertical): "); + result += getSwingVertical() ? F("On") : F("Off"); return result; } @@ -438,7 +478,7 @@ std::string IRKelvinatorAC::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: ALPHA / Untested. +// Status: STABLE / Known working. bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits, bool strict) { if (results->rawlen < @@ -540,7 +580,7 @@ bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits, } // Success - results->decode_type = KELVINATOR; + results->decode_type = decode_type_t::KELVINATOR; results->bits = state_pos * 8; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index 2cc4e5a..73b8a03 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -2,6 +2,18 @@ // // Copyright 2016 David Conran +// Supports: +// Brand: Kelvinator, Model: YALIF Remote +// Brand: Kelvinator, Model: KSV26CRC A/C +// Brand: Kelvinator, Model: KSV26HRC A/C +// Brand: Kelvinator, Model: KSV35CRC A/C +// Brand: Kelvinator, Model: KSV35HRC A/C +// Brand: Kelvinator, Model: KSV53HRC A/C +// Brand: Kelvinator, Model: KSV62HRC A/C +// Brand: Kelvinator, Model: KSV70CRC A/C +// Brand: Kelvinator, Model: KSV70HRC A/C +// Brand: Kelvinator, Model: KSV80HRC A/C + #ifndef IR_KELVINATOR_H_ #define IR_KELVINATOR_H_ @@ -9,17 +21,12 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR -// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint8_t kKelvinatorAuto = 0; @@ -128,53 +135,58 @@ class IRKelvinatorAC { public: explicit IRKelvinatorAC(uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_KELVINATOR void send(const uint16_t repeat = kKelvinatorDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_KELVINATOR - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - void setQuiet(bool state); - bool getQuiet(); - void setIonFilter(bool state); - bool getIonFilter(); - void setLight(bool state); - bool getLight(); - void setXFan(bool state); - bool getXFan(); - void setTurbo(bool state); - bool getTurbo(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setIonFilter(const bool on); + bool getIonFilter(void); + void setLight(const bool on); + bool getLight(void); + void setXFan(const bool on); + bool getXFan(void); + void setTurbo(const bool on); + bool getTurbo(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static uint8_t calcBlockChecksum( const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2); static bool validChecksum(const uint8_t state[], const uint16_t length = kKelvinatorStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + uint8_t convertMode(const stdAc::opmode_t mode); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kKelvinatorStateLength]; void checksum(const uint16_t length = kKelvinatorStateLength); - void fixup(); - IRsend _irsend; + void fixup(void); }; #endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp index f9d922f..ded6fef 100644 --- a/lib/IRremoteESP8266/src/ir_LG.cpp +++ b/lib/IRremoteESP8266/src/ir_LG.cpp @@ -2,25 +2,18 @@ // Copyright 2015 cheaplin // Copyright 2017, 2018 David Conran +// Supports: +// Brand: LG, Model: 6711A20083V remote +// Brand: LG, Model: AKB74395308 remote + #include "ir_LG.h" #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - // LG decode originally added by Darryl Smith (based on the JVC protocol) // LG send originally added by https://github.com/chaeplin -// -// Known supported devices: -// IR Remotes: -// 6711A20083V -// AKB74395308 // Constants const uint16_t kLgTick = 50; @@ -85,11 +78,13 @@ uint8_t calcLGChecksum(uint16_t data) { // IR Remote models: 6711A20083V void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { uint16_t repeatHeaderMark = 0; + uint8_t duty = kDutyDefault; if (nbits >= kLg32Bits) { // LG 32bit protocol is near identical to Samsung except for repeats. sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message. repeatHeaderMark = kLg32RptHdrMark; + duty = 33; repeat++; } else { // LG (28-bit) protocol. @@ -97,7 +92,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { sendGeneric(kLgHdrMark, kLgHdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark, kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data, nbits, 38, true, 0, // Repeats are handled later. - 50); + duty); } // Repeat @@ -105,7 +100,7 @@ void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { if (repeat) sendGeneric(repeatHeaderMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent. kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data. - 38, true, repeat - 1, 50); + 38, true, repeat - 1, duty); } // Send an LG Variant-2 formatted message. diff --git a/lib/IRremoteESP8266/src/ir_LG.h b/lib/IRremoteESP8266/src/ir_LG.h index 25d56bc..01f81e2 100644 --- a/lib/IRremoteESP8266/src/ir_LG.h +++ b/lib/IRremoteESP8266/src/ir_LG.h @@ -1,14 +1,12 @@ // Copyright 2017 David Conran +// Supports: +// Brand: LG, Model: 6711A20083V remote +// Brand: LG, Model: AKB74395308 remote + #ifndef IR_LG_H_ #define IR_LG_H_ -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - #define __STDC_LIMIT_MACROS #include diff --git a/lib/IRremoteESP8266/src/ir_Lasertag.cpp b/lib/IRremoteESP8266/src/ir_Lasertag.cpp index 7f0b89a..f52b747 100644 --- a/lib/IRremoteESP8266/src/ir_Lasertag.cpp +++ b/lib/IRremoteESP8266/src/ir_Lasertag.cpp @@ -1,20 +1,15 @@ // Copyright 2017 David Conran +// Lasertag #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG -// LL AAAAA SS EE RR RR TTT AAAAA GG GG -// LL AA AA SSSSS EEEEE RRRRRR TTT AA AA GG -// LL AAAAAAA SS EE RR RR TTT AAAAAAA GG GG -// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG - // Constants const uint16_t kLasertagMinSamples = 13; const uint16_t kLasertagTick = 333; -const uint32_t kLasertagMinGap = 100000; // Completely made up amount. +const uint32_t kLasertagMinGap = kDefaultMessageGap; // Just a guess. const uint8_t kLasertagTolerance = 0; // Percentage error margin. const uint16_t kLasertagExcess = 0; // See kMarkExcess. const uint16_t kLasertagDelta = 150; // Use instead of Excess and Tolerance. diff --git a/lib/IRremoteESP8266/src/ir_Lego.cpp b/lib/IRremoteESP8266/src/ir_Lego.cpp new file mode 100644 index 0000000..726b6cd --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Lego.cpp @@ -0,0 +1,128 @@ +// Copyright 2019 David Conran + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// LEGO +// (LEGO is a Registrated Trademark of the Lego Group.) +// +// Supports: +// Brand: LEGO Power Functions, Model: IR Receiver +// +// Ref: +// - https://github.com/markszabo/IRremoteESP8266/issues/641 +// - https://github.com/markszabo/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf + +// Constants +const uint16_t kLegoPfBitMark = 158; +const uint16_t kLegoPfHdrSpace = 1026; +const uint16_t kLegoPfZeroSpace = 263; +const uint16_t kLegoPfOneSpace = 553; +const uint32_t kLegoPfMinCommandLength = 16000; // 16ms + + +#if SEND_LEGOPF +// Send a LEGO Power Functions message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kLegoPfBits. +// repeat: Nr. of additional times the message is to be sent. +// Note: Non-zero repeats results in at least 5 messages per spec. +// +// Status: Beta / Should work. +void IRsend::sendLegoPf(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint8_t channelid = ((data >> (nbits - 4)) & 0b11) + 1; + if (repeat) { + // We are in repeat mode. + // Spec says a pause before transmittion. + if (channelid < 4) space((4 - channelid) * kLegoPfMinCommandLength); + // Spec says there are a minimum of 5 message repeats. + for (uint16_t r = 0; r < std::max(repeat, (uint16_t)5); r++) { + // Lego has a special repeat mode which repeats a message with varying + // start to start times. + sendGeneric(kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kLegoPfBitMark, kLegoPfHdrSpace, + ((r < 2) ? 5 : (6 + 2 * channelid)) * kLegoPfMinCommandLength, + data, nbits, 38000, true, 0, kDutyDefault); + } + } else { // No repeat, just a simple message. + sendGeneric(kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfMinCommandLength * 5, + data, nbits, 38000, true, 0, kDutyDefault); + } +} +#endif // SEND_LEGO + +#if DECODE_LEGOPF +// Decode the supplied LEGO Power Functions message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kLegoPfBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: Alpha / Untested. +bool IRrecv::decodeLegoPf(decode_results* results, + const uint16_t nbits, const bool strict) { + // Check if can possibly be a valid LEGO message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict && nbits != kLegoPfBits) return false; // Not what is expected + + uint64_t data = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kLegoPfHdrSpace)) return false; + // Data (Typically 16 bits) + data_result = + matchData(&(results->rawbuf[offset]), nbits, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kTolerance, kMarkExcess, true); + if (data_result.success == false) return false; + data = data_result.data; + offset += data_result.used; + uint16_t actualBits = data_result.used / 2; + + // Footer. + if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kLegoPfMinCommandLength)) + return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict) { + if (actualBits != nbits) return false; // Not as we expected. + // Verify the Longitudinal Redundancy Check (LRC) + uint16_t lrc_data = data; + uint8_t lrc = 0xF; + for (uint8_t i = 0; i < 4; i++) { + lrc ^= (lrc_data & 0xF); + lrc_data >>= 4; + } + if (lrc) return false; + } + + // Success + results->decode_type = LEGOPF; + results->bits = actualBits; + results->value = data; + results->address = ((data >> (nbits - 4)) & 0b11) + 1; // Channel Id + results->command = (data >> 4) & 0xFF; // Stuff between Channel Id and LRC. + return true; +} +#endif // DECODE_LEGOPF diff --git a/lib/IRremoteESP8266/src/ir_Lutron.cpp b/lib/IRremoteESP8266/src/ir_Lutron.cpp index 00eb938..544f26d 100644 --- a/lib/IRremoteESP8266/src/ir_Lutron.cpp +++ b/lib/IRremoteESP8266/src/ir_Lutron.cpp @@ -1,4 +1,5 @@ // Copyright 2018 David Conran +// Lutron #define __STDC_LIMIT_MACROS #include @@ -7,12 +8,6 @@ #include "IRsend.h" #include "IRutils.h" -// LL UU UU TTTTTTT RRRRRR OOOOO NN NN -// LL UU UU TTT RR RR OO OO NNN NN -// LL UU UU TTT RRRRRR OO OO NN N NN -// LL UU UU TTT RR RR OO OO NN NNN -// LLLLLLL UUUUU TTT RR RR OOOO0 NN NN - // Notes: // The Lutron protocol uses a sort of Run Length encoding to encode // its data. There is no header or footer per-se. diff --git a/lib/IRremoteESP8266/src/ir_MWM.cpp b/lib/IRremoteESP8266/src/ir_MWM.cpp index a75e99e..b747b22 100644 --- a/lib/IRremoteESP8266/src/ir_MWM.cpp +++ b/lib/IRremoteESP8266/src/ir_MWM.cpp @@ -1,4 +1,7 @@ // Copyright 2018 Brett T. Warden + +// MWM + // derived from ir_Lasertag.cpp, Copyright 2017 David Conran #include @@ -6,12 +9,6 @@ #include "IRsend.h" #include "IRutils.h" -// MM MM WW WW MM MM -// MMM MMM WW WW MMM MMM -// MM M MM WW W WW MM M MM -// MM MM WWW WWW MM MM -// MM MM WW WW MM MM - // Constants const uint16_t kMWMMinSamples = 6; // Msgs are >=3 bytes, bytes have >=2 // samples @@ -37,7 +34,8 @@ const int16_t kMark = 0; // // Status: Implemented. // -void IRsend::sendMWM(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendMWM(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < 3) return; // Shortest possible message is 3 bytes // Set 38kHz IR carrier frequency & a 1/4 (25%) duty cycle. @@ -105,7 +103,7 @@ bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) { for (; offset < results->rawlen && results->bits < 8 * kStateSizeMax; frame_bits++) { DPRINT("DEBUG: decodeMWM: offset = "); - DPRINTLN(uint64ToString(offset)); + DPRINTLN(offset); int16_t level = getRClevel(results, &offset, &used, kMWMTick, kMWMTolerance, kMWMExcess, kMWMDelta, kMWMMaxWidth); if (level < 0) { @@ -129,7 +127,7 @@ bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) { DPRINT("DEBUG: decodeMWM: data_bits = "); DPRINTLN(data_bits); DPRINT("DEBUG: decodeMWM: Finished byte: "); - DPRINTLN(data); + DPRINTLN(uint64ToString(data)); results->state[data_bits / 8 - 1] = data & 0xFF; results->bits = data_bits; data = 0; diff --git a/lib/IRremoteESP8266/src/ir_Magiquest.h b/lib/IRremoteESP8266/src/ir_Magiquest.h index d2d82d1..81fff53 100644 --- a/lib/IRremoteESP8266/src/ir_Magiquest.h +++ b/lib/IRremoteESP8266/src/ir_Magiquest.h @@ -31,5 +31,5 @@ const uint16_t kMagiQuestMarkZero = 280; const uint16_t kMagiQuestSpaceZero = 850; const uint16_t kMagiQuestMarkOne = 580; const uint16_t kMagiQuestSpaceOne = 600; -const uint32_t kMagiQuestGap = 100000; // A guess of the gap between messages -#endif // IR_MAGIQUEST_H_ +const uint32_t kMagiQuestGap = kDefaultMessageGap; // Just a guess. +#endif // IR_MAGIQUEST_H_ diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp index af40fcc..6a82f05 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266/src/ir_Midea.cpp @@ -1,4 +1,5 @@ // Copyright 2017 bwze, crankyoldgit +// Midea #include "ir_Midea.h" #include @@ -9,12 +10,6 @@ #include "IRsend.h" #include "IRutils.h" -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA - // Midea A/C added by (send) bwze/crankyoldgit & (decode) crankyoldgit // // Equipment it seems compatible with: @@ -91,52 +86,54 @@ void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) { // Warning: Consider this very alpha code. // Initialise the object. -IRMideaAC::IRMideaAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRMideaAC::IRMideaAC(const uint16_t pin) : _irsend(pin) { this->stateReset(); } // Reset the state of the remote to a known good state/sequence. -void IRMideaAC::stateReset() { +void IRMideaAC::stateReset(void) { // Power On, Mode Auto, Fan Auto, Temp = 25C/77F remote_state = 0xA1826FFFFF62; } // Configure the pin for output. -void IRMideaAC::begin() { _irsend.begin(); } +void IRMideaAC::begin(void) { _irsend.begin(); } #if SEND_MIDEA // Send the current desired state to the IR LED. void IRMideaAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. + this->checksum(); // Ensure correct checksum before sending. _irsend.sendMidea(remote_state, kMideaBits, repeat); } #endif // SEND_MIDEA // Return a pointer to the internal state date of the remote. -uint64_t IRMideaAC::getRaw() { - checksum(); +uint64_t IRMideaAC::getRaw(void) { + this->checksum(); return remote_state & kMideaACStateMask; } // Override the internal state with the new state. -void IRMideaAC::setRaw(uint64_t newState) { +void IRMideaAC::setRaw(const uint64_t newState) { remote_state = newState & kMideaACStateMask; } // Set the requested power state of the A/C to off. -void IRMideaAC::on() { remote_state |= kMideaACPower; } +void IRMideaAC::on(void) { remote_state |= kMideaACPower; } // Set the requested power state of the A/C to off. -void IRMideaAC::off() { remote_state &= (kMideaACStateMask ^ kMideaACPower); } +void IRMideaAC::off(void) { + remote_state &= (kMideaACStateMask ^ kMideaACPower); +} // Set the requested power state of the A/C. -void IRMideaAC::setPower(const bool state) { - if (state) - on(); +void IRMideaAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the requested power state of the A/C. -bool IRMideaAC::getPower() { return (remote_state & kMideaACPower); } +bool IRMideaAC::getPower(void) { return (remote_state & kMideaACPower); } // Set the temperature. // Args: @@ -147,7 +144,8 @@ void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) { if (useCelsius) { new_temp = std::max(kMideaACMinTempC, new_temp); new_temp = std::min(kMideaACMaxTempC, new_temp); - new_temp = (uint8_t)((new_temp * 1.8) + 32.5); // 0.5 so we rounding. + // Convert and add 0.5 for rounding. + new_temp = celsiusToFahrenheit(new_temp) + 0.5; } new_temp = std::max(kMideaACMinTempF, new_temp); new_temp = std::min(kMideaACMaxTempF, new_temp); @@ -164,7 +162,7 @@ void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) { uint8_t IRMideaAC::getTemp(const bool useCelsius) { uint8_t temp = ((remote_state >> 24) & 0x1F) + kMideaACMinTempF; if (useCelsius) { - temp = (uint8_t)((temp - 32) / 1.8); + temp = fahrenheitToCelsius(temp); } return temp; } @@ -187,42 +185,39 @@ void IRMideaAC::setFan(const uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRMideaAC::getFan() { return (remote_state >> 35) & 0b111; } +uint8_t IRMideaAC::getFan(void) { return (remote_state >> 35) & 0b111; } // Get the requested climate operation mode of the a/c unit. // Returns: // A uint8_t containing the A/C mode. -uint8_t IRMideaAC::getMode() { return ((remote_state >> 32) & 0b111); } +uint8_t IRMideaAC::getMode(void) { return ((remote_state >> 32) & 0b111); } // Set the requested climate operation mode of the a/c unit. void IRMideaAC::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - uint64_t new_mode; switch (mode) { case kMideaACAuto: case kMideaACCool: case kMideaACHeat: case kMideaACDry: case kMideaACFan: - new_mode = mode; - break; + remote_state &= kMideaACModeMask; + remote_state |= ((uint64_t)mode << 32); + return; default: - new_mode = kMideaACAuto; + this->setMode(kMideaACAuto); } - remote_state &= kMideaACModeMask; - remote_state |= (new_mode << 32); } // Set the Sleep state of the A/C. -void IRMideaAC::setSleep(const bool state) { - if (state) +void IRMideaAC::setSleep(const bool on) { + if (on) remote_state |= kMideaACSleep; else remote_state &= (kMideaACStateMask ^ kMideaACSleep); } // Return the Sleep state of the A/C. -bool IRMideaAC::getSleep() { return (remote_state & kMideaACSleep); } +bool IRMideaAC::getSleep(void) { return (remote_state & kMideaACSleep); } // Calculate the checksum for a given array. // Args: @@ -251,67 +246,146 @@ bool IRMideaAC::validChecksum(const uint64_t state) { } // Calculate & set the checksum for the current internal state of the remote. -void IRMideaAC::checksum() { +void IRMideaAC::checksum(void) { // Stored the checksum value in the last byte. remote_state &= kMideaACChecksumMask; remote_state |= calcChecksum(remote_state); } + +// Convert a standard A/C mode into its native mode. +uint8_t IRMideaAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kMideaACCool; + case stdAc::opmode_t::kHeat: + return kMideaACHeat; + case stdAc::opmode_t::kDry: + return kMideaACDry; + case stdAc::opmode_t::kFan: + return kMideaACFan; + default: + return kMideaACAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMideaAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kMideaACFanLow; + case stdAc::fanspeed_t::kMedium: + return kMideaACFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kMideaACFanHigh; + default: + return kMideaACFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRMideaAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMideaACCool: return stdAc::opmode_t::kCool; + case kMideaACHeat: return stdAc::opmode_t::kHeat; + case kMideaACDry: return stdAc::opmode_t::kDry; + case kMideaACFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMideaAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMideaACFanHigh: return stdAc::fanspeed_t::kMax; + case kMideaACFanMed: return stdAc::fanspeed_t::kMedium; + case kMideaACFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMideaAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MIDEA; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(result.celsius); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRMideaAC::toString() { +String IRMideaAC::toString(void) { String result = ""; -#else -std::string IRMideaAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kMideaACAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kMideaACCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kMideaACHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kMideaACDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kMideaACFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp(true)) + "C/" + - uint64ToString(getTemp(false)) + "F"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp(true)); + result += F("C/"); + result += uint64ToString(getTemp(false)); + result += F("F, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kMideaACFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kMideaACFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kMideaACFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kMideaACFanHigh: - result += " (HI)"; + result += F(" (HI)"); break; } - result += ", Sleep: "; + result += F(", Sleep: "); if (getSleep()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); return result; } diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index c749b32..e57e2fc 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -1,4 +1,10 @@ // Copyright 2017 David Conran +// Midea + +// Supports: +// Brand: Pioneer System, Model: RYBO12GMFILCAD A/C (12K BTU) +// Brand: Pioneer System, Model: RUBO18GMFILCAD A/C (18K BTU) + #ifndef IR_MIDEA_H_ #define IR_MIDEA_H_ @@ -6,17 +12,12 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Midea added by crankyoldgit & bwze // Ref: @@ -63,41 +64,45 @@ const uint64_t kMideaACChecksumMask = 0x0000FFFFFFFFFF00; class IRMideaAC { public: - explicit IRMideaAC(uint16_t pin); + explicit IRMideaAC(const uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_MIDEA void send(const uint16_t repeat = kMideaMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MIDEA - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp, const bool useCelsius = false); uint8_t getTemp(const bool useCelsius = false); void setFan(const uint8_t fan); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); - void setRaw(uint64_t newState); - uint64_t getRaw(); + uint8_t getMode(void); + void setRaw(const uint64_t newState); + uint64_t getRaw(void); static bool validChecksum(const uint64_t state); - void setSleep(const bool state); - bool getSleep(); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + void setSleep(const bool on); + bool getSleep(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint64_t remote_state; - void checksum(); + void checksum(void); static uint8_t calcChecksum(const uint64_t state); - IRsend _irsend; }; #endif // IR_MIDEA_H_ diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp index 655dc5b..79fe929 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp @@ -2,6 +2,8 @@ // Copyright 2017-2018 David Conran // Copyright 2018 Denes Varga +// Mitsubishi + #include "ir_Mitsubishi.h" #include #ifndef ARDUINO @@ -11,12 +13,6 @@ #include "IRsend.h" #include "IRutils.h" -// MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII -// M M M I T S U U B B I S H H I -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII - // Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran @@ -165,7 +161,7 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, // This protocol appears to have a manditory in-protocol repeat. // That is in *addition* to the entire message needing to be sent twice // for the device to accept the command. That is separate from the repeat. -// i.e. Allegedly, the real remote requires the "OFF" button pressed twice. +// i.e. Allegedly, the real remote requires the "Off" button pressed twice. // You will need to add a suitable gap yourself. // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/441 @@ -268,8 +264,8 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, // // Status: BETA / Appears to be working. // -void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendMitsubishiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kMitsubishiACStateLength) return; // Not enough bytes to send a proper message. @@ -420,10 +416,12 @@ bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits, // Equipment it seems compatible with: // * // Initialise the object. -IRMitsubishiAC::IRMitsubishiAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRMitsubishiAC::IRMitsubishiAC(const uint16_t pin) : _irsend(pin) { + this->stateReset(); +} // Reset the state of the remote to a known good state/sequence. -void IRMitsubishiAC::stateReset() { +void IRMitsubishiAC::stateReset(void) { // The state of the IR remote in IR code form. // Known good state obtained from: // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108 @@ -445,39 +443,39 @@ void IRMitsubishiAC::stateReset() { for (uint8_t i = 11; i < kMitsubishiACStateLength - 1; i++) remote_state[i] = 0; remote_state[kMitsubishiACStateLength - 1] = 0x1F; - checksum(); // Calculate the checksum + this->checksum(); // Calculate the checksum } // Configure the pin for output. -void IRMitsubishiAC::begin() { _irsend.begin(); } +void IRMitsubishiAC::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHI_AC // Send the current desired state to the IR LED. void IRMitsubishiAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. + this->checksum(); // Ensure correct checksum before sending. _irsend.sendMitsubishiAC(remote_state, kMitsubishiACStateLength, repeat); } #endif // SEND_MITSUBISHI_AC // Return a pointer to the internal state date of the remote. -uint8_t *IRMitsubishiAC::getRaw() { - checksum(); +uint8_t *IRMitsubishiAC::getRaw(void) { + this->checksum(); return remote_state; } -void IRMitsubishiAC::setRaw(uint8_t *data) { +void IRMitsubishiAC::setRaw(const uint8_t *data) { for (uint8_t i = 0; i < (kMitsubishiACStateLength - 1); i++) { remote_state[i] = data[i]; } - checksum(); + this->checksum(); } // Calculate the checksum for the current internal state of the remote. -void IRMitsubishiAC::checksum() { - remote_state[17] = calculateChecksum(remote_state); +void IRMitsubishiAC::checksum(void) { + remote_state[17] = this->calculateChecksum(remote_state); } -uint8_t IRMitsubishiAC::calculateChecksum(uint8_t *data) { +uint8_t IRMitsubishiAC::calculateChecksum(const uint8_t *data) { uint8_t sum = 0; // Checksum is simple addition of all previous bytes stored // as an 8 bit value. @@ -486,45 +484,46 @@ uint8_t IRMitsubishiAC::calculateChecksum(uint8_t *data) { } // Set the requested power state of the A/C to off. -void IRMitsubishiAC::on() { +void IRMitsubishiAC::on(void) { // state = ON; remote_state[5] |= kMitsubishiAcPower; } // Set the requested power state of the A/C to off. -void IRMitsubishiAC::off() { +void IRMitsubishiAC::off(void) { // state = OFF; remote_state[5] &= ~kMitsubishiAcPower; } // Set the requested power state of the A/C. -void IRMitsubishiAC::setPower(bool state) { - if (state) - on(); +void IRMitsubishiAC::setPower(bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the requested power state of the A/C. -bool IRMitsubishiAC::getPower() { +bool IRMitsubishiAC::getPower(void) { return ((remote_state[5] & kMitsubishiAcPower) != 0); } // Set the temp. in deg C -void IRMitsubishiAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kMitsubishiAcMinTemp, temp); +void IRMitsubishiAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kMitsubishiAcMinTemp, degrees); temp = std::min((uint8_t)kMitsubishiAcMaxTemp, temp); remote_state[7] = temp - kMitsubishiAcMinTemp; } // Return the set temp. in deg C -uint8_t IRMitsubishiAC::getTemp() { +uint8_t IRMitsubishiAC::getTemp(void) { return (remote_state[7] + kMitsubishiAcMinTemp); } // Set the speed of the fan, 0-6. // 0 is auto, 1-5 is the speed, 6 is silent. -void IRMitsubishiAC::setFan(uint8_t fan) { +void IRMitsubishiAC::setFan(const uint8_t speed) { + uint8_t fan = speed; // Bounds check if (fan > kMitsubishiAcFanSilent) fan = kMitsubishiAcFanMax; // Set the fan to maximum if out of range. @@ -539,17 +538,17 @@ void IRMitsubishiAC::setFan(uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRMitsubishiAC::getFan() { +uint8_t IRMitsubishiAC::getFan(void) { uint8_t fan = remote_state[9] & 0b111; if (fan == kMitsubishiAcFanMax) return kMitsubishiAcFanSilent; return fan; } // Return the requested climate operation mode of the a/c unit. -uint8_t IRMitsubishiAC::getMode() { return (remote_state[6]); } +uint8_t IRMitsubishiAC::getMode(void) { return (remote_state[6]); } // Set the requested climate operation mode of the a/c unit. -void IRMitsubishiAC::setMode(uint8_t mode) { +void IRMitsubishiAC::setMode(const uint8_t mode) { // If we get an unexpected mode, default to AUTO. switch (mode) { case kMitsubishiAcAuto: @@ -565,48 +564,54 @@ void IRMitsubishiAC::setMode(uint8_t mode) { remote_state[8] = 0b00110000; break; default: - mode = kMitsubishiAcAuto; - remote_state[8] = 0b00110000; + this->setMode(kMitsubishiAcAuto); + return; } remote_state[6] = mode; } // Set the requested vane operation mode of the a/c unit. -void IRMitsubishiAC::setVane(uint8_t mode) { - mode = std::min(mode, (uint8_t)0b111); // bounds check - mode |= 0b1000; - mode <<= 3; +void IRMitsubishiAC::setVane(const uint8_t position) { + uint8_t pos = std::min(position, (uint8_t)0b111); // bounds check + pos |= 0b1000; + pos <<= 3; remote_state[9] &= 0b11000111; // Clear the previous setting. - remote_state[9] |= mode; + remote_state[9] |= pos; } // Return the requested vane operation mode of the a/c unit. -uint8_t IRMitsubishiAC::getVane() { +uint8_t IRMitsubishiAC::getVane(void) { return ((remote_state[9] & 0b00111000) >> 3); } // Return the clock setting of the message. 1=1/6 hour. e.g. 4pm = 48 -uint8_t IRMitsubishiAC::getClock() { return remote_state[10]; } +uint8_t IRMitsubishiAC::getClock(void) { return remote_state[10]; } // Set the current time. 1 = 1/6 hour. e.g. 6am = 36. -void IRMitsubishiAC::setClock(uint8_t clock) { remote_state[10] = clock; } +void IRMitsubishiAC::setClock(const uint8_t clock) { + remote_state[10] = clock; +} // Return the desired start time. 1 = 1/6 hour. e.g. 1am = 6 -uint8_t IRMitsubishiAC::getStartClock() { return remote_state[12]; } +uint8_t IRMitsubishiAC::getStartClock(void) { return remote_state[12]; } -// Set the desired start tiem of the AC. 1 = 1/6 hour. e.g. 8pm = 120 -void IRMitsubishiAC::setStartClock(uint8_t clock) { remote_state[12] = clock; } +// Set the desired start time of the AC. 1 = 1/6 hour. e.g. 8pm = 120 +void IRMitsubishiAC::setStartClock(const uint8_t clock) { + remote_state[12] = clock; +} // Return the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132 -uint8_t IRMitsubishiAC::getStopClock() { return remote_state[11]; } +uint8_t IRMitsubishiAC::getStopClock(void) { return remote_state[11]; } // Set the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132 -void IRMitsubishiAC::setStopClock(uint8_t clock) { remote_state[11] = clock; } +void IRMitsubishiAC::setStopClock(const uint8_t clock) { + remote_state[11] = clock; +} // Return the timer setting. Possible values: kMitsubishiAcNoTimer, // kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, // kMitsubishiAcStartStopTimer -uint8_t IRMitsubishiAC::getTimer() { return remote_state[13] & 0b111; } +uint8_t IRMitsubishiAC::getTimer(void) { return remote_state[13] & 0b111; } // Set the timer setting. Possible values: kMitsubishiAcNoTimer, // kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, @@ -615,100 +620,198 @@ void IRMitsubishiAC::setTimer(uint8_t timer) { remote_state[13] = timer & 0b111; } -#ifdef ARDUINO -String IRMitsubishiAC::timeToString(uint64_t time) { +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishiAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kMitsubishiAcCool; + case stdAc::opmode_t::kHeat: + return kMitsubishiAcHeat; + case stdAc::opmode_t::kDry: + return kMitsubishiAcDry; + default: + return kMitsubishiAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiAcFanSilent; + case stdAc::fanspeed_t::kLow: + return kMitsubishiAcFanRealMax - 3; + case stdAc::fanspeed_t::kMedium: + return kMitsubishiAcFanRealMax - 2; + case stdAc::fanspeed_t::kHigh: + return kMitsubishiAcFanRealMax - 1; + case stdAc::fanspeed_t::kMax: + return kMitsubishiAcFanRealMax; + default: + return kMitsubishiAcFanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kMitsubishiAcVaneAutoMove; + default: + return kMitsubishiAcVaneAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRMitsubishiAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishiAcCool: return stdAc::opmode_t::kCool; + case kMitsubishiAcHeat: return stdAc::opmode_t::kHeat; + case kMitsubishiAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMitsubishiAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishiAcFanRealMax: return stdAc::fanspeed_t::kMax; + case kMitsubishiAcFanRealMax - 1: return stdAc::fanspeed_t::kHigh; + case kMitsubishiAcFanRealMax - 2: return stdAc::fanspeed_t::kMedium; + case kMitsubishiAcFanRealMax - 3: return stdAc::fanspeed_t::kLow; + case kMitsubishiAcFanSilent: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRMitsubishiAC::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case 1: return stdAc::swingv_t::kHighest; + case 2: return stdAc::swingv_t::kHigh; + case 3: return stdAc::swingv_t::kMiddle; + case 4: return stdAc::swingv_t::kLow; + case 5: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMitsubishiAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_AC; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getVane()); + result.quiet = this->getFan() == kMitsubishiAcFanSilent; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +String IRMitsubishiAC::timeToString(const uint64_t time) { String result = ""; -#else -std::string IRMitsubishiAC::timeToString(uint64_t time) { - std::string result = ""; -#endif // ARDUINO - if (time / 6 < 10) result += "0"; + result.reserve(6); + if (time / 6 < 10) result += '0'; result += uint64ToString(time / 6); - result += ":"; - if (time * 10 % 60 < 10) result += "0"; + result += ':'; + if (time * 10 % 60 < 10) result += '0'; result += uint64ToString(time * 10 % 60); return result; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRMitsubishiAC::toString() { +String IRMitsubishiAC::toString(void) { String result = ""; -#else -std::string IRMitsubishiAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + if (this->getPower()) + result += F("On"); else - result += "Off"; - switch (getMode()) { + result += F("Off"); + switch (this->getMode()) { case MITSUBISHI_AC_AUTO: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case MITSUBISHI_AC_COOL: - result += " (COOL)"; + result += F(" (COOL)"); break; case MITSUBISHI_AC_DRY: - result += " (DRY)"; + result += F(" (DRY)"); break; case MITSUBISHI_AC_HEAT: - result += " (HEAT)"; + result += F(" (HEAT)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", FAN: "; - switch (getFan()) { + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, FAN: "); + switch (this->getFan()) { case MITSUBISHI_AC_FAN_AUTO: - result += "AUTO"; + result += F("AUTO"); break; case MITSUBISHI_AC_FAN_MAX: - result += "MAX"; + result += F("MAX"); break; case MITSUBISHI_AC_FAN_SILENT: - result += "SILENT"; + result += F("SILENT"); break; default: - result += uint64ToString(getFan()); + result += uint64ToString(this->getFan()); } - result += ", VANE: "; - switch (getVane()) { + result += F(", VANE: "); + switch (this->getVane()) { case MITSUBISHI_AC_VANE_AUTO: - result += "AUTO"; + result += F("AUTO"); break; case MITSUBISHI_AC_VANE_AUTO_MOVE: - result += "AUTO MOVE"; + result += F("AUTO MOVE"); break; default: - result += uint64ToString(getVane()); + result += uint64ToString(this->getVane()); } - result += ", Time: "; - result += timeToString(getClock()); - result += ", On timer: "; - result += timeToString(getStartClock()); - result += ", Off timer: "; - result += timeToString(getStopClock()); - result += ", Timer: "; - switch (getTimer()) { + result += F(", Time: "); + result += this->timeToString(this->getClock()); + result += F(", On timer: "); + result += this->timeToString(this->getStartClock()); + result += F(", Off timer: "); + result += this->timeToString(this->getStopClock()); + result += F(", Timer: "); + switch (this->getTimer()) { case kMitsubishiAcNoTimer: - result += "-"; + result += '-'; break; case kMitsubishiAcStartTimer: - result += "Start"; + result += F("Start"); break; case kMitsubishiAcStopTimer: - result += "Stop"; + result += F("Stop"); break; case kMitsubishiAcStartStopTimer: - result += "Start+Stop"; + result += F("Start+Stop"); break; default: - result += "? ("; - result += getTimer(); - result += ")\n"; + result += F("? ("); + result += this->getTimer(); + result += F(")\n"); } return result; } diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index 0d897bc..bdd8c45 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -1,5 +1,12 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran + +// Mitsubishi + +// Supports: +// Brand: Mitsubishi, Model: TV +// Brand: Mitsubishi, Model: HC3000 Projector + #ifndef IR_MITSUBISHI_H_ #define IR_MITSUBISHI_H_ @@ -7,17 +14,12 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII -// M M M I T S U U B B I S H H I -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran @@ -58,52 +60,56 @@ const uint8_t kMitsubishiAcStartStopTimer = 7; class IRMitsubishiAC { public: - explicit IRMitsubishiAC(uint16_t pin); + explicit IRMitsubishiAC(const uint16_t pin); - static uint8_t calculateChecksum(uint8_t* data); + static uint8_t calculateChecksum(const uint8_t* data); - void stateReset(); + void stateReset(void); #if SEND_MITSUBISHI_AC void send(const uint16_t repeat = kMitsubishiACMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MITSUBISHI_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setVane(uint8_t mode); - uint8_t getVane(); - uint8_t* getRaw(); - void setRaw(uint8_t* data); - uint8_t getClock(); - void setClock(uint8_t clock); - uint8_t getStartClock(); - void setStartClock(uint8_t clock); - uint8_t getStopClock(); - void setStopClock(uint8_t clock); - uint8_t getTimer(); - void setTimer(uint8_t timer); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setVane(const uint8_t position); + uint8_t getVane(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + uint8_t getClock(void); + void setClock(const uint8_t clock); + uint8_t getStartClock(void); + void setStartClock(const uint8_t clock); + uint8_t getStopClock(void); + void setStopClock(const uint8_t clock); + uint8_t getTimer(void); + void setTimer(const uint8_t timer); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: -#ifdef ARDUINO - String timeToString(uint64_t time); -#else - std::string timeToString(uint64_t time); -#endif - uint8_t remote_state[kMitsubishiACStateLength]; - void checksum(); IRsend _irsend; +#else + IRsendTest _irsend; +#endif + String timeToString(const uint64_t time); + uint8_t remote_state[kMitsubishiACStateLength]; + void checksum(void); }; #endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp new file mode 100644 index 0000000..63d7b26 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp @@ -0,0 +1,1141 @@ +// Copyright 2019 David Conran +// Mitsubishi Heavy Industries A/C remote emulation. + +// Code to emulate Mitsubishi Heavy Industries A/C IR remote control units, +// which should control at least the following A/C units: +// Remote Control RLA502A700B: +// Model SRKxxZM-S +// Model SRKxxZMXA-S +// Remote Control RKX502A001C: +// Model SRKxxZJ-S + +// Note: This code was *heavily* influenced by @ToniA's great work & code, +// but it has been written from scratch. +// Nothing was copied other than constants and message analysis. + +#include "ir_MitsubishiHeavy.h" +#include +#include "IRremoteESP8266.h" +#include "IRutils.h" +#ifndef ARDUINO +#include +#endif + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp +// https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp + +// Constants +const uint16_t kMitsubishiHeavyHdrMark = 3140; +const uint16_t kMitsubishiHeavyHdrSpace = 1630; +const uint16_t kMitsubishiHeavyBitMark = 370; +const uint16_t kMitsubishiHeavyOneSpace = 420; +const uint16_t kMitsubishiHeavyZeroSpace = 1220; +const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. + +#if SEND_MITSUBISHIHEAVY +// Send a MitsubishiHeavy 88 bit A/C message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kMitsubishiHeavy88Bits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +void IRsend::sendMitsubishiHeavy88(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy88StateLength) + return; // Not enough bytes to send a proper message. + sendGeneric(kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, + data, nbytes, 38000, false, repeat, kDutyDefault); +} + +// Send a MitsubishiHeavy 152 bit A/C message. +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kMitsubishiHeavy152Bits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +void IRsend::sendMitsubishiHeavy152(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishiHeavy152StateLength) + return; // Not enough bytes to send a proper message. + sendMitsubishiHeavy88(data, nbytes, repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +// Class for decoding and constructing MitsubishiHeavy152 AC messages. +IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac( + const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy152(this->getRaw(), kMitsubishiHeavy152StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +void IRMitsubishiHeavy152Ac::stateReset(void) { + uint8_t i = 0; + for (; i < kMitsubishiHeavySigLength; i++) + remote_state[i] = kMitsubishiHeavyZmsSig[i]; + for (; i < kMitsubishiHeavy152StateLength - 3; i += 2) remote_state[i] = 0; + remote_state[17] = 0x80; +} + +uint8_t *IRMitsubishiHeavy152Ac::getRaw(void) { + checksum(); + return remote_state; +} + +void IRMitsubishiHeavy152Ac::setRaw(const uint8_t *data) { + for (uint8_t i = 0; i < kMitsubishiHeavy152StateLength; i++) + remote_state[i] = data[i]; +} + +void IRMitsubishiHeavy152Ac::on(void) { + remote_state[5] |= kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::off(void) { + remote_state[5] &= ~kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRMitsubishiHeavy152Ac::getPower(void) { + return remote_state[5] & kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy152Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + + remote_state[7] &= ~kMitsubishiHeavyTempMask; + remote_state[7] |= newtemp - kMitsubishiHeavyMinTemp; +} + +uint8_t IRMitsubishiHeavy152Ac::getTemp(void) { + return (remote_state[7] & kMitsubishiHeavyTempMask) + kMitsubishiHeavyMinTemp; +} + +// Set the speed of the fan +void IRMitsubishiHeavy152Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy152FanLow: + case kMitsubishiHeavy152FanMed: + case kMitsubishiHeavy152FanHigh: + case kMitsubishiHeavy152FanMax: + case kMitsubishiHeavy152FanEcono: + case kMitsubishiHeavy152FanTurbo: + break; + default: + newspeed = kMitsubishiHeavy152FanAuto; + } + remote_state[9] &= ~kMitsubishiHeavyFanMask; + remote_state[9] |= newspeed; +} + +uint8_t IRMitsubishiHeavy152Ac::getFan(void) { + return remote_state[9] & kMitsubishiHeavyFanMask; +} + +void IRMitsubishiHeavy152Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + remote_state[5] &= ~kMitsubishiHeavyModeMask; + remote_state[5] |= newmode; +} + +uint8_t IRMitsubishiHeavy152Ac::getMode(void) { + return remote_state[5] & kMitsubishiHeavyModeMask; +} + +void IRMitsubishiHeavy152Ac::setSwingVertical(const uint8_t pos) { + uint8_t newpos = std::min(pos, kMitsubishiHeavy152SwingVOff); + remote_state[11] &= ~kMitsubishiHeavy152SwingVMask; + remote_state[11] |= (newpos << 5); +} + +uint8_t IRMitsubishiHeavy152Ac::getSwingVertical(void) { + return remote_state[11] >> 5; +} + +void IRMitsubishiHeavy152Ac::setSwingHorizontal(const uint8_t pos) { + uint8_t newpos = std::min(pos, kMitsubishiHeavy152SwingHOff); + remote_state[13] &= ~kMitsubishiHeavy152SwingHMask; + remote_state[13] |= (newpos & kMitsubishiHeavy152SwingHMask); +} + +uint8_t IRMitsubishiHeavy152Ac::getSwingHorizontal(void) { + return remote_state[13] & kMitsubishiHeavy152SwingHMask; +} + +void IRMitsubishiHeavy152Ac::setNight(const bool on) { + if (on) + remote_state[15] |= kMitsubishiHeavyNightBit; + else + remote_state[15] &= ~kMitsubishiHeavyNightBit; +} + +bool IRMitsubishiHeavy152Ac::getNight(void) { + return remote_state[15] & kMitsubishiHeavyNightBit; +} + +void IRMitsubishiHeavy152Ac::set3D(const bool on) { + if (on) + remote_state[11] |= kMitsubishiHeavy3DMask; + else + remote_state[11] &= ~kMitsubishiHeavy3DMask; +} + +bool IRMitsubishiHeavy152Ac::get3D(void) { + return (remote_state[11] & kMitsubishiHeavy3DMask) == kMitsubishiHeavy3DMask; +} + +void IRMitsubishiHeavy152Ac::setSilent(const bool on) { + if (on) + remote_state[15] |= kMitsubishiHeavySilentBit; + else + remote_state[15] &= ~kMitsubishiHeavySilentBit; +} + +bool IRMitsubishiHeavy152Ac::getSilent(void) { + return remote_state[15] & kMitsubishiHeavySilentBit; +} + +void IRMitsubishiHeavy152Ac::setFilter(const bool on) { + if (on) + remote_state[5] |= kMitsubishiHeavyFilterBit; + else + remote_state[5] &= ~kMitsubishiHeavyFilterBit; +} + +bool IRMitsubishiHeavy152Ac::getFilter(void) { + return remote_state[5] & kMitsubishiHeavyFilterBit; +} + +void IRMitsubishiHeavy152Ac::setClean(const bool on) { + this->setFilter(on); + if (on) + remote_state[5] |= kMitsubishiHeavyCleanBit; + else + remote_state[5] &= ~kMitsubishiHeavyCleanBit; +} + +bool IRMitsubishiHeavy152Ac::getClean(void) { + return remote_state[5] & kMitsubishiHeavyCleanBit && this->getFilter(); +} + +void IRMitsubishiHeavy152Ac::setTurbo(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy152FanTurbo); + else if (this->getTurbo()) this->setFan(kMitsubishiHeavy152FanAuto); +} + +bool IRMitsubishiHeavy152Ac::getTurbo(void) { + return this->getFan() == kMitsubishiHeavy152FanTurbo; +} + +void IRMitsubishiHeavy152Ac::setEcono(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy152FanEcono); + else if (this->getEcono()) this->setFan(kMitsubishiHeavy152FanAuto); +} + +bool IRMitsubishiHeavy152Ac::getEcono(void) { + return this->getFan() == kMitsubishiHeavy152FanEcono; +} + +// Verify the given state has a ZM-S signature. +bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZmsSig[i]) return false; + return true; +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +void IRMitsubishiHeavy152Ac::checksum(void) { + for (uint8_t i = kMitsubishiHeavySigLength - 2; + i < kMitsubishiHeavy152StateLength; + i += 2) { + remote_state[i + 1] = ~remote_state[i]; + } +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + // Assume anything too short is fine. + if (length < kMitsubishiHeavySigLength) return true; + // Check all the byte pairs. + for (uint16_t i = kMitsubishiHeavySigLength - 2; + i < length; + i += 2) { + // XOR of a byte and it's self inverted should be 0xFF; + if ((state[i] ^ state[i + 1]) != 0xFF) return false; + } + return true; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kMitsubishiHeavyCool; + case stdAc::opmode_t::kHeat: + return kMitsubishiHeavyHeat; + case stdAc::opmode_t::kDry: + return kMitsubishiHeavyDry; + case stdAc::opmode_t::kFan: + return kMitsubishiHeavyFan; + default: + return kMitsubishiHeavyAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiHeavy152Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiHeavy152FanEcono; // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kLow: + return kMitsubishiHeavy152FanLow; + case stdAc::fanspeed_t::kMedium: + return kMitsubishiHeavy152FanMed; + case stdAc::fanspeed_t::kHigh: + return kMitsubishiHeavy152FanHigh; + case stdAc::fanspeed_t::kMax: + return kMitsubishiHeavy152FanMax; + default: + return kMitsubishiHeavy152FanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishiHeavy152Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: + return kMitsubishiHeavy152SwingVAuto; + case stdAc::swingv_t::kHighest: + return kMitsubishiHeavy152SwingVHighest; + case stdAc::swingv_t::kHigh: + return kMitsubishiHeavy152SwingVHigh; + case stdAc::swingv_t::kMiddle: + return kMitsubishiHeavy152SwingVMiddle; + case stdAc::swingv_t::kLow: + return kMitsubishiHeavy152SwingVLow; + case stdAc::swingv_t::kLowest: + return kMitsubishiHeavy152SwingVLowest; + default: + return kMitsubishiHeavy152SwingVOff; + } +} + +// Convert a standard A/C horizontal swing into its native setting. +uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: + return kMitsubishiHeavy152SwingHAuto; + case stdAc::swingh_t::kLeftMax: + return kMitsubishiHeavy152SwingHLeftMax; + case stdAc::swingh_t::kLeft: + return kMitsubishiHeavy152SwingHLeft; + case stdAc::swingh_t::kMiddle: + return kMitsubishiHeavy152SwingHMiddle; + case stdAc::swingh_t::kRight: + return kMitsubishiHeavy152SwingHRight; + case stdAc::swingh_t::kRightMax: + return kMitsubishiHeavy152SwingHRightMax; + default: + return kMitsubishiHeavy152SwingHOff; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRMitsubishiHeavy152Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishiHeavyCool: return stdAc::opmode_t::kCool; + case kMitsubishiHeavyHeat: return stdAc::opmode_t::kHeat; + case kMitsubishiHeavyDry: return stdAc::opmode_t::kDry; + case kMitsubishiHeavyFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMitsubishiHeavy152Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kMitsubishiHeavy152FanMax: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy152FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy152FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy152FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy152FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingh_t IRMitsubishiHeavy152Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy152SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy152SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy152SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy152SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy152SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy152SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy152SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy152SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy152SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy152SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy152SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_152; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.turbo = this->getTurbo(); + result.econo = this->getEcono(); + result.clean = this->getClean(); + result.quiet = this->getSilent(); + result.filter = this->getFilter(); + result.sleep = this->getNight() ? 0 : -1; + // Not supported. + result.light = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRMitsubishiHeavy152Ac::toString(void) { + String result = ""; + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kMitsubishiHeavyAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavyCool: + result += F(" (Cool)"); + break; + case kMitsubishiHeavyHeat: + result += F(" (Heat)"); + break; + case kMitsubishiHeavyDry: + result += F(" (Dry)"); + break; + case kMitsubishiHeavyFan: + result += F(" (Fan)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()) + 'C'; + result += F(", Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kMitsubishiHeavy152FanAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152FanHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy152FanLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy152FanMed: + result += F(" (Med)"); + break; + case kMitsubishiHeavy152FanMax: + result += F(" (Max)"); + break; + case kMitsubishiHeavy152FanEcono: + result += F(" (Econo)"); + break; + case kMitsubishiHeavy152FanTurbo: + result += F(" (Turbo)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (V): "); + result += uint64ToString(this->getSwingVertical()); + switch (this->getSwingVertical()) { + case kMitsubishiHeavy152SwingVAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152SwingVHighest: + result += F(" (Highest)"); + break; + case kMitsubishiHeavy152SwingVHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy152SwingVMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy152SwingVLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy152SwingVLowest: + result += F(" (Lowest)"); + break; + case kMitsubishiHeavy152SwingVOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (H): "); + result += uint64ToString(this->getSwingHorizontal()); + switch (this->getSwingHorizontal()) { + case kMitsubishiHeavy152SwingHAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy152SwingHLeftMax: + result += F(" (Max Left)"); + break; + case kMitsubishiHeavy152SwingHLeft: + result += F(" (Left)"); + break; + case kMitsubishiHeavy152SwingHMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy152SwingHRight: + result += F(" (Right)"); + break; + case kMitsubishiHeavy152SwingHRightMax: + result += F(" (Max Right)"); + break; + case kMitsubishiHeavy152SwingHLeftRight: + result += F(" (Left Right)"); + break; + case kMitsubishiHeavy152SwingHRightLeft: + result += F(" (Right Left)"); + break; + case kMitsubishiHeavy152SwingHOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Silent: "); + result += (this->getSilent() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += F(", Night: "); + result += (this->getNight() ? F("On") : F("Off")); + result += F(", Filter: "); + result += (this->getFilter() ? F("On") : F("Off")); + result += F(", 3D: "); + result += (this->get3D() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (this->getClean() ? F("On") : F("Off")); + return result; +} + + +// Class for decoding and constructing MitsubishiHeavy88 AC messages. +IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac( + const uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHIHEAVY +void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { + _irsend.sendMitsubishiHeavy88(this->getRaw(), kMitsubishiHeavy88StateLength, + repeat); +} +#endif // SEND_MITSUBISHIHEAVY + +void IRMitsubishiHeavy88Ac::stateReset(void) { + uint8_t i = 0; + for (; i < kMitsubishiHeavySigLength; i++) + remote_state[i] = kMitsubishiHeavyZjsSig[i]; + for (; i < kMitsubishiHeavy88StateLength; i++) remote_state[i] = 0; +} + +uint8_t *IRMitsubishiHeavy88Ac::getRaw(void) { + checksum(); + return remote_state; +} + +void IRMitsubishiHeavy88Ac::setRaw(const uint8_t *data) { + for (uint8_t i = 0; i < kMitsubishiHeavy88StateLength; i++) + remote_state[i] = data[i]; +} + +void IRMitsubishiHeavy88Ac::on(void) { + remote_state[9] |= kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::off(void) { + remote_state[9] &= ~kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRMitsubishiHeavy88Ac::getPower(void) { + return remote_state[9] & kMitsubishiHeavyPowerBit; +} + +void IRMitsubishiHeavy88Ac::setTemp(const uint8_t temp) { + uint8_t newtemp = temp; + newtemp = std::min(newtemp, kMitsubishiHeavyMaxTemp); + newtemp = std::max(newtemp, kMitsubishiHeavyMinTemp); + + remote_state[9] &= kMitsubishiHeavyTempMask; + remote_state[9] |= ((newtemp - kMitsubishiHeavyMinTemp) << 4); +} + +uint8_t IRMitsubishiHeavy88Ac::getTemp(void) { + return (remote_state[9] >> 4) + kMitsubishiHeavyMinTemp; +} + +// Set the speed of the fan +void IRMitsubishiHeavy88Ac::setFan(const uint8_t speed) { + uint8_t newspeed = speed; + switch (speed) { + case kMitsubishiHeavy88FanLow: + case kMitsubishiHeavy88FanMed: + case kMitsubishiHeavy88FanHigh: + case kMitsubishiHeavy88FanTurbo: + case kMitsubishiHeavy88FanEcono: + break; + default: + newspeed = kMitsubishiHeavy88FanAuto; + } + remote_state[7] &= ~kMitsubishiHeavy88FanMask; + remote_state[7] |= (newspeed << 5); +} + +uint8_t IRMitsubishiHeavy88Ac::getFan(void) { + return remote_state[7] >> 5; +} + +void IRMitsubishiHeavy88Ac::setMode(const uint8_t mode) { + uint8_t newmode = mode; + switch (mode) { + case kMitsubishiHeavyCool: + case kMitsubishiHeavyDry: + case kMitsubishiHeavyFan: + case kMitsubishiHeavyHeat: + break; + default: + newmode = kMitsubishiHeavyAuto; + } + remote_state[9] &= ~kMitsubishiHeavyModeMask; + remote_state[9] |= newmode; +} + +uint8_t IRMitsubishiHeavy88Ac::getMode(void) { + return remote_state[9] & kMitsubishiHeavyModeMask; +} + +void IRMitsubishiHeavy88Ac::setSwingVertical(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingVAuto: + case kMitsubishiHeavy88SwingVHighest: + case kMitsubishiHeavy88SwingVHigh: + case kMitsubishiHeavy88SwingVMiddle: + case kMitsubishiHeavy88SwingVLow: + case kMitsubishiHeavy88SwingVLowest: + newpos = pos; + break; + default: + newpos = kMitsubishiHeavy88SwingVOff; + } + remote_state[5] &= ~kMitsubishiHeavy88SwingVMaskByte5; + remote_state[5] |= (newpos & kMitsubishiHeavy88SwingVMaskByte5); + remote_state[7] &= ~kMitsubishiHeavy88SwingVMaskByte7; + remote_state[7] |= (newpos & kMitsubishiHeavy88SwingVMaskByte7); +} + +uint8_t IRMitsubishiHeavy88Ac::getSwingVertical(void) { + return (remote_state[5] & kMitsubishiHeavy88SwingVMaskByte5) | + (remote_state[7] & kMitsubishiHeavy88SwingVMaskByte7); +} + +void IRMitsubishiHeavy88Ac::setSwingHorizontal(const uint8_t pos) { + uint8_t newpos; + switch (pos) { + case kMitsubishiHeavy88SwingHAuto: + case kMitsubishiHeavy88SwingHLeftMax: + case kMitsubishiHeavy88SwingHLeft: + case kMitsubishiHeavy88SwingHMiddle: + case kMitsubishiHeavy88SwingHRight: + case kMitsubishiHeavy88SwingHRightMax: + case kMitsubishiHeavy88SwingHLeftRight: + case kMitsubishiHeavy88SwingHRightLeft: + case kMitsubishiHeavy88SwingH3D: + newpos = pos; + break; + default: + newpos = kMitsubishiHeavy88SwingHOff; + } + remote_state[5] &= ~kMitsubishiHeavy88SwingHMask; + remote_state[5] |= newpos; +} + +uint8_t IRMitsubishiHeavy88Ac::getSwingHorizontal(void) { + return remote_state[5] & kMitsubishiHeavy88SwingHMask; +} + +void IRMitsubishiHeavy88Ac::setTurbo(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy88FanTurbo); + else if (this->getTurbo()) this->setFan(kMitsubishiHeavy88FanAuto); +} + +bool IRMitsubishiHeavy88Ac::getTurbo(void) { + return this->getFan() == kMitsubishiHeavy88FanTurbo; +} + +void IRMitsubishiHeavy88Ac::setEcono(const bool on) { + if (on) + this->setFan(kMitsubishiHeavy88FanEcono); + else if (this->getEcono()) this->setFan(kMitsubishiHeavy88FanAuto); +} + +bool IRMitsubishiHeavy88Ac::getEcono(void) { + return this->getFan() == kMitsubishiHeavy88FanEcono; +} + +void IRMitsubishiHeavy88Ac::set3D(const bool on) { + if (on) + this->setSwingHorizontal(kMitsubishiHeavy88SwingH3D); + else if (this->get3D()) + this->setSwingHorizontal(kMitsubishiHeavy88SwingHOff); +} + +bool IRMitsubishiHeavy88Ac::get3D(void) { + return this->getSwingHorizontal() == kMitsubishiHeavy88SwingH3D; +} + +void IRMitsubishiHeavy88Ac::setClean(const bool on) { + if (on) + remote_state[5] |= kMitsubishiHeavy88CleanBit; + else + remote_state[5] &= ~kMitsubishiHeavy88CleanBit; +} + +bool IRMitsubishiHeavy88Ac::getClean(void) { + return remote_state[5] & kMitsubishiHeavy88CleanBit; +} + +// Verify the given state has a ZJ-S signature. +bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) { + for (uint8_t i = 0; i < kMitsubishiHeavySigLength; i++) + if (state[i] != kMitsubishiHeavyZjsSig[i]) return false; + return true; +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +void IRMitsubishiHeavy88Ac::checksum(void) { + for (uint8_t i = kMitsubishiHeavySigLength - 2; + i < kMitsubishiHeavy88StateLength; + i += 2) { + remote_state[i + 1] = ~remote_state[i]; + } +} + +// Protocol technically has no checksum, but does has inverted byte pairs. +bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state, + const uint16_t length) { + return IRMitsubishiHeavy152Ac::validChecksum(state, length); +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishiHeavy88Ac::convertMode(const stdAc::opmode_t mode) { + return IRMitsubishiHeavy152Ac::convertMode(mode); +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishiHeavy88Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kMitsubishiHeavy88FanEcono; // Assumes Econo is slower than Low. + case stdAc::fanspeed_t::kLow: + return kMitsubishiHeavy88FanLow; + case stdAc::fanspeed_t::kMedium: + return kMitsubishiHeavy88FanMed; + case stdAc::fanspeed_t::kHigh: + return kMitsubishiHeavy88FanHigh; + case stdAc::fanspeed_t::kMax: + return kMitsubishiHeavy88FanTurbo; + default: + return kMitsubishiHeavy88FanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishiHeavy88Ac::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kAuto: + return kMitsubishiHeavy88SwingVAuto; + case stdAc::swingv_t::kHighest: + return kMitsubishiHeavy88SwingVHighest; + case stdAc::swingv_t::kHigh: + return kMitsubishiHeavy88SwingVHigh; + case stdAc::swingv_t::kMiddle: + return kMitsubishiHeavy88SwingVMiddle; + case stdAc::swingv_t::kLow: + return kMitsubishiHeavy88SwingVLow; + case stdAc::swingv_t::kLowest: + return kMitsubishiHeavy88SwingVLowest; + default: + return kMitsubishiHeavy88SwingVOff; + } +} + +// Convert a standard A/C horizontal swing into its native setting. +uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kAuto: + return kMitsubishiHeavy88SwingHAuto; + case stdAc::swingh_t::kLeftMax: + return kMitsubishiHeavy88SwingHLeftMax; + case stdAc::swingh_t::kLeft: + return kMitsubishiHeavy88SwingHLeft; + case stdAc::swingh_t::kMiddle: + return kMitsubishiHeavy88SwingHMiddle; + case stdAc::swingh_t::kRight: + return kMitsubishiHeavy88SwingHRight; + case stdAc::swingh_t::kRightMax: + return kMitsubishiHeavy88SwingHRightMax; + default: + return kMitsubishiHeavy88SwingHOff; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMitsubishiHeavy88Ac::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishiHeavy88FanTurbo: return stdAc::fanspeed_t::kMax; + case kMitsubishiHeavy88FanHigh: return stdAc::fanspeed_t::kHigh; + case kMitsubishiHeavy88FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishiHeavy88FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishiHeavy88FanEcono: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingh_t IRMitsubishiHeavy88Ac::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishiHeavy88SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishiHeavy88SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishiHeavy88SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishiHeavy88SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishiHeavy88SwingHOff: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishiHeavy88SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishiHeavy88SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishiHeavy88SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishiHeavy88SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishiHeavy88SwingVLowest: return stdAc::swingv_t::kLowest; + case kMitsubishiHeavy88SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI_HEAVY_88; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = IRMitsubishiHeavy152Ac::toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.turbo = this->getTurbo(); + result.econo = this->getEcono(); + result.clean = this->getClean(); + // Not supported. + result.quiet = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRMitsubishiHeavy88Ac::toString(void) { + String result = ""; + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kMitsubishiHeavyAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavyCool: + result += F(" (Cool)"); + break; + case kMitsubishiHeavyHeat: + result += F(" (Heat)"); + break; + case kMitsubishiHeavyDry: + result += F(" (Dry)"); + break; + case kMitsubishiHeavyFan: + result += F(" (Fan)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()) + 'C'; + result += F(", Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kMitsubishiHeavy88FanAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88FanHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy88FanLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy88FanMed: + result += F(" (Med)"); + break; + case kMitsubishiHeavy88FanEcono: + result += F(" (Econo)"); + break; + case kMitsubishiHeavy88FanTurbo: + result += F(" (Turbo)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (V): "); + result += uint64ToString(this->getSwingVertical()); + switch (this->getSwingVertical()) { + case kMitsubishiHeavy88SwingVAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88SwingVHighest: + result += F(" (Highest)"); + break; + case kMitsubishiHeavy88SwingVHigh: + result += F(" (High)"); + break; + case kMitsubishiHeavy88SwingVMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy88SwingVLow: + result += F(" (Low)"); + break; + case kMitsubishiHeavy88SwingVLowest: + result += F(" (Lowest)"); + break; + case kMitsubishiHeavy88SwingVOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Swing (H): "); + result += uint64ToString(this->getSwingHorizontal()); + switch (this->getSwingHorizontal()) { + case kMitsubishiHeavy88SwingHAuto: + result += F(" (Auto)"); + break; + case kMitsubishiHeavy88SwingHLeftMax: + result += F(" (Max Left)"); + break; + case kMitsubishiHeavy88SwingHLeft: + result += F(" (Left)"); + break; + case kMitsubishiHeavy88SwingHMiddle: + result += F(" (Middle)"); + break; + case kMitsubishiHeavy88SwingHRight: + result += F(" (Right)"); + break; + case kMitsubishiHeavy88SwingHRightMax: + result += F(" (Max Right)"); + break; + case kMitsubishiHeavy88SwingHLeftRight: + result += F(" (Left Right)"); + break; + case kMitsubishiHeavy88SwingHRightLeft: + result += F(" (Right Left)"); + break; + case kMitsubishiHeavy88SwingH3D: + result += F(" (3D)"); + break; + case kMitsubishiHeavy88SwingHOff: + result += F(" (Off)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += F(", 3D: "); + result += (this->get3D() ? F("On") : F("Off")); + result += F(", Clean: "); + result += (this->getClean() ? F("On") : F("Off")); + return result; +} + +#if DECODE_MITSUBISHIHEAVY +// Decode the supplied MitsubishiHeavy message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. +// Typically kMitsubishiHeavy88Bits or kMitsubishiHeavy152Bits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Appears to be working. Needs testing against a real device. +bool IRrecv::decodeMitsubishiHeavy(decode_results* results, + const uint16_t nbits, const bool strict) { + // Check if can possibly be a valid MitsubishiHeavy message. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; + if (strict) { + switch (nbits) { + case kMitsubishiHeavy88Bits: + case kMitsubishiHeavy152Bits: + break; + default: + return false; // Not what is expected + } + } + + uint16_t actualBits = 0; + uint16_t offset = kStartOffset; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyHdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kMitsubishiHeavyHdrSpace)) + return false; + // Data + // Keep reading bytes until we either run out of section or state to fill. + for (uint16_t i = 0; + offset <= results->rawlen - 16 && actualBits < nbits; + i++, actualBits += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kTolerance, 0, false); + if (data_result.success == false) { + DPRINT("DEBUG: offset = "); + DPRINTLN(offset + data_result.used); + return false; // Fail + } + results->state[i] = data_result.data; + } + // Footer. + if (!matchMark(results->rawbuf[offset++], kMitsubishiHeavyBitMark)) + return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kMitsubishiHeavyGap)) return false; + + // Compliance + if (actualBits < nbits) return false; + if (strict && actualBits != nbits) return false; // Not as we expected. + switch (actualBits) { + case kMitsubishiHeavy88Bits: + if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && + IRMitsubishiHeavy88Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_88; + break; + case kMitsubishiHeavy152Bits: + if (strict && !(IRMitsubishiHeavy152Ac::checkZmsSig(results->state) && + IRMitsubishiHeavy152Ac::validChecksum(results->state))) + return false; + results->decode_type = MITSUBISHI_HEAVY_152; + break; + default: + return false; + } + + // Success + results->bits = actualBits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_MITSUBISHIHEAVY diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h new file mode 100644 index 0000000..94f1ab3 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h @@ -0,0 +1,271 @@ +// Copyright 2019 David Conran + +// Supports: +// Brand: Mitsubishi Heavy Industries, Model: RLA502A700B remote +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZM-S A/C +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZMXA-S A/C +// Brand: Mitsubishi Heavy Industries, Model: RKX502A001C remote +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZJ-S A/C + +#ifndef IR_MITSUBISHIHEAVY_H_ +#define IR_MITSUBISHIHEAVY_H_ + +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp +// https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp + +// Constants. +const uint8_t kMitsubishiHeavySigLength = 5; + + +// ZMS (152 bit) +const uint8_t kMitsubishiHeavyZmsSig[kMitsubishiHeavySigLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A}; +// Byte[5] +const uint8_t kMitsubishiHeavyFilterBit = 0b01000000; +const uint8_t kMitsubishiHeavyCleanBit = 0b00100000; +const uint8_t kMitsubishiHeavyPowerBit = 0b00001000; // Byte 9 on ZJS +const uint8_t kMitsubishiHeavyModeMask = 0b00000111; // Byte 9 on ZJS +const uint8_t kMitsubishiHeavyAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavyCool = 1; // 0b001 +const uint8_t kMitsubishiHeavyDry = 2; // 0b010 +const uint8_t kMitsubishiHeavyFan = 3; // 0b011 +const uint8_t kMitsubishiHeavyHeat = 4; // 0b100 +// Byte[7] +const uint8_t kMitsubishiHeavyTempMask = 0b00001111; +const uint8_t kMitsubishiHeavyMinTemp = 17; // 17C +const uint8_t kMitsubishiHeavyMaxTemp = 31; // 31C +// Byte[9] +const uint8_t kMitsubishiHeavyFanMask = 0b00001111; // ~Byte 7 on ZJS. +const uint8_t kMitsubishiHeavy152FanAuto = 0x0; // 0b0000 +const uint8_t kMitsubishiHeavy152FanLow = 0x1; // 0b0001 +const uint8_t kMitsubishiHeavy152FanMed = 0x2; // 0b0010 +const uint8_t kMitsubishiHeavy152FanHigh = 0x3; // 0b0011 +const uint8_t kMitsubishiHeavy152FanMax = 0x4; // 0b0100 +const uint8_t kMitsubishiHeavy152FanEcono = 0x6; // 0b0110 +const uint8_t kMitsubishiHeavy152FanTurbo = 0x8; // 0b1000 +// Byte[11] +const uint8_t kMitsubishiHeavy3DMask = 0b00010010; +const uint8_t kMitsubishiHeavy152SwingVMask = 0b11100000; +const uint8_t kMitsubishiHeavy152SwingVAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavy152SwingVHighest = 1; // 0b001 +const uint8_t kMitsubishiHeavy152SwingVHigh = 2; // 0b010 +const uint8_t kMitsubishiHeavy152SwingVMiddle = 3; // 0b011 +const uint8_t kMitsubishiHeavy152SwingVLow = 4; // 0b100 +const uint8_t kMitsubishiHeavy152SwingVLowest = 5; // 0b101 +const uint8_t kMitsubishiHeavy152SwingVOff = 6; // 0b110 +// Byte[13] +const uint8_t kMitsubishiHeavy152SwingHMask = 0b00001111; +const uint8_t kMitsubishiHeavy152SwingHAuto = 0; // 0b0000 +const uint8_t kMitsubishiHeavy152SwingHLeftMax = 1; // 0b0001 +const uint8_t kMitsubishiHeavy152SwingHLeft = 2; // 0b0010 +const uint8_t kMitsubishiHeavy152SwingHMiddle = 3; // 0b0011 +const uint8_t kMitsubishiHeavy152SwingHRight = 4; // 0b0100 +const uint8_t kMitsubishiHeavy152SwingHRightMax = 5; // 0b0101 +const uint8_t kMitsubishiHeavy152SwingHRightLeft = 6; // 0b0110 +const uint8_t kMitsubishiHeavy152SwingHLeftRight = 7; // 0b0111 +const uint8_t kMitsubishiHeavy152SwingHOff = 8; // 0b1000 +// Byte[15] +const uint8_t kMitsubishiHeavyNightBit = 0b01000000; +const uint8_t kMitsubishiHeavySilentBit = 0b10000000; + + +// ZJS (88 bit) +const uint8_t kMitsubishiHeavyZjsSig[kMitsubishiHeavySigLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26}; +// Byte [5] +const uint8_t kMitsubishiHeavy88CleanBit = 0b00100000; +const uint8_t kMitsubishiHeavy88SwingHMask = 0b11001100; +const uint8_t kMitsubishiHeavy88SwingHAuto = 0x80; // 0b10000000 +const uint8_t kMitsubishiHeavy88SwingHLeftMax = 0x04; // 0b00000100 +const uint8_t kMitsubishiHeavy88SwingHLeft = 0x44; // 0b01000100 +const uint8_t kMitsubishiHeavy88SwingHMiddle = 0x84; // 0b10000100 +const uint8_t kMitsubishiHeavy88SwingHRight = 0xC4; // 0b11000100 +const uint8_t kMitsubishiHeavy88SwingHRightMax = 0x08; // 0b00001000 +const uint8_t kMitsubishiHeavy88SwingHRightLeft = 0x88; // 0b10001000 +const uint8_t kMitsubishiHeavy88SwingHLeftRight = 0x48; // 0b01001000 +const uint8_t kMitsubishiHeavy88SwingHOff = 0x00; // 0b00000000 +const uint8_t kMitsubishiHeavy88SwingH3D = 0xC8; // 0b11001000 +// Byte[7] +const uint8_t kMitsubishiHeavy88FanMask = 0b11100000; +const uint8_t kMitsubishiHeavy88FanAuto = 0; // 0b000 +const uint8_t kMitsubishiHeavy88FanLow = 2; // 0b010 +const uint8_t kMitsubishiHeavy88FanMed = 3; // 0b011 +const uint8_t kMitsubishiHeavy88FanHigh = 4; // 0b100 +const uint8_t kMitsubishiHeavy88FanTurbo = 6; // 0b110 +const uint8_t kMitsubishiHeavy88FanEcono = 7; // 0b111 +const uint8_t kMitsubishiHeavy88SwingVMaskByte5 = 0b00000010; +const uint8_t kMitsubishiHeavy88SwingVMaskByte7 = 0b00011000; +const uint8_t kMitsubishiHeavy88SwingVMask = + kMitsubishiHeavy88SwingVMaskByte5 | kMitsubishiHeavy88SwingVMaskByte7; + // i.e. 0b00011010 +const uint8_t kMitsubishiHeavy88SwingVAuto = 0b00010000; // 0x10 +const uint8_t kMitsubishiHeavy88SwingVHighest = 0b00011000; // 0x18 +const uint8_t kMitsubishiHeavy88SwingVHigh = 0b00000010; // 0x02 +const uint8_t kMitsubishiHeavy88SwingVMiddle = 0b00001010; // 0x0A +const uint8_t kMitsubishiHeavy88SwingVLow = 0b00010010; // 0x12 +const uint8_t kMitsubishiHeavy88SwingVLowest = 0b00011010; // 0x1A +const uint8_t kMitsubishiHeavy88SwingVOff = 0b00000000; // 0x00 +// Byte[9] is Power & Mode & Temp. + + +// Classes +class IRMitsubishiHeavy152Ac { + public: + explicit IRMitsubishiHeavy152Ac(const uint16_t pin); + + void stateReset(void); +#if SEND_MITSUBISHIHEAVY + void send(const uint16_t repeat = kMitsubishiHeavy152MinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_MITSUBISHIHEAVY + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwingVertical(const uint8_t pos); + uint8_t getSwingVertical(void); + void setSwingHorizontal(const uint8_t pos); + uint8_t getSwingHorizontal(void); + + void setNight(const bool on); + bool getNight(void); + + void set3D(const bool on); + bool get3D(void); + + void setSilent(const bool on); + bool getSilent(void); + + void setFilter(const bool on); + bool getFilter(void); + + void setClean(const bool on); + bool getClean(void); + + void setTurbo(const bool on); + bool getTurbo(void); + + void setEcono(const bool on); + bool getEcono(void); + + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + + static bool checkZmsSig(const uint8_t *state); + static bool validChecksum( + const uint8_t *state, + const uint16_t length = kMitsubishiHeavy152StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST + // The state of the IR remote in IR code form. + uint8_t remote_state[kMitsubishiHeavy152StateLength]; + void checksum(void); +}; + +class IRMitsubishiHeavy88Ac { + public: + explicit IRMitsubishiHeavy88Ac(const uint16_t pin); + + void stateReset(void); +#if SEND_MITSUBISHIHEAVY + void send(const uint16_t repeat = kMitsubishiHeavy88MinRepeat); +#endif // SEND_MITSUBISHIHEAVY + void begin(void); + void on(void); + void off(void); + + void setPower(const bool on); + bool getPower(void); + + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + + void setFan(const uint8_t fan); + uint8_t getFan(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setSwingVertical(const uint8_t pos); + uint8_t getSwingVertical(void); + void setSwingHorizontal(const uint8_t pos); + uint8_t getSwingHorizontal(void); + + void setTurbo(const bool on); + bool getTurbo(void); + + void setEcono(const bool on); + bool getEcono(void); + + void set3D(const bool on); + bool get3D(void); + + void setClean(const bool on); + bool getClean(void); + + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + + static bool checkZjsSig(const uint8_t *state); + static bool validChecksum( + const uint8_t *state, + const uint16_t length = kMitsubishiHeavy88StateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else // UNIT_TEST + IRsendTest _irsend; +#endif // UNIT_TEST + // The state of the IR remote in IR code form. + uint8_t remote_state[kMitsubishiHeavy152StateLength]; + void checksum(void); +}; +#endif // IR_MITSUBISHIHEAVY_H_ diff --git a/lib/IRremoteESP8266/src/ir_NEC.cpp b/lib/IRremoteESP8266/src/ir_NEC.cpp index 660b511..5ea7a39 100644 --- a/lib/IRremoteESP8266/src/ir_NEC.cpp +++ b/lib/IRremoteESP8266/src/ir_NEC.cpp @@ -1,6 +1,8 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ + #define __STDC_LIMIT_MACROS #include "ir_NEC.h" #include @@ -9,14 +11,6 @@ #include "IRsend.h" #include "IRutils.h" -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ - #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ SEND_PIONEER) // Send a raw NEC(Renesas) formatted message. diff --git a/lib/IRremoteESP8266/src/ir_NEC.h b/lib/IRremoteESP8266/src/ir_NEC.h index c274c10..fef1a65 100644 --- a/lib/IRremoteESP8266/src/ir_NEC.h +++ b/lib/IRremoteESP8266/src/ir_NEC.h @@ -1,20 +1,14 @@ // Copyright 2009 Ken Shirriff // Copyright 2017, 2018 David Conran +// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ + #ifndef IR_NEC_H_ #define IR_NEC_H_ #include #include "IRremoteESP8266.h" -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ - // Constants // Ref: // http://www.sbprojects.com/knowledge/ir/nec.php diff --git a/lib/IRremoteESP8266/src/ir_Nikai.cpp b/lib/IRremoteESP8266/src/ir_Nikai.cpp index 9ac22a8..375cb46 100644 --- a/lib/IRremoteESP8266/src/ir_Nikai.cpp +++ b/lib/IRremoteESP8266/src/ir_Nikai.cpp @@ -1,17 +1,13 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// Nikai + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// NN NN IIIII KK KK AAA IIIII -// NNN NN III KK KK AAAAA III -// NN N NN III KKKK AA AA III -// NN NNN III KK KK AAAAAAA III -// NN NN IIIII KK KK AA AA IIIII - // Constants // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/309 diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp index be96ce0..fe10215 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266/src/ir_Panasonic.cpp @@ -1,6 +1,8 @@ // Copyright 2015 Kristian Lauszus // Copyright 2017, 2018 David Conran +// Panasonic devices + #include "ir_Panasonic.h" #include #ifndef ARDUINO @@ -10,12 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC -// P P A A NN N A A S O O NN N I C -// PPPP AAAAA N N N AAAAA SSS O O N N N I C -// P A A N NN A A S O O N NN I C -// P A A N N A A SSSS OOO N N IIIII CCCC - // Panasonic protocol originally added by Kristian Lauszus from: // https://github.com/z3t0/Arduino-IRremote // (Thanks to zenwheel and other people at the original blog post) @@ -27,8 +23,8 @@ // Code by crankyoldgit // Panasonic A/C models supported: // A/C Series/models: -// JKE, LKE, DKE, CKP, & NKE series. (In theory) -// CS-YW9MKD (confirmed) +// JKE, LKE, DKE, CKP, RKR, & NKE series. (In theory) +// CS-YW9MKD, CS-Z9RKR (confirmed) // CS-ME14CKPG / CS-ME12CKPG / CS-ME10CKPG // A/C Remotes: // A75C3747 (confirmed) @@ -63,7 +59,7 @@ const uint32_t kPanasonicMinGap = kPanasonicMinGapTicks * kPanasonicTick; const uint16_t kPanasonicAcSectionGap = 10000; const uint16_t kPanasonicAcSection1Length = 8; -const uint32_t kPanasonicAcMessageGap = 100000; // A complete guess. +const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. #if (SEND_PANASONIC || SEND_DENON) // Send a Panasonic formatted message. @@ -77,7 +73,8 @@ const uint32_t kPanasonicAcMessageGap = 100000; // A complete guess. // // Note: // This protocol is a modified version of Kaseikyo. -void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendPanasonic64(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength, @@ -96,8 +93,8 @@ void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) { // // Note: // This protocol is a modified version of Kaseikyo. -void IRsend::sendPanasonic(uint16_t address, uint32_t data, uint16_t nbits, - uint16_t repeat) { +void IRsend::sendPanasonic(const uint16_t address, const uint32_t data, + const uint16_t nbits, const uint16_t repeat) { sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat); } @@ -117,8 +114,10 @@ void IRsend::sendPanasonic(uint16_t address, uint32_t data, uint16_t nbits, // Panasonic 48-bit protocol is a modified version of Kaseikyo. // Ref: // http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -uint64_t IRsend::encodePanasonic(uint16_t manufacturer, uint8_t device, - uint8_t subdevice, uint8_t function) { +uint64_t IRsend::encodePanasonic(const uint16_t manufacturer, + const uint8_t device, + const uint8_t subdevice, + const uint8_t function) { uint8_t checksum = device ^ subdevice ^ function; return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) | ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum); @@ -141,8 +140,8 @@ uint64_t IRsend::encodePanasonic(uint16_t manufacturer, uint8_t device, // Ref: // http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 // http://www.hifi-remote.com/wiki/index.php?title=Panasonic -bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, - bool strict, uint32_t manufacturer) { +bool IRrecv::decodePanasonic(decode_results *results, const uint16_t nbits, + const bool strict, const uint32_t manufacturer) { if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Not enough entries to be a Panasonic message. if (strict && nbits != kPanasonicBits) @@ -211,13 +210,14 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, //: // Panasonic A/C models supported: // A/C Series/models: -// JKE, LKE, DKE, & NKE series. +// JKE, LKE, DKE, CKP, RKR, & NKE series. // CS-YW9MKD // A/C Remotes: // A75C3747 // A75C3704 // -void IRsend::sendPanasonicAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendPanasonicAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kPanasonicAcSection1Length) return; for (uint16_t r = 0; r <= repeat; r++) { // First section. (8 bytes) @@ -236,16 +236,18 @@ void IRsend::sendPanasonicAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { } #endif // SEND_PANASONIC_AC -IRPanasonicAc::IRPanasonicAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRPanasonicAc::IRPanasonicAc(const uint16_t pin) : _irsend(pin) { + this->stateReset(); +} -void IRPanasonicAc::stateReset() { +void IRPanasonicAc::stateReset(void) { for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) remote_state[i] = kPanasonicKnownGoodState[i]; _temp = 25; // An initial saved desired temp. Completely made up. _swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing. } -void IRPanasonicAc::begin() { _irsend.begin(); } +void IRPanasonicAc::begin(void) { _irsend.begin(); } // Verify the checksum is valid for a given state. // Args: @@ -264,12 +266,12 @@ uint8_t IRPanasonicAc::calcChecksum(uint8_t state[], const uint16_t length) { } void IRPanasonicAc::fixChecksum(const uint16_t length) { - remote_state[length - 1] = calcChecksum(remote_state, length); + remote_state[length - 1] = this->calcChecksum(remote_state, length); } #if SEND_PANASONIC_AC void IRPanasonicAc::send(const uint16_t repeat) { - fixChecksum(); + this->fixChecksum(); _irsend.sendPanasonicAC(remote_state, kPanasonicAcStateLength, repeat); } #endif // SEND_PANASONIC_AC @@ -281,6 +283,7 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { case kPanasonicLke: case kPanasonicNke: case kPanasonicCkp: + case kPanasonicRkr: break; default: // Only proceed if we know what to do. return; @@ -301,7 +304,7 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { remote_state[23] = 0x01; remote_state[25] = 0x06; // Has to be done last as setSwingHorizontal has model check built-in - setSwingHorizontal(_swingh); + this->setSwingHorizontal(_swingh); break; case kPanasonicNke: remote_state[17] = 0x06; @@ -311,12 +314,17 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { case kPanasonicCkp: remote_state[21] |= 0x10; remote_state[23] = 0x01; + break; + case kPanasonicRkr: + remote_state[13] |= 0x08; + remote_state[23] = 0x89; default: break; } } -panasonic_ac_remote_model_t IRPanasonicAc::getModel() { +panasonic_ac_remote_model_t IRPanasonicAc::getModel(void) { + if (remote_state[23] == 0x89) return kPanasonicRkr; if (remote_state[17] == 0x00) { if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) return kPanasonicCkp; @@ -329,8 +337,8 @@ panasonic_ac_remote_model_t IRPanasonicAc::getModel() { return kPanasonicUnknown; } -uint8_t *IRPanasonicAc::getRaw() { - fixChecksum(); +uint8_t *IRPanasonicAc::getRaw(void) { + this->fixChecksum(); return remote_state; } @@ -351,32 +359,32 @@ void IRPanasonicAc::setRaw(const uint8_t state[]) { // // For all other models, setPower(true) should set the internal state to // turn it on, and setPower(false) should turn it off. -void IRPanasonicAc::setPower(const bool state) { - if (state) - on(); +void IRPanasonicAc::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the A/C power state of the remote. // Except for CKP models, where it returns if the power state will be toggled // on the A/C unit when the next message is sent. -bool IRPanasonicAc::getPower() { +bool IRPanasonicAc::getPower(void) { return (remote_state[13] & kPanasonicAcPower) == kPanasonicAcPower; } -void IRPanasonicAc::on() { remote_state[13] |= kPanasonicAcPower; } +void IRPanasonicAc::on(void) { remote_state[13] |= kPanasonicAcPower; } -void IRPanasonicAc::off() { remote_state[13] &= ~kPanasonicAcPower; } +void IRPanasonicAc::off(void) { remote_state[13] &= ~kPanasonicAcPower; } -uint8_t IRPanasonicAc::getMode() { return remote_state[13] >> 4; } +uint8_t IRPanasonicAc::getMode(void) { return remote_state[13] >> 4; } void IRPanasonicAc::setMode(const uint8_t desired) { uint8_t mode = kPanasonicAcAuto; // Default to Auto mode. switch (desired) { case kPanasonicAcFan: // Allegedly Fan mode has a temperature of 27. - setTemp(kPanasonicAcFanModeTemp, false); + this->setTemp(kPanasonicAcFanModeTemp, false); mode = desired; break; case kPanasonicAcAuto: @@ -385,16 +393,16 @@ void IRPanasonicAc::setMode(const uint8_t desired) { case kPanasonicAcDry: mode = desired; // Set the temp to the saved temp, just incase our previous mode was Fan. - setTemp(_temp); + this->setTemp(_temp); break; } remote_state[13] &= 0x0F; // Clear the previous mode bits. remote_state[13] |= mode << 4; } -uint8_t IRPanasonicAc::getTemp() { return remote_state[14] >> 1; } +uint8_t IRPanasonicAc::getTemp(void) { return remote_state[14] >> 1; } -// Set the desitred temperature in Celcius. +// Set the desitred temperature in Celsius. // Args: // celsius: The temperature to set the A/C unit to. // remember: A boolean flag for the class to remember the temperature. @@ -408,7 +416,9 @@ void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) { if (remember) _temp = temperature; } -uint8_t IRPanasonicAc::getSwingVertical() { return remote_state[16] & 0x0F; } +uint8_t IRPanasonicAc::getSwingVertical(void) { + return remote_state[16] & 0x0F; +} void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { uint8_t elevation = desired_elevation; @@ -420,7 +430,7 @@ void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { remote_state[16] |= elevation; } -uint8_t IRPanasonicAc::getSwingHorizontal() { return remote_state[17]; } +uint8_t IRPanasonicAc::getSwingHorizontal(void) { return remote_state[17]; } void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { switch (desired_direction) { @@ -436,8 +446,9 @@ void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { } _swingh = desired_direction; // Store the direction for later. uint8_t direction = desired_direction; - switch (getModel()) { + switch (this->getModel()) { case kPanasonicDke: + case kPanasonicRkr: break; case kPanasonicNke: case kPanasonicLke: @@ -455,48 +466,62 @@ void IRPanasonicAc::setFan(const uint8_t speed) { (remote_state[16] & 0x0F) | ((speed + kPanasonicAcFanOffset) << 4); } -uint8_t IRPanasonicAc::getFan() { +uint8_t IRPanasonicAc::getFan(void) { return (remote_state[16] >> 4) - kPanasonicAcFanOffset; } -bool IRPanasonicAc::getQuiet() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcQuietCkp; - else - return remote_state[21] & kPanasonicAcQuiet; +bool IRPanasonicAc::getQuiet(void) { + switch (this->getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return remote_state[21] & kPanasonicAcQuietCkp; + default: + return remote_state[21] & kPanasonicAcQuiet; + } } -void IRPanasonicAc::setQuiet(const bool state) { +void IRPanasonicAc::setQuiet(const bool on) { uint8_t quiet; - if (getModel() == kPanasonicCkp) - quiet = kPanasonicAcQuietCkp; - else - quiet = kPanasonicAcQuiet; + switch (this->getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + quiet = kPanasonicAcQuietCkp; + break; + default: + quiet = kPanasonicAcQuiet; + } - if (state) { - setPowerful(false); // Powerful is mutually exclusive. + if (on) { + this->setPowerful(false); // Powerful is mutually exclusive. remote_state[21] |= quiet; } else { remote_state[21] &= ~quiet; } } -bool IRPanasonicAc::getPowerful() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcPowerfulCkp; - else - return remote_state[21] & kPanasonicAcPowerful; +bool IRPanasonicAc::getPowerful(void) { + switch (this->getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + return remote_state[21] & kPanasonicAcPowerfulCkp; + default: + return remote_state[21] & kPanasonicAcPowerful; + } } -void IRPanasonicAc::setPowerful(const bool state) { +void IRPanasonicAc::setPowerful(const bool on) { uint8_t powerful; - if (getModel() == kPanasonicCkp) - powerful = kPanasonicAcPowerfulCkp; - else - powerful = kPanasonicAcPowerful; + switch (this->getModel()) { + case kPanasonicRkr: + case kPanasonicCkp: + powerful = kPanasonicAcPowerfulCkp; + break; + default: + powerful = kPanasonicAcPowerful; + } - if (state) { - setQuiet(false); // Quiet is mutually exclusive. + if (on) { + this->setQuiet(false); // Quiet is mutually exclusive. remote_state[21] |= powerful; } else { remote_state[21] &= ~powerful; @@ -507,7 +532,7 @@ uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) { return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59); } -uint16_t IRPanasonicAc::getClock() { +uint16_t IRPanasonicAc::getClock(void) { uint16_t result = ((remote_state[25] & 0b00000111) << 8) + remote_state[24]; if (result == kPanasonicAcTimeSpecial) return 0; return result; @@ -522,7 +547,7 @@ void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) { remote_state[25] |= (corrected >> 8); } -uint16_t IRPanasonicAc::getOnTimer() { +uint16_t IRPanasonicAc::getOnTimer(void) { uint16_t result = ((remote_state[19] & 0b00000111) << 8) + remote_state[18]; if (result == kPanasonicAcTimeSpecial) return 0; return result; @@ -546,13 +571,13 @@ void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight, remote_state[19] |= (corrected >> 8); } -void IRPanasonicAc::cancelOnTimer() { setOnTimer(0, false); } +void IRPanasonicAc::cancelOnTimer(void) { this->setOnTimer(0, false); } -bool IRPanasonicAc::isOnTimerEnabled() { +bool IRPanasonicAc::isOnTimerEnabled(void) { return remote_state[13] & kPanasonicAcOnTimer; } -uint16_t IRPanasonicAc::getOffTimer() { +uint16_t IRPanasonicAc::getOffTimer(void) { uint16_t result = ((remote_state[20] & 0b01111111) << 4) + (remote_state[19] >> 4); if (result == kPanasonicAcTimeSpecial) return 0; @@ -578,111 +603,247 @@ void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, remote_state[20] |= corrected >> 4; } -void IRPanasonicAc::cancelOffTimer() { setOffTimer(0, false); } +void IRPanasonicAc::cancelOffTimer(void) { this->setOffTimer(0, false); } -bool IRPanasonicAc::isOffTimerEnabled() { +bool IRPanasonicAc::isOffTimerEnabled(void) { return remote_state[13] & kPanasonicAcOffTimer; } -#ifdef ARDUINO String IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { String result = ""; -#else -std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { - std::string result = ""; -#endif // ARDUINO - result += uint64ToString(mins_since_midnight / 60) + ":"; + result.reserve(6); + result += uint64ToString(mins_since_midnight / 60) + ':'; uint8_t mins = mins_since_midnight % 60; - if (mins < 10) result += "0"; // Zero pad the minutes. + if (mins < 10) result += '0'; // Zero pad the minutes. return result + uint64ToString(mins); } +// Convert a standard A/C mode into its native mode. +uint8_t IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kPanasonicAcCool; + case stdAc::opmode_t::kHeat: + return kPanasonicAcHeat; + case stdAc::opmode_t::kDry: + return kPanasonicAcDry; + case stdAc::opmode_t::kFan: + return kPanasonicAcFan; + default: + return kPanasonicAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRPanasonicAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kPanasonicAcFanMin; + case stdAc::fanspeed_t::kLow: + return kPanasonicAcFanMin + 1; + case stdAc::fanspeed_t::kMedium: + return kPanasonicAcFanMin + 2; + case stdAc::fanspeed_t::kHigh: + return kPanasonicAcFanMin + 3; + case stdAc::fanspeed_t::kMax: + return kPanasonicAcFanMax; + default: + return kPanasonicAcFanAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRPanasonicAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + return kPanasonicAcSwingVUp; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + return kPanasonicAcSwingVDown; + default: + return kPanasonicAcSwingVAuto; + } +} + +// Convert a standard A/C horizontal swing into its native setting. +uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: + return kPanasonicAcSwingHFullLeft; + case stdAc::swingh_t::kLeft: + return kPanasonicAcSwingHLeft; + case stdAc::swingh_t::kMiddle: + return kPanasonicAcSwingHMiddle; + case stdAc::swingh_t::kRight: + return kPanasonicAcSwingHRight; + case stdAc::swingh_t::kRightMax: + return kPanasonicAcSwingHFullRight; + default: + return kPanasonicAcSwingHAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRPanasonicAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kPanasonicAcCool: return stdAc::opmode_t::kCool; + case kPanasonicAcHeat: return stdAc::opmode_t::kHeat; + case kPanasonicAcDry: return stdAc::opmode_t::kDry; + case kPanasonicAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRPanasonicAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kPanasonicAcFanMax: return stdAc::fanspeed_t::kMax; + case kPanasonicAcFanMin + 3: return stdAc::fanspeed_t::kHigh; + case kPanasonicAcFanMin + 2: return stdAc::fanspeed_t::kMedium; + case kPanasonicAcFanMin + 1: return stdAc::fanspeed_t::kLow; + case kPanasonicAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingh_t IRPanasonicAc::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kPanasonicAcSwingHFullLeft: return stdAc::swingh_t::kLeftMax; + case kPanasonicAcSwingHLeft: return stdAc::swingh_t::kLeft; + case kPanasonicAcSwingHMiddle: return stdAc::swingh_t::kMiddle; + case kPanasonicAcSwingHRight: return stdAc::swingh_t::kRight; + case kPanasonicAcSwingHFullRight: return stdAc::swingh_t::kRightMax; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRPanasonicAc::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kPanasonicAcSwingVUp: return stdAc::swingv_t::kHighest; + case kPanasonicAcSwingVDown: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRPanasonicAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::PANASONIC_AC; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingVertical()); + result.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + // Not supported. + result.econo = false; + result.clean = false; + result.filter = false; + result.light = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRPanasonicAc::toString() { +String IRPanasonicAc::toString(void) { String result = ""; -#else -std::string IRPanasonicAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Model: " + uint64ToString(getModel()); + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += F("Model: "); + result += uint64ToString(getModel()); switch (getModel()) { case kPanasonicDke: - result += " (DKE)"; + result += F(" (DKE)"); break; case kPanasonicJke: - result += " (JKE)"; + result += F(" (JKE)"); break; case kPanasonicNke: - result += " (NKE)"; + result += F(" (NKE)"); break; case kPanasonicLke: - result += " (LKE)"; + result += F(" (LKE)"); break; case kPanasonicCkp: - result += " (CKP)"; + result += F(" (CKP)"); + break; + case kPanasonicRkr: + result += F(" (RKR)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Power: "; + result += F(", Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kPanasonicAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kPanasonicAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kPanasonicAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kPanasonicAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kPanasonicAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; case kPanasonicAcFanMin: - result += " (MIN)"; + result += F(" (MIN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing (Vertical): " + uint64ToString(getSwingVertical()); + result += F(", Swing (Vertical): "); + result += uint64ToString(getSwingVertical()); switch (getSwingVertical()) { case kPanasonicAcSwingVAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcSwingVUp: - result += " (Full Up)"; + result += F(" (Full Up)"); break; case kPanasonicAcSwingVDown: - result += " (Full Down)"; + result += F(" (Full Down)"); break; case 2: case 3: case 4: break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } switch (getModel()) { @@ -690,52 +851,54 @@ std::string IRPanasonicAc::toString() { case kPanasonicCkp: break; // No Horizontal Swing support. default: - result += ", Swing (Horizontal): " + uint64ToString(getSwingHorizontal()); + result += F(", Swing (Horizontal): "); + result += uint64ToString(getSwingHorizontal()); switch (getSwingHorizontal()) { case kPanasonicAcSwingHAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kPanasonicAcSwingHFullLeft: - result += " (Full Left)"; + result += F(" (Full Left)"); break; case kPanasonicAcSwingHLeft: - result += " (Left)"; + result += F(" (Left)"); break; case kPanasonicAcSwingHMiddle: - result += " (Middle)"; + result += F(" (Middle)"); break; case kPanasonicAcSwingHFullRight: - result += " (Full Right)"; + result += F(" (Full Right)"); break; case kPanasonicAcSwingHRight: - result += " (Right)"; + result += F(" (Right)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } } - result += ", Quiet: "; + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Powerful: "; + result += F("Off"); + result += F(", Powerful: "); if (getPowerful()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Clock: " + timeToString(getClock()); - result += ", On Timer: "; + result += F("Off"); + result += F(", Clock: "); + result += timeToString(getClock()); + result += F(", On Timer: "); if (isOnTimerEnabled()) result += timeToString(getOnTimer()); else - result += "Off"; - result += ", Off Timer: "; + result += F("Off"); + result += F(", Off Timer: "); if (isOffTimerEnabled()) result += timeToString(getOffTimer()); else - result += "Off"; + result += F("Off"); return result; } @@ -758,8 +921,8 @@ std::string IRPanasonicAc::toString() { // A/C Remotes: // A75C3747 (Confirmed) // A75C3704 -bool IRrecv::decodePanasonicAC(decode_results *results, uint16_t nbits, - bool strict) { +bool IRrecv::decodePanasonicAC(decode_results *results, const uint16_t nbits, + const bool strict) { if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. return false; diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.h b/lib/IRremoteESP8266/src/ir_Panasonic.h index 9d9da38..27cdb59 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266/src/ir_Panasonic.h @@ -1,5 +1,24 @@ // Copyright 2018 David Conran +// Supports: +// Brand: Panasonic, Model: TV +// Brand: Panasonic, Model: JKE series A/C +// Brand: Panasonic, Model: DKE series A/C +// Brand: Panasonic, Model: CKP series A/C +// Brand: Panasonic, Model: CS-ME10CKPG A/C +// Brand: Panasonic, Model: CS-ME12CKPG A/C +// Brand: Panasonic, Model: CS-ME14CKPG A/C +// Brand: Panasonic, Model: RKR series A/C +// Brand: Panasonic, Model: CS-Z9RKR A/C +// Brand: Panasonic, Model: NKE series A/C +// Brand: Panasonic, Model: CS-YW9MKD A/C +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3704 remote +// Brand: Panasonic, Model: A75C2311 remote (CKP) +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3747 remote + #ifndef IR_PANASONIC_H_ #define IR_PANASONIC_H_ @@ -7,17 +26,12 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC -// P P A A NN N A A S O O NN N I C -// PPPP AAAAA N N N AAAAA SSS O O N N N I C -// P A A N NN A A S O O N NN I C -// P A A N N A A SSSS OOO N N IIIII CCCC +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Panasonic A/C support heavily influenced by: // https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp @@ -43,7 +57,7 @@ const uint8_t kPanasonicAcMaxTemp = 30; // Celsius const uint8_t kPanasonicAcFanModeTemp = 27; // Celsius const uint8_t kPanasonicAcQuiet = 1; // 0b1 const uint8_t kPanasonicAcPowerful = 0x20; // 0b100000 -// CKP models have Powerful and Quiet bits swapped. +// CKP & RKR models have Powerful and Quiet bits swapped. const uint8_t kPanasonicAcQuietCkp = 0x20; // 0b100000 const uint8_t kPanasonicAcPowerfulCkp = 1; // 0b1 const uint8_t kPanasonicAcSwingVAuto = 0xF; @@ -73,65 +87,74 @@ enum panasonic_ac_remote_model_t { kPanasonicDke = 3, kPanasonicJke = 4, kPanasonicCkp = 5, + kPanasonicRkr = 6, }; class IRPanasonicAc { public: - explicit IRPanasonicAc(uint16_t pin); + explicit IRPanasonicAc(const uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_PANASONIC void send(const uint16_t repeat = kPanasonicAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_PANASONIC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp, const bool remember = true); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t fan); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setRaw(const uint8_t state[]); - uint8_t *getRaw(); + uint8_t *getRaw(void); static bool validChecksum(uint8_t *state, const uint16_t length = kPanasonicAcStateLength); static uint8_t calcChecksum(uint8_t *state, const uint16_t length = kPanasonicAcStateLength); - void setQuiet(const bool state); - bool getQuiet(); - void setPowerful(const bool state); - bool getPowerful(); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); void setModel(const panasonic_ac_remote_model_t model); - panasonic_ac_remote_model_t getModel(); + panasonic_ac_remote_model_t getModel(void); void setSwingVertical(const uint8_t elevation); - uint8_t getSwingVertical(); + uint8_t getSwingVertical(void); void setSwingHorizontal(const uint8_t direction); - uint8_t getSwingHorizontal(); + uint8_t getSwingHorizontal(void); static uint16_t encodeTime(const uint8_t hours, const uint8_t mins); - uint16_t getClock(); + uint16_t getClock(void); void setClock(const uint16_t mins_since_midnight); - uint16_t getOnTimer(); + uint16_t getOnTimer(void); void setOnTimer(const uint16_t mins_since_midnight, const bool enable = true); - void cancelOnTimer(); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); + void cancelOnTimer(void); + bool isOnTimerEnabled(void); + uint16_t getOffTimer(void); void setOffTimer(const uint16_t mins_since_midnight, const bool enable = true); - void cancelOffTimer(); - bool isOffTimerEnabled(); -#ifdef ARDUINO - String toString(); + void cancelOffTimer(void); + bool isOffTimerEnabled(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + uint8_t convertSwingH(const stdAc::swingh_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); static String timeToString(const uint16_t mins_since_midnight); -#else - std::string toString(); - static std::string timeToString(const uint16_t mins_since_midnight); -#endif #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint8_t remote_state[kPanasonicAcStateLength]; uint8_t _swingh; @@ -139,7 +162,6 @@ class IRPanasonicAc { void fixChecksum(const uint16_t length = kPanasonicAcStateLength); static uint8_t calcChecksum(const uint8_t *state, const uint16_t length = kPanasonicAcStateLength); - IRsend _irsend; }; #endif // IR_PANASONIC_H_ diff --git a/lib/IRremoteESP8266/src/ir_Pioneer.cpp b/lib/IRremoteESP8266/src/ir_Pioneer.cpp index 9134e36..dbb4d46 100644 --- a/lib/IRremoteESP8266/src/ir_Pioneer.cpp +++ b/lib/IRremoteESP8266/src/ir_Pioneer.cpp @@ -2,6 +2,8 @@ // Copyright 2017, 2018 David Conran // Copyright 2018 Kamil Palczewski +// Pioneer remote emulation + #define __STDC_LIMIT_MACROS #include #include @@ -10,12 +12,6 @@ #include "IRutils.h" #include "ir_NEC.h" -// PPPP III OOO N N EEEE EEEE RRRR -// P P I O O NN N E E R R -// PPPP I O O N N N EEE EEE RRRR -// P I O O N NN E E R R -// P III OOO N N EEEE EEEE R RR - // Ref: // http://adrian-kingston.com/IRFormatPioneer.htm diff --git a/lib/IRremoteESP8266/src/ir_Pronto.cpp b/lib/IRremoteESP8266/src/ir_Pronto.cpp index 9ab5c76..a408afe 100644 --- a/lib/IRremoteESP8266/src/ir_Pronto.cpp +++ b/lib/IRremoteESP8266/src/ir_Pronto.cpp @@ -1,14 +1,10 @@ // Copyright 2017 David Conran +// Pronto code message generation + #include #include "IRsend.h" -// PPPPPP tt -// PP PP rr rr oooo nn nnn tt oooo -// PPPPPP rrr r oo oo nnn nn tttt oo oo -// PP rr oo oo nn nn tt oo oo -// PP rr oooo nn nn tttt oooo - // Constants const float kProntoFreqFactor = 0.241246; const uint16_t kProntoTypeOffset = 0; diff --git a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp index ef1500d..b794166 100644 --- a/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp +++ b/lib/IRremoteESP8266/src/ir_RC5_RC6.cpp @@ -1,21 +1,15 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote +// RC-5X support added by David Conran + #include #include "IRrecv.h" #include "IRsend.h" #include "IRtimer.h" #include "IRutils.h" -// RRRRRR CCCCC 555555 XX XX RRRRRR CCCCC 666 -// RR RR CC C 55 XX XX RR RR CC C 66 -// RRRRRR CC _____ 555555 XXXX RRRRRR CC _____ 666666 -// RR RR CC C 5555 XX XX RR RR CC C 66 66 -// RR RR CCCCC 555555 XX XX RR RR CCCCC 66666 - -// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote -// RC-5X support added by David Conran - // Constants // RC-5/RC-5X // Ref: diff --git a/lib/IRremoteESP8266/src/ir_RCMM.cpp b/lib/IRremoteESP8266/src/ir_RCMM.cpp index 1b03d2c..cbc5e40 100644 --- a/lib/IRremoteESP8266/src/ir_RCMM.cpp +++ b/lib/IRremoteESP8266/src/ir_RCMM.cpp @@ -1,19 +1,16 @@ // Copyright 2017 David Conran +// Send & decode support for Phillips RC-MM added by David Conran + +// Supports: +// Brand: Microsoft, Model: XBOX 360 + #include #include "IRrecv.h" #include "IRsend.h" #include "IRtimer.h" #include "IRutils.h" -// RRRRRR CCCCC MM MM MM MM -// RR RR CC C MMM MMM MMM MMM -// RRRRRR CC _____ MM MM MM MM MM MM -// RR RR CC C MM MM MM MM -// RR RR CCCCC MM MM MM MM - -// Send & decode support for RC-MM added by David Conran - // Constants // Ref: // http://www.sbprojects.com/knowledge/ir/rcmm.php diff --git a/lib/IRremoteESP8266/src/ir_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp index 6c90e74..03d2694 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266/src/ir_Samsung.cpp @@ -1,6 +1,8 @@ // Copyright 2009 Ken Shirriff // Copyright 2017, 2018, 2019 David Conran +// Samsung remote emulation + #include "ir_Samsung.h" #include #ifndef ARDUINO @@ -10,12 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// SSSS AAA MMM SSSS U U N N GGGG -// S A A M M M S U U NN N G -// SSS AAAAA M M M SSS U U N N N G GG -// S A A M M S U U N NN G G -// SSSS A A M M SSSS UUU N N GGG - // Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ // Constants @@ -68,7 +64,8 @@ const uint16_t kSamsungAcZeroSpace = 436; // Status: BETA / Should be working. // // Ref: http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendSAMSUNG(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, kSamsungBitMark, kSamsungOneSpace, kSamsungBitMark, kSamsungZeroSpace, kSamsungBitMark, kSamsungMinGap, kSamsungMinMessageLength, data, @@ -85,11 +82,11 @@ void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) { // A raw 32-bit Samsung message suitable for sendSAMSUNG(). // // Status: BETA / Should be working. -uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) { - customer = reverseBits(customer, sizeof(customer) * 8); - command = reverseBits(command, sizeof(command) * 8); - return ((command ^ 0xFF) | (command << 8) | (customer << 16) | - (customer << 24)); +uint32_t IRsend::encodeSAMSUNG(const uint8_t customer, const uint8_t command) { + uint8_t revcustomer = reverseBits(customer, sizeof(customer) * 8); + uint8_t revcommand = reverseBits(command, sizeof(command) * 8); + return ((revcommand ^ 0xFF) | (revcommand << 8) | (revcustomer << 16) | + (revcustomer << 24)); } #endif @@ -113,8 +110,8 @@ uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) { // They differ on their compliance criteria and how they repeat. // Ref: // http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeSAMSUNG(decode_results *results, const uint16_t nbits, + const bool strict) { if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid Samsung message. if (strict && nbits != kSamsungBits) @@ -297,11 +294,12 @@ bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, // nbytes: Nr. of bytes of data in the array. (>=kSamsungAcStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: Stable / Known working. // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/505 -void IRsend::sendSamsungAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kSamsungAcStateLength && nbytes % kSamsungACSectionLength) return; // Not an appropriate number of bytes to send a proper message. @@ -325,9 +323,11 @@ void IRsend::sendSamsungAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { } #endif // SEND_SAMSUNG_AC -IRSamsungAc::IRSamsungAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRSamsungAc::IRSamsungAc(const uint16_t pin) : _irsend(pin) { + this->stateReset(); +} -void IRSamsungAc::stateReset() { +void IRSamsungAc::stateReset(void) { for (uint8_t i = 0; i < kSamsungAcExtendedStateLength; i++) remote_state[i] = 0x0; remote_state[0] = 0x02; @@ -340,9 +340,10 @@ void IRSamsungAc::stateReset() { remote_state[10] = 0x71; remote_state[12] = 0x15; remote_state[13] = 0xF0; + _sendpower = false; } -void IRSamsungAc::begin() { _irsend.begin(); } +void IRSamsungAc::begin(void) { _irsend.begin(); } uint8_t IRSamsungAc::calcChecksum(const uint8_t state[], const uint16_t length) { @@ -364,35 +365,41 @@ bool IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) { return true; // No checksum to compare with. Assume okay. uint8_t offset = 0; if (length >= kSamsungAcExtendedStateLength) offset = 7; - return ((state[length - 6] >> 4) == calcChecksum(state, length) && - (state[length - (13 + offset)] >> 4) == calcChecksum(state, length - - (7 + offset))); + return ((state[length - 6] >> 4) == IRSamsungAc::calcChecksum(state, length) + && (state[length - (13 + offset)] >> 4) == IRSamsungAc::calcChecksum( + state, length - (7 + offset))); } // Update the checksum for the internal state. void IRSamsungAc::checksum(uint16_t length) { if (length < 13) return; remote_state[length - 6] &= 0x0F; - remote_state[length - 6] |= (calcChecksum(remote_state, length) << 4); + remote_state[length - 6] |= (this->calcChecksum(remote_state, length) << 4); remote_state[length - 13] &= 0x0F; - remote_state[length - 13] |= (calcChecksum(remote_state, length - 7) << 4); + remote_state[length - 13] |= (this->calcChecksum(remote_state, + length - 7) << 4); } #if SEND_SAMSUNG_AC // Use for most function/mode/settings changes to the unit. // i.e. When the device is already running. void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); + if (_sendpower) { // Do we need to send a the special power on/off message? + if (this->getPower()) + this->sendOn(); + else + this->sendOff(); + _sendpower = false; // It's now been sent. + } _irsend.sendSamsungAC(remote_state, kSamsungAcStateLength, repeat); } -#endif // SEND_SAMSUNG_AC -#if SEND_SAMSUNG_AC // Use this for when you need to power on/off the device. // Samsung A/C requires an extended length message when you want to // change the power operating mode of the A/C unit. void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); uint8_t extended_state[kSamsungAcExtendedStateLength] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, @@ -407,10 +414,32 @@ void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { // Send it. _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); } + +// Send the special extended "On" message as the library can't seem to reproduce +// this message automatically. +// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOn(const uint16_t repeat) { + const uint8_t extended_state[21] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); +} + +// Send the special extended "Off" message as the library can't seem to +// reproduce this message automatically. +// See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 +void IRSamsungAc::sendOff(const uint16_t repeat) { + const uint8_t extended_state[21] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; + _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); +} #endif // SEND_SAMSUNG_AC -uint8_t *IRSamsungAc::getRaw() { - checksum(); +uint8_t *IRSamsungAc::getRaw(void) { + this->checksum(); return remote_state; } @@ -425,26 +454,28 @@ void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { } } -void IRSamsungAc::on() { - remote_state[1] &= ~kSamsungAcPowerMask1; - remote_state[6] |= kSamsungAcPowerMask2; +void IRSamsungAc::on(void) { + remote_state[1] &= ~kSamsungAcPowerMask1; // Bit needs to be cleared. + remote_state[6] |= kSamsungAcPowerMask6; // Bit needs to be set. + _sendpower = true; // Flag that we need to send the special power message(s). } -void IRSamsungAc::off() { - remote_state[1] |= kSamsungAcPowerMask1; - remote_state[6] &= ~kSamsungAcPowerMask2; +void IRSamsungAc::off(void) { + remote_state[1] |= kSamsungAcPowerMask1; // Bit needs to be set. + remote_state[6] &= ~kSamsungAcPowerMask6; // Bit needs to be cleared. + _sendpower = true; // Flag that we need to send the special power message(s). } -void IRSamsungAc::setPower(const bool state) { - if (state) - on(); +void IRSamsungAc::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -bool IRSamsungAc::getPower() { - return ((remote_state[6] & kSamsungAcPowerMask2) != 0) && - ((remote_state[1] & kSamsungAcPowerMask1) == 0); +bool IRSamsungAc::getPower(void) { + return (remote_state[6] & kSamsungAcPowerMask6) && + !(remote_state[1] & kSamsungAcPowerMask1); } // Set the temp. in deg C @@ -456,7 +487,7 @@ void IRSamsungAc::setTemp(const uint8_t temp) { } // Return the set temp. in deg C -uint8_t IRSamsungAc::getTemp() { +uint8_t IRSamsungAc::getTemp(void) { return ((remote_state[11] & kSamsungAcTempMask) >> 4) + kSamsungAcMinTemp; } @@ -468,14 +499,15 @@ void IRSamsungAc::setMode(const uint8_t mode) { // Auto mode has a special fan setting valid only in auto mode. if (newmode == kSamsungAcAuto) { - setFan(kSamsungAcFanAuto2); + this->setFan(kSamsungAcFanAuto2); } else { - if (getFan() == kSamsungAcFanAuto2) // Non-Auto can't have this fan setting - setFan(kSamsungAcFanAuto); // Default to something safe. + // Non-Auto can't have this fan setting + if (this->getFan() == kSamsungAcFanAuto2) + this->setFan(kSamsungAcFanAuto); // Default to something safe. } } -uint8_t IRSamsungAc::getMode() { +uint8_t IRSamsungAc::getMode(void) { return (remote_state[12] & kSamsungAcModeMask) >> 4; } @@ -486,10 +518,10 @@ void IRSamsungAc::setFan(const uint8_t speed) { case kSamsungAcFanMed: case kSamsungAcFanHigh: case kSamsungAcFanTurbo: - if (getMode() == kSamsungAcAuto) return; // Not valid in Auto mode. + if (this->getMode() == kSamsungAcAuto) return; // Not valid in Auto mode. break; case kSamsungAcFanAuto2: // Special fan setting for when in Auto mode. - if (getMode() != kSamsungAcAuto) return; + if (this->getMode() != kSamsungAcAuto) return; break; default: return; @@ -497,42 +529,44 @@ void IRSamsungAc::setFan(const uint8_t speed) { remote_state[12] = (remote_state[12] & ~kSamsungAcFanMask) | (speed << 1); } -uint8_t IRSamsungAc::getFan() { +uint8_t IRSamsungAc::getFan(void) { return ((remote_state[12] & kSamsungAcFanMask) >> 1); } -bool IRSamsungAc::getSwing() { +bool IRSamsungAc::getSwing(void) { // TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1. // e.g. 0xAE or 0XAF for swing move. return ((remote_state[9] & kSamsungAcSwingMask) >> 4) == kSamsungAcSwingMove; } -void IRSamsungAc::setSwing(const bool state) { +void IRSamsungAc::setSwing(const bool on) { // TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1. // e.g. 0xAE or 0XAF for swing move. remote_state[9] &= ~kSamsungAcSwingMask; // Clear the previous swing state. - if (state) + if (on) remote_state[9] |= (kSamsungAcSwingMove << 4); else remote_state[9] |= (kSamsungAcSwingStop << 4); } -bool IRSamsungAc::getBeep() { return remote_state[13] & kSamsungAcBeepMask; } +bool IRSamsungAc::getBeep(void) { + return remote_state[13] & kSamsungAcBeepMask; +} -void IRSamsungAc::setBeep(const bool state) { - if (state) +void IRSamsungAc::setBeep(const bool on) { + if (on) remote_state[13] |= kSamsungAcBeepMask; else remote_state[13] &= ~kSamsungAcBeepMask; } -bool IRSamsungAc::getClean() { +bool IRSamsungAc::getClean(void) { return (remote_state[10] & kSamsungAcCleanMask10) && (remote_state[11] & kSamsungAcCleanMask11); } -void IRSamsungAc::setClean(const bool state) { - if (state) { +void IRSamsungAc::setClean(const bool on) { + if (on) { remote_state[10] |= kSamsungAcCleanMask10; remote_state[11] |= kSamsungAcCleanMask11; } else { @@ -541,97 +575,206 @@ void IRSamsungAc::setClean(const bool state) { } } -// Very unsure this is correct. -bool IRSamsungAc::getQuiet() { - return remote_state[11] & kSamsungAcQuietMask11; +bool IRSamsungAc::getQuiet(void) { + return !(remote_state[1] & kSamsungAcQuietMask1) && + (remote_state[5] & kSamsungAcQuietMask5); } -// Very unsure this is correct. -void IRSamsungAc::setQuiet(const bool state) { - if (state) { - remote_state[11] |= kSamsungAcQuietMask11; - setFan(kSamsungAcFanAuto); // Quiet mode seems to set fan speed to auto. +void IRSamsungAc::setQuiet(const bool on) { + if (on) { + remote_state[1] &= ~kSamsungAcQuietMask1; // Bit needs to be cleared. + remote_state[5] |= kSamsungAcQuietMask5; // Bit needs to be set. + // Quiet mode seems to set fan speed to auto. + this->setFan(kSamsungAcFanAuto); + this->setPowerful(false); // Quiet 'on' is mutually exclusive to Powerful. } else { - remote_state[11] &= ~kSamsungAcQuietMask11; + remote_state[1] |= kSamsungAcQuietMask1; // Bit needs to be set. + remote_state[5] &= ~kSamsungAcQuietMask5; // Bit needs to be cleared. } } +bool IRSamsungAc::getPowerful(void) { + return !(remote_state[8] & kSamsungAcPowerfulMask8) && + (remote_state[10] & kSamsungAcPowerfulMask10) && + this->getFan() == kSamsungAcFanTurbo; +} + +void IRSamsungAc::setPowerful(const bool on) { + if (on) { + remote_state[8] &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared. + remote_state[10] |= kSamsungAcPowerfulMask10; // Bit needs to be set. + // Powerful mode sets fan speed to Turbo. + this->setFan(kSamsungAcFanTurbo); + this->setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet. + } else { + remote_state[8] |= kSamsungAcPowerfulMask8; // Bit needs to be set. + remote_state[10] &= ~kSamsungAcPowerfulMask10; // Bit needs to be cleared. + // Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode + if (this->getFan() == kSamsungAcFanTurbo) this->setFan(kSamsungAcFanAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRSamsungAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kSamsungAcCool; + case stdAc::opmode_t::kHeat: + return kSamsungAcHeat; + case stdAc::opmode_t::kDry: + return kSamsungAcDry; + case stdAc::opmode_t::kFan: + return kSamsungAcFan; + default: + return kSamsungAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRSamsungAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kSamsungAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kSamsungAcFanMed; + case stdAc::fanspeed_t::kHigh: + return kSamsungAcFanHigh; + case stdAc::fanspeed_t::kMax: + return kSamsungAcFanTurbo; + default: + return kSamsungAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRSamsungAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSamsungAcCool: return stdAc::opmode_t::kCool; + case kSamsungAcHeat: return stdAc::opmode_t::kHeat; + case kSamsungAcDry: return stdAc::opmode_t::kDry; + case kSamsungAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRSamsungAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSamsungAcFanTurbo: return stdAc::fanspeed_t::kMax; + case kSamsungAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSamsungAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSamsungAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRSamsungAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::SAMSUNG_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + result.clean = this->getClean(); + result.beep = this->getBeep(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.econo = false; + result.filter = false; + result.light = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRSamsungAc::toString() { +String IRSamsungAc::toString(void) { String result = ""; -#else -std::string IRSamsungAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); if (getPower()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(getMode()); switch (getMode()) { case kSamsungAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kSamsungAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kSamsungAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kSamsungAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kSamsungAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (getFan()) { case kSamsungAcFanAuto: case kSamsungAcFanAuto2: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kSamsungAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kSamsungAcFanMed: - result += " (MED)"; + result += F(" (MED)"); break; case kSamsungAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kSamsungAcFanTurbo: - result += " (TURBO)"; + result += F(" (TURBO)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing: "; + result += F(", Swing: "); if (getSwing()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Beep: "; + result += F("Off"); + result += F(", Beep: "); if (getBeep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Clean: "; + result += F("Off"); + result += F(", Clean: "); if (getBeep()) - result += "On"; + result += F("On"); else - result += "Off"; - result += ", Quiet: "; + result += F("Off"); + result += F(", Quiet: "); if (getQuiet()) - result += "On"; + result += F("On"); else - result += "Off"; + result += F("Off"); + result += F(", Powerful: "); + if (getPowerful()) + result += F("On"); + else + result += F("Off"); return result; } @@ -645,12 +788,12 @@ std::string IRSamsungAc::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Appears to mostly work. +// Status: Stable / Known to be working. // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/505 -bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeSamsungAC(decode_results *results, const uint16_t nbits, + const bool strict) { if (results->rawlen < 2 * nbits + kHeader * 3 + kFooter * 2 - 1) return false; // Can't possibly be a valid Samsung A/C message. if (nbits != kSamsungAcBits && nbits != kSamsungAcExtendedBits) return false; diff --git a/lib/IRremoteESP8266/src/ir_Samsung.h b/lib/IRremoteESP8266/src/ir_Samsung.h index e8bcc4c..aa417e4 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -9,17 +9,17 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif -// SSSS AAA MMM SSSS U U N N GGGG -// S A A M M M S U U NN N G -// SSS AAAAA M M M SSS U U N N N G GG -// S A A M M S U U N NN G G -// SSSS A A M M SSSS UUU N N GGG +// Supports: +// Brand: Samsung, Model: UA55H6300 TV +// Brand: Samsung, Model: IEC-R03 remote +// Brand: Samsung, Model: AR12KSFPEWQNET A/C // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/505 @@ -30,7 +30,7 @@ const uint8_t kSamsungAcCool = 1; const uint8_t kSamsungAcDry = 2; const uint8_t kSamsungAcFan = 3; const uint8_t kSamsungAcHeat = 4; -const uint8_t kSamsungAcModeMask = 0x70; +const uint8_t kSamsungAcModeMask = 0x70; // 0b01110000 const uint8_t kSamsungAcFanAuto = 0; const uint8_t kSamsungAcFanLow = 2; const uint8_t kSamsungAcFanMed = 4; @@ -40,17 +40,20 @@ const uint8_t kSamsungAcFanTurbo = 7; const uint8_t kSamsungAcMinTemp = 16; // 16C const uint8_t kSamsungAcMaxTemp = 30; // 30C const uint8_t kSamsungAcAutoTemp = 25; // 25C -const uint8_t kSamsungAcTempMask = 0xF0; -const uint8_t kSamsungAcPowerMask1 = 0x20; -const uint8_t kSamsungAcPowerMask2 = 0x30; -const uint8_t kSamsungAcFanMask = 0x0E; -const uint8_t kSamsungAcSwingMask = 0x70; +const uint8_t kSamsungAcTempMask = 0xF0; // 0b11110000 +const uint8_t kSamsungAcPowerMask1 = 0x20; // 0b00100000 +const uint8_t kSamsungAcPowerMask6 = 0x30; // 0b00110000 +const uint8_t kSamsungAcFanMask = 0x0E; // 0b00001110 +const uint8_t kSamsungAcSwingMask = 0x70; // 0b01110000 const uint8_t kSamsungAcSwingMove = 0b010; const uint8_t kSamsungAcSwingStop = 0b111; -const uint8_t kSamsungAcBeepMask = 0x02; -const uint8_t kSamsungAcCleanMask10 = 0x80; -const uint8_t kSamsungAcCleanMask11 = 0x02; -const uint8_t kSamsungAcQuietMask11 = 0x01; +const uint8_t kSamsungAcBeepMask = 0x02; // 0b00000010 +const uint8_t kSamsungAcCleanMask10 = 0x80; // 0b10000000 +const uint8_t kSamsungAcCleanMask11 = 0x02; // 0b00000010 +const uint8_t kSamsungAcQuietMask1 = 0x10; // 0b00010000 +const uint8_t kSamsungAcQuietMask5 = 0x20; // 0b00100000 +const uint8_t kSamsungAcPowerfulMask8 = 0x50; // 0b01010000 +const uint8_t kSamsungAcPowerfulMask10 = 0x06; // 0b00000110 const uint16_t kSamsungACSectionLength = 7; const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; @@ -58,52 +61,63 @@ const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; // Classes class IRSamsungAc { public: - explicit IRSamsungAc(uint16_t pin); + explicit IRSamsungAc(const uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_SAMSUNG_AC void send(const uint16_t repeat = kSamsungAcDefaultRepeat, const bool calcchecksum = true); void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat, const bool calcchecksum = true); + void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat); + void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_SAMSUNG_AC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); - void setSwing(const bool state); - bool getSwing(); - void setBeep(const bool state); - bool getBeep(); - void setClean(const bool state); - bool getClean(); - void setQuiet(const bool state); - bool getQuiet(); - uint8_t* getRaw(); + uint8_t getMode(void); + void setSwing(const bool on); + bool getSwing(void); + void setBeep(const bool on); + bool getBeep(void); + void setClean(const bool on); + bool getClean(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); + uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kSamsungAcStateLength); static bool validChecksum(const uint8_t state[], const uint16_t length = kSamsungAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kSamsungAcStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint8_t remote_state[kSamsungAcExtendedStateLength]; + bool _sendpower; // Hack to know when we need to send a special power mesg. void checksum(const uint16_t length = kSamsungAcStateLength); - IRsend _irsend; }; #endif // IR_SAMSUNG_H_ diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/IRremoteESP8266/src/ir_Sanyo.cpp index b2b4d78..b05e767 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.cpp +++ b/lib/IRremoteESP8266/src/ir_Sanyo.cpp @@ -6,12 +6,6 @@ #include "IRrecv.h" #include "IRsend.h" -// SSSS AAA N N Y Y OOO -// S A A NN N Y Y O O -// SSS AAAAA N N N Y O O -// S A A N NN Y O O -// SSSS A A N N Y OOO - // Sanyo SA 8650B originally added from: // https://github.com/shirriff/Arduino-IRremote/ // Sanyo LC7461 support originally by marcosamarinho diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp index ae1b59c..e162409 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.cpp +++ b/lib/IRremoteESP8266/src/ir_Sharp.cpp @@ -1,17 +1,17 @@ // Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran +// Copyright 2017, 2019 David Conran +// Sharp remote emulation + +#include "ir_Sharp.h" #include +#ifndef ARDUINO +#include +#endif #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// SSSS H H AAA RRRR PPPP -// S H H A A R R P P -// SSS HHHHH AAAAA RRRR PPPP -// S H H A A R R P -// SSSS H H A A R R P - // Equipment it seems compatible with: // * Sharp LC-52D62U // * @@ -59,7 +59,9 @@ const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; // http://lirc.sourceforge.net/remotes/sharp/GA538WJSA // http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf // http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -void IRsend::sendSharpRaw(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendSharpRaw(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + uint64_t tempdata = data; for (uint16_t i = 0; i <= repeat; i++) { // Protocol demands that the data be sent twice; once normally, // then with all but the address bits inverted. @@ -68,12 +70,12 @@ void IRsend::sendSharpRaw(uint64_t data, uint16_t nbits, uint16_t repeat) { for (uint8_t n = 0; n < 2; n++) { sendGeneric(0, 0, // No Header kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, data, nbits, 38, true, + kSharpBitMark, kSharpGap, tempdata, nbits, 38, true, 0, // Repeats are handled already. 33); // Invert the data per protocol. This is always called twice, so it's // retured to original upon exiting the inner loop. - data ^= kSharpToggleMask; + tempdata ^= kSharpToggleMask; } } } @@ -102,22 +104,22 @@ void IRsend::sendSharpRaw(uint64_t data, uint16_t nbits, uint16_t repeat) { // http://www.sbprojects.com/knowledge/ir/sharp.htm // http://lirc.sourceforge.net/remotes/sharp/GA538WJSA // http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -uint32_t IRsend::encodeSharp(uint16_t address, uint16_t command, - uint16_t expansion, uint16_t check, - bool MSBfirst) { +uint32_t IRsend::encodeSharp(const uint16_t address, const uint16_t command, + const uint16_t expansion, const uint16_t check, + const bool MSBfirst) { // Mask any unexpected bits. - address &= ((1 << kSharpAddressBits) - 1); - command &= ((1 << kSharpCommandBits) - 1); - expansion &= 1; - check &= 1; + uint16_t tempaddress = address & ((1 << kSharpAddressBits) - 1); + uint16_t tempcommand = command & ((1 << kSharpCommandBits) - 1); + uint16_t tempexpansion = expansion & 1; + uint16_t tempcheck = check & 1; if (!MSBfirst) { // Correct bit order if needed. - address = reverseBits(address, kSharpAddressBits); - command = reverseBits(command, kSharpCommandBits); + tempaddress = reverseBits(tempaddress, kSharpAddressBits); + tempcommand = reverseBits(tempcommand, kSharpCommandBits); } // Concatinate all the bits. - return (address << (kSharpCommandBits + 2)) | (command << 2) | - (expansion << 1) | check; + return (tempaddress << (kSharpCommandBits + 2)) | (tempcommand << 2) | + (tempexpansion << 1) | tempcheck; } // Send a Sharp message @@ -144,8 +146,8 @@ uint32_t IRsend::encodeSharp(uint16_t address, uint16_t command, // http://www.sbprojects.com/knowledge/ir/sharp.htm // http://lirc.sourceforge.net/remotes/sharp/GA538WJSA // http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -void IRsend::sendSharp(uint16_t address, uint16_t command, uint16_t nbits, - uint16_t repeat) { +void IRsend::sendSharp(const uint16_t address, uint16_t const command, + const uint16_t nbits, const uint16_t repeat) { sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); } #endif // (SEND_SHARP || SEND_DENON) @@ -172,8 +174,8 @@ void IRsend::sendSharp(uint16_t address, uint16_t command, uint16_t nbits, // http://www.sbprojects.com/knowledge/ir/sharp.php // http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf // http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -bool IRrecv::decodeSharp(decode_results *results, uint16_t nbits, bool strict, - bool expansion) { +bool IRrecv::decodeSharp(decode_results *results, const uint16_t nbits, + const bool strict, const bool expansion) { if (results->rawlen < 2 * nbits + kFooter - 1) return false; // Not enough entries to be a Sharp message. // Compliance @@ -265,3 +267,366 @@ bool IRrecv::decodeSharp(decode_results *results, uint16_t nbits, bool strict, return true; } #endif // (DECODE_SHARP || DECODE_DENON) + +#if SEND_SHARP_AC +// Send a Sharp A/C message. +// +// Args: +// data: An array of kSharpAcStateLength bytes containing the IR command. +// nbytes: Nr. of bytes of data to send. i.e. length of `data`. +// repeat: Nr. of times the message should be repeated. +// +// Status: Alpha / Untested. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/638 +// https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +void IRsend::sendSharpAc(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kSharpAcStateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, + data, nbytes, 38000, false, repeat, 50); +} +#endif // SEND_SHARP_AC + +IRSharpAc::IRSharpAc(const uint16_t pin) : _irsend(pin) { this->stateReset(); } + +void IRSharpAc::begin(void) { _irsend.begin(); } + +#if SEND_SHARP_AC +void IRSharpAc::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendSharpAc(remote, kSharpAcStateLength, repeat); +} +#endif // SEND_SHARP_AC + +// Calculate the checksum for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// The 4 bit checksum. +uint8_t IRSharpAc::calcChecksum(uint8_t state[], const uint16_t length) { + uint8_t xorsum = xorBytes(state, length - 1); + xorsum ^= (state[length - 1] & 0xF); + xorsum ^= xorsum >> 4; + return xorsum & 0xF; +} + +// Verify the checksums are valid for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRSharpAc::validChecksum(uint8_t state[], const uint16_t length) { + return (state[length - 1] >> 4) == IRSharpAc::calcChecksum(state, length); +} + +// Calculate and set the checksum values for the internal state. +void IRSharpAc::checksum(void) { + remote[kSharpAcStateLength - 1] &= 0x0F; + remote[kSharpAcStateLength - 1] |= this->calcChecksum(remote) << 4; +} + +void IRSharpAc::stateReset(void) { + static const uint8_t reset[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x01, 0x00, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + for (uint8_t i = 0; i < kSharpAcStateLength; i++) remote[i] = reset[i]; +} + +uint8_t *IRSharpAc::getRaw(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; +} + +void IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kSharpAcStateLength; i++) + remote[i] = new_code[i]; +} + +void IRSharpAc::on(void) { remote[kSharpAcBytePower] |= kSharpAcBitPower; } + +void IRSharpAc::off(void) { remote[kSharpAcBytePower] &= ~kSharpAcBitPower; } + +void IRSharpAc::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRSharpAc::getPower(void) { + return remote[kSharpAcBytePower] & kSharpAcBitPower; +} + +// Set the temp in deg C +void IRSharpAc::setTemp(const uint8_t temp) { + switch (this->getMode()) { + // Auto & Dry don't allow temp changes and have a special temp. + case kSharpAcAuto: + case kSharpAcDry: + remote[kSharpAcByteTemp] = 0; + remote[kSharpAcByteManual] = 0; // When in Dry/Auto this byte is 0. + return; + default: + remote[kSharpAcByteTemp] = 0xC0; + remote[kSharpAcByteManual] |= kSharpAcBitTempManual; + } + uint8_t degrees = std::max(temp, kSharpAcMinTemp); + degrees = std::min(degrees, kSharpAcMaxTemp); + remote[kSharpAcByteTemp] &= ~kSharpAcMaskTemp; + remote[kSharpAcByteTemp] |= (degrees - kSharpAcMinTemp); +} + +uint8_t IRSharpAc::getTemp(void) { + return (remote[kSharpAcByteTemp] & kSharpAcMaskTemp) + kSharpAcMinTemp; +} + +uint8_t IRSharpAc::getMode(void) { + return remote[kSharpAcByteMode] & kSharpAcMaskMode; +} + +void IRSharpAc::setMode(const uint8_t mode) { + const uint8_t special = 0x20; // Non-auto modes have this bit set. + remote[kSharpAcBytePower] |= special; + switch (mode) { + case kSharpAcAuto: + remote[kSharpAcBytePower] &= ~special; // Auto has this bit cleared. + // FALLTHRU + case kSharpAcDry: + this->setTemp(0); // Dry/Auto have no temp setting. + // FALLTHRU + case kSharpAcCool: + case kSharpAcHeat: + remote[kSharpAcByteMode] &= ~kSharpAcMaskMode; + remote[kSharpAcByteMode] |= mode; + + break; + default: + this->setMode(kSharpAcAuto); + } +} + +// Set the speed of the fan +void IRSharpAc::setFan(const uint8_t speed) { + remote[kSharpAcByteManual] |= kSharpAcBitFanManual; // Manual fan mode. + switch (speed) { + case kSharpAcFanAuto: + // Clear the manual fan bit. + remote[kSharpAcByteManual] &= ~kSharpAcBitFanManual; + // FALLTHRU + case kSharpAcFanMin: + case kSharpAcFanMed: + case kSharpAcFanHigh: + case kSharpAcFanMax: + remote[kSharpAcByteFan] &= ~kSharpAcMaskFan; + remote[kSharpAcByteFan] |= (speed << 4); + break; + default: + this->setFan(kSharpAcFanAuto); + } +} + +uint8_t IRSharpAc::getFan(void) { + return (remote[kSharpAcByteFan] & kSharpAcMaskFan) >> 4; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRSharpAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kSharpAcCool; + case stdAc::opmode_t::kHeat: + return kSharpAcHeat; + case stdAc::opmode_t::kDry: + return kSharpAcDry; + // No Fan mode. + default: + return kSharpAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRSharpAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kSharpAcFanMin; + case stdAc::fanspeed_t::kMedium: + return kSharpAcFanMed; + case stdAc::fanspeed_t::kHigh: + return kSharpAcFanHigh; + case stdAc::fanspeed_t::kMax: + return kSharpAcFanMax; + default: + return kSharpAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRSharpAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSharpAcCool: return stdAc::opmode_t::kCool; + case kSharpAcHeat: return stdAc::opmode_t::kHeat; + case kSharpAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRSharpAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kSharpAcFanMax: return stdAc::fanspeed_t::kMax; + case kSharpAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSharpAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSharpAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRSharpAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::SHARP_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.clean = false; + result.beep = false; + result.econo = false; + result.filter = false; + result.light = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRSharpAc::toString(void) { + String result = ""; + result.reserve(60); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += this->getPower() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kSharpAcAuto: + result += F(" (AUTO)"); + break; + case kSharpAcCool: + result += F(" (COOL)"); + break; + case kSharpAcHeat: + result += F(" (HEAT)"); + break; + case kSharpAcDry: + result += F(" (DRY)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { + case kSharpAcFanAuto: + result += F(" (AUTO)"); + break; + case kSharpAcFanMin: + result += F(" (MIN)"); + break; + case kSharpAcFanMed: + result += F(" (MED)"); + break; + case kSharpAcFanHigh: + result += F(" (HIGH)"); + break; + case kSharpAcFanMax: + result += F(" (MAX)"); + break; + } + return result; +} + +#if DECODE_SHARP_AC +// Decode the supplied Sharp A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kSharpAcBits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Should be working. +// +// Ref: +// https://github.com/markszabo/IRremoteESP8266/issues/638 +// https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp +bool IRrecv::decodeSharpAc(decode_results *results, const uint16_t nbits, + const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) + return false; + + // Compliance + if (strict && nbits != kSharpAcBits) return false; + + uint16_t offset = kStartOffset; + match_result_t data_result; + uint16_t dataBitsSoFar = 0; + // Header + if (!matchMark(results->rawbuf[offset++], kSharpAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kSharpAcHdrSpace)) return false; + + // Data + // Keep reading bytes until we run out of state to fill. + for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits; + i++, dataBitsSoFar += 8, offset += data_result.used) { + // Read in a byte at a time. + data_result = + matchData(&(results->rawbuf[offset]), 8, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kTolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = (uint8_t)data_result.data; + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kSharpAcBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kSharpAcGap)) + return false; + + // Compliance + if (strict) { + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != kSharpAcBits) return false; + if (!IRSharpAc::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = SHARP_AC; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_SHARP_AC diff --git a/lib/IRremoteESP8266/src/ir_Sharp.h b/lib/IRremoteESP8266/src/ir_Sharp.h new file mode 100644 index 0000000..8bcdc2d --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sharp.h @@ -0,0 +1,98 @@ +// Copyright 2019 crankyoldgit + +// Supports: +// Brand: Sharp, Model: LC-52D62U TV +// Brand: Sharp, Model: AY-ZP40KR A/C + +#ifndef IR_SHARP_H_ +#define IR_SHARP_H_ + +#ifndef UNIT_TEST +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants +const uint16_t kSharpAcHdrMark = 3800; +const uint16_t kSharpAcHdrSpace = 1900; +const uint16_t kSharpAcBitMark = 470; +const uint16_t kSharpAcZeroSpace = 500; +const uint16_t kSharpAcOneSpace = 1400; +const uint32_t kSharpAcGap = kDefaultMessageGap; + +const uint8_t kSharpAcAuto = 0b000; +const uint8_t kSharpAcDry = 0b011; +const uint8_t kSharpAcCool = 0b010; +const uint8_t kSharpAcHeat = 0b001; +const uint8_t kSharpAcMinTemp = 15; // Celsius +const uint8_t kSharpAcMaxTemp = 30; // Celsius +const uint8_t kSharpAcFanAuto = 0b010; // 2 +const uint8_t kSharpAcFanMin = 0b100; // 4 (FAN1) +const uint8_t kSharpAcFanMed = 0b011; // 3 (FAN2) +const uint8_t kSharpAcFanHigh = 0b101; // 5 (FAN3) +const uint8_t kSharpAcFanMax = 0b111; // 7 (FAN4) +const uint8_t kSharpAcByteTemp = 4; +const uint8_t kSharpAcMaskTemp = 0b00001111; +const uint8_t kSharpAcBytePower = 5; +const uint8_t kSharpAcBitPower = 0b00010000; +const uint8_t kSharpAcByteMode = 6; +const uint8_t kSharpAcMaskMode = 0b00000011; +const uint8_t kSharpAcByteFan = kSharpAcByteMode; +const uint8_t kSharpAcMaskFan = 0b01110000; +const uint8_t kSharpAcByteManual = 10; +const uint8_t kSharpAcBitFanManual = 0b00000001; +const uint8_t kSharpAcBitTempManual = 0b00000100; + + +class IRSharpAc { + public: + explicit IRSharpAc(const uint16_t pin); + +#if SEND_SHARP_AC + void send(const uint16_t repeat = kSharpAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_SHARP_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kSharpAcStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kSharpAcStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); + static String renderTime(const uint16_t timemins); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote[kSharpAcStateLength]; + void stateReset(void); + void checksum(void); + static uint8_t calcChecksum(uint8_t state[], + const uint16_t length = kSharpAcStateLength); +}; + +#endif // IR_SHARP_H_ diff --git a/lib/IRremoteESP8266/src/ir_Sherwood.cpp b/lib/IRremoteESP8266/src/ir_Sherwood.cpp index 8af7dfb..47c6790 100644 --- a/lib/IRremoteESP8266/src/ir_Sherwood.cpp +++ b/lib/IRremoteESP8266/src/ir_Sherwood.cpp @@ -1,14 +1,14 @@ // Copyright 2017 David Conran +// Sherwood IR remote emulation + +// Supports: +// Brand: Sherwood, Model: RC-138 remote +// Brand: Sherwood, Model: RD6505(B) Receiver + #include #include "IRsend.h" -// SSSSS HH HH EEEEEEE RRRRRR WW WW OOOOO OOOOO DDDDD -// SS HH HH EE RR RR WW WW OO OO OO OO DD DD -// SSSSS HHHHHHH EEEEE RRRRRR WW W WW OO OO OO OO DD DD -// SS HH HH EE RR RR WW WWW WW OO OO OO OO DD DD -// SSSSS HH HH EEEEEEE RR RR WW WW OOOO0 OOOO0 DDDDDD - #if SEND_SHERWOOD // Send an IR command to a Sherwood device. // diff --git a/lib/IRremoteESP8266/src/ir_Sony.cpp b/lib/IRremoteESP8266/src/ir_Sony.cpp index efa6e6a..ba583a8 100644 --- a/lib/IRremoteESP8266/src/ir_Sony.cpp +++ b/lib/IRremoteESP8266/src/ir_Sony.cpp @@ -2,17 +2,13 @@ // Copyright 2016 marcosamarinho // Copyright 2017 David Conran +// Sony Remote Emulation + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// SSSS OOO N N Y Y -// S O O NN N Y Y -// SSS O O N N N Y -// S O O N NN Y -// SSSS OOO N N Y - // Sony originally added from https://github.com/shirriff/Arduino-IRremote/ // Updates from marcosamarinho diff --git a/lib/IRremoteESP8266/src/ir_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp index 39d6ba7..2c950d8 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.cpp +++ b/lib/IRremoteESP8266/src/ir_Tcl.cpp @@ -22,9 +22,9 @@ void IRsend::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, } #endif // SEND_TCL112AC -IRTcl112Ac::IRTcl112Ac(uint16_t pin) : _irsend(pin) { stateReset(); } +IRTcl112Ac::IRTcl112Ac(const uint16_t pin) : _irsend(pin) { stateReset(); } -void IRTcl112Ac::begin() { this->_irsend.begin(); } +void IRTcl112Ac::begin(void) { this->_irsend.begin(); } #if SEND_TCL112AC void IRTcl112Ac::send(const uint16_t repeat) { @@ -64,7 +64,7 @@ bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) { return (length > 1 && state[length - 1] == calcChecksum(state, length)); } -void IRTcl112Ac::stateReset() { +void IRTcl112Ac::stateReset(void) { for (uint8_t i = 0; i < kTcl112AcStateLength; i++) remote_state[i] = 0x0; // A known good state. (On, Cool, 24C) @@ -79,7 +79,7 @@ void IRTcl112Ac::stateReset() { remote_state[13] = 0x03; } -uint8_t* IRTcl112Ac::getRaw() { +uint8_t* IRTcl112Ac::getRaw(void) { this->checksum(); return remote_state; } @@ -112,19 +112,23 @@ bool IRTcl112Ac::getPower(void) { // Get the requested climate operation mode of the a/c unit. // Returns: // A uint8_t containing the A/C mode. -uint8_t IRTcl112Ac::getMode() { +uint8_t IRTcl112Ac::getMode(void) { return remote_state[6] & 0xF; } // Set the requested climate operation mode of the a/c unit. +// Note: Fan/Ventilation mode sets the fan speed to high. +// Unknown values default to Auto. void IRTcl112Ac::setMode(const uint8_t mode) { // If we get an unexpected mode, default to AUTO. switch (mode) { + case kTcl112AcFan: + this->setFan(kTcl112AcFanHigh); + // FALLTHRU case kTcl112AcAuto: case kTcl112AcCool: case kTcl112AcHeat: case kTcl112AcDry: - case kTcl112AcFan: remote_state[6] &= 0xF0; remote_state[6] |= mode; break; @@ -147,46 +151,253 @@ void IRTcl112Ac::setTemp(const float celsius) { remote_state[7] |= ((uint8_t)kTcl112AcTempMax - nrHalfDegrees / 2); } -float IRTcl112Ac::getTemp() { +float IRTcl112Ac::getTemp(void) { float result = kTcl112AcTempMax - (remote_state[7] & 0xF); if (remote_state[12] & kTcl112AcHalfDegree) result += 0.5; return result; } -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRTcl112Ac::toString() { - String result = ""; -#else -std::string IRTcl112Ac::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - result += (this->getPower() ? "On" : "Off"); - result += ", Mode: " + uint64ToString(getMode()); - switch (this->getMode()) { - case kTcl112AcAuto: - result += " (AUTO)"; - break; - case kTcl112AcCool: - result += " (COOL)"; - break; - case kTcl112AcHeat: - result += " (HEAT)"; - break; - case kTcl112AcDry: - result += " (DRY)"; - break; - case kTcl112AcFan: - result += " (FAN)"; +// Set the speed of the fan. +// Unknown speeds will default to Auto. +void IRTcl112Ac::setFan(const uint8_t speed) { + switch (speed) { + case kTcl112AcFanAuto: + case kTcl112AcFanLow: + case kTcl112AcFanMed: + case kTcl112AcFanHigh: + remote_state[8] &= ~kTcl112AcFanMask; + remote_state[8] |= speed; break; default: - result += " (UNKNOWN)"; + this->setFan(kTcl112AcFanAuto); + } +} + +// Return the currect fan speed. +uint8_t IRTcl112Ac::getFan(void) { + return remote_state[8] & kTcl112AcFanMask; +} + +// Control economy mode. +void IRTcl112Ac::setEcono(const bool on) { + if (on) + remote_state[5] |= kTcl112AcBitEcono; + else + remote_state[5] &= ~kTcl112AcBitEcono; +} + +// Return the economy state of the A/C. +bool IRTcl112Ac::getEcono(void) { + return remote_state[5] & kTcl112AcBitEcono; +} + +// Control Health mode. +void IRTcl112Ac::setHealth(const bool on) { + if (on) + remote_state[6] |= kTcl112AcBitHealth; + else + remote_state[6] &= ~kTcl112AcBitHealth; +} + +// Return the Health mode state of the A/C. +bool IRTcl112Ac::getHealth(void) { + return remote_state[6] & kTcl112AcBitHealth; +} + +// Control Light/Display mode. +void IRTcl112Ac::setLight(const bool on) { + if (on) + remote_state[5] &= ~kTcl112AcBitLight; + else + remote_state[5] |= kTcl112AcBitLight; +} + +// Return the Light/Display mode state of the A/C. +bool IRTcl112Ac::getLight(void) { + return !(remote_state[5] & kTcl112AcBitLight); +} + +// Control Horizontal Swing. +void IRTcl112Ac::setSwingHorizontal(const bool on) { + if (on) + remote_state[12] |= kTcl112AcBitSwingH; + else + remote_state[12] &= ~kTcl112AcBitSwingH; +} + +// Return the Horizontal Swing state of the A/C. +bool IRTcl112Ac::getSwingHorizontal(void) { + return remote_state[12] & kTcl112AcBitSwingH; +} + +// Control Vertical Swing. +void IRTcl112Ac::setSwingVertical(const bool on) { + if (on) + remote_state[8] |= kTcl112AcBitSwingV; + else + remote_state[8] &= ~kTcl112AcBitSwingV; +} + +// Return the Vertical Swing state of the A/C. +bool IRTcl112Ac::getSwingVertical(void) { + return remote_state[8] & kTcl112AcBitSwingV; +} + +// Control the Turbo setting. +void IRTcl112Ac::setTurbo(const bool on) { + if (on) { + remote_state[6] |= kTcl112AcBitTurbo; + this->setFan(kTcl112AcFanHigh); + this->setSwingVertical(true); + } else { + remote_state[6] &= ~kTcl112AcBitTurbo; + } +} + +// Return the Turbo setting state of the A/C. +bool IRTcl112Ac::getTurbo(void) { + return remote_state[6] & kTcl112AcBitTurbo; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRTcl112Ac::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTcl112AcCool; + case stdAc::opmode_t::kHeat: + return kTcl112AcHeat; + case stdAc::opmode_t::kDry: + return kTcl112AcDry; + case stdAc::opmode_t::kFan: + return kTcl112AcFan; + default: + return kTcl112AcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTcl112AcFanLow; + case stdAc::fanspeed_t::kMedium: + return kTcl112AcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTcl112AcFanHigh; + default: + return kTcl112AcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTcl112Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTcl112AcCool: return stdAc::opmode_t::kCool; + case kTcl112AcHeat: return stdAc::opmode_t::kHeat; + case kTcl112AcDry: return stdAc::opmode_t::kDry; + case kTcl112AcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax; + case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium; + case kTcl112AcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTcl112Ac::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TCL112AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.filter = this->getHealth(); + result.econo = this->getEcono(); + // Not supported. + result.quiet = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRTcl112Ac::toString(void) { + String result = ""; + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(getMode()); + switch (this->getMode()) { + case kTcl112AcAuto: + result += F(" (AUTO)"); + break; + case kTcl112AcCool: + result += F(" (COOL)"); + break; + case kTcl112AcHeat: + result += F(" (HEAT)"); + break; + case kTcl112AcDry: + result += F(" (DRY)"); + break; + case kTcl112AcFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); } uint16_t nrHalfDegrees = this->getTemp() * 2; - result += ", Temp: " + uint64ToString(nrHalfDegrees / 2); - if (nrHalfDegrees & 1) result += ".5"; - result += "C"; + result += F(", Temp: "); + result += uint64ToString(nrHalfDegrees / 2); + if (nrHalfDegrees & 1) result += F(".5"); + result += F("C, Fan: "); + result += uint64ToString(getFan()); + switch (getFan()) { + case kTcl112AcFanAuto: + result += F(" (Auto)"); + break; + case kTcl112AcFanLow: + result += F(" (Low)"); + break; + case kTcl112AcFanMed: + result += F(" (Med)"); + break; + case kTcl112AcFanHigh: + result += F(" (High)"); + break; + } + result += F(", Econo: "); + result += (this->getEcono() ? F("On") : F("Off")); + result += ", Health: "; + result += (this->getHealth() ? F("On") : F("Off")); + result += F(", Light: "); + result += (this->getLight() ? F("On") : F("Off")); + result += F(", Turbo: "); + result += (this->getTurbo() ? F("On") : F("Off")); + result += ", Swing (H): "; + result += (this->getSwingHorizontal() ? F("On") : F("Off")); + result += F(", Swing (V): "); + result += (this->getSwingVertical() ? F("On") : F("Off")); return result; } @@ -204,8 +415,8 @@ std::string IRTcl112Ac::toString() { // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/619 -bool IRrecv::decodeTcl112Ac(decode_results *results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeTcl112Ac(decode_results *results, const uint16_t nbits, + const bool strict) { if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; // Can't possibly be a valid Samsung A/C message. if (strict && nbits != kTcl112AcBits) return false; @@ -224,7 +435,7 @@ bool IRrecv::decodeTcl112Ac(decode_results *results, uint16_t nbits, i++, dataBitsSoFar += 8, offset += data_result.used) { data_result = matchData(&(results->rawbuf[offset]), 8, kTcl112AcBitMark, kTcl112AcOneSpace, kTcl112AcBitMark, - kTcl112AcZeroSpace, kTolerance, 0, false); + kTcl112AcZeroSpace, kTcl112AcTolerance, 0, false); if (data_result.success == false) { DPRINT("DEBUG: offset = "); DPRINTLN(offset + data_result.used); diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index 6bbdee1..a4dfea5 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -5,11 +5,13 @@ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#include "IRrecv.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint16_t kTcl112AcHdrMark = 3000; @@ -17,25 +19,40 @@ const uint16_t kTcl112AcHdrSpace = 1650; const uint16_t kTcl112AcBitMark = 500; const uint16_t kTcl112AcOneSpace = 1050; const uint16_t kTcl112AcZeroSpace = 325; -const uint32_t kTcl112AcGap = 100000; // Just a guess. +const uint32_t kTcl112AcGap = kDefaultMessageGap; // Just a guess. +const uint8_t kTcl112AcTolerance = kTolerance + 5; // Percent const uint8_t kTcl112AcHeat = 1; const uint8_t kTcl112AcDry = 2; const uint8_t kTcl112AcCool = 3; const uint8_t kTcl112AcFan = 7; const uint8_t kTcl112AcAuto = 8; +const uint8_t kTcl112AcFanMask = 0b00000111; +const uint8_t kTcl112AcFanAuto = 0b00000000; +const uint8_t kTcl112AcFanLow = 0b00000010; +const uint8_t kTcl112AcFanMed = 0b00000011; +const uint8_t kTcl112AcFanHigh = 0b00000101; const uint8_t kTcl112AcHalfDegree = 0b00100000; -const float kTcl112AcTempMax = 31.0; -const float kTcl112AcTempMin = 16.0; -const uint8_t kTcl112AcPowerMask = 0x04; +const float kTcl112AcTempMax = 31.0; +const float kTcl112AcTempMin = 16.0; + +const uint8_t kTcl112AcPowerMask = 0b00000100; +const uint8_t kTcl112AcBitEcono = 0b10000000; +const uint8_t kTcl112AcBitLight = 0b01000000; +const uint8_t kTcl112AcBitHealth = 0b00010000; +const uint8_t kTcl112AcBitSwingH = 0b00001000; +const uint8_t kTcl112AcBitSwingV = 0b00111000; +const uint8_t kTcl112AcBitTurbo = 0b01000000; + class IRTcl112Ac { public: - explicit IRTcl112Ac(uint16_t pin); + explicit IRTcl112Ac(const uint16_t pin); #if SEND_TCL112AC void send(const uint16_t repeat = kTcl112AcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TCL void begin(void); uint8_t* getRaw(void); @@ -53,17 +70,36 @@ class IRTcl112Ac { const uint16_t length = kTcl112AcStateLength); static bool validChecksum(uint8_t state[], const uint16_t length = kTcl112AcStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setEcono(const bool on); + bool getEcono(void); + void setHealth(const bool on); + bool getHealth(void); + void setLight(const bool on); + bool getLight(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setTurbo(const bool on); + bool getTurbo(void); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: - uint8_t remote_state[kTcl112AcStateLength]; - void stateReset(); - void checksum(const uint16_t length = kTcl112AcStateLength); IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kTcl112AcStateLength]; + void stateReset(void); + void checksum(const uint16_t length = kTcl112AcStateLength); }; #endif // IR_TCL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp index db73839..154e76d 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.cpp +++ b/lib/IRremoteESP8266/src/ir_Teco.cpp @@ -27,7 +27,8 @@ const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. // data: Contents of the message to be sent. // nbits: Nr. of bits of data to be sent. Typically kTecoBits. // repeat: Nr. of additional times the message is to be sent. -void IRsend::sendTeco(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendTeco(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace, kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap, data, nbits, 38000, false, repeat, kDutyDefault); @@ -35,9 +36,9 @@ void IRsend::sendTeco(uint64_t data, uint16_t nbits, uint16_t repeat) { #endif // SEND_TECO // Class for decoding and constructing Teco AC messages. -IRTecoAc::IRTecoAc(const uint16_t pin) : _irsend(pin) { stateReset(); } +IRTecoAc::IRTecoAc(const uint16_t pin) : _irsend(pin) { this->stateReset(); } -void IRTecoAc::begin() { _irsend.begin(); } +void IRTecoAc::begin(void) { _irsend.begin(); } #if SEND_TECO void IRTecoAc::send(const uint16_t repeat) { @@ -136,58 +137,136 @@ void IRTecoAc::setSleep(const bool on) { bool IRTecoAc::getSleep(void) { return remote_state & kTecoSleep; } +// Convert a standard A/C mode into its native mode. +uint8_t IRTecoAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTecoCool; + case stdAc::opmode_t::kHeat: + return kTecoHeat; + case stdAc::opmode_t::kDry: + return kTecoDry; + case stdAc::opmode_t::kFan: + return kTecoFan; + default: + return kTecoAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTecoFanLow; + case stdAc::fanspeed_t::kMedium: + return kTecoFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTecoFanHigh; + default: + return kTecoFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTecoAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTecoCool: return stdAc::opmode_t::kCool; + case kTecoHeat: return stdAc::opmode_t::kHeat; + case kTecoDry: return stdAc::opmode_t::kDry; + case kTecoFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTecoAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kTecoFanHigh: return stdAc::fanspeed_t::kMax; + case kTecoFanMed: return stdAc::fanspeed_t::kMedium; + case kTecoFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTecoAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TECO; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRTecoAc::toString(void) { String result = ""; -#else -std::string IRTecoAc::toString(void) { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - result += (this->getPower() ? "On" : "Off"); - result += ", Mode: " + uint64ToString(this->getMode()); + result.reserve(80); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); switch (this->getMode()) { case kTecoAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kTecoCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kTecoHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kTecoDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kTecoFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(this->getTemp()) + "C"; - result += ", Fan: " + uint64ToString(this->getFan()); + result += F(", Temp: "); + result += uint64ToString(getTemp()); + result += F("C, Fan: "); + result += uint64ToString(getFan()); switch (this->getFan()) { case kTecoFanAuto: - result += " (Auto)"; + result += F(" (Auto)"); break; case kTecoFanHigh: - result += " (High)"; + result += F(" (High)"); break; case kTecoFanLow: - result += " (Low)"; + result += F(" (Low)"); break; case kTecoFanMed: - result += " (Med)"; + result += F(" (Med)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Sleep: "; - result += (this->getSleep() ? "On" : "Off"); - result += ", Swing: "; - result += (this->getSwing() ? "On" : "Off"); + result += F(", Sleep: "); + result += (this->getSleep() ? F("On") : F("Off")); + result += F(", Swing: "); + result += (this->getSwing() ? F("On") : F("Off")); return result; } @@ -202,7 +281,8 @@ std::string IRTecoAc::toString(void) { // boolean: True if it can decode it, false if it can't. // // Status: STABLE / Tested. -bool IRrecv::decodeTeco(decode_results* results, uint16_t nbits, bool strict) { +bool IRrecv::decodeTeco(decode_results* results, + const uint16_t nbits, const bool strict) { // Check if can possibly be a valid Teco message. if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; if (strict && nbits != kTecoBits) return false; // Not what is expected diff --git a/lib/IRremoteESP8266/src/ir_Teco.h b/lib/IRremoteESP8266/src/ir_Teco.h index 9a020f9..5aedd6c 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.h +++ b/lib/IRremoteESP8266/src/ir_Teco.h @@ -5,11 +5,12 @@ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants. Using LSB to be able to send only 35bits. const uint8_t kTecoAuto = 0; // 0b000 @@ -120,16 +121,21 @@ class IRTecoAc { uint64_t getRaw(void); void setRaw(const uint64_t new_code); -#ifdef ARDUINO + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); String toString(void); -#else - std::string toString(void); -#endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif // The state of the IR remote in IR code form. uint64_t remote_state; - IRsend _irsend; }; #endif // IR_TECO_H_ diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.cpp b/lib/IRremoteESP8266/src/ir_Toshiba.cpp index 467745e..cb3dea5 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266/src/ir_Toshiba.cpp @@ -1,5 +1,8 @@ // Copyright 2017 David Conran +// Toshiba A/C support added by David Conran + + #include "ir_Toshiba.h" #include #ifndef ARDUINO @@ -9,13 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran // // Equipment it seems compatible with: // * Toshiba RAS-B13N3KV2 / Akita EVO II @@ -46,8 +42,8 @@ const uint16_t kToshibaAcMinGap = 7048; // // Status: StABLE / Working. // -void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendToshibaAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kToshibaACStateLength) return; // Not enough bytes to send a proper message. sendGeneric(kToshibaAcHdrMark, kToshibaAcHdrSpace, kToshibaAcBitMark, @@ -64,10 +60,12 @@ void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes, // Status: STABLE / Working. // // Initialise the object. -IRToshibaAC::IRToshibaAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRToshibaAC::IRToshibaAC(const uint16_t pin) : _irsend(pin) { + this->stateReset(); +} // Reset the state of the remote to a known good state/sequence. -void IRToshibaAC::stateReset() { +void IRToshibaAC::stateReset(void) { // The state of the IR remote in IR code form. // Known good state obtained from: // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 @@ -81,32 +79,32 @@ void IRToshibaAC::stateReset() { remote_state[4] = 0x01; for (uint8_t i = 5; i < kToshibaACStateLength; i++) remote_state[i] = 0; mode_state = remote_state[6] & 0b00000011; - checksum(); // Calculate the checksum + this->checksum(); // Calculate the checksum } // Configure the pin for output. -void IRToshibaAC::begin() { _irsend.begin(); } +void IRToshibaAC::begin(void) { _irsend.begin(); } #if SEND_TOSHIBA_AC // Send the current desired state to the IR LED. void IRToshibaAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. + this->checksum(); // Ensure correct checksum before sending. _irsend.sendToshibaAC(remote_state, kToshibaACStateLength, repeat); } #endif // SEND_TOSHIBA_AC // Return a pointer to the internal state date of the remote. -uint8_t* IRToshibaAC::getRaw() { - checksum(); +uint8_t* IRToshibaAC::getRaw(void) { + this->checksum(); return remote_state; } // Override the internal state with the new state. -void IRToshibaAC::setRaw(uint8_t newState[]) { +void IRToshibaAC::setRaw(const uint8_t newState[]) { for (uint8_t i = 0; i < kToshibaACStateLength; i++) { remote_state[i] = newState[i]; } - mode_state = getMode(true); + mode_state = this->getMode(true); } // Calculate the checksum for a given array. @@ -133,56 +131,59 @@ uint8_t IRToshibaAC::calcChecksum(const uint8_t state[], // Returns: // A boolean. bool IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) { - return (length > 1 && state[length - 1] == calcChecksum(state, length)); + return (length > 1 && state[length - 1] == IRToshibaAC::calcChecksum(state, + length)); } // Calculate & set the checksum for the current internal state of the remote. void IRToshibaAC::checksum(const uint16_t length) { // Stored the checksum value in the last byte. - if (length > 1) remote_state[length - 1] = calcChecksum(remote_state, length); + if (length > 1) remote_state[length - 1] = this->calcChecksum(remote_state, + length); } // Set the requested power state of the A/C to off. -void IRToshibaAC::on() { +void IRToshibaAC::on(void) { // state = ON; remote_state[6] &= ~kToshibaAcPower; setMode(mode_state); } // Set the requested power state of the A/C to off. -void IRToshibaAC::off() { +void IRToshibaAC::off(void) { // state = OFF; remote_state[6] |= (kToshibaAcPower | 0b00000011); } // Set the requested power state of the A/C. -void IRToshibaAC::setPower(bool state) { - if (state) - on(); +void IRToshibaAC::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } // Return the requested power state of the A/C. -bool IRToshibaAC::getPower() { +bool IRToshibaAC::getPower(void) { return ((remote_state[6] & kToshibaAcPower) == 0); } // Set the temp. in deg C -void IRToshibaAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kToshibaAcMinTemp, temp); +void IRToshibaAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kToshibaAcMinTemp, degrees); temp = std::min((uint8_t)kToshibaAcMaxTemp, temp); remote_state[5] = (temp - kToshibaAcMinTemp) << 4; } // Return the set temp. in deg C -uint8_t IRToshibaAC::getTemp() { +uint8_t IRToshibaAC::getTemp(void) { return ((remote_state[5] >> 4) + kToshibaAcMinTemp); } // Set the speed of the fan, 0-5. // 0 is auto, 1-5 is the speed, 5 is Max. -void IRToshibaAC::setFan(uint8_t fan) { +void IRToshibaAC::setFan(const uint8_t speed) { + uint8_t fan = speed; // Bounds check if (fan > kToshibaAcFanMax) fan = kToshibaAcFanMax; // Set the fan to maximum if out of range. @@ -192,7 +193,7 @@ void IRToshibaAC::setFan(uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRToshibaAC::getFan() { +uint8_t IRToshibaAC::getFan(void) { uint8_t fan = remote_state[6] >> 5; if (fan == kToshibaAcFanAuto) return kToshibaAcFanAuto; return --fan; @@ -203,7 +204,7 @@ uint8_t IRToshibaAC::getFan() { // useRaw: Indicate to get the mode from the state array. (Default: false) // Returns: // A uint8_t containing the A/C mode. -uint8_t IRToshibaAC::getMode(bool useRaw) { +uint8_t IRToshibaAC::getMode(const bool useRaw) { if (useRaw) return (remote_state[6] & 0b00000011); else @@ -211,66 +212,143 @@ uint8_t IRToshibaAC::getMode(bool useRaw) { } // Set the requested climate operation mode of the a/c unit. -void IRToshibaAC::setMode(uint8_t mode) { +void IRToshibaAC::setMode(const uint8_t mode) { // If we get an unexpected mode, default to AUTO. switch (mode) { case kToshibaAcAuto: - break; case kToshibaAcCool: - break; case kToshibaAcDry: - break; case kToshibaAcHeat: - break; + mode_state = mode; + // Only adjust the remote_state if we have power set to on. + if (getPower()) { + remote_state[6] &= 0b11111100; // Clear the previous mode. + remote_state[6] |= mode_state; + } + return; default: - mode = kToshibaAcAuto; - } - mode_state = mode; - // Only adjust the remote_state if we have power set to on. - if (getPower()) { - remote_state[6] &= 0b11111100; // Clear the previous mode. - remote_state[6] |= mode_state; + // THere is no Fan mode. + this->setMode(kToshibaAcAuto); } } +// Convert a standard A/C mode into its native mode. +uint8_t IRToshibaAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kToshibaAcCool; + case stdAc::opmode_t::kHeat: + return kToshibaAcHeat; + case stdAc::opmode_t::kDry: + return kToshibaAcDry; + // No Fan mode. + default: + return kToshibaAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRToshibaAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kToshibaAcFanMax - 4; + case stdAc::fanspeed_t::kLow: + return kToshibaAcFanMax - 3; + case stdAc::fanspeed_t::kMedium: + return kToshibaAcFanMax - 2; + case stdAc::fanspeed_t::kHigh: + return kToshibaAcFanMax - 1; + case stdAc::fanspeed_t::kMax: + return kToshibaAcFanMax; + default: + return kToshibaAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRToshibaAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kToshibaAcCool: return stdAc::opmode_t::kCool; + case kToshibaAcHeat: return stdAc::opmode_t::kHeat; + case kToshibaAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRToshibaAC::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kToshibaAcFanMax: return stdAc::fanspeed_t::kMax; + case kToshibaAcFanMax - 1: return stdAc::fanspeed_t::kHigh; + case kToshibaAcFanMax - 2: return stdAc::fanspeed_t::kMedium; + case kToshibaAcFanMax - 3: return stdAc::fanspeed_t::kLow; + case kToshibaAcFanMax - 4: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRToshibaAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TOSHIBA_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + // Not supported. + result.turbo = false; + result.light = false; + result.filter = false; + result.econo = false; + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRToshibaAC::toString() { +String IRToshibaAC::toString(void) { String result = ""; -#else -std::string IRToshibaAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; + result.reserve(40); + result += F("Power: "); + if (this->getPower()) + result += F("On"); else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { + result += F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kToshibaAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kToshibaAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kToshibaAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kToshibaAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { case kToshibaAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kToshibaAcFanMax: - result += " (MAX)"; + result += F(" (MAX)"); break; } return result; @@ -290,8 +368,8 @@ std::string IRToshibaAC::toString() { // // Ref: // -bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeToshibaAC(decode_results* results, const uint16_t nbits, + const bool strict) { uint16_t offset = kStartOffset; uint16_t dataBitsSoFar = 0; diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h index a6ea6c6..2518cc4 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -1,4 +1,15 @@ // Copyright 2017 David Conran + +// Toshiba A/C support added by David Conran + +// Supports: +// Brand: Toshiba, Model: RAS-B13N3KV2 +// Brand: Toshiba, Model: Akita EVO II +// Brand: Toshiba, Model: RAS-B13N3KVP-E +// Brand: Toshiba, Model: RAS 18SKP-ES +// Brand: Toshiba, Model: WH-TA04NE +// Brand: Toshiba, Model: WC-L03SE + #ifndef IR_TOSHIBA_H_ #define IR_TOSHIBA_H_ @@ -6,19 +17,12 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint8_t kToshibaAcAuto = 0; @@ -44,42 +48,46 @@ const uint8_t kToshibaAcMaxTemp = 30; // 30C class IRToshibaAC { public: - explicit IRToshibaAC(uint16_t pin); + explicit IRToshibaAC(const uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_TOSHIBA_AC void send(const uint16_t repeat = kToshibaACMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TOSHIBA_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(bool useRaw = false); - void setRaw(uint8_t newState[]); - uint8_t* getRaw(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(const bool useRaw = false); + void setRaw(const uint8_t newState[]); + uint8_t* getRaw(void); static bool validChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint8_t remote_state[kToshibaACStateLength]; void checksum(const uint16_t length = kToshibaACStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); uint8_t mode_state; - IRsend _irsend; }; #endif // IR_TOSHIBA_H_ diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp index 77d84ee..e16d608 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.cpp +++ b/lib/IRremoteESP8266/src/ir_Trotec.cpp @@ -1,127 +1,333 @@ // Copyright 2017 stufisher +// Copyright 2019 crankyoldgit #include "ir_Trotec.h" +#include +#ifndef UNIT_TEST +#include +#endif #include "IRremoteESP8266.h" #include "IRutils.h" // Constants const uint16_t kTrotecHdrMark = 5952; const uint16_t kTrotecHdrSpace = 7364; -const uint16_t kTrotecOneMark = 592; +const uint16_t kTrotecBitMark = 592; const uint16_t kTrotecOneSpace = 1560; -const uint16_t kTrotecZeroMark = 592; const uint16_t kTrotecZeroSpace = 592; const uint16_t kTrotecGap = 6184; const uint16_t kTrotecGapEnd = 1500; // made up value #if SEND_TROTEC -void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendTrotec(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kTrotecStateLength) return; for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark, - kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace, - kTrotecOneMark, kTrotecGap, data, nbytes, 36, false, + sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecBitMark, + kTrotecOneSpace, kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, data, nbytes, 36, false, 0, // Repeats handled elsewhere 50); // More footer enableIROut(36); - mark(kTrotecOneMark); + mark(kTrotecBitMark); space(kTrotecGapEnd); } } #endif // SEND_TROTEC -IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } +IRTrotecESP::IRTrotecESP(const uint16_t pin) : _irsend(pin) { + this->stateReset(); +} -void IRTrotecESP::begin() { _irsend.begin(); } +void IRTrotecESP::begin(void) { _irsend.begin(); } #if SEND_TROTEC void IRTrotecESP::send(const uint16_t repeat) { - checksum(); - _irsend.sendTrotec(trotec, kTrotecStateLength, repeat); + this->checksum(); + _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); } #endif // SEND_TROTEC -void IRTrotecESP::checksum() { - uint8_t sum = 0; - uint8_t i; - - for (i = 2; i < 8; i++) sum += trotec[i]; - - trotec[8] = sum & 0xFF; +uint8_t IRTrotecESP::calcChecksum(const uint8_t state[], + const uint16_t length) { + return sumBytes(state + 2, length - 3); } -void IRTrotecESP::stateReset() { - for (uint8_t i = 2; i < kTrotecStateLength; i++) trotec[i] = 0x0; - - trotec[0] = kTrotecIntro1; - trotec[1] = kTrotecIntro2; - - setPower(false); - setTemp(kTrotecDefTemp); - setSpeed(kTrotecFanMed); - setMode(kTrotecAuto); +bool IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); } -uint8_t* IRTrotecESP::getRaw() { - checksum(); - return trotec; +void IRTrotecESP::checksum(void) { + remote_state[kTrotecStateLength - 1] = sumBytes(remote_state + 2, + kTrotecStateLength - 3); } -void IRTrotecESP::setPower(bool state) { - if (state) - trotec[2] |= (kTrotecOn << 3); +void IRTrotecESP::stateReset(void) { + for (uint8_t i = 2; i < kTrotecStateLength; i++) remote_state[i] = 0x0; + + remote_state[0] = kTrotecIntro1; + remote_state[1] = kTrotecIntro2; + + this->setPower(false); + this->setTemp(kTrotecDefTemp); + this->setSpeed(kTrotecFanMed); + this->setMode(kTrotecAuto); +} + +uint8_t* IRTrotecESP::getRaw(void) { + this->checksum(); + return remote_state; +} + +void IRTrotecESP::setRaw(const uint8_t state[]) { + for (uint16_t i = 0; i < kTrotecStateLength; i++) remote_state[i] = state[i]; +} + +void IRTrotecESP::setPower(const bool on) { + if (on) + remote_state[2] |= kTrotecPowerBit; else - trotec[2] &= ~(kTrotecOn << 3); + remote_state[2] &= ~kTrotecPowerBit; } -uint8_t IRTrotecESP::getPower() { return trotec[2] & (kTrotecOn << 3); } +bool IRTrotecESP::getPower(void) { return remote_state[2] & kTrotecPowerBit; } -void IRTrotecESP::setSpeed(uint8_t speed) { - trotec[2] = (trotec[2] & 0xcf) | (speed << 4); +void IRTrotecESP::setSpeed(const uint8_t fan) { + uint8_t speed = std::min(fan, kTrotecFanHigh); + remote_state[2] = (remote_state[2] & 0b11001111) | (speed << 4); } -uint8_t IRTrotecESP::getSpeed() { return trotec[2] & 0x30; } - -void IRTrotecESP::setMode(uint8_t mode) { - trotec[2] = (trotec[2] & 0xfc) | mode; +uint8_t IRTrotecESP::getSpeed(void) { + return (remote_state[2] & 0b00110000) >> 4; } -uint8_t IRTrotecESP::getMode() { return trotec[2] & 0x03; } - -void IRTrotecESP::setTemp(uint8_t temp) { - if (temp < kTrotecMinTemp) - temp = kTrotecMinTemp; - else if (temp > kTrotecMaxTemp) - temp = kTrotecMaxTemp; - - trotec[3] = (trotec[3] & 0x80) | (temp - kTrotecMinTemp); -} - -uint8_t IRTrotecESP::getTemp() { return trotec[3] & 0x7f; } - -void IRTrotecESP::setSleep(bool sleep) { - if (sleep) - trotec[3] |= (kTrotecSleepOn << 7); - else - trotec[3] &= ~(kTrotecSleepOn << 7); -} - -bool IRTrotecESP::getSleep(void) { return trotec[3] & (kTrotecSleepOn << 7); } - -void IRTrotecESP::setTimer(uint8_t timer) { - if (timer > kTrotecMaxTimer) timer = kTrotecMaxTimer; - - if (timer) { - trotec[5] |= (kTrotecTimerOn << 6); - trotec[6] = timer; - } else { - trotec[5] &= ~(kTrotecTimerOn << 6); - trotec[6] = 0; +void IRTrotecESP::setMode(const uint8_t mode) { + switch (mode) { + case kTrotecAuto: + case kTrotecCool: + case kTrotecDry: + case kTrotecFan: + remote_state[2] = (remote_state[2] & 0b11111100) | mode; + return; + default: + this->setMode(kTrotecAuto); } } -uint8_t IRTrotecESP::getTimer() { return trotec[6]; } +uint8_t IRTrotecESP::getMode(void) { return remote_state[2] & 0b00000011; } + +void IRTrotecESP::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrotecMinTemp); + temp = std::min(temp, kTrotecMaxTemp); + remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); +} + +uint8_t IRTrotecESP::getTemp(void) { + return (remote_state[3] & 0b01111111) + kTrotecMinTemp; +} + +void IRTrotecESP::setSleep(const bool on) { + if (on) + remote_state[3] |= kTrotecSleepBit; + else + remote_state[3] &= ~kTrotecSleepBit; +} + +bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } + +void IRTrotecESP::setTimer(const uint8_t timer) { + if (timer) + remote_state[5] |= kTrotecTimerBit; + else + remote_state[5] &= ~kTrotecTimerBit; + remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; +} + +uint8_t IRTrotecESP::getTimer(void) { return remote_state[6]; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRTrotecESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTrotecCool; + case stdAc::opmode_t::kDry: + return kTrotecDry; + case stdAc::opmode_t::kFan: + return kTrotecFan; + // Note: No Heat mode. + default: + return kTrotecAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: + return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTrotecFanHigh; + default: + return kTrotecFanMed; + } +} + + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTrotecESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrotecCool: return stdAc::opmode_t::kCool; + case kTrotecDry: return stdAc::opmode_t::kDry; + case kTrotecFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; + case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; + case kTrotecFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTrotecESP::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TROTEC; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getSpeed()); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRTrotecESP::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { + case kTrotecAuto: + result += F(" (AUTO)"); + break; + case kTrotecCool: + result += F(" (COOL)"); + break; + case kTrotecDry: + result += F(" (DRY)"); + break; + case kTrotecFan: + result += F(" (FAN)"); + break; + default: + result += F(" (UNKNOWN)"); + } + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan Speed: "); + result += uint64ToString(this->getSpeed()); + switch (this->getSpeed()) { + case kTrotecFanLow: + result += F(" (Low)"); + break; + case kTrotecFanMed: + result += F(" (Med)"); + break; + case kTrotecFanHigh: + result += F(" (High)"); + break; + } + result += F(", Sleep: "); + result += (this->getSleep() ? F("On") : F("Off")); + return result; +} + +#if DECODE_TROTEC +// Decode the supplied Trotec message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kTrotecBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Probably works. Untested on real devices. +// +// Ref: +bool IRrecv::decodeTrotec(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + 2 * kFooter - 1) + return false; // Can't possibly be a valid Samsung A/C message. + if (strict && nbits != kTrotecBits) return false; + + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + match_result_t data_result; + + // Message Header + if (!matchMark(results->rawbuf[offset++], kTrotecHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTrotecHdrSpace)) return false; + + // Data + // Keep reading bytes until we either run out of data or state to fill. + for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData(&(results->rawbuf[offset]), 8, kTrotecBitMark, + kTrotecOneSpace, kTrotecBitMark, + kTrotecZeroSpace, kTolerance, 0, false); + if (data_result.success == false) { + DPRINT("DEBUG: offset = "); + DPRINTLN(offset + data_result.used); + return false; // Fail + } + results->state[i] = data_result.data; + } + + // Footer + if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTrotecGap)) return false; + if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kTrotecGapEnd)) return false; + // Compliance + // Re-check we got the correct size/length due to the way we read the data. + if (dataBitsSoFar != nbits) return false; + // Verify we got a valid checksum. + if (strict && !IRTrotecESP::validChecksum(results->state)) return false; + // Success + results->decode_type = TROTEC; + results->bits = dataBitsSoFar; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_TROTEC diff --git a/lib/IRremoteESP8266/src/ir_Trotec.h b/lib/IRremoteESP8266/src/ir_Trotec.h index 846381b..55b1f89 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -1,10 +1,17 @@ // Copyright 2017 stufisher +// Copyright 2019 crankyoldgit #ifndef IR_TROTEC_H_ #define IR_TROTEC_H_ +#ifndef UNIT_TEST +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants // Byte 0 @@ -19,8 +26,7 @@ const uint8_t kTrotecCool = 1; const uint8_t kTrotecDry = 2; const uint8_t kTrotecFan = 3; -const uint8_t kTrotecOn = 1; -const uint8_t kTrotecOff = 0; +const uint8_t kTrotecPowerBit = 0b00001000; const uint8_t kTrotecFanLow = 1; const uint8_t kTrotecFanMed = 2; @@ -31,13 +37,12 @@ const uint8_t kTrotecMinTemp = 18; const uint8_t kTrotecDefTemp = 25; const uint8_t kTrotecMaxTemp = 32; -const uint8_t kTrotecSleepOn = 1; +const uint8_t kTrotecSleepBit = 0b10000000; // Byte 5 -const uint8_t kTrotecTimerOn = 1; +const uint8_t kTrotecTimerBit = 0b01000000; // Byte 6 -const uint8_t kTrotecMinTimer = 0; const uint8_t kTrotecMaxTimer = 23; // Legacy defines. (Deperecated) @@ -50,7 +55,6 @@ const uint8_t kTrotecMaxTimer = 23; #define TROTEC_FAN_HIGH kTrotecFanHigh #define TROTEC_MIN_TEMP kTrotecMinTemp #define TROTEC_MAX_TEMP kTrotecMaxTemp -#define TROTEC_MIN_TIMER kTrotecMinTimer #define TROTEC_MAX_TIMER kTrotecMaxTimer class IRTrotecESP { @@ -59,34 +63,50 @@ class IRTrotecESP { #if SEND_TROTEC void send(const uint16_t repeat = kTrotecDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TROTEC - void begin(); + void begin(void); - void setPower(bool state); - uint8_t getPower(); + void setPower(const bool state); + bool getPower(void); - void setTemp(uint8_t temp); - uint8_t getTemp(); + void setTemp(const uint8_t celsius); + uint8_t getTemp(void); - void setSpeed(uint8_t fan); - uint8_t getSpeed(); + void setSpeed(const uint8_t fan); + uint8_t getSpeed(void); - uint8_t getMode(); - void setMode(uint8_t mode); + uint8_t getMode(void); + void setMode(const uint8_t mode); - bool getSleep(); - void setSleep(bool sleep); + bool getSleep(void); + void setSleep(const bool on); - uint8_t getTimer(); - void setTimer(uint8_t timer); + uint8_t getTimer(void); + void setTimer(const uint8_t timer); - uint8_t* getRaw(); + uint8_t* getRaw(void); + void setRaw(const uint8_t state[]); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kTrotecStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: - uint8_t trotec[kTrotecStateLength]; - void stateReset(); - void checksum(); IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kTrotecStateLength]; + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kTrotecStateLength); + void stateReset(void); + void checksum(void); }; #endif // IR_TROTEC_H_ diff --git a/lib/IRremoteESP8266/src/ir_Vestel.cpp b/lib/IRremoteESP8266/src/ir_Vestel.cpp index d9e6355..f00ebdd 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.cpp +++ b/lib/IRremoteESP8266/src/ir_Vestel.cpp @@ -1,12 +1,12 @@ // Copyright 2018 Erdem U. Altinyurt // Copyright 2019 David Conran +// Vestel added by Erdem U. Altinyurt + #include "ir_Vestel.h" #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" @@ -14,14 +14,6 @@ #include "IRutils.h" #include "ir_Haier.h" -// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL -// VV VV EE S TT EE LL -// VV VV EEEEE SSSS TT EEEEE LL -// VV VV EE S TT EE LL -// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL - -// Vestel added by Erdem U. Altinyurt - // Equipment it seems compatible with: // * Vestel AC Model BIOX CXP-9 (9K BTU) // * @@ -53,25 +45,27 @@ void IRsend::sendVestelAc(const uint64_t data, const uint16_t nbits, // Code to emulate Vestel A/C IR remote control unit. // Initialise the object. -IRVestelAc::IRVestelAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRVestelAc::IRVestelAc(const uint16_t pin) : _irsend(pin) { + this->stateReset(); +} // Reset the state of the remote to a known good state/sequence. -void IRVestelAc::stateReset() { +void IRVestelAc::stateReset(void) { // Power On, Mode Auto, Fan Auto, Temp = 25C/77F - remote_state = 0x0F00D9001FEF201ULL; - remote_time_state = 0x201ULL; + remote_state = kVestelAcStateDefault; + remote_time_state = kVestelAcTimeStateDefault; use_time_state = false; } // Configure the pin for output. -void IRVestelAc::begin() { +void IRVestelAc::begin(void) { _irsend.begin(); } #if SEND_VESTEL_AC // Send the current desired state to the IR LED. -void IRVestelAc::send() { - checksum(); // Ensure correct checksum before sending. +void IRVestelAc::send(void) { + this->checksum(); // Ensure correct checksum before sending. uint64_t code_to_send; if (use_time_state) code_to_send = remote_time_state; @@ -82,36 +76,42 @@ void IRVestelAc::send() { #endif // SEND_VESTEL_AC // Return the internal state date of the remote. -uint64_t IRVestelAc::getRaw() { - checksum(); +uint64_t IRVestelAc::getRaw(void) { + this->checksum(); if (use_time_state) return remote_time_state; return remote_state; } // Override the internal state with the new state. -void IRVestelAc::setRaw(uint8_t* newState) { +void IRVestelAc::setRaw(const uint8_t* newState) { uint64_t upState = 0; for (int i = 0; i < 7; i++) upState |= static_cast(newState[i]) << (i * 8); - remote_state = upState; - remote_time_state = upState; + this->setRaw(upState); } void IRVestelAc::setRaw(const uint64_t newState) { + use_time_state = false; remote_state = newState; remote_time_state = newState; + if (this->isTimeCommand()) { + use_time_state = true; + remote_state = kVestelAcStateDefault; + } else { + remote_time_state = kVestelAcTimeStateDefault; + } } // Set the requested power state of the A/C to on. -void IRVestelAc::on() { setPower(true); } +void IRVestelAc::on(void) { setPower(true); } // Set the requested power state of the A/C to off. -void IRVestelAc::off() { setPower(false); } +void IRVestelAc::off(void) { setPower(false); } // Set the requested power state of the A/C. -void IRVestelAc::setPower(const bool state) { +void IRVestelAc::setPower(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcPowerOffset); - if (state) + if (on) remote_state |= ((uint64_t)0xF << kVestelAcPowerOffset); else remote_state |= ((uint64_t)0xC << kVestelAcPowerOffset); @@ -119,7 +119,7 @@ void IRVestelAc::setPower(const bool state) { } // Return the requested power state of the A/C. -bool IRVestelAc::getPower() { +bool IRVestelAc::getPower(void) { return (remote_state >> kVestelAcPowerOffset == 0xF); } @@ -159,14 +159,14 @@ void IRVestelAc::setFan(const uint8_t fan) { } // Return the requested state of the unit's fan. -uint8_t IRVestelAc::getFan() { +uint8_t IRVestelAc::getFan(void) { return (remote_state >> kVestelAcFanOffset) & 0xF; } // Get the requested climate operation mode of the a/c unit. // Returns: // A uint8_t containing the A/C mode. -uint8_t IRVestelAc::getMode() { +uint8_t IRVestelAc::getMode(void) { return (remote_state >> kVestelAcModeOffset) & 0xF; } @@ -307,53 +307,54 @@ uint16_t IRVestelAc::getOffTimer(void) { } // Set the Sleep state of the A/C. -void IRVestelAc::setSleep(const bool state) { +void IRVestelAc::setSleep(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); - remote_state |= (uint64_t)(state ? kVestelAcSleep : kVestelAcNormal) + remote_state |= (uint64_t)(on ? kVestelAcSleep : kVestelAcNormal) << kVestelAcTurboSleepOffset; use_time_state = false; } // Return the Sleep state of the A/C. -bool IRVestelAc::getSleep() { +bool IRVestelAc::getSleep(void) { return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcSleep; } // Set the Turbo state of the A/C. -void IRVestelAc::setTurbo(const bool state) { +void IRVestelAc::setTurbo(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset); - remote_state |= (uint64_t)(state ? kVestelAcTurbo : kVestelAcNormal) + remote_state |= (uint64_t)(on ? kVestelAcTurbo : kVestelAcNormal) << kVestelAcTurboSleepOffset; use_time_state = false; } // Return the Turbo state of the A/C. -bool IRVestelAc::getTurbo() { +bool IRVestelAc::getTurbo(void) { return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcTurbo; } // Set the Ion state of the A/C. -void IRVestelAc::setIon(const bool state) { +void IRVestelAc::setIon(const bool on) { remote_state &= ~((uint64_t)0x1 << kVestelAcIonOffset); - remote_state |= (uint64_t)(state ? 1 : 0) << kVestelAcIonOffset; + remote_state |= (uint64_t)(on ? 1 : 0) << kVestelAcIonOffset; use_time_state = false; } // Return the Ion state of the A/C. -bool IRVestelAc::getIon() { return (remote_state >> kVestelAcIonOffset) & 1; } +bool IRVestelAc::getIon(void) { + return (remote_state >> kVestelAcIonOffset) & 1; +} // Set the Swing Roaming state of the A/C. -void IRVestelAc::setSwing(const bool state) { +void IRVestelAc::setSwing(const bool on) { remote_state &= ~((uint64_t)0xF << kVestelAcSwingOffset); - remote_state |= (uint64_t)(state ? kVestelAcSwing : 0xF) - << kVestelAcSwingOffset; + remote_state |= (uint64_t)(on ? kVestelAcSwing : 0xF) << kVestelAcSwingOffset; use_time_state = false; } // Return the Swing Roaming state of the A/C. -bool IRVestelAc::getSwing() { +bool IRVestelAc::getSwing(void) { return ((remote_state >> kVestelAcSwingOffset) & 0xF) == kVestelAcSwing; } @@ -379,104 +380,186 @@ uint8_t IRVestelAc::calcChecksum(const uint64_t state) { // Returns: // A boolean. bool IRVestelAc::validChecksum(const uint64_t state) { - return (((state >> kVestelAcChecksumOffset) & 0xFF) == calcChecksum(state)); + return (((state >> kVestelAcChecksumOffset) & 0xFF) == + IRVestelAc::calcChecksum(state)); } // Calculate & set the checksum for the current internal state of the remote. -void IRVestelAc::checksum() { +void IRVestelAc::checksum(void) { // Stored the checksum value in the last byte. remote_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); - remote_state |= (uint64_t)calcChecksum(remote_state) + remote_state |= (uint64_t)this->calcChecksum(remote_state) << kVestelAcChecksumOffset; remote_time_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset); - remote_time_state |= (uint64_t)calcChecksum(remote_time_state) + remote_time_state |= (uint64_t)this->calcChecksum(remote_time_state) << kVestelAcChecksumOffset; } -bool IRVestelAc::isTimeCommand() { +bool IRVestelAc::isTimeCommand(void) { return (remote_state >> kVestelAcPowerOffset == 0x00 || use_time_state); } + +// Convert a standard A/C mode into its native mode. +uint8_t IRVestelAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kVestelAcCool; + case stdAc::opmode_t::kHeat: + return kVestelAcHeat; + case stdAc::opmode_t::kDry: + return kVestelAcDry; + case stdAc::opmode_t::kFan: + return kVestelAcFan; + default: + return kVestelAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kVestelAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kVestelAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kVestelAcFanHigh; + default: + return kVestelAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRVestelAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kVestelAcCool: return stdAc::opmode_t::kCool; + case kVestelAcHeat: return stdAc::opmode_t::kHeat; + case kVestelAcDry: return stdAc::opmode_t::kDry; + case kVestelAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRVestelAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kVestelAcFanHigh: return stdAc::fanspeed_t::kMax; + case kVestelAcFanMed: return stdAc::fanspeed_t::kMedium; + case kVestelAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRVestelAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::VESTEL_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.turbo = this->getTurbo(); + result.filter = this->getIon(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRVestelAc::toString() { +String IRVestelAc::toString(void) { String result = ""; -#else -std::string IRVestelAc::toString() { - std::string result = ""; -#endif // ARDUINO - if (isTimeCommand()) { - result += "Time: " + IRHaierAC::timeToString(getTime()); + result.reserve(100); // Reserve some heap for the string to reduce fragging. + if (this->isTimeCommand()) { + result += F("Time: "); + result += IRHaierAC::timeToString(getTime()); - result += ", Timer: "; - result += isTimerActive() ? IRHaierAC::timeToString(getTimer()) : "Off"; + result += F(", Timer: "); + result += this->isTimerActive() ? IRHaierAC::timeToString(this->getTimer()) + : F("Off"); + result += F(", On Timer: "); + result += (this->isOnTimerActive() && !this->isTimerActive()) + ? IRHaierAC::timeToString(this->getOnTimer()) + : F("Off"); - result += ", On Timer: "; - result += (isOnTimerActive() && !isTimerActive()) - ? IRHaierAC::timeToString(getOnTimer()) - : "Off"; - - result += ", Off Timer: "; + result += F(", Off Timer: "); result += - isOffTimerActive() ? IRHaierAC::timeToString(getOffTimer()) : "Off"; + this->isOffTimerActive() ? IRHaierAC::timeToString(this->getOffTimer()) + : F("Off"); return result; } // Not a time command, it's a normal command. - result += "Power: "; - result += (getPower() ? "On" : "Off"); - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { + result += F("Power: "); + result += (this->getPower() ? F("On") : F("Off")); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kVestelAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kVestelAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kVestelAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kVestelAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kVestelAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); + switch (this->getFan()) { case kVestelAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kVestelAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; case kVestelAcFanMed: - result += " (MEDIUM)"; + result += F(" (MEDIUM)"); break; case kVestelAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kVestelAcFanAutoCool: - result += " (AUTO COOL)"; + result += F(" (AUTO COOL)"); break; case kVestelAcFanAutoHot: - result += " (AUTO HOT)"; + result += F(" (AUTO HOT)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Sleep: "; - result += (getSleep() ? "On" : "Off"); - result += ", Turbo: "; - result += (getTurbo() ? "On" : "Off"); - result += ", Ion: "; - result += (getIon() ? "On" : "Off"); - result += ", Swing: "; - result += (getSwing() ? "On" : "Off"); + result += F(", Sleep: "); + result += this->getSleep() ? F("On") : F("Off"); + result += F(", Turbo: "); + result += this->getTurbo() ? F("On") : F("Off"); + result += F(", Ion: "); + result += this->getIon() ? F("On") : F("Off"); + result += F(", Swing: "); + result += this->getSwing() ? F("On") : F("Off"); return result; } @@ -492,8 +575,8 @@ std::string IRVestelAc::toString() { // // Status: Alpha / Needs testing against a real device. // -bool IRrecv::decodeVestelAc(decode_results* results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeVestelAc(decode_results* results, const uint16_t nbits, + const bool strict) { if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. return false; diff --git a/lib/IRremoteESP8266/src/ir_Vestel.h b/lib/IRremoteESP8266/src/ir_Vestel.h index 5c93ead..57576b6 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.h +++ b/lib/IRremoteESP8266/src/ir_Vestel.h @@ -1,6 +1,9 @@ // Copyright 2018 Erdem U. Altinyurt // Copyright 2019 David Conran +// Supports: +// Brand: Vestel, Model: BIOX CXP-9 A/C (9K BTU) + #ifndef IR_VESTEL_H_ #define IR_VESTEL_H_ @@ -8,17 +11,12 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL -// VV VV EE S TT EE LL -// VV VV EEEEE SSSS TT EEEEE LL -// VV VV EE S TT EE LL -// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Vestel added by Erdem U. Altinyurt @@ -28,7 +26,7 @@ // Swing: 4 bits. (auto 0xA, stop 0xF) // turbo_sleep_normal: 4bits. (normal 0x1, sleep 0x3, turbo 0x7) // Unused: 8 bits. (0x00) -// Temperature: 4 bits. (Celcius, but offset by -16 degrees. e.g. 0x0 = 16C) +// Temperature: 4 bits. (Celsius, but offset by -16 degrees. e.g. 0x0 = 16C) // Fan Speed: 4 bits (auto 0x1, low 0x5, mid 0x9, high 0xB, 0xD auto hot, // 0xC auto cool) // Mode: 3 bits. (auto 0x0, cold 0x1, dry 0x2, fan 0x3, hot 0x4) @@ -100,20 +98,23 @@ const uint8_t kVestelAcOffTimerFlagOffset = kVestelAcHourOffset + 6; const uint8_t kVestelAcTimerFlagOffset = kVestelAcHourOffset + 7; const uint8_t kVestelAcMinuteOffset = 44; +const uint64_t kVestelAcStateDefault = 0x0F00D9001FEF201ULL; +const uint64_t kVestelAcTimeStateDefault = 0x201ULL; class IRVestelAc { public: - explicit IRVestelAc(uint16_t pin); + explicit IRVestelAc(const uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_VESTEL_AC - void send(); + void send(void); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_VESTEL_AC void begin(void); void on(void); void off(void); - void setPower(const bool state); - bool getPower(); + void setPower(const bool on); + bool getPower(void); void setAuto(const int8_t autoLevel); void setTimer(const uint16_t minutes); uint16_t getTimer(void); @@ -129,17 +130,17 @@ class IRVestelAc { uint8_t getFan(void); void setMode(const uint8_t mode); uint8_t getMode(void); - void setRaw(uint8_t* newState); + void setRaw(const uint8_t* newState); void setRaw(const uint64_t newState); uint64_t getRaw(void); static bool validChecksum(const uint64_t state); - void setSwing(const bool state); + void setSwing(const bool on); bool getSwing(void); - void setSleep(const bool state); + void setSleep(const bool on); bool getSleep(void); - void setTurbo(const bool state); + void setTurbo(const bool on); bool getTurbo(void); - void setIon(const bool state); + void setIon(const bool on); bool getIon(void); bool isTimeCommand(void); bool isOnTimerActive(void); @@ -149,18 +150,23 @@ class IRVestelAc { bool isTimerActive(void); void setTimerActive(const bool on); static uint8_t calcChecksum(const uint64_t state); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint64_t remote_state; uint64_t remote_time_state; bool use_time_state; - void checksum(); - IRsend _irsend; + void checksum(void); }; #endif // IR_VESTEL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp index bc5c988..f73c252 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp @@ -24,12 +24,6 @@ #include "IRsend.h" #include "IRutils.h" -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - // Constants // Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 const uint16_t kWhirlpoolAcHdrMark = 8950; @@ -38,7 +32,7 @@ const uint16_t kWhirlpoolAcBitMark = 597; const uint16_t kWhirlpoolAcOneSpace = 1649; const uint16_t kWhirlpoolAcZeroSpace = 533; const uint16_t kWhirlpoolAcGap = 7920; -const uint32_t kWhirlpoolAcMinGap = 100000; // Completely made up value. +const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. const uint8_t kWhirlpoolAcSections = 3; #if SEND_WHIRLPOOL_AC @@ -53,8 +47,8 @@ const uint8_t kWhirlpoolAcSections = 3; // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/509 -void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendWhirlpoolAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kWhirlpoolAcStateLength) return; // Not enough bytes to send a proper message. for (uint16_t r = 0; r <= repeat; r++) { @@ -85,17 +79,19 @@ void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, // Decoding help from: // @redmusicxd, @josh929800, @raducostea -IRWhirlpoolAc::IRWhirlpoolAc(uint16_t pin) : _irsend(pin) { stateReset(); } +IRWhirlpoolAc::IRWhirlpoolAc(const uint16_t pin) : _irsend(pin) { + this->stateReset(); +} -void IRWhirlpoolAc::stateReset() { +void IRWhirlpoolAc::stateReset(void) { for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) remote_state[i] = 0x0; remote_state[0] = 0x83; remote_state[1] = 0x06; remote_state[6] = 0x80; - _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. + this->_setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. } -void IRWhirlpoolAc::begin() { _irsend.begin(); } +void IRWhirlpoolAc::begin(void) { _irsend.begin(); } bool IRWhirlpoolAc::validChecksum(uint8_t state[], const uint16_t length) { if (length > kWhirlpoolAcChecksumByte1 && @@ -128,13 +124,13 @@ void IRWhirlpoolAc::checksum(uint16_t length) { #if SEND_WHIRLPOOL_AC void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); _irsend.sendWhirlpoolAC(remote_state, kWhirlpoolAcStateLength, repeat); } #endif // SEND_WHIRLPOOL_AC uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { - if (calcchecksum) checksum(); + if (calcchecksum) this->checksum(); return remote_state; } @@ -143,7 +139,7 @@ void IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { remote_state[i] = new_code[i]; } -whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel() { +whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel(void) { if (remote_state[kWhirlpoolAcAltTempPos] & kWhirlpoolAcAltTempMask) return DG11J191; else @@ -160,13 +156,13 @@ void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { default: remote_state[kWhirlpoolAcAltTempPos] &= ~kWhirlpoolAcAltTempMask; } - _setTemp(_desiredtemp); // Different models have different temp values. + this->_setTemp(_desiredtemp); // Different models have different temp values. } // Return the temp. offset in deg C for the current model. -int8_t IRWhirlpoolAc::getTempOffset() { - switch (getModel()) { - case DG11J191: +int8_t IRWhirlpoolAc::getTempOffset(void) { + switch (this->getModel()) { + case whirlpool_ac_remote_model_t::DG11J191: return -2; break; default: @@ -177,7 +173,7 @@ int8_t IRWhirlpoolAc::getTempOffset() { // Set the temp. in deg C void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { if (remember) _desiredtemp = temp; - int8_t offset = getTempOffset(); // Cache the min temp for the model. + int8_t offset = this->getTempOffset(); // Cache the min temp for the model. uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); remote_state[kWhirlpoolAcTempPos] = @@ -187,23 +183,23 @@ void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { // Set the temp. in deg C void IRWhirlpoolAc::setTemp(const uint8_t temp) { - _setTemp(temp); - setSuper(false); // Changing temp cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandTemp); + this->_setTemp(temp); + this->setSuper(false); // Changing temp cancels Super/Jet mode. + this->setCommand(kWhirlpoolAcCommandTemp); } // Return the set temp. in deg C -uint8_t IRWhirlpoolAc::getTemp() { +uint8_t IRWhirlpoolAc::getTemp(void) { return ((remote_state[kWhirlpoolAcTempPos] & kWhirlpoolAcTempMask) >> 4) + - + kWhirlpoolAcMinTemp + getTempOffset(); + + kWhirlpoolAcMinTemp + this->getTempOffset(); } void IRWhirlpoolAc::_setMode(const uint8_t mode) { switch (mode) { case kWhirlpoolAcAuto: - setFan(kWhirlpoolAcFanAuto); - _setTemp(kWhirlpoolAcAutoTemp, false); - setSleep(false); // Cancel sleep mode when in auto/6thsense mode. + this->setFan(kWhirlpoolAcFanAuto); + this->_setTemp(kWhirlpoolAcAutoTemp, false); + this->setSleep(false); // Cancel sleep mode when in auto/6thsense mode. // FALL THRU case kWhirlpoolAcHeat: case kWhirlpoolAcCool: @@ -211,20 +207,20 @@ void IRWhirlpoolAc::_setMode(const uint8_t mode) { case kWhirlpoolAcFan: remote_state[kWhirlpoolAcModePos] &= ~kWhirlpoolAcModeMask; remote_state[kWhirlpoolAcModePos] |= mode; - setCommand(kWhirlpoolAcCommandMode); + this->setCommand(kWhirlpoolAcCommandMode); break; default: return; } - if (mode == kWhirlpoolAcAuto) setCommand(kWhirlpoolAcCommand6thSense); + if (mode == kWhirlpoolAcAuto) this->setCommand(kWhirlpoolAcCommand6thSense); } void IRWhirlpoolAc::setMode(const uint8_t mode) { - setSuper(false); // Changing mode cancels Super/Jet mode. - _setMode(mode); + this->setSuper(false); // Changing mode cancels Super/Jet mode. + this->_setMode(mode); } -uint8_t IRWhirlpoolAc::getMode() { +uint8_t IRWhirlpoolAc::getMode(void) { return remote_state[kWhirlpoolAcModePos] & kWhirlpoolAcModeMask; } @@ -236,13 +232,13 @@ void IRWhirlpoolAc::setFan(const uint8_t speed) { case kWhirlpoolAcFanHigh: remote_state[kWhirlpoolAcFanPos] = (remote_state[kWhirlpoolAcFanPos] & ~kWhirlpoolAcFanMask) | speed; - setSuper(false); // Changing fan speed cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandFanSpeed); + this->setSuper(false); // Changing fan speed cancels Super/Jet mode. + this->setCommand(kWhirlpoolAcCommandFanSpeed); break; } } -uint8_t IRWhirlpoolAc::getFan() { +uint8_t IRWhirlpoolAc::getFan(void) { return remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcFanMask; } @@ -257,7 +253,7 @@ void IRWhirlpoolAc::setSwing(const bool on) { setCommand(kWhirlpoolAcCommandSwing); } -bool IRWhirlpoolAc::getSwing() { +bool IRWhirlpoolAc::getSwing(void) { return (remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcSwing1Mask) && (remote_state[kWhirlpoolAcOffTimerPos] & kWhirlpoolAcSwing2Mask); } @@ -269,7 +265,7 @@ void IRWhirlpoolAc::setLight(const bool on) { remote_state[kWhirlpoolAcClockPos] |= kWhirlpoolAcLightMask; } -bool IRWhirlpoolAc::getLight() { +bool IRWhirlpoolAc::getLight(void) { return !(remote_state[kWhirlpoolAcClockPos] & kWhirlpoolAcLightMask); } @@ -292,49 +288,53 @@ bool IRWhirlpoolAc::isTimerEnabled(const uint16_t pos) { return remote_state[pos - 1] & kWhirlpoolAcTimerEnableMask; } -void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool state) { - if (state) +void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool on) { + if (on) remote_state[pos - 1] |= kWhirlpoolAcTimerEnableMask; else remote_state[pos - 1] &= ~kWhirlpoolAcTimerEnableMask; } void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcClockPos, minspastmidnight); + this->setTime(kWhirlpoolAcClockPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getClock() { return getTime(kWhirlpoolAcClockPos); } +uint16_t IRWhirlpoolAc::getClock(void) { + return this->getTime(kWhirlpoolAcClockPos); +} void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); + this->setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getOffTimer() { - return getTime(kWhirlpoolAcOffTimerPos); +uint16_t IRWhirlpoolAc::getOffTimer(void) { + return this->getTime(kWhirlpoolAcOffTimerPos); } -bool IRWhirlpoolAc::isOffTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOffTimerPos); +bool IRWhirlpoolAc::isOffTimerEnabled(void) { + return this->isTimerEnabled(kWhirlpoolAcOffTimerPos); } -void IRWhirlpoolAc::enableOffTimer(const bool state) { - enableTimer(kWhirlpoolAcOffTimerPos, state); - setCommand(kWhirlpoolAcCommandOffTimer); +void IRWhirlpoolAc::enableOffTimer(const bool on) { + this->enableTimer(kWhirlpoolAcOffTimerPos, on); + this->setCommand(kWhirlpoolAcCommandOffTimer); } void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); + this->setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getOnTimer() { return getTime(kWhirlpoolAcOnTimerPos); } - -bool IRWhirlpoolAc::isOnTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOnTimerPos); +uint16_t IRWhirlpoolAc::getOnTimer(void) { + return this->getTime(kWhirlpoolAcOnTimerPos); } -void IRWhirlpoolAc::enableOnTimer(const bool state) { - enableTimer(kWhirlpoolAcOnTimerPos, state); - setCommand(kWhirlpoolAcCommandOnTimer); +bool IRWhirlpoolAc::isOnTimerEnabled(void) { + return this->isTimerEnabled(kWhirlpoolAcOnTimerPos); +} + +void IRWhirlpoolAc::enableOnTimer(const bool on) { + this->enableTimer(kWhirlpoolAcOnTimerPos, on); + this->setCommand(kWhirlpoolAcCommandOnTimer); } void IRWhirlpoolAc::setPowerToggle(const bool on) { @@ -342,54 +342,54 @@ void IRWhirlpoolAc::setPowerToggle(const bool on) { remote_state[kWhirlpoolAcPowerTogglePos] |= kWhirlpoolAcPowerToggleMask; else remote_state[kWhirlpoolAcPowerTogglePos] &= ~kWhirlpoolAcPowerToggleMask; - setSuper(false); // Changing power cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandPower); + this->setSuper(false); // Changing power cancels Super/Jet mode. + this->setCommand(kWhirlpoolAcCommandPower); } -bool IRWhirlpoolAc::getPowerToggle() { +bool IRWhirlpoolAc::getPowerToggle(void) { return remote_state[kWhirlpoolAcPowerTogglePos] & kWhirlpoolAcPowerToggleMask; } -uint8_t IRWhirlpoolAc::getCommand() { +uint8_t IRWhirlpoolAc::getCommand(void) { return remote_state[kWhirlpoolAcCommandPos]; } void IRWhirlpoolAc::setSleep(const bool on) { if (on) { remote_state[kWhirlpoolAcSleepPos] |= kWhirlpoolAcSleepMask; - setFan(kWhirlpoolAcFanLow); + this->setFan(kWhirlpoolAcFanLow); } else { remote_state[kWhirlpoolAcSleepPos] &= ~kWhirlpoolAcSleepMask; } - setCommand(kWhirlpoolAcCommandSleep); + this->setCommand(kWhirlpoolAcCommandSleep); } -bool IRWhirlpoolAc::getSleep() { +bool IRWhirlpoolAc::getSleep(void) { return remote_state[kWhirlpoolAcSleepPos] & kWhirlpoolAcSleepMask; } // AKA Jet/Turbo mode. void IRWhirlpoolAc::setSuper(const bool on) { if (on) { - setFan(kWhirlpoolAcFanHigh); - switch (getMode()) { + this->setFan(kWhirlpoolAcFanHigh); + switch (this->getMode()) { case kWhirlpoolAcHeat: - setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); + this->setTemp(kWhirlpoolAcMaxTemp + this->getTempOffset()); break; case kWhirlpoolAcCool: default: - setTemp(kWhirlpoolAcMinTemp + getTempOffset()); - setMode(kWhirlpoolAcCool); + this->setTemp(kWhirlpoolAcMinTemp + this->getTempOffset()); + this->setMode(kWhirlpoolAcCool); break; } remote_state[kWhirlpoolAcSuperPos] |= kWhirlpoolAcSuperMask; } else { remote_state[kWhirlpoolAcSuperPos] &= ~kWhirlpoolAcSuperMask; } - setCommand(kWhirlpoolAcCommandSuper); + this->setCommand(kWhirlpoolAcCommandSuper); } -bool IRWhirlpoolAc::getSuper() { +bool IRWhirlpoolAc::getSuper(void) { return remote_state[kWhirlpoolAcSuperPos] & kWhirlpoolAcSuperMask; } @@ -397,158 +397,218 @@ void IRWhirlpoolAc::setCommand(const uint8_t code) { remote_state[kWhirlpoolAcCommandPos] = code; } -#ifdef ARDUINO +// Convert a standard A/C mode into its native mode. +uint8_t IRWhirlpoolAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kWhirlpoolAcCool; + case stdAc::opmode_t::kHeat: + return kWhirlpoolAcHeat; + case stdAc::opmode_t::kDry: + return kWhirlpoolAcDry; + case stdAc::opmode_t::kFan: + return kWhirlpoolAcFan; + default: + return kWhirlpoolAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kWhirlpoolAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kWhirlpoolAcFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kWhirlpoolAcFanHigh; + default: + return kWhirlpoolAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRWhirlpoolAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kWhirlpoolAcCool: return stdAc::opmode_t::kCool; + case kWhirlpoolAcHeat: return stdAc::opmode_t::kHeat; + case kWhirlpoolAcDry: return stdAc::opmode_t::kDry; + case kWhirlpoolAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRWhirlpoolAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kWhirlpoolAcFanHigh: return stdAc::fanspeed_t::kMax; + case kWhirlpoolAcFanMedium: return stdAc::fanspeed_t::kMedium; + case kWhirlpoolAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRWhirlpoolAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::WHIRLPOOL_AC; + result.model = this->getModel(); + result.power = this->getPowerToggle(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.turbo = this->getSuper(); + result.light = this->getLight(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.filter = false; + result.econo = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { String result = ""; -#else -std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { - std::string result = ""; -#endif // ARDUINO uint8_t hours = minspastmidnight / 60; - if (hours < 10) result += "0"; + if (hours < 10) result += '0'; result += uint64ToString(hours); - result += ":"; + result += ':'; uint8_t mins = minspastmidnight % 60; - if (mins < 10) result += "0"; + if (mins < 10) result += '0'; result += uint64ToString(mins); return result; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRWhirlpoolAc::toString() { +String IRWhirlpoolAc::toString(void) { String result = ""; -#else -std::string IRWhirlpoolAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Model: " + uint64ToString(getModel()); - switch (getModel()) { + result.reserve(200); // Reserve some heap for the string to reduce fragging. + result += F("Model: "); + result += uint64ToString(this->getModel()); + switch (this->getModel()) { case DG11J191: - result += " (DG11J191)"; + result += F(" (DG11J191)"); break; case DG11J13A: - result += " (DG11J13A)"; + result += F(" (DG11J13A)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Power toggle: "; - if (getPowerToggle()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { + result += F(", Power toggle: "); + result += this->getPowerToggle() ? F("On") : F("Off"); + result += F(", Mode: "); + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kWhirlpoolAcHeat: - result += " (HEAT)"; + result += F(" (HEAT)"); break; case kWhirlpoolAcAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kWhirlpoolAcCool: - result += " (COOL)"; + result += F(" (COOL)"); break; case kWhirlpoolAcDry: - result += " (DRY)"; + result += F(" (DRY)"); break; case kWhirlpoolAcFan: - result += " (FAN)"; + result += F(" (FAN)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); + result += F(", Temp: "); + result += uint64ToString(this->getTemp()); + result += F("C, Fan: "); + result += uint64ToString(this->getFan()); switch (getFan()) { case kWhirlpoolAcFanAuto: - result += " (AUTO)"; + result += F(" (AUTO)"); break; case kWhirlpoolAcFanHigh: - result += " (HIGH)"; + result += F(" (HIGH)"); break; case kWhirlpoolAcFanMedium: - result += " (MEDIUM)"; + result += F(" (MEDIUM)"); break; case kWhirlpoolAcFanLow: - result += " (LOW)"; + result += F(" (LOW)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } - result += ", Swing: "; - if (getSwing()) - result += "On"; + result += F(", Swing: "); + result += this->getSwing() ? F("On") : F("Off"); + result += F(", Light: "); + result += this->getLight() ? F("On") : F("Off"); + result += F(", Clock: "); + result += this->timeToString(this->getClock()); + result += F(", On Timer: "); + if (this->isOnTimerEnabled()) + result += this->timeToString(this->getOnTimer()); else - result += "Off"; - result += ", Light: "; - if (getLight()) - result += "On"; + result += F("Off"); + result += F(", Off Timer: "); + if (this->isOffTimerEnabled()) + result += this->timeToString(this->getOffTimer()); else - result += "Off"; - result += ", Clock: "; - result += timeToString(getClock()); - result += ", On Timer: "; - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); - else - result += "Off"; - result += ", Off Timer: "; - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); - else - result += "Off"; - result += ", Sleep: "; - if (getSleep()) - result += "On"; - else - result += "Off"; - result += ", Super: "; - if (getSuper()) - result += "On"; - else - result += "Off"; - result += ", Command: " + uint64ToString(getCommand()); - switch (getCommand()) { + result += F("Off"); + result += F(", Sleep: "); + result += this->getSleep() ? F("On") : F("Off"); + result += F(", Super: "); + result += this->getSuper() ? F("On") : F("Off"); + result += F(", Command: "); + result += uint64ToString(this->getCommand()); + switch (this->getCommand()) { case kWhirlpoolAcCommandLight: - result += " (LIGHT)"; + result += F(" (LIGHT)"); break; case kWhirlpoolAcCommandPower: - result += " (POWER)"; + result += F(" (POWER)"); break; case kWhirlpoolAcCommandTemp: - result += " (TEMP)"; + result += F(" (TEMP)"); break; case kWhirlpoolAcCommandSleep: - result += " (SLEEP)"; + result += F(" (SLEEP)"); break; case kWhirlpoolAcCommandSuper: - result += " (SUPER)"; + result += F(" (SUPER)"); break; case kWhirlpoolAcCommandOnTimer: - result += " (ONTIMER)"; + result += F(" (ONTIMER)"); break; case kWhirlpoolAcCommandMode: - result += " (MODE)"; + result += F(" (MODE)"); break; case kWhirlpoolAcCommandSwing: - result += " (SWING)"; + result += F(" (SWING)"); break; case kWhirlpoolAcCommandIFeel: - result += " (IFEEL)"; + result += F(" (IFEEL)"); break; case kWhirlpoolAcCommandFanSpeed: - result += " (FANSPEED)"; + result += F(" (FANSPEED)"); break; case kWhirlpoolAcCommand6thSense: - result += " (6THSENSE)"; + result += F(" (6THSENSE)"); break; case kWhirlpoolAcCommandOffTimer: - result += " (OFFTIMER)"; + result += F(" (OFFTIMER)"); break; default: - result += " (UNKNOWN)"; + result += F(" (UNKNOWN)"); break; } return result; @@ -569,8 +629,8 @@ std::string IRWhirlpoolAc::toString() { // // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/509 -bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, - bool strict) { +bool IRrecv::decodeWhirlpoolAC(decode_results *results, const uint16_t nbits, + const bool strict) { if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1) return false; // Can't possibly be a valid Whirlpool A/C message. if (strict) { diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.h b/lib/IRremoteESP8266/src/ir_Whirlpool.h index 211e30f..dff6c2e 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.h @@ -2,6 +2,16 @@ // // Copyright 2018 David Conran +// Supports: +// Brand: Whirlpool, Model: DG11J1-3A remote +// Brand: Whirlpool, Model: DG11J1-04 remote +// Brand: Whirlpool, Model: DG11J1-91 remote +// Brand: Whirlpool, Model: SPIS409L A/C +// Brand: Whirlpool, Model: SPIS412L A/C +// Brand: Whirlpool, Model: SPIW409L A/C +// Brand: Whirlpool, Model: SPIW412L A/C +// Brand: Whirlpool, Model: SPIW418L A/C + #ifndef IR_WHIRLPOOL_H_ #define IR_WHIRLPOOL_H_ @@ -9,17 +19,12 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" - -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Ref: // https://github.com/markszabo/IRremoteESP8266/issues/509 @@ -84,64 +89,67 @@ enum whirlpool_ac_remote_model_t { // Classes class IRWhirlpoolAc { public: - explicit IRWhirlpoolAc(uint16_t pin); + explicit IRWhirlpoolAc(const uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_WHIRLPOOL_AC void send(const uint16_t repeat = kWhirlpoolAcDefaultRepeat, const bool calcchecksum = true); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_WHIRLPOOL_AC - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); void setPowerToggle(const bool on); - bool getPowerToggle(); + bool getPowerToggle(void); void setSleep(const bool on); - bool getSleep(); + bool getSleep(void); void setSuper(const bool on); - bool getSuper(); + bool getSuper(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setSwing(const bool on); - bool getSwing(); + bool getSwing(void); void setLight(const bool on); - bool getLight(); - uint16_t getClock(); + bool getLight(void); + uint16_t getClock(void); void setClock(const uint16_t minspastmidnight); - uint16_t getOnTimer(); + uint16_t getOnTimer(void); void setOnTimer(const uint16_t minspastmidnight); - void enableOnTimer(const bool state); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); + void enableOnTimer(const bool on); + bool isOnTimerEnabled(void); + uint16_t getOffTimer(void); void setOffTimer(const uint16_t minspastmidnight); - void enableOffTimer(const bool state); - bool isOffTimerEnabled(); + void enableOffTimer(const bool on); + bool isOffTimerEnabled(void); void setCommand(const uint8_t code); - uint8_t getCommand(); - whirlpool_ac_remote_model_t getModel(); + uint8_t getCommand(void); + whirlpool_ac_remote_model_t getModel(void); void setModel(const whirlpool_ac_remote_model_t model); uint8_t* getRaw(const bool calcchecksum = true); void setRaw(const uint8_t new_code[], const uint16_t length = kWhirlpoolAcStateLength); static bool validChecksum(uint8_t state[], const uint16_t length = kWhirlpoolAcStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif // The state of the IR remote in IR code form. uint8_t remote_state[kWhirlpoolAcStateLength]; - IRsend _irsend; uint8_t _desiredtemp; void checksum(const uint16_t length = kWhirlpoolAcStateLength); uint16_t getTime(const uint16_t pos); @@ -150,12 +158,8 @@ class IRWhirlpoolAc { void enableTimer(const uint16_t pos, const bool state); void _setTemp(const uint8_t temp, const bool remember = true); void _setMode(const uint8_t mode); - int8_t getTempOffset(); -#ifdef ARDUINO + int8_t getTempOffset(void); String timeToString(uint16_t minspastmidnight); -#else - std::string timeToString(uint16_t minspastmidnight); -#endif }; #endif // IR_WHIRLPOOL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Whynter.cpp b/lib/IRremoteESP8266/src/ir_Whynter.cpp index 555c507..576b35f 100644 --- a/lib/IRremoteESP8266/src/ir_Whynter.cpp +++ b/lib/IRremoteESP8266/src/ir_Whynter.cpp @@ -1,20 +1,17 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// Whynter A/C ARC-110WD added by Francesco Meschia +// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ + +// Supports: +// Brand: Whynter, Model: ARC-110WD A/C + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// W W H H Y Y N N TTTTT EEEEE RRRRR -// W W H H Y Y NN N T E R R -// W W W HHHHH Y N N N T EEE RRRR -// W W W H H Y N NN T E R R -// WWW H H Y N N T EEEEE R R - -// Whynter A/C ARC-110WD added by Francesco Meschia -// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ - // Constants const uint16_t kWhynterTick = 50; diff --git a/lib/IRremoteESP8266/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp new file mode 100644 index 0000000..208afbc --- /dev/null +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -0,0 +1,998 @@ +// Copyright 2019 David Conran + +#include +#include "ir_Argo.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.h" +#include "ir_Goodweather.h" +#include "ir_Gree.h" +#include "ir_Haier.h" +#include "ir_Hitachi.h" +#include "ir_Kelvinator.h" +#include "ir_Midea.h" +#include "ir_Mitsubishi.h" +#include "ir_MitsubishiHeavy.h" +#include "ir_Panasonic.h" +#include "ir_Samsung.h" +#include "ir_Sharp.h" +#include "ir_Tcl.h" +#include "ir_Teco.h" +#include "ir_Toshiba.h" +#include "ir_Trotec.h" +#include "ir_Vestel.h" +#include "ir_Whirlpool.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for IRac class. + +TEST(TestIRac, Argo) { + IRArgoAC ac(0); + IRac irac(0); + + ac.begin(); + irac.argo(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 21, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + false, // Turbo + -1); // Sleep + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(kArgoHeat, ac.getMode()); + EXPECT_EQ(21, ac.getTemp()); + EXPECT_EQ(kArgoFlapAuto, ac.getFlap()); + EXPECT_FALSE(ac.getMax()); // Turbo + EXPECT_FALSE(ac.getNight()); // Sleep +} + +TEST(TestIRac, Coolix) { + IRCoolixAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (HEAT), Fan: 1 (MAX), Temp: 21C, Zone Follow: Off, " + "Sensor Temp: Ignored"; + + ac.begin(); + irac.coolix(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 21, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Turbo + false, // Light + false, // Clean + -1); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(COOLIX, ac._irsend.capture.decode_type); + ASSERT_EQ(kCoolixBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); + // Confirm we are sending with a repeat of 1. i.e. two messages. + EXPECT_EQ( + "f38000d50" // 38kHz Frequency and 50% duty-cycle. + // Start of message #1 (i.e. Repeat '0') + // Header + "m4480s4480" + // Data + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s560" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // Footer + "m560s5040" + // End of message #1 (i.e. Repeat '0') + // Start of message #2 (i.e. Repeat '1') + // Header + "m4480s4480" + // Data + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s560" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // Footer + "m560s5040", + // End of message #2 (i.e. Repeat '1') + // Note: the two messages (#1 & #2) are identical. + ac._irsend.outputStr()); +} + +TEST(TestIRac, Daikin) { + IRDaikinESP ac(0); + IRac irac(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Powerful: Off, " + "Quiet: Off, Sensor: Off, Mold: On, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 0:00, Current Day: (UNKNOWN), On Time: Off, " + "Off Time: Off, Weekly Timer: On"; + + ac.begin(); + irac.daikin(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Filter + true); // Clean + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin2) { + IRDaikin2 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Swing (V): 14 (Auto), " + "Swing (H): 0, Clock: 0:00, On Time: Off, Off Time: Off, " + "Sleep Time: Off, Beep: 1 (Quiet), Light: 1 (Bright), Mold: On, " + "Clean: Off, Fresh Air: Off, Eye: Off, Eye Auto: Off, Quiet: Off, " + "Powerful: Off, Purify: On, Econo: Off"; + + ac.begin(); + irac.daikin2(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Light + false, // Econo + true, // Filter + true, // Clean (aka Mold) + -1, // Sleep time + -1); // Current time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN2, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin2Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Daikin216) { + IRDaikin216 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 4 (HEAT), Temp: 31C, Fan: 11 (QUIET), " + "Swing (Horizontal): On, Swing (Vertical): On, Quiet: On, Powerful: Off"; + + ac.begin(); + irac.daikin216(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 31, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft, // Horizontal swing + true, // Quiet + false); // Turbo (Powerful) + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN216, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Fujitsu) { + IRFujitsuAC ac(0); + IRac irac(0); + IRrecv capture(0); + std::string ardb1_expected = + "Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (MED), " + "Command: N/A"; + std::string arrah2e_expected = + "Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (MED), " + "Swing: Off, Command: N/A"; + + ac.begin(); + irac.fujitsu(&ac, + ARDB1, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo (Powerful) + false); // Econo + ASSERT_EQ(ardb1_expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(ardb1_expected, ac.toString()); + + ac._irsend.reset(); + irac.fujitsu(&ac, + ARRAH2E, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo (Powerful) + false); // Econo + ASSERT_EQ(arrah2e_expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state, ac._irsend.capture.bits / 8); + ASSERT_EQ(arrah2e_expected, ac.toString()); +} + +TEST(TestIRac, Goodweather) { + IRGoodweatherAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (MED), Turbo: Toggle, " + "Light: Toggle, Sleep: Toggle, Swing: 1 (Slow), Command: 0 (Power)"; + + ac.begin(); + irac.goodweather(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Turbo + true, // Light + 8 * 60 + 0); // Sleep time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(GOODWEATHER, ac._irsend.capture.decode_type); + ASSERT_EQ(kGoodweatherBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Gree) { + IRGreeAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 2, Turbo: Off, XFan: On, " + "Light: On, Sleep: On, Swing Vertical Mode: Manual, " + "Swing Vertical Pos: 3"; + + ac.begin(); + irac.gree(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 22, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Light + true, // Clean (aka Mold/XFan) + 8 * 60 + 0); // Sleep time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(GREE, ac._irsend.capture.decode_type); + ASSERT_EQ(kGreeBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Haier) { + IRHaierAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Command: 1 (On), Mode: 1 (COOL), Temp: 24C, Fan: 2, Swing: 1 (Up), " + "Sleep: On, Health: On, Current Time: 13:45, On Timer: Off, " + "Off Timer: Off"; + + ac.begin(); + irac.haier(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 24, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kHaierACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + + +TEST(TestIRac, HaierYrwo2) { + IRHaierACYRW02 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Button: 5 (Power), Mode: 2 (Cool), Temp: 23C, Fan: 4 (Med), " + "Turbo: 1 (High), Swing: 1 (Top), Sleep: On, Health: On"; + + ac.begin(); + irac.haierYrwo2(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 23, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + true, // Turbo + true, // Filter + 8 * 60 + 0); // Sleep time + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC_YRW02, ac._irsend.capture.decode_type); + ASSERT_EQ(kHaierACYRW02Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Hitachi) { + IRHitachiAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (AUTO), Temp: 22C, Fan: 3 (UNKNOWN), " + "Swing (Vertical): Off, Swing (Horizontal): On"; + + ac.begin(); + irac.hitachi(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto); // Horizontal swing + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Kelvinator) { + IRKelvinatorAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 3, Turbo: Off, Quiet: Off, " + "XFan: On, IonFilter: On, Light: On, Swing (Horizontal): Off, " + "Swing (Vertical): Off"; + + ac.begin(); + irac.kelvinator(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 19, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo + true, // Light + true, // Filter + true); // Clean + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(KELVINATOR, ac._irsend.capture.decode_type); + ASSERT_EQ(kKelvinatorBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Midea) { + IRMideaAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (DRY), Temp: 27C/81F, Fan: 2 (MED), Sleep: On"; + + ac.begin(); + irac.midea(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 27, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + 8 * 60 + 0); // Sleep time + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MIDEA, ac._irsend.capture.decode_type); + ASSERT_EQ(kMideaBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Mitsubishi) { + IRMitsubishiAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On (COOL), Temp: 20C, FAN: 2, VANE: AUTO, Time: 14:30, " + "On timer: 00:00, Off timer: 00:00, Timer: -"; + + ac.begin(); + irac.mitsubishi(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + false, // Silent + 14 * 60 + 35); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, MitsubishiHeavy88) { + IRMitsubishiHeavy88Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 3 (Med), " + "Swing (V): 16 (Auto), Swing (H): 0 (Off), Turbo: Off, Econo: Off, " + "3D: Off, Clean: On"; + + ac.begin(); + irac.mitsubishiHeavy88(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Turbo + false, // Econo + true); // Clean + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_88, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy88Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, MitsubishiHeavy152) { + IRMitsubishiHeavy152Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 6 (Econo), " + "Swing (V): 6 (Off), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " + "Econo: On, Night: On, Filter: On, 3D: Off, Clean: Off"; + + ac.begin(); + irac.mitsubishiHeavy152(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto, // Horizontal swing + true, // Silent + false, // Turbo + true, // Econo + true, // Filter + false, // Clean + 8 * 60); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, ac._irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Panasonic) { + IRPanasonicAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected_nke[] = + "Model: 2 (NKE), Power: On, Mode: 4 (HEAT), Temp: 28C, Fan: 2 (UNKNOWN), " + "Swing (Vertical): 15 (AUTO), Swing (Horizontal): 6 (Middle), Quiet: On, " + "Powerful: Off, Clock: 19:17, On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.panasonic(&ac, + kPanasonicNke, // Model + true, // Power + stdAc::opmode_t::kHeat, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft, // Horizontal swing + true, // Quiet + false, // Turbo + 19 * 60 + 17); // Clock + ASSERT_EQ(expected_nke, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_nke, ac.toString()); + + char expected_dke[] = + "Model: 3 (DKE), Power: On, Mode: 3 (COOL), Temp: 18C, Fan: 4 (MAX), " + "Swing (Vertical): 1 (Full Up), Swing (Horizontal): 6 (Middle), " + "Quiet: Off, Powerful: On, Clock: 19:17, On Timer: Off, Off Timer: Off"; + ac._irsend.reset(); + irac.panasonic(&ac, + kPanasonicDke, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + 18, // Celsius + stdAc::fanspeed_t::kMax, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + stdAc::swingh_t::kMiddle, // Horizontal swing + false, // Quiet + true, // Turbo + 19 * 60 + 17); // Clock + ASSERT_EQ(expected_dke, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(PANASONIC_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_dke, ac.toString()); +} + +TEST(TestIRac, Samsung) { + IRSamsungAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 28C, Fan: 6 (AUTO), Swing: On, " + "Beep: On, Clean: On, Quiet: On, Powerful: Off"; + + ac.begin(); + irac.samsung(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + true, // Quiet + false, // Turbo + true, // Clean + true, // Beep + false); // with dopower Off + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSamsungAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + irac.samsung(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + true, // Quiet + false, // Turbo + true, // Clean + true, // Beep + true); // with dopower On + ASSERT_EQ(expected, ac.toString()); // Class should be in the desired mode. + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + // However, we expect a plain "on" state as it should be sent before the + // desired state. + char expected_on[] = + "Power: On, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off"; + ASSERT_EQ(expected_on, ac.toString()); +} + +TEST(TestIRac, Sharp) { + IRSharpAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 3 (MED)"; + + ac.begin(); + irac.sharp(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium); // Fan speed + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SHARP_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kSharpAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} +TEST(TestIRac, Tcl112) { + IRTcl112Ac ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 3 (Med), Econo: On, " + "Health: On, Light: On, Turbo: Off, Swing (H): On, Swing (V): Off"; + + ac.begin(); + irac.tcl112(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 20, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kOff, // Veritcal swing + stdAc::swingh_t::kAuto, // Horizontal swing + false, // Turbo + true, // Light + true, // Econo + true); // Filter (aka. Health) + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Teco) { + IRTecoAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 21C, Fan: 2 (Med), Sleep: On, " + "Swing: On"; + + ac.begin(); + irac.teco(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + 8 * 60 + 30); // Sleep + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TECO, ac._irsend.capture.decode_type); + ASSERT_EQ(kTecoBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.value); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Toshiba) { + IRToshibaAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = "Power: On, Mode: 2 (DRY), Temp: 29C, Fan: 2"; + + ac.begin(); + irac.toshiba(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 29, // Celsius + stdAc::fanspeed_t::kLow); // Fan speed + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TOSHIBA_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kToshibaACBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Trotec) { + IRTrotecESP ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 18C, Fan Speed: 3 (High), Sleep: On"; + + ac.begin(); + irac.trotec(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 18, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + 8 * 60 + 17); // Sleep + EXPECT_TRUE(ac.getPower()); + EXPECT_EQ(kTrotecCool, ac.getMode()); + EXPECT_EQ(18, ac.getTemp()); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); + EXPECT_TRUE(ac.getSleep()); + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(TROTEC, ac._irsend.capture.decode_type); + ASSERT_EQ(kTrotecBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, Vestel) { + IRVestelAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 0 (AUTO), Temp: 22C, Fan: 5 (LOW), Sleep: On, " + "Turbo: Off, Ion: On, Swing: On"; + + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0); // Sleep time + // 13 * 60 + 45); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); + + ac._irsend.reset(); + char expected_clocks[] = + "Time: 13:45, Timer: Off, On Timer: Off, Off Timer: Off"; + + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45, // Clock + false); // Don't send the normal message. + // Just for testing purposes. + ASSERT_EQ(expected_clocks, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(VESTEL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected_clocks, ac.toString()); + + // Now check it sends both messages during normal operation when the + // clock is set. + ac._irsend.reset(); + ac.begin(); + irac.vestel(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 22, // Celsius + stdAc::fanspeed_t::kLow, // Fan speed + stdAc::swingv_t::kHigh, // Veritcal swing + false, // Turbo + true, // Filter + 8 * 60 + 0, // Sleep time + 13 * 60 + 45); // Clock + + EXPECT_EQ( + "f38000d50" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s1535m520s1535m520s1535m520s1535m520s480m520s1535m520s480m520s1535" + "m520s1535m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s480m520s1535m520s1535m520s480" + "m520s1535m520s480m520s1535m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s1535m520s480m520s1535m520s1535m520s1535m520s1535" + "m520s100000" + "m3110s9066" + "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s1535m520s480m520s480m520s480m520s1535m520s1535m520s480" + "m520s1535m520s1535m520s1535m520s1535m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s1535m520s1535" + "m520s480m520s480m520s480m520s480m520s1535m520s480m520s1535m520s1535" + "m520s480m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" + "m520s100000", ac._irsend.outputStr()); +} + + +TEST(TestIRac, Whirlpool) { + IRWhirlpoolAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Model: 1 (DG11J13A), Power toggle: On, Mode: 1 (AUTO), Temp: 21C, " + "Fan: 3 (LOW), Swing: On, Light: On, Clock: 23:58, On Timer: Off, " + "Off Timer: Off, Sleep: On, Super: Off, Command: 1 (POWER)"; + + ac.begin(); + irac.whirlpool(&ac, + DG11J13A, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 21, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + false, // Turbo + true, // Light + 8 * 60 + 30, // Sleep + 23 * 60 + 58); // Clock + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(WHIRLPOOL_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kWhirlpoolAcBits, ac._irsend.capture.bits); + ac.setRaw(ac._irsend.capture.state); + ASSERT_EQ(expected, ac.toString()); +} + +TEST(TestIRac, strToBool) { + EXPECT_TRUE(IRac::strToBool("ON")); + EXPECT_TRUE(IRac::strToBool("1")); + EXPECT_TRUE(IRac::strToBool("TRUE")); + EXPECT_TRUE(IRac::strToBool("YES")); + EXPECT_FALSE(IRac::strToBool("OFF")); + EXPECT_FALSE(IRac::strToBool("0")); + EXPECT_FALSE(IRac::strToBool("FALSE")); + EXPECT_FALSE(IRac::strToBool("NO")); + EXPECT_FALSE(IRac::strToBool("FOOBAR")); + EXPECT_TRUE(IRac::strToBool("FOOBAR", true)); +} + +TEST(TestIRac, strToOpmode) { + EXPECT_EQ(stdAc::opmode_t::kAuto, IRac::strToOpmode("AUTO")); + EXPECT_EQ(stdAc::opmode_t::kCool, IRac::strToOpmode("COOL")); + EXPECT_EQ(stdAc::opmode_t::kHeat, IRac::strToOpmode("HEAT")); + EXPECT_EQ(stdAc::opmode_t::kDry, IRac::strToOpmode("DRY")); + EXPECT_EQ(stdAc::opmode_t::kFan, IRac::strToOpmode("FAN")); + EXPECT_EQ(stdAc::opmode_t::kFan, IRac::strToOpmode("FAN_ONLY")); + EXPECT_EQ(stdAc::opmode_t::kAuto, IRac::strToOpmode("FOOBAR")); + EXPECT_EQ(stdAc::opmode_t::kOff, IRac::strToOpmode("OFF")); + EXPECT_EQ(stdAc::opmode_t::kOff, IRac::strToOpmode("FOOBAR", + stdAc::opmode_t::kOff)); +} + +TEST(TestIRac, strToFanspeed) { + EXPECT_EQ(stdAc::fanspeed_t::kAuto, IRac::strToFanspeed("AUTO")); + EXPECT_EQ(stdAc::fanspeed_t::kMin, IRac::strToFanspeed("MIN")); + EXPECT_EQ(stdAc::fanspeed_t::kLow, IRac::strToFanspeed("LOW")); + EXPECT_EQ(stdAc::fanspeed_t::kMedium, IRac::strToFanspeed("MEDIUM")); + EXPECT_EQ(stdAc::fanspeed_t::kHigh, IRac::strToFanspeed("HIGH")); + EXPECT_EQ(stdAc::fanspeed_t::kMax, IRac::strToFanspeed("MAX")); + EXPECT_EQ(stdAc::fanspeed_t::kAuto, IRac::strToFanspeed("FOOBAR")); + EXPECT_EQ(stdAc::fanspeed_t::kMin, + IRac::strToFanspeed("FOOBAR", stdAc::fanspeed_t::kMin)); +} + +TEST(TestIRac, strToSwingV) { + EXPECT_EQ(stdAc::swingv_t::kAuto, IRac::strToSwingV("AUTO")); + EXPECT_EQ(stdAc::swingv_t::kLowest, IRac::strToSwingV("LOWEST")); + EXPECT_EQ(stdAc::swingv_t::kLow, IRac::strToSwingV("LOW")); + EXPECT_EQ(stdAc::swingv_t::kMiddle, IRac::strToSwingV("MIDDLE")); + EXPECT_EQ(stdAc::swingv_t::kHigh, IRac::strToSwingV("HIGH")); + EXPECT_EQ(stdAc::swingv_t::kHighest, IRac::strToSwingV("HIGHEST")); + EXPECT_EQ(stdAc::swingv_t::kOff, IRac::strToSwingV("OFF")); + EXPECT_EQ(stdAc::swingv_t::kOff, IRac::strToSwingV("FOOBAR")); + EXPECT_EQ(stdAc::swingv_t::kAuto, + IRac::strToSwingV("FOOBAR", stdAc::swingv_t::kAuto)); +} + +TEST(TestIRac, strToSwingH) { + EXPECT_EQ(stdAc::swingh_t::kAuto, IRac::strToSwingH("AUTO")); + EXPECT_EQ(stdAc::swingh_t::kLeftMax, IRac::strToSwingH("MAX LEFT")); + EXPECT_EQ(stdAc::swingh_t::kLeft, IRac::strToSwingH("LEFT")); + EXPECT_EQ(stdAc::swingh_t::kMiddle, IRac::strToSwingH("CENTRE")); + EXPECT_EQ(stdAc::swingh_t::kRight, IRac::strToSwingH("RIGHT")); + EXPECT_EQ(stdAc::swingh_t::kRightMax, IRac::strToSwingH("RIGHTMAX")); + EXPECT_EQ(stdAc::swingh_t::kOff, IRac::strToSwingH("OFF")); + EXPECT_EQ(stdAc::swingh_t::kOff, IRac::strToSwingH("FOOBAR")); + EXPECT_EQ(stdAc::swingh_t::kAuto, + IRac::strToSwingH("FOOBAR", stdAc::swingh_t::kAuto)); +} + +TEST(TestIRac, strToModel) { + EXPECT_EQ(panasonic_ac_remote_model_t::kPanasonicLke, + IRac::strToModel("LKE")); + EXPECT_EQ(panasonic_ac_remote_model_t::kPanasonicLke, + IRac::strToModel("PANASONICLKE")); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, + IRac::strToModel("ARRAH2E")); + EXPECT_EQ(whirlpool_ac_remote_model_t::DG11J13A, + IRac::strToModel("DG11J13A")); + EXPECT_EQ(1, IRac::strToModel("1")); + EXPECT_EQ(10, IRac::strToModel("10")); + EXPECT_EQ(-1, IRac::strToModel("0")); + EXPECT_EQ(-1, IRac::strToModel("FOOBAR")); + EXPECT_EQ(0, IRac::strToModel("FOOBAR", 0)); +} + +TEST(TestIRac, boolToString) { + EXPECT_EQ("on", IRac::boolToString(true)); + EXPECT_EQ("off", IRac::boolToString(false)); +} + +TEST(TestIRac, opmodeToString) { + EXPECT_EQ("off", IRac::opmodeToString(stdAc::opmode_t::kOff)); + EXPECT_EQ("auto", IRac::opmodeToString(stdAc::opmode_t::kAuto)); + EXPECT_EQ("cool", IRac::opmodeToString(stdAc::opmode_t::kCool)); + EXPECT_EQ("unknown", IRac::opmodeToString((stdAc::opmode_t)500)); +} + +TEST(TestIRac, fanspeedToString) { + EXPECT_EQ("low", IRac::fanspeedToString(stdAc::fanspeed_t::kLow)); + EXPECT_EQ("auto", IRac::fanspeedToString(stdAc::fanspeed_t::kAuto)); + EXPECT_EQ("unknown", IRac::fanspeedToString((stdAc::fanspeed_t)500)); +} + +TEST(TestIRac, swingvToString) { + EXPECT_EQ("off", IRac::swingvToString(stdAc::swingv_t::kOff)); + EXPECT_EQ("low", IRac::swingvToString(stdAc::swingv_t::kLow)); + EXPECT_EQ("auto", IRac::swingvToString(stdAc::swingv_t::kAuto)); + EXPECT_EQ("unknown", IRac::swingvToString((stdAc::swingv_t)500)); +} + +TEST(TestIRac, swinghToString) { + EXPECT_EQ("off", IRac::swinghToString(stdAc::swingh_t::kOff)); + EXPECT_EQ("left", IRac::swinghToString(stdAc::swingh_t::kLeft)); + EXPECT_EQ("auto", IRac::swinghToString(stdAc::swingh_t::kAuto)); + EXPECT_EQ("unknown", IRac::swinghToString((stdAc::swingh_t)500)); +} diff --git a/lib/IRremoteESP8266/test/IRrecv_test.cpp b/lib/IRremoteESP8266/test/IRrecv_test.cpp index 85b6685..c6268a3 100644 --- a/lib/IRremoteESP8266/test/IRrecv_test.cpp +++ b/lib/IRremoteESP8266/test/IRrecv_test.cpp @@ -399,7 +399,7 @@ TEST(TestDecode, DecodeDenon) { irsend.makeDecodeResult(); ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); + EXPECT_EQ(kDenonBits, irsend.capture.bits); EXPECT_EQ(0x2278, irsend.capture.value); // Legacy Denon 14-bit message. irsend.reset(); @@ -407,15 +407,15 @@ TEST(TestDecode, DecodeDenon) { irsend.makeDecodeResult(); ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); + EXPECT_EQ(kDenonBits, irsend.capture.bits); EXPECT_EQ(0x1278, irsend.capture.value); // Normal Denon 48-bit message. (Panasonic/Kaseikyo) irsend.reset(); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits); irsend.makeDecodeResult(); ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_48_BITS, irsend.capture.bits); + EXPECT_EQ(kDenon48Bits, irsend.capture.bits); EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); } diff --git a/lib/IRremoteESP8266/test/IRsend_test.cpp b/lib/IRremoteESP8266/test/IRsend_test.cpp index 03a59fc..9afe944 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266/test/IRsend_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017,2019 David Conran #include "IRsend_test.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRutils.h" #include "gtest/gtest.h" @@ -20,9 +21,9 @@ TEST(TestSendData, SendSingleBit) { IRsendTest irsend(4); irsend.begin(); irsend.sendData(1, 2, 3, 4, 0b1, 1, true); - EXPECT_EQ("m1s2", irsend.outputStr()); + EXPECT_EQ("d50m1s2", irsend.outputStr()); irsend.sendData(1, 2, 3, 4, 0b0, 1, true); - EXPECT_EQ("m3s4", irsend.outputStr()); + EXPECT_EQ("d50m3s4", irsend.outputStr()); } // Test sending bit order. @@ -30,11 +31,11 @@ TEST(TestSendData, TestingBitSendOrder) { IRsendTest irsend(4); irsend.begin(); irsend.sendData(1, 2, 3, 4, 0b10, 2, true); - EXPECT_EQ("m1s2m3s4", irsend.outputStr()); + EXPECT_EQ("d50m1s2m3s4", irsend.outputStr()); irsend.sendData(1, 2, 3, 4, 0b10, 2, false); - EXPECT_EQ("m3s4m1s2", irsend.outputStr()); + EXPECT_EQ("d50m3s4m1s2", irsend.outputStr()); irsend.sendData(1, 2, 3, 4, 0b0001, 4, false); - EXPECT_EQ("m1s2m3s4m3s4m3s4", irsend.outputStr()); + EXPECT_EQ("d50m1s2m3s4m3s4m3s4", irsend.outputStr()); } // Test sending typical data. @@ -42,10 +43,12 @@ TEST(TestSendData, SendTypicalData) { IRsendTest irsend(4); irsend.begin(); irsend.sendData(1, 2, 3, 4, 0b1010110011110000, 16, true); - EXPECT_EQ("m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4m3s4", - irsend.outputStr()); + EXPECT_EQ( + "d50m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4m3s4", + irsend.outputStr()); irsend.sendData(1, 2, 3, 4, 0x1234567890ABCDEF, 64, true); EXPECT_EQ( + "d50" "m3s4m3s4m3s4m1s2m3s4m3s4m1s2m3s4m3s4m3s4m1s2m1s2m3s4m1s2m3s4m3s4" "m3s4m1s2m3s4m1s2m3s4m1s2m1s2m3s4m3s4m1s2m1s2m1s2m1s2m3s4m3s4m3s4" "m1s2m3s4m3s4m1s2m3s4m3s4m3s4m3s4m1s2m3s4m1s2m3s4m1s2m3s4m1s2m1s2" @@ -59,6 +62,7 @@ TEST(TestSendData, SendOverLargeData) { irsend.begin(); irsend.sendData(1, 2, 3, 4, 0xFFFFFFFFFFFFFFFF, 70, true); EXPECT_EQ( + "d50" "m3s4m3s4m3s4m3s4m3s4m3s4" "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" @@ -72,9 +76,92 @@ TEST(TestIRSend, InvertedOutput) { IRsendTest irsend(4, true); irsend.begin(); irsend.sendData(1, 2, 3, 4, 0b1, 1, true); - EXPECT_EQ("s1m2", irsend.outputStr()); + EXPECT_EQ("d50s1m2", irsend.outputStr()); irsend.sendData(1, 2, 3, 4, 0b0, 1, true); - EXPECT_EQ("s3m4", irsend.outputStr()); + EXPECT_EQ("d50s3m4", irsend.outputStr()); +} + +// Test we correctly pick up frequency changes. +TEST(TestIRSend, DetectFreqChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ( + "f40000d50" + "m1s2" + "f38000" + "m1s2" + "f40000" + "m1s2" + "f38000" + "m1s2", + irsend.outputStr()); + irsend.reset(); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(40); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + irsend.enableIROut(38); // 40kHz + irsend.sendData(1, 2, 3, 4, 0b1, 1, true); + EXPECT_EQ( + "f40000d50" + "m1s2m1s2" + "f38000m1s2m1s2", + irsend.outputStr()); +} + +// Test we correctly pick up duty cycle changes. +TEST(TestIRSend, DetectDutyChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + EXPECT_EQ( + "f38000d33" + "m1s2m3s4m7s8", + irsend.outputStr()); + + irsend.reset(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 50); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 25); + EXPECT_EQ( + "f38000d50" + "m1s2m3s4m7s8" + "d33" + "m1s2m3s4m7s8" + "d25" + "m1s2m3s4m7s8", + irsend.outputStr()); +} + + +// Test we correctly pick up frequency AND duty changes. +TEST(TestIRSend, DetectFreqAndDutyChanges) { + IRsendTest irsend(0); + + irsend.begin(); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 50); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 38000, true, 0, 33); + irsend.sendGeneric(1, 2, 3, 4, 5, 6, 7, 8, 0b1, 1, 40000, true, 0, 25); + EXPECT_EQ( + "f38000d50" + "m1s2m3s4m7s8" + "d33" + "m1s2m3s4m7s8" + "f40000d25" + "m1s2m3s4m7s8", + irsend.outputStr()); } // Test typical use of sendRaw(). @@ -94,6 +181,7 @@ TEST(TestSendRaw, GeneralUse) { irsend.sendRaw(rawData, 67, 38); EXPECT_EQ( + "f38000d50" "m8950s4500" "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" @@ -110,6 +198,7 @@ TEST(TestSendRaw, GeneralUse) { EXPECT_EQ(32, irsend.capture.bits); EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); EXPECT_EQ( + "f38000d50" "m8950s4500" "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" @@ -290,7 +379,7 @@ TEST(TestLowLevelSend, SpaceNoModulation) { EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); } -// Test expected to work/produce a message for irsend:send() +// Test expected to work/produce a message for simple irsend:send() TEST(TestSend, GenericSimpleSendMethod) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -569,7 +658,7 @@ TEST(TestSend, GenericSimpleSendMethod) { EXPECT_EQ(0x1234, irsend.capture.value); } -// Test some expected types to NOT work/produce a message via irsend:send() +// Test some expected types to NOT work/produce a message via irsend:send() TEST(TestSend, GenericSimpleSendMethodFailure) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -582,17 +671,71 @@ TEST(TestSend, GenericSimpleSendMethodFailure) { ASSERT_FALSE(irrecv.decode(&irsend.capture)); // For every A/C protocol which decodes to having a state[]. - for (int i = 0; i < 200; i++) { + for (int i = 0; i <= kLastDecodeType; i++) { if (hasACState((decode_type_t)i) && i != GREE) { // Gree is an exception. // Check it fails. - ASSERT_FALSE(irsend.send((decode_type_t)i, 0, 0)); + ASSERT_FALSE(irsend.send((decode_type_t)i, (uint64_t)0, 0)); } } // Test some other special cases. - ASSERT_FALSE(irsend.send(UNKNOWN, 0, 0)); - ASSERT_FALSE(irsend.send(UNUSED, 0, 0)); - ASSERT_FALSE(irsend.send(RAW, 0, 0)); - ASSERT_FALSE(irsend.send(PRONTO, 0, 0)); - ASSERT_FALSE(irsend.send(GLOBALCACHE, 0, 0)); + ASSERT_FALSE(irsend.send(UNKNOWN, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(UNUSED, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(RAW, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(PRONTO, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(GLOBALCACHE, (uint64_t)0, 0)); +} + +// Test expected to work/produce a message for complex irsend:send() +TEST(TestSend, GenericComplexSendMethod) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint8_t kelvinator[kKelvinatorStateLength] = { + 0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, + 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0}; + ASSERT_TRUE(irsend.send(KELVINATOR, kelvinator, kKelvinatorStateLength)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(KELVINATOR, irsend.capture.decode_type); + EXPECT_EQ(kKelvinatorStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(kelvinator, irsend.capture.state, irsend.capture.bits / 8); + + irsend.reset(); + uint8_t panasonic[kPanasonicAcStateLength] = { + 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, + 0x20, 0xE0, 0x04, 0x00, 0x4E, 0x2E, 0x80, 0xAF, 0x00, + 0x00, 0x0E, 0xE0, 0x11, 0x00, 0x01, 0x00, 0x06, 0xB7}; + ASSERT_TRUE(irsend.send(PANASONIC_AC, panasonic, kPanasonicAcStateLength)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PANASONIC_AC, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(panasonic, irsend.capture.state, irsend.capture.bits / 8); +} + +// Test some expected types to NOT work/produce a complex message via +// irsend:send() +TEST(TestSend, GenericComplexSendMethodFailure) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Check nothing is sent for unexpected protocols + uint8_t state[kStateSizeMax] = {}; + irsend.reset(); + ASSERT_FALSE(irsend.send(NEC, state, kNECBits)); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decode(&irsend.capture)); + + // For every A/C protocol which DOESN'T decode to having a state[]. + for (int i = -1; i <= kLastDecodeType; i++) { + if (!hasACState((decode_type_t)i)) + // Check it fails. + ASSERT_FALSE(irsend.send((decode_type_t)i, state, 0)); + else // Or if it is okay. + ASSERT_TRUE(irsend.send((decode_type_t)i, state, 0)); + } } diff --git a/lib/IRremoteESP8266/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index 6d9fe51..f434094 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -17,12 +17,14 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. -uint32_t _IRtimer_unittest_now = 0; +extern uint32_t _IRtimer_unittest_now; #endif // UNIT_TEST class IRsendTest : public IRsend { public: uint32_t output[OUTPUT_BUF]; + uint32_t freq[OUTPUT_BUF]; + uint8_t duty[OUTPUT_BUF]; uint16_t last; uint16_t rawbuf[RAW_BUF]; decode_results capture; @@ -40,8 +42,22 @@ class IRsendTest : public IRsend { std::string outputStr() { std::stringstream result; + uint8_t lastduty = UINT8_MAX; // An impossible duty cycle value. + uint32_t lastfreq = 0; // An impossible frequency value. if (last == 0 && output[0] == 0) return ""; for (uint16_t i = 0; i <= last; i++) { + // Display the frequency only if it changes. + if (freq[i] != lastfreq) { + result << "f"; + result << freq[i]; + lastfreq = freq[i]; + } + // Display the duty cycle only if it changes. + if (duty[i] != lastduty) { + result << "d"; + result << static_cast(duty[i]); + lastduty = duty[i]; + } if ((i & 1) != outputOff) // Odd XOR outputOff result << "s"; else @@ -92,6 +108,8 @@ class IRsendTest : public IRsend { output[++last] = usec; else output[last] += usec; + duty[last] = _dutycycle; + freq[last] = _freq_unittest; return 0; } @@ -103,6 +121,8 @@ class IRsendTest : public IRsend { } else { output[++last] = time; } + duty[last] = _dutycycle; + freq[last] = _freq_unittest; } }; diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 57eceed..9ce0fa2 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -3,6 +3,7 @@ #include "IRutils.h" #include #include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -395,3 +396,96 @@ TEST(TestCountBits, Integer) { ASSERT_EQ(64, countBits(data, 64)); ASSERT_EQ(0, countBits(data, 64, false)); } + +TEST(TestStrToDecodeType, strToDecodeType) { + EXPECT_EQ(decode_type_t::NEC, strToDecodeType("NEC")); + EXPECT_EQ(decode_type_t::KELVINATOR, strToDecodeType("KELVINATOR")); + EXPECT_EQ(decode_type_t::UNKNOWN, strToDecodeType("foo")); +} + +TEST(TestUtils, htmlEscape) { + EXPECT_EQ("", htmlEscape("")); + EXPECT_EQ("No Changes", htmlEscape("No Changes")); + EXPECT_EQ("No\tChanges+_%^$@~`\n:\\", htmlEscape("No\tChanges+_%^$@~`\n:\\")); + EXPECT_EQ(""With Changes"", htmlEscape("\"With Changes\"")); + EXPECT_EQ( + "';!‐"<>&#equals;&#{}" + "()", htmlEscape("';!-\"<>=&#{}()")); + EXPECT_EQ("""", htmlEscape("\"\"")); + EXPECT_EQ( + "&quot;&lt;&apos;&gt;&amp;", + htmlEscape(""<'>&")); +} + +TEST(TestUtils, TemperatureConversion) { + // Freezing point of water. + ASSERT_EQ(32.0, celsiusToFahrenheit(0.0)); + ASSERT_EQ(0.0, fahrenheitToCelsius(32.0)); + // Boiling point of water. + ASSERT_EQ(212.0, celsiusToFahrenheit(100.0)); + ASSERT_EQ(100.0, fahrenheitToCelsius(212.0)); + // Room Temp. (RTP) + ASSERT_EQ(77.0, celsiusToFahrenheit(25.0)); + ASSERT_EQ(25.0, fahrenheitToCelsius(77.0)); + // Misc + ASSERT_EQ(-40.0, fahrenheitToCelsius(-40.0)); +} + +TEST(TestResultToRawArray, TypicalCase) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Generate a known message. + irsend.reset(); + irsend.sendNikai(0xD0F2F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(NIKAI, irsend.capture.decode_type); + ASSERT_EQ(kNikaiBits, irsend.capture.bits); + EXPECT_EQ( + "uint16_t rawData[52] = {4000, 4000, 500, 2000, 500, 2000, " + "500, 2000, 500, 2000, 500, 1000, 500, 1000, 500, 2000, 500, 1000, " + "500, 2000, 500, 2000, 500, 2000, 500, 2000, 500, 1000, 500, 1000, " + "500, 1000, 500, 1000, 500, 2000, 500, 2000, 500, 1000, 500, 2000, " + "500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 8500 };" + " // NIKAI D0F2F\n" + "uint64_t data = 0xD0F2F;\n", + resultToSourceCode(&irsend.capture)); + uint16_t rawData[52] = { // Data taken from above. + 4000, 4000, 500, 2000, 500, 2000, 500, 2000, 500, 2000, 500, 1000, 500, + 1000, 500, 2000, 500, 1000, 500, 2000, 500, 2000, 500, 2000, 500, 2000, + 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 2000, 500, 2000, 500, + 1000, 500, 2000, 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 8500}; + uint16_t * result = resultToRawArray(&irsend.capture); + ASSERT_EQ(52, getCorrectedRawLength(&irsend.capture)); + EXPECT_STATE_EQ(rawData, result, getCorrectedRawLength(&irsend.capture)); + if (result != NULL) delete[] result; +} + +TEST(TestResultToRawArray, LargeValues) { + IRsendTest irsend(0); + IRrecv irrecv(1); + uint16_t test_data[9] = {10, 20, 30, 40, 50, 60, 70, 80, 90}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(test_data, 9, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + uint16_t * result = resultToRawArray(&irsend.capture); + ASSERT_EQ(9, getCorrectedRawLength(&irsend.capture)); + EXPECT_STATE_EQ(test_data, result, 9); + if (result != NULL) delete[] result; + // Stick in some large values. + irsend.capture.rawbuf[3] = 60000; + EXPECT_EQ( + "uint16_t rawData[11] = {10, 20, 65535, 0, 54465, 40, 50, 60, 70, " + "80, 90}; // UNKNOWN 54051FFD\n", + resultToSourceCode(&irsend.capture)); + uint16_t large_test_data[11] = { + 10, 20, 65535, 0, 54465, 40, 50, 60, 70, 80, 90}; + ASSERT_EQ(11, getCorrectedRawLength(&irsend.capture)); + result = resultToRawArray(&irsend.capture); + EXPECT_STATE_EQ(large_test_data, result, 11); + if (result != NULL) delete[] result; +} diff --git a/lib/IRremoteESP8266/test/Makefile b/lib/IRremoteESP8266/test/Makefile index 5787dc5..0a82e27 100644 --- a/lib/IRremoteESP8266/test/Makefile +++ b/lib/IRremoteESP8266/test/Makefile @@ -16,6 +16,7 @@ GTEST_DIR = ../lib/googletest/googletest # Where to find user code. USER_DIR = ../src +INCLUDES = -I$(USER_DIR) -I. # Flags passed to the preprocessor. # Set Google Test's header directory as a system directory, such that @@ -23,7 +24,7 @@ USER_DIR = ../src CPPFLAGS += -isystem $(GTEST_DIR)/include -DUNIT_TEST # Flags passed to the C++ compiler. -CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 # All tests produced by this Makefile. Remember to add new tests you # created to the list. @@ -36,7 +37,9 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \ ir_Toshiba_test ir_Midea_test ir_Magiquest_test ir_Lasertag_test \ ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \ ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test \ - ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test + ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test ir_Lego_test IRac_test \ + ir_MitsubishiHeavy_test ir_Trotec_test ir_Argo_test ir_Goodweather_test \ + ir_Inax_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -80,7 +83,8 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Kelvinator.o ir_Daikin.o ir_Gree.o ir_Pronto.o ir_Nikai.o ir_Toshiba.o \ ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \ ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \ - ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o + ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o ir_Argo.o \ + ir_Trotec.o ir_MitsubishiHeavy.o ir_Goodweather.o ir_Inax.o # All the IR Protocol header files. PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ @@ -91,9 +95,12 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Midea.h \ $(USER_DIR)/ir_Toshiba.h \ $(USER_DIR)/ir_Daikin.h \ + $(USER_DIR)/ir_Goodweather.h \ $(USER_DIR)/ir_Kelvinator.h \ $(USER_DIR)/ir_Mitsubishi.h \ + $(USER_DIR)/ir_MitsubishiHeavy.h \ $(USER_DIR)/ir_NEC.h \ + $(USER_DIR)/ir_Sharp.h \ $(USER_DIR)/ir_Samsung.h \ $(USER_DIR)/ir_Trotec.h \ $(USER_DIR)/ir_Fujitsu.h \ @@ -102,14 +109,15 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Whirlpool.h \ $(USER_DIR)/ir_Vestel.h \ $(USER_DIR)/ir_Tcl.h \ - $(USER_DIR)/ir_Teco.h + $(USER_DIR)/ir_Teco.h \ + $(USER_DIR)/ir_Trotec.h # Common object files -COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o ir_GlobalCache.o \ +COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o IRac.o ir_GlobalCache.o \ $(PROTOCOLS) gtest_main.a # Common dependencies COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \ $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h \ - $(PROTOCOLS_H) + $(USER_DIR)/IRac.h $(PROTOCOLS_H) # Common test dependencies COMMON_TEST_DEPS = $(COMMON_DEPS) IRrecv_test.h IRsend_test.h @@ -140,7 +148,7 @@ IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteES $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRutils.cpp IRutils_test.o : IRutils_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRutils_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRutils_test.cpp IRutils_test : IRutils_test.o ir_NEC.o ir_Nikai.o ir_Toshiba.o $(COMMON_OBJ) gtest_main.a $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -152,7 +160,7 @@ IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP82 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRsend.cpp IRsend_test.o : IRsend_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRsend_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRsend_test.cpp IRsend_test : IRsend_test.o $(COMMON_OBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -161,16 +169,25 @@ IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP82 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp IRrecv_test.o : IRrecv_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c IRrecv_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRrecv_test.cpp IRrecv_test : IRrecv_test.o $(COMMON_OBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ +IRac.o : $(USER_DIR)/IRac.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/IRac.cpp + +IRac_test.o : IRac_test.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRac_test.cpp + +IRac_test : IRac_test.o $(COMMON_OBJ) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(USER_DIR)/ir_NEC.h $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp ir_NEC_test.o : ir_NEC_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_NEC_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_NEC_test.cpp ir_NEC_test : $(COMMON_OBJ) ir_NEC_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -179,7 +196,7 @@ ir_GlobalCache.o : $(USER_DIR)/ir_GlobalCache.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GlobalCache.cpp ir_GlobalCache_test.o : ir_GlobalCache_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_GlobalCache_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_GlobalCache_test.cpp ir_GlobalCache_test : $(COMMON_OBJ) ir_GlobalCache_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -188,7 +205,7 @@ ir_Sherwood.o : $(USER_DIR)/ir_Sherwood.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sherwood.cpp ir_Sherwood_test.o : ir_Sherwood_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sherwood_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sherwood_test.cpp ir_Sherwood_test : $(COMMON_OBJ) ir_Sherwood_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -197,25 +214,25 @@ ir_Sony.o : $(USER_DIR)/ir_Sony.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sony.cpp ir_Sony_test.o : ir_Sony_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sony_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sony_test.cpp ir_Sony_test : $(COMMON_OBJ) ir_Sony_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Samsung.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Samsung.cpp ir_Samsung_test.o : ir_Samsung_test.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Samsung_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Samsung_test.cpp ir_Samsung_test : $(COMMON_OBJ) ir_Samsung_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Kelvinator.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp ir_Kelvinator_test.o : ir_Kelvinator_test.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Kelvinator_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Kelvinator_test.cpp ir_Kelvinator_test : $(COMMON_OBJ) ir_Kelvinator_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -224,7 +241,7 @@ ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp ir_JVC_test.o : ir_JVC_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_JVC_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_JVC_test.cpp ir_JVC_test : $(COMMON_OBJ) ir_JVC_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -233,7 +250,7 @@ ir_RCMM.o : $(USER_DIR)/ir_RCMM.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RCMM.cpp ir_RCMM_test.o : ir_RCMM_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_RCMM_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_RCMM_test.cpp ir_RCMM_test : $(COMMON_OBJ) ir_RCMM_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -242,34 +259,43 @@ ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp ir_LG_test.o : ir_LG_test.cpp $(USER_DIR)/ir_LG.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_LG_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_LG_test.cpp ir_LG_test : $(COMMON_OBJ) ir_LG_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Mitsubishi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Mitsubishi.cpp ir_Mitsubishi_test.o : ir_Mitsubishi_test.cpp $(USER_DIR)/ir_Mitsubishi.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Mitsubishi_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Mitsubishi_test.cpp ir_Mitsubishi_test : $(COMMON_OBJ) ir_Mitsubishi_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ +ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_MitsubishiHeavy.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_MitsubishiHeavy.cpp + +ir_MitsubishiHeavy_test.o : ir_MitsubishiHeavy_test.cpp $(USER_DIR)/ir_MitsubishiHeavy.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_MitsubishiHeavy_test.cpp + +ir_MitsubishiHeavy_test : $(COMMON_OBJ) ir_MitsubishiHeavy_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Fujitsu.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Fujitsu.cpp ir_Fujitsu_test.o : ir_Fujitsu_test.cpp $(USER_DIR)/ir_Fujitsu.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Fujitsu_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Fujitsu_test.cpp ir_Fujitsu_test : $(COMMON_OBJ) ir_Fujitsu_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Sharp.cpp ir_Sharp_test.o : ir_Sharp_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sharp_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sharp_test.cpp ir_Sharp_test : $(COMMON_OBJ) ir_Sharp_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -278,16 +304,16 @@ ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp ir_RC5_RC6_test.o : ir_RC5_RC6_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_RC5_RC6_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_RC5_RC6_test.cpp ir_RC5_RC6_test : $(COMMON_OBJ) ir_RC5_RC6_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Panasonic.o : $(USER_DIR)/ir_Panasonic.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Panasonic.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Panasonic.cpp ir_Panasonic_test.o : ir_Panasonic_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Panasonic_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Panasonic_test.cpp ir_Panasonic_test : $(COMMON_OBJ) ir_Panasonic_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -296,7 +322,7 @@ ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp ir_Dish_test.o : ir_Dish_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Dish_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Dish_test.cpp ir_Dish_test : $(COMMON_OBJ) ir_Dish_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -305,16 +331,16 @@ ir_Whynter.o : $(USER_DIR)/ir_Whynter.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whynter.cpp ir_Whynter_test.o : ir_Whynter_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Whynter_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Whynter_test.cpp ir_Whynter_test : $(COMMON_OBJ) ir_Whynter_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Coolix.o : $(USER_DIR)/ir_Coolix.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Coolix.cpp ir_Coolix_test.o : ir_Coolix_test.cpp $(USER_DIR)/ir_Coolix.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Coolix_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Coolix_test.cpp ir_Coolix_test : $(COMMON_OBJ) ir_Coolix_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -323,7 +349,7 @@ ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp ir_Aiwa_test.o : ir_Aiwa_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Aiwa_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Aiwa_test.cpp ir_Aiwa_test : $(COMMON_OBJ) ir_Aiwa_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -332,7 +358,7 @@ ir_Denon.o : $(USER_DIR)/ir_Denon.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Denon.cpp ir_Denon_test.o : ir_Denon_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Denon_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Denon_test.cpp ir_Denon_test : $(COMMON_OBJ) ir_Denon_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -341,25 +367,25 @@ ir_Sanyo.o : $(USER_DIR)/ir_Sanyo.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sanyo.cpp ir_Sanyo_test.o : ir_Sanyo_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Sanyo_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Sanyo_test.cpp ir_Sanyo_test : $(COMMON_OBJ) ir_Sanyo_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Daikin.o : $(USER_DIR)/ir_Daikin.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Daikin.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Daikin.cpp ir_Daikin_test.o : ir_Daikin_test.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Daikin_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Daikin_test.cpp ir_Daikin_test : $(COMMON_OBJ) ir_Daikin_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Gree.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Gree.cpp ir_Gree_test.o : ir_Gree_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Gree_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Gree_test.cpp ir_Gree_test : $(COMMON_OBJ) ir_Gree_test.o ir_Kelvinator.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -368,7 +394,7 @@ ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pronto.cpp ir_Pronto_test.o : ir_Pronto_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Pronto_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Pronto_test.cpp ir_Pronto_test : $(COMMON_OBJ) ir_Pronto_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -377,25 +403,25 @@ ir_Nikai.o : $(USER_DIR)/ir_Nikai.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Nikai.cpp ir_Nikai_test.o : ir_Nikai_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Nikai_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Nikai_test.cpp ir_Nikai_test : $(COMMON_OBJ) ir_Nikai_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Toshiba.o : $(USER_DIR)/ir_Toshiba.cpp $(USER_DIR)/ir_Toshiba.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Toshiba.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Toshiba.cpp ir_Toshiba_test.o : ir_Toshiba_test.cpp $(USER_DIR)/ir_Toshiba.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Toshiba_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Toshiba_test.cpp ir_Toshiba_test : $(COMMON_OBJ) ir_Toshiba_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Midea.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Midea.cpp ir_Midea_test.o : ir_Midea_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Midea_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Midea_test.cpp ir_Midea_test : $(COMMON_OBJ) ir_Midea_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -404,7 +430,7 @@ ir_Magiquest.o : $(USER_DIR)/ir_Magiquest.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Magiquest.cpp ir_Magiquest_test.o : ir_Magiquest_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Magiquest_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Magiquest_test.cpp ir_Magiquest_test : $(COMMON_OBJ) ir_Magiquest_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -413,7 +439,7 @@ ir_Lasertag.o : $(USER_DIR)/ir_Lasertag.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(GTEST_H $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lasertag.cpp ir_Lasertag_test.o : ir_Lasertag_test.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Lasertag_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lasertag_test.cpp ir_Lasertag_test : $(COMMON_OBJ) ir_Lasertag_test.o ir_RC5_RC6.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -422,25 +448,25 @@ ir_Carrier.o : $(USER_DIR)/ir_Carrier.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Carrier.cpp ir_Carrier_test.o : ir_Carrier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Carrier_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Carrier_test.cpp ir_Carrier_test : $(COMMON_OBJ) ir_Carrier_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Haier.o : $(USER_DIR)/ir_Haier.cpp $(USER_DIR)/ir_Haier.h $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Haier.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Haier.cpp ir_Haier_test.o : ir_Haier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Haier_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Haier_test.cpp ir_Haier_test : $(COMMON_OBJ) ir_Haier_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Hitachi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Hitachi.cpp ir_Hitachi_test.o : ir_Hitachi_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Hitachi_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Hitachi_test.cpp ir_Hitachi_test : $(COMMON_OBJ) ir_Hitachi_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -449,16 +475,16 @@ ir_GICable.o : $(USER_DIR)/ir_GICable.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GICable.cpp ir_GICable_test.o : ir_GICable_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_GICable_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_GICable_test.cpp ir_GICable_test : $(COMMON_OBJ) ir_GICable_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Whirlpool.o : $(USER_DIR)/ir_Whirlpool.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whirlpool.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Whirlpool.cpp ir_Whirlpool_test.o : ir_Whirlpool_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Whirlpool_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Whirlpool_test.cpp ir_Whirlpool_test : $(COMMON_OBJ) ir_Whirlpool_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -467,7 +493,7 @@ ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp ir_Lutron_test.o : ir_Lutron_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Lutron_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lutron_test.cpp ir_Lutron_test : $(COMMON_OBJ) ir_Lutron_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -476,7 +502,7 @@ ir_Electra.o : $(USER_DIR)/ir_Electra.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Electra.cpp ir_Electra_test.o : ir_Electra_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Electra_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Electra_test.cpp ir_Electra_test : $(COMMON_OBJ) ir_Electra_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -485,7 +511,7 @@ ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(COMMON_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pioneer.cpp ir_Pioneer_test.o : ir_Pioneer_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Pioneer_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Pioneer_test.cpp ir_Pioneer_test : $(COMMON_OBJ) ir_Pioneer_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -494,34 +520,79 @@ ir_MWM.o : $(USER_DIR)/ir_MWM.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_MWM.cpp ir_MWM_test.o : ir_MWM_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_MWM_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_MWM_test.cpp ir_MWM_test : $(COMMON_OBJ) ir_MWM_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Vestel.o : $(USER_DIR)/ir_Vestel.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Vestel.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Vestel.cpp ir_Vestel_test.o : ir_Vestel_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Vestel_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Vestel_test.cpp ir_Vestel_test : $(COMMON_OBJ) ir_Vestel_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Teco.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Teco.cpp ir_Teco_test.o : ir_Teco_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Teco_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Teco_test.cpp ir_Teco_test : $(COMMON_OBJ) ir_Teco_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Tcl.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Tcl.cpp ir_Tcl_test.o : ir_Tcl_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Tcl_test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Tcl_test.cpp ir_Tcl_test : $(COMMON_OBJ) ir_Tcl_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Lego.o : $(USER_DIR)/ir_Lego.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp + +ir_Lego_test.o : ir_Lego_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Lego_test.cpp + +ir_Lego_test : $(COMMON_OBJ) ir_Lego_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Argo.o : $(USER_DIR)/ir_Argo.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Argo.cpp + +ir_Argo_test.o : ir_Argo_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Argo_test.cpp + +ir_Argo_test : $(COMMON_OBJ) ir_Argo_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Trotec.o : $(USER_DIR)/ir_Trotec.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Trotec.cpp + +ir_Trotec_test.o : ir_Trotec_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Trotec_test.cpp + +ir_Trotec_test : $(COMMON_OBJ) ir_Trotec_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Goodweather.o : $(USER_DIR)/ir_Goodweather.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Goodweather.cpp + +ir_Goodweather_test.o : ir_Goodweather_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Goodweather_test.cpp + +ir_Goodweather_test : $(COMMON_OBJ) ir_Goodweather_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +ir_Inax.o : $(USER_DIR)/ir_Inax.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Inax.cpp + +ir_Inax_test.o : ir_Inax_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Inax_test.cpp + +ir_Inax_test : $(COMMON_OBJ) ir_Inax_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ diff --git a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp index c5469d4..2fa63af 100644 --- a/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Aiwa_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendAiwa, SendDataOnly) { irsend.reset(); irsend.sendAiwaRCT501(0x7F); // Aiwa Power Toggle. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -43,6 +44,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 0); // No repeats. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -54,6 +56,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -66,6 +69,7 @@ TEST(TestSendAiwa, SendWithRepeats) { irsend.reset(); irsend.sendAiwaRCT501(0x7F, kAiwaRcT501Bits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -86,6 +90,7 @@ TEST(TestSendAiwa, SendUnusualSize) { irsend.reset(); irsend.sendAiwaRCT501(0x12, 8); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -98,6 +103,7 @@ TEST(TestSendAiwa, SendUnusualSize) { irsend.reset(); irsend.sendAiwaRCT501(0x1234567890, 37); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266/test/ir_Argo_test.cpp b/lib/IRremoteESP8266/test/ir_Argo_test.cpp new file mode 100644 index 0000000..7932416 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Argo_test.cpp @@ -0,0 +1,221 @@ +// Copyright 2019 David Conran +#include "ir_Argo.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + + +TEST(TestArgoACClass, toCommon) { + IRArgoAC ac(0); + ac.setPower(true); + ac.setMode(kArgoCool); + ac.setTemp(20); + ac.setFan(kArgoFan3); + ac.setMax(true); + ac.setNight(true); + // Now test it. + ASSERT_EQ(decode_type_t::ARGO, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(0, ac.toCommon().sleep); + ASSERT_TRUE(ac.toCommon().turbo); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestArgoACClass, MessageConstructon) { + IRArgoAC ac(0); + ac.setPower(true); + ac.setTemp(20); + ac.setMode(kArgoCool); + ac.setFan(kArgoFanAuto); + ac.setRoomTemp(21); + ac.setiFeel(true); + ac.setMax(true); + ac.setNight(true); + + // Don't implicitly trust this. It's just a guess. + uint8_t expected[kArgoStateLength] = { + 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01}; + EXPECT_STATE_EQ(expected, ac.getRaw(), kArgoBits); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Fan: 0 (AUTO), Temp: 20C, Room Temp: 21C, " + "Max: On, iFeel: On, Night: On", + ac.toString()); +} + +// Tests for sendArgo(). + +// Test sending typical data only. +TEST(TestSendArgo, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kArgoStateLength] = { + 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01}; + + irsend.sendArgo(data); + EXPECT_EQ( + "f38000d50" + "m6400s3300" + "m400s900m400s900m400s2200m400s2200m400s900m400s2200m400s900m400s2200" + "m400s2200m400s900m400s2200m400s900m400s2200m400s2200m400s2200m400s2200" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s2200m400s900m400s900m400s2200m400s900m400s900" + "m400s900m400s2200m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900m400s900m400s2200m400s2200m400s900m400s2200m400s900m400s2200" + "m400s900m400s2200m400s2200m400s900m400s2200m400s900m400s2200m400s2200" + "m400s2200m400s900m400s900m400s900m400s900m400s900m400s900" + "m400s900", + irsend.outputStr()); +} + +// Tests for decodeArgo(). +// Decode normal Argo messages. + +TEST(TestDecodeArgo, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal Argo message. + irsend.reset(); + uint8_t expectedState[kArgoStateLength] = { + 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01}; + irsend.sendArgo(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::ARGO, irsend.capture.decode_type); + EXPECT_EQ(kArgoBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + + +TEST(TestArgoACClass, SetAndGetTemp) { + IRArgoAC ac(0); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kArgoMinTemp); + EXPECT_EQ(kArgoMinTemp, ac.getTemp()); + ac.setTemp(kArgoMaxTemp); + EXPECT_EQ(kArgoMaxTemp, ac.getTemp()); + ac.setTemp(kArgoMinTemp - 1); + EXPECT_EQ(kArgoMinTemp, ac.getTemp()); + ac.setTemp(kArgoMaxTemp + 1); + EXPECT_EQ(kArgoMaxTemp, ac.getTemp()); +} + +TEST(TestArgoACClass, SetAndGetRoomTemp) { + IRArgoAC ac(0); + + ac.setRoomTemp(25); + EXPECT_EQ(25, ac.getRoomTemp()); + ac.setRoomTemp(kArgoTempOffset); + EXPECT_EQ(kArgoTempOffset, ac.getRoomTemp()); + ac.setRoomTemp(kArgoMaxRoomTemp); + EXPECT_EQ(kArgoMaxRoomTemp, ac.getRoomTemp()); + ac.setRoomTemp(kArgoTempOffset - 1); + EXPECT_EQ(kArgoTempOffset, ac.getRoomTemp()); + ac.setRoomTemp(kArgoMaxRoomTemp + 1); + EXPECT_EQ(kArgoMaxRoomTemp, ac.getRoomTemp()); +} + +TEST(TestArgoACClass, SetAndGetMode) { + IRArgoAC ac(0); + + ac.setMode(kArgoHeat); + EXPECT_EQ(kArgoHeat, ac.getMode()); + ac.setMode(kArgoCool); + EXPECT_EQ(kArgoCool, ac.getMode()); + ac.setMode(kArgoDry); + EXPECT_EQ(kArgoDry, ac.getMode()); + ac.setMode(kArgoAuto); + EXPECT_EQ(kArgoAuto, ac.getMode()); + ac.setMode(kArgoHeatAuto); + EXPECT_EQ(kArgoHeatAuto, ac.getMode()); + ac.setMode(kArgoOff); + EXPECT_EQ(kArgoOff, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kArgoAuto, ac.getMode()); +} + +TEST(TestArgoACClass, SetAndGetFan) { + IRArgoAC ac(0); + + ac.setFan(kArgoFan3); + EXPECT_EQ(kArgoFan3, ac.getFan()); + ac.setFan(kArgoFan1); + EXPECT_EQ(kArgoFan1, ac.getFan()); + ac.setFan(kArgoFanAuto); + EXPECT_EQ(kArgoFanAuto, ac.getFan()); + ac.setFan(kArgoFan3); + EXPECT_EQ(kArgoFan3, ac.getFan()); + ASSERT_NE(7, kArgoFan3); + // Now try some unexpected value. + ac.setFan(7); + EXPECT_EQ(kArgoFan3, ac.getFan()); +} + +TEST(TestArgoACClass, Night) { + IRArgoAC ac(0); + ac.setNight(false); + ASSERT_FALSE(ac.getNight()); + ac.setNight(true); + ASSERT_TRUE(ac.getNight()); + ac.setNight(false); + ASSERT_FALSE(ac.getNight()); +} + +TEST(TestArgoACClass, iFeel) { + IRArgoAC ac(0); + ac.setiFeel(false); + ASSERT_FALSE(ac.getiFeel()); + ac.setiFeel(true); + ASSERT_TRUE(ac.getiFeel()); + ac.setiFeel(false); + ASSERT_FALSE(ac.getiFeel()); +} + +TEST(TestArgoACClass, Power) { + IRArgoAC ac(0); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); + ac.setPower(true); + ASSERT_TRUE(ac.getPower()); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); +} + +TEST(TestArgoACClass, Max) { + IRArgoAC ac(0); + ac.setMax(false); + ASSERT_FALSE(ac.getMax()); + ac.setMax(true); + ASSERT_TRUE(ac.getMax()); + ac.setMax(false); + ASSERT_FALSE(ac.getMax()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("ARGO", typeToString(decode_type_t::ARGO)); + ASSERT_EQ(decode_type_t::ARGO, strToDecodeType("ARGO")); + ASSERT_TRUE(hasACState(decode_type_t::ARGO)); +} diff --git a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp index 24bdc23..053b31d 100644 --- a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x0); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" "m628s532m628s532m628s532m628s532m628s532m628s532m628s532m628s532" @@ -37,6 +38,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x12345678); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" @@ -60,6 +62,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { irsend.reset(); irsend.sendCarrierAC(0x4CCA541D); EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s1320m628s532m628s532m628s1320m628s1320m628s532m628s532" "m628s1320m628s1320m628s532m628s532m628s1320m628s532m628s1320m628s532" @@ -89,6 +92,7 @@ TEST(TestSendCarrierAC, SendWithRepeats) { irsend.reset(); irsend.sendCarrierAC(0x12345678, kCarrierAcBits, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m8532s4228" "m628s532m628s532m628s532m628s1320m628s532m628s532m628s1320m628s532" "m628s532m628s532m628s1320m628s1320m628s532m628s1320m628s532m628s532" diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index 537a64e..1f68c76 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0x0); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -36,6 +37,7 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0xAA55AA); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -57,6 +59,7 @@ TEST(TestSendCoolix, SendDataOnly) { irsend.reset(); irsend.sendCOOLIX(0xFFFFFF); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -84,6 +87,7 @@ TEST(TestSendCoolix, SendWithRepeats) { irsend.reset(); irsend.sendCOOLIX(0xAA55AA, kCoolixBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -103,6 +107,7 @@ TEST(TestSendCoolix, SendWithRepeats) { irsend.outputStr()); irsend.sendCOOLIX(0xAA55AA, kCoolixBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" @@ -138,6 +143,7 @@ TEST(TestSendCoolix, SendUnusualSize) { irsend.reset(); irsend.sendCOOLIX(0x0, 8); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -151,6 +157,7 @@ TEST(TestSendCoolix, SendUnusualSize) { irsend.reset(); irsend.sendCOOLIX(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680" @@ -388,6 +395,8 @@ TEST(TestCoolixACClass, SetAndGetMode) { TEST(TestCoolixACClass, SetAndGetFan) { IRCoolixAC ircoolix(0); + // This mode allows pretty much everything except Auto0 speed. + ircoolix.setMode(kCoolixCool); ircoolix.setFan(kCoolixFanMax); EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); ircoolix.setFan(kCoolixFanMin); @@ -396,12 +405,29 @@ TEST(TestCoolixACClass, SetAndGetFan) { EXPECT_EQ(kCoolixFanZoneFollow, ircoolix.getFan()); ircoolix.setFan(kCoolixFanAuto); EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto0); + EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); ircoolix.setFan(kCoolixFanMax); EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); ASSERT_NE(3, kCoolixFanAuto); // Now try some unexpected value. ircoolix.setFan(3); EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); + + // These modes allows pretty much everything except Auto speed. + ircoolix.setMode(kCoolixDry); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanMax); + EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + + ircoolix.setMode(kCoolixAuto); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanMax); + EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto0); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); } TEST(TestCoolixACClass, SetGetClearSensorTempAndZoneFollow) { @@ -590,3 +616,132 @@ TEST(TestCoolixACClass, Issue624HandleSpecialStatesBetter) { ac.toString()); EXPECT_EQ(0xB2BF40, ac.getRaw()); } + +TEST(TestCoolixACClass, toCommon) { + IRCoolixAC ac(0); + ac.setPower(true); + ac.setMode(kCoolixCool); + ac.setTemp(20); + ac.setFan(kCoolixFanMax); + + // Now test it. + ASSERT_EQ(decode_type_t::COOLIX, 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().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, 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().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestCoolixACClass, Issue722) { + IRrecv irrecv(0); + IRCoolixAC ac(0); + + // Auto 17C ON pressed + uint32_t on_auto_17c_fan_auto0 = 0xB21F08; + ac.begin(); + ac.setPower(true); + ac.setMode(kCoolixAuto); + ac.setFan(kCoolixFanAuto); + ac.setTemp(17); + EXPECT_EQ(on_auto_17c_fan_auto0, ac.getRaw()); + + // Off + uint32_t off = 0xB27BE0; + ac.off(); + EXPECT_EQ(off, ac.getRaw()); + + // ON Auto Temp 18C + uint32_t on_auto_18c_fan_auto0 = 0xB21F18; + ac.setTemp(18); + EXPECT_EQ(on_auto_18c_fan_auto0, ac.getRaw()); + + // Set Mode Cool 18C + uint32_t on_cool_18c_fan_auto = 0xB2BF10; + ac.setMode(kCoolixCool); + EXPECT_EQ(on_cool_18c_fan_auto, ac.getRaw()); + + // Set Mode DRY 18C + uint32_t on_dry_18c_fan_auto0 = 0xB21F14; + ac.setMode(kCoolixDry); + EXPECT_EQ(on_dry_18c_fan_auto0, ac.getRaw()); + + // Set Mode HEAT 18C + uint32_t on_heat_18c_fan_auto = 0xB2BF1C; + ac.setMode(kCoolixHeat); + EXPECT_EQ(on_heat_18c_fan_auto, ac.getRaw()); + + // Set mode FAN + uint32_t on_fan_18c_fan_auto = 0xB2BFE4; + ac.setMode(kCoolixFan); + EXPECT_EQ(on_fan_18c_fan_auto, ac.getRaw()); + + // Fan level 2 (initial was auto) + uint32_t on_fan_18c_fan_min = 0xB29FE4; + ac.setFan(kCoolixFanMin); + EXPECT_EQ(on_fan_18c_fan_min, ac.getRaw()); + + // Fan level 3 + uint32_t on_fan_18c_fan_med = 0xB25FE4; + ac.setFan(kCoolixFanMed); + EXPECT_EQ(on_fan_18c_fan_med, ac.getRaw()); + + // Fan level 4 + uint32_t on_fan_18c_fan_max = 0xB23FE4; + ac.setFan(kCoolixFanMax); + EXPECT_EQ(on_fan_18c_fan_max, ac.getRaw()); + + // Test sending the last message to verify the class send() method works. + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(COOLIX, ac._irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, ac._irsend.capture.bits); + EXPECT_EQ(on_fan_18c_fan_max, ac._irsend.capture.value); + EXPECT_EQ(0x0, ac._irsend.capture.address); + EXPECT_EQ(0x0, ac._irsend.capture.command); + EXPECT_EQ( + // Raw data supplied by @mariusmotea + "f38000d50" + // 4434,4376, + "m4480s4480" + // 566,1614,592,504,566,1618,566,1616,568,528,564,532,564,1616,568,532, + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + // 566,530,566,1620,568,528,566,530,566,1618,564,1618,566,530,564,1624, + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + // 538,560,566,530,564,1620,566,1618,566,1618,566,1616,566,1616,566,1620, + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + // 568,1620,566,1616,566,530,566,530,564,530,562,532,564,530,566,530, + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + // 566,1622,566,1616,540,1642,566,528,566,530,566,1616,566,530,566,532, + "m560s1680m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s560" + // 564,532,564,530,566,530,566,1614,566,1616,562,532,564,1620,566,1618, + "m560s560m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s1680" + // 538,5254,4432,4364,566,1616,568,530,564,1620,568,1616,564,532,564,530, + "m560s5040m4480s4480m560s1680m560s560m560s1680m560s1680m560s560m560s560" + // 566,1616,566,532,564,532,566,1620,568,528,566,530,566,1616,564,1618, + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // 566,530,566,1622,566,532,566,528,566,1620,568,1614,566,1618,566,1618, + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + // 566,1614,568,1618,566,1622,568,1616,566,530,564,530,566,530,566,528, + "m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" + // 564,530,566,532,566,1622,564,1616,566,1616,564,532,564,530,564,1616, + "m560s560m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s1680" + // 564,530,564,532,566,530,564,530,566,528,564,1618,564,1618,564,532, + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s1680m560s560" + // 564,1620,566,1618,562 // Raw data matches what is expected. + "m560s1680m560s1680m560s5040", ac._irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index e258993..3eb82fe 100644 --- a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp @@ -10,10 +10,11 @@ // Test sending typical data only. TEST(TestSendDaikin, SendDataOnly) { - IRsendTest irsend(4); + IRsendTest irsend(0); irsend.begin(); uint8_t daikin_code[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; @@ -21,8 +22,10 @@ TEST(TestSendDaikin, SendDataOnly) { irsend.reset(); irsend.sendDaikin(daikin_code); EXPECT_EQ( + "f38000d50" "m428s428m428s428m428s428m428s428m428s428" - "m428s29428m3650s1623" + "m428s29428" + "m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" @@ -31,7 +34,8 @@ TEST(TestSendDaikin, SendDataOnly) { "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s1280m428s1280m428s1280m428s428m428s1280m428s428m428s1280m428s1280" - "m428s29428m3650s1623" + "m428s29428" + "m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" @@ -40,7 +44,8 @@ TEST(TestSendDaikin, SendDataOnly) { "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s428m428s428m428s428" "m428s428m428s428m428s428m428s428m428s428m428s1280m428s428m428s428" - "m428s29428m3650s1623" + "m428s29428" + "m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" "m428s428m428s1280m428s428m428s1280m428s1280m428s428m428s1280m428s1280" "m428s1280m428s1280m428s1280m428s428m428s428m428s1280m428s428m428s428" @@ -66,18 +71,19 @@ TEST(TestSendDaikin, SendDataOnly) { // Test sending with repeats. TEST(TestSendDaikin, SendWithRepeats) { - IRsendTest irsend(4); + IRsendTest irsend(0); irsend.begin(); irsend.reset(); - uint8_t daikin_code[kDaikinStateLength] = { + uint8_t daikin_code[kDaikinStateLengthShort] = { 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3}; irsend.reset(); - irsend.sendDaikin(daikin_code, kDaikinStateLength, 1); + irsend.sendDaikin(daikin_code, kDaikinStateLengthShort, 1); EXPECT_EQ( + "f38000d50" "m428s428m428s428m428s428m428s428m428s428" "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" @@ -166,22 +172,23 @@ TEST(TestSendDaikin, SendUnexpectedSizes) { IRsendTest irsend(4); irsend.begin(); - uint8_t daikin_short_code[kDaikinStateLength - 1] = { + uint8_t daikin_short_code[kDaikinStateLengthShort - 1] = { 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00}; irsend.reset(); - irsend.sendDaikin(daikin_short_code, kDaikinStateLength - 1); + irsend.sendDaikin(daikin_short_code, kDaikinStateLengthShort - 1); ASSERT_EQ("", irsend.outputStr()); - uint8_t daikin_long_code[kDaikinStateLength + 1] = { + uint8_t daikin_long_code[kDaikinStateLengthShort + 1] = { 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x20, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE3, 0x11}; irsend.reset(); - irsend.sendDaikin(daikin_long_code, kDaikinStateLength + 1); + irsend.sendDaikin(daikin_long_code, kDaikinStateLengthShort + 1); ASSERT_EQ( + "f38000d50" "m428s428m428s428m428s428m428s428m428s428" "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" @@ -229,367 +236,396 @@ TEST(TestSendDaikin, SendUnexpectedSizes) { // Tests for IRDaikinESP class. TEST(TestDaikinClass, Power) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.on(); - EXPECT_TRUE(irdaikin.getPower()); + ac.on(); + EXPECT_TRUE(ac.getPower()); - irdaikin.off(); - EXPECT_FALSE(irdaikin.getPower()); + ac.off(); + EXPECT_FALSE(ac.getPower()); - irdaikin.setPower(true); - EXPECT_TRUE(irdaikin.getPower()); + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); - irdaikin.setPower(false); - EXPECT_FALSE(irdaikin.getPower()); + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); } TEST(TestDaikinClass, Temperature) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setTemp(0); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); - irdaikin.setTemp(255); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); - irdaikin.setTemp(kDaikinMinTemp); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); - irdaikin.setTemp(kDaikinMaxTemp); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); - irdaikin.setTemp(kDaikinMinTemp - 1); - EXPECT_EQ(kDaikinMinTemp, irdaikin.getTemp()); + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); - irdaikin.setTemp(kDaikinMaxTemp + 1); - EXPECT_EQ(kDaikinMaxTemp, irdaikin.getTemp()); + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); - irdaikin.setTemp(kDaikinMinTemp + 1); - EXPECT_EQ(kDaikinMinTemp + 1, irdaikin.getTemp()); + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); - irdaikin.setTemp(21); - EXPECT_EQ(21, irdaikin.getTemp()); + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); - irdaikin.setTemp(25); - EXPECT_EQ(25, irdaikin.getTemp()); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); - irdaikin.setTemp(29); - EXPECT_EQ(29, irdaikin.getTemp()); + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); } TEST(TestDaikinClass, OperatingMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setMode(kDaikinAuto); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikinAuto, ac.getMode()); - irdaikin.setMode(kDaikinCool); - EXPECT_EQ(kDaikinCool, irdaikin.getMode()); + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikinCool, ac.getMode()); - irdaikin.setMode(kDaikinHeat); - EXPECT_EQ(kDaikinHeat, irdaikin.getMode()); + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikinHeat, ac.getMode()); - irdaikin.setMode(kDaikinDry); - EXPECT_EQ(kDaikinDry, irdaikin.getMode()); + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); - irdaikin.setMode(kDaikinFan); - EXPECT_EQ(kDaikinFan, irdaikin.getMode()); + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); - irdaikin.setMode(kDaikinFan + 1); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); + ac.setMode(kDaikinFan + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); - irdaikin.setMode(kDaikinAuto + 1); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); - irdaikin.setMode(255); - EXPECT_EQ(kDaikinAuto, irdaikin.getMode()); + ac.setMode(255); + EXPECT_EQ(kDaikinAuto, ac.getMode()); } TEST(TestDaikinClass, VaneSwing) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setSwingHorizontal(true); - irdaikin.setSwingVertical(false); + ac.setSwingHorizontal(true); + ac.setSwingVertical(false); - irdaikin.setSwingHorizontal(true); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getSwingVertical()); + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); - irdaikin.setSwingVertical(true); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_TRUE(irdaikin.getSwingVertical()); + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); - irdaikin.setSwingHorizontal(false); - EXPECT_FALSE(irdaikin.getSwingHorizontal()); - EXPECT_TRUE(irdaikin.getSwingVertical()); + ac.setSwingHorizontal(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); - irdaikin.setSwingVertical(false); - EXPECT_FALSE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getSwingVertical()); + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); } TEST(TestDaikinClass, QuietMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getQuiet()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); - irdaikin.setQuiet(false); - EXPECT_FALSE(irdaikin.getQuiet()); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getQuiet()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); // Setting Econo mode should NOT change out of quiet mode. - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getQuiet()); - irdaikin.setEcono(false); - EXPECT_TRUE(irdaikin.getQuiet()); + ac.setEcono(true); + EXPECT_TRUE(ac.getQuiet()); + ac.setEcono(false); + EXPECT_TRUE(ac.getQuiet()); // But setting Powerful mode should exit out of quiet mode. - irdaikin.setPowerful(true); - EXPECT_FALSE(irdaikin.getQuiet()); + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); } TEST(TestDaikinClass, PowerfulMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setPowerful(true); - EXPECT_TRUE(irdaikin.getPowerful()); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); - irdaikin.setPowerful(false); - EXPECT_FALSE(irdaikin.getPowerful()); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); - irdaikin.setPowerful(true); - EXPECT_TRUE(irdaikin.getPowerful()); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); - irdaikin.setQuiet(true); - EXPECT_FALSE(irdaikin.getPowerful()); + ac.setQuiet(true); + EXPECT_FALSE(ac.getPowerful()); - irdaikin.setPowerful(true); - irdaikin.setEcono(true); - EXPECT_FALSE(irdaikin.getPowerful()); + ac.setPowerful(true); + ac.setEcono(true); + EXPECT_FALSE(ac.getPowerful()); } TEST(TestDaikinClass, EconoMode) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); - irdaikin.setEcono(false); - EXPECT_FALSE(irdaikin.getEcono()); + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); - irdaikin.setEcono(true); - EXPECT_TRUE(irdaikin.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); // Setting Quiet mode should NOT change out of Econo mode. - irdaikin.setQuiet(true); - EXPECT_TRUE(irdaikin.getEcono()); - irdaikin.setQuiet(false); - EXPECT_TRUE(irdaikin.getEcono()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getEcono()); + ac.setQuiet(false); + EXPECT_TRUE(ac.getEcono()); // But setting Powerful mode should exit out of Econo mode. - irdaikin.setPowerful(true); - EXPECT_FALSE(irdaikin.getEcono()); + ac.setPowerful(true); + EXPECT_FALSE(ac.getEcono()); } TEST(TestDaikinClass, FanSpeed) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); // Unexpected value should default to Auto. - irdaikin.setFan(0); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); // Unexpected value should default to Auto. - irdaikin.setFan(255); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); - irdaikin.setFan(kDaikinFanMax); - EXPECT_EQ(kDaikinFanMax, irdaikin.getFan()); + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); // Beyond Max should default to Auto. - irdaikin.setFan(kDaikinFanMax + 1); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); - irdaikin.setFan(kDaikinFanMax - 1); - EXPECT_EQ(kDaikinFanMax - 1, irdaikin.getFan()); + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); - irdaikin.setFan(kDaikinFanMin); - EXPECT_EQ(kDaikinFanMin, irdaikin.getFan()); + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); - irdaikin.setFan(kDaikinFanMin + 1); - EXPECT_EQ(kDaikinFanMin + 1, irdaikin.getFan()); + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); // Beyond Min should default to Auto. - irdaikin.setFan(kDaikinFanMin - 1); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); - irdaikin.setFan(3); - EXPECT_EQ(3, irdaikin.getFan()); + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); - irdaikin.setFan(kDaikinFanAuto); - EXPECT_EQ(kDaikinFanAuto, irdaikin.getFan()); + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); - irdaikin.setFan(kDaikinFanQuiet); - EXPECT_EQ(kDaikinFanQuiet, irdaikin.getFan()); + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); } TEST(TestDaikinClass, CurrentTime) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setCurrentTime(0); // 00:00 - EXPECT_EQ(0, irdaikin.getCurrentTime()); + ac.setCurrentTime(0); // 00:00 + EXPECT_EQ(0, ac.getCurrentTime()); - irdaikin.setCurrentTime(754); // 12:34 - EXPECT_EQ(754, irdaikin.getCurrentTime()); + ac.setCurrentTime(754); // 12:34 + EXPECT_EQ(754, ac.getCurrentTime()); - irdaikin.setCurrentTime(1439); // 23:59 - EXPECT_EQ(1439, irdaikin.getCurrentTime()); + ac.setCurrentTime(1439); // 23:59 + EXPECT_EQ(1439, ac.getCurrentTime()); } TEST(TestDaikinClass, OnOffTimers) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); // Both timers turned off. - irdaikin.disableOnTimer(); - irdaikin.disableOffTimer(); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(kDaikinUnusedTime, irdaikin.getOnTime()); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(kDaikinUnusedTime, irdaikin.getOffTime()); + ac.disableOnTimer(); + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); // Turn on just the On Timer. - irdaikin.enableOnTimer(123); - EXPECT_TRUE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(123, irdaikin.getOnTime()); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(kDaikinUnusedTime, irdaikin.getOffTime()); + ac.enableOnTimer(123); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); // Now turn on the Off Timer. - irdaikin.enableOffTimer(754); - EXPECT_TRUE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(754, irdaikin.getOffTime()); - EXPECT_TRUE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(123, irdaikin.getOnTime()); + ac.enableOffTimer(754); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); + EXPECT_TRUE(ac.getOnTimerEnabled()); + EXPECT_EQ(123, ac.getOnTime()); // Turn off the just the On Timer. - irdaikin.disableOnTimer(); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(kDaikinUnusedTime, irdaikin.getOnTime()); - EXPECT_TRUE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(754, irdaikin.getOffTime()); + ac.disableOnTimer(); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); + EXPECT_TRUE(ac.getOffTimerEnabled()); + EXPECT_EQ(754, ac.getOffTime()); // Now turn off the Off Timer. - irdaikin.disableOffTimer(); - EXPECT_FALSE(irdaikin.getOffTimerEnabled()); - EXPECT_EQ(kDaikinUnusedTime, irdaikin.getOffTime()); - EXPECT_FALSE(irdaikin.getOnTimerEnabled()); - EXPECT_EQ(kDaikinUnusedTime, irdaikin.getOnTime()); + ac.disableOffTimer(); + EXPECT_FALSE(ac.getOffTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOffTime()); + EXPECT_FALSE(ac.getOnTimerEnabled()); + EXPECT_EQ(kDaikinUnusedTime, ac.getOnTime()); // Use some canary values around the timers to ensure no accidental // bit flips happen. i.e. Neighbouring bytes in the state. // (Found some during testing on systems with different endian-ness) // Tests here to make sure it never happens again. - irdaikin.setSwingHorizontal(true); - irdaikin.setPowerful(true); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - irdaikin.enableOnTimer(123); - irdaikin.enableOffTimer(456); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_TRUE(irdaikin.getSwingHorizontal()); - ASSERT_TRUE(irdaikin.getPowerful()); + ac.setSwingHorizontal(true); + ac.setPowerful(true); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + ac.enableOnTimer(123); + ac.enableOffTimer(456); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_TRUE(ac.getSwingHorizontal()); + ASSERT_TRUE(ac.getPowerful()); - irdaikin.setSwingHorizontal(false); - irdaikin.setPowerful(false); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); - irdaikin.enableOnTimer(123); - irdaikin.enableOffTimer(456); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); - irdaikin.disableOffTimer(); - irdaikin.disableOnTimer(); - ASSERT_FALSE(irdaikin.getSwingHorizontal()); - ASSERT_FALSE(irdaikin.getPowerful()); + ac.setSwingHorizontal(false); + ac.setPowerful(false); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); + ac.enableOnTimer(123); + ac.enableOffTimer(456); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); + ac.disableOffTimer(); + ac.disableOnTimer(); + ASSERT_FALSE(ac.getSwingHorizontal()); + ASSERT_FALSE(ac.getPowerful()); } -// Test Eye mode. -TEST(TestDaikinClass, EyeSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); +TEST(TestDaikinClass, WeeklyTimerEnable) { + IRDaikinESP ac(0); + ac.begin(); - // The Eye setting is stored in the same byte as Econo mode. + // The Weekly Timer Enabled flag is stored in the same byte as Econo mode. // Econo mode tests are there to make sure it isn't harmed and vice-versa. - irdaikin.setEcono(false); - irdaikin.setEye(false); - ASSERT_FALSE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); + ac.setEcono(false); + ac.setWeeklyTimerEnable(false); + ASSERT_FALSE(ac.getWeeklyTimerEnable()); + EXPECT_FALSE(ac.getEcono()); - irdaikin.setEye(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); + ac.setWeeklyTimerEnable(true); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); + EXPECT_FALSE(ac.getEcono()); - irdaikin.setEcono(false); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); + ac.setEcono(false); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); + EXPECT_FALSE(ac.getEcono()); - irdaikin.setEcono(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); + ac.setEcono(true); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); + EXPECT_TRUE(ac.getEcono()); - irdaikin.setEye(false); - ASSERT_FALSE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); + ac.setWeeklyTimerEnable(false); + ASSERT_FALSE(ac.getWeeklyTimerEnable()); + EXPECT_TRUE(ac.getEcono()); + + // Tests with real data from: + // https://github.com/markszabo/IRremoteESP8266/issues/704#issuecomment-493731421 + uint8_t on[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, + 0x42, 0xE3, 0x0B, 0x42, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x68, 0x32, 0x00, + 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x03}; + uint8_t off[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, + 0x42, 0xE3, 0x0B, 0x42, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x68, 0x32, 0x00, + 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x00, 0x83}; + ac.setRaw(on); + EXPECT_TRUE(ac.getWeeklyTimerEnable()); + ac.setRaw(off); + EXPECT_FALSE(ac.getWeeklyTimerEnable()); } // Test Mold mode. TEST(TestDaikinClass, MoldSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setMold(false); - ASSERT_FALSE(irdaikin.getMold()); + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); - irdaikin.setMold(true); - ASSERT_TRUE(irdaikin.getMold()); + ac.setMold(true); + ASSERT_TRUE(ac.getMold()); - irdaikin.setMold(false); - ASSERT_FALSE(irdaikin.getMold()); + ac.setMold(false); + ASSERT_FALSE(ac.getMold()); +} + +// Test Comfort mode. +TEST(TestDaikinClass, ComfortSetting) { + IRDaikinESP ac(0); + ac.begin(); + + ac.setComfort(false); + ASSERT_FALSE(ac.getComfort()); + + ac.setComfort(true); + ASSERT_TRUE(ac.getComfort()); + + ac.setComfort(false); + ASSERT_FALSE(ac.getComfort()); } // Test Sensor mode. TEST(TestDaikinClass, SensorSetting) { - IRDaikinESP irdaikin(0); - irdaikin.begin(); + IRDaikinESP ac(0); + ac.begin(); - irdaikin.setSensor(false); - ASSERT_FALSE(irdaikin.getSensor()); + ac.setSensor(false); + ASSERT_FALSE(ac.getSensor()); - irdaikin.setSensor(true); - ASSERT_TRUE(irdaikin.getSensor()); + ac.setSensor(true); + ASSERT_TRUE(ac.getSensor()); - irdaikin.setSensor(false); - ASSERT_FALSE(irdaikin.getSensor()); + ac.setSensor(false); + ASSERT_FALSE(ac.getSensor()); } TEST(TestDaikinClass, RenderTime) { @@ -600,28 +636,37 @@ TEST(TestDaikinClass, RenderTime) { } TEST(TestDaikinClass, SetAndGetRaw) { - IRDaikinESP irdaikin(0); - uint8_t initialState[kDaikinStateLength] = { + IRDaikinESP ac(0); + uint8_t shortState[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; + uint8_t longState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x49, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x4F}; uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, 0x42, 0x00, 0x00, 0x54, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x48, 0x2A, 0x00, 0xB0, 0x00, - 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x02, 0x5A}; + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x02, 0x5C}; - EXPECT_STATE_EQ(initialState, irdaikin.getRaw(), kDaikinBits); + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); // toggle the power state. - irdaikin.setPower(!irdaikin.getPower()); - irdaikin.setTemp(21); - irdaikin.setMold(true); - EXPECT_STATE_EQ(expectedState, irdaikin.getRaw(), kDaikinBits); - irdaikin.setRaw(initialState); - EXPECT_STATE_EQ(initialState, irdaikin.getRaw(), kDaikinBits); + ac.setPower(!ac.getPower()); + ac.setTemp(21); + ac.setMold(true); + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikinBits); + ac.setRaw(longState); + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); + ac.setRaw(shortState, kDaikinStateLengthShort); + EXPECT_STATE_EQ(longState, ac.getRaw(), kDaikinBits); } TEST(TestDaikinClass, ChecksumValidation) { uint8_t daikin_code[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x41, 0x1E, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE1}; @@ -640,66 +685,76 @@ TEST(TestDaikinClass, ChecksumValidation) { EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); daikin_code[10] ^= 0xFF; EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); + // Change something in the 3rd block. + daikin_code[20] ^= 0xFF; + EXPECT_FALSE(IRDaikinESP::validChecksum(daikin_code)); + daikin_code[20] ^= 0xFF; + EXPECT_TRUE(IRDaikinESP::validChecksum(daikin_code)); } // Test human readable output. TEST(TestDaikinClass, HumanReadable) { - IRDaikinESP irdaikin(0); + IRDaikinESP ac(0); EXPECT_EQ( "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (QUIET), " - "Powerful: Off, Quiet: Off, Sensor: Off, Eye: Off, Mold: Off, " - "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 0:00, On Time: Off, Off Time: Off", - irdaikin.toString()); - irdaikin.setMode(kDaikinAuto); - irdaikin.setTemp(25); - irdaikin.setFan(kDaikinFanAuto); - irdaikin.setQuiet(true); - irdaikin.setSensor(true); - irdaikin.setEye(true); - irdaikin.setMold(true); - irdaikin.setSwingVertical(true); - irdaikin.setSwingHorizontal(true); - irdaikin.setCurrentTime(9 * 60 + 15); - irdaikin.enableOnTimer(8 * 60 + 0); - irdaikin.enableOffTimer(17 * 60 + 30); - irdaikin.off(); + "Powerful: Off, Quiet: Off, Sensor: Off, Mold: Off, " + "Comfort: Off, Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 0:00, Current Day: (UNKNOWN), On Time: Off, " + "Off Time: Off, Weekly Timer: On", + ac.toString()); + ac.setMode(kDaikinAuto); + ac.setTemp(25); + ac.setFan(kDaikinFanAuto); + ac.setQuiet(true); + ac.setSensor(true); + ac.setMold(true); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setCurrentTime(9 * 60 + 15); + ac.setCurrentDay(4); + ac.enableOnTimer(8 * 60 + 0); + ac.enableOffTimer(17 * 60 + 30); + ac.setComfort(true); + ac.setWeeklyTimerEnable(false); + ac.off(); EXPECT_EQ( "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (AUTO), " - "Powerful: Off, Quiet: On, Sensor: On, Eye: On, Mold: On, " + "Powerful: Off, Quiet: On, Sensor: On, Mold: On, Comfort: On, " "Swing (Horizontal): On, Swing (Vertical): On, " - "Current Time: 9:15, On Time: 8:00, Off Time: 17:30", - irdaikin.toString()); + "Current Time: 9:15, Current Day: WED, On Time: 8:00, Off Time: 17:30, " + "Weekly Timer: Off", + ac.toString()); } // Test general message construction after tweaking some settings. TEST(TestDaikinClass, MessageConstuction) { - IRDaikinESP irdaikin(0); + IRDaikinESP ac(0); IRsendTest irsend(4); - irdaikin.begin(); + ac.begin(); irsend.begin(); - irdaikin.setFan(kDaikinFanMin); - irdaikin.setMode(kDaikinCool); - irdaikin.setTemp(27); - irdaikin.setSwingVertical(false); - irdaikin.setSwingHorizontal(true); - irdaikin.setQuiet(false); - irdaikin.setPower(true); + ac.setFan(kDaikinFanMin); + ac.setMode(kDaikinCool); + ac.setTemp(27); + ac.setSwingVertical(false); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPower(true); // Check everything for kicks. - EXPECT_EQ(kDaikinFanMin, irdaikin.getFan()); - EXPECT_EQ(kDaikinCool, irdaikin.getMode()); - EXPECT_EQ(27, irdaikin.getTemp()); - EXPECT_FALSE(irdaikin.getSwingVertical()); - EXPECT_TRUE(irdaikin.getSwingHorizontal()); - EXPECT_FALSE(irdaikin.getQuiet()); - EXPECT_TRUE(irdaikin.getPower()); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + EXPECT_EQ(kDaikinCool, ac.getMode()); + EXPECT_EQ(27, ac.getTemp()); + EXPECT_FALSE(ac.getSwingVertical()); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPower()); irsend.reset(); - irsend.sendDaikin(irdaikin.getRaw()); + irsend.sendDaikin(ac.getRaw()); EXPECT_EQ( + "f38000d50" "m428s428m428s428m428s428m428s428m428s428" "m428s29428m3650s1623" "m428s1280m428s428m428s428m428s428m428s1280m428s428m428s428m428s428" @@ -747,16 +802,17 @@ TEST(TestDaikinClass, MessageConstuction) { // Test decoding a message captured from a real IR remote. TEST(TestDecodeDaikin, RealExample) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - IRrecv irrecv(4); + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); irsend.begin(); uint8_t expectedState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; - uint16_t rawData[kDaikinRawBits] = { + uint16_t rawData[583] = { 416, 446, 416, 446, 416, 446, 418, 446, 416, 446, 416, 25434, 3436, 1768, 390, 1336, 390, 446, 416, 446, 416, 446, 416, 1336, 390, 446, 416, 446, 416, 446, 416, 446, 416, 1336, 390, 448, @@ -808,33 +864,81 @@ TEST(TestDecodeDaikin, RealExample) { 390, 1336, 390, 446, 416, 446, 416}; // Captured by @sillyfrog irsend.reset(); - irsend.sendRaw(rawData, kDaikinRawBits, 38000); + irsend.sendRaw(rawData, 583, 38000); irsend.makeDecodeResult(); EXPECT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(DAIKIN, irsend.capture.decode_type); ASSERT_EQ(kDaikinBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 6:10, Weekly Timer: On", ac.toString()); } // Decoding a message we entirely constructed based solely on a given state. -TEST(TestDecodeDaikin, SyntheticExample) { - IRDaikinESP irdaikin(0); - IRsendTest irsend(4); - IRrecv irrecv(4); +TEST(TestDecodeDaikin, ShortSyntheticExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + uint8_t shortState[kDaikinStateLengthShort] = { + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + + uint8_t longState[kDaikinStateLength] = { + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, + 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; + irsend.reset(); + irsend.sendDaikin(shortState, kDaikinStateLengthShort); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN, irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, irsend.capture.bits); + EXPECT_STATE_EQ(longState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 6:10, Weekly Timer: On", ac.toString()); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin, LongSyntheticExample) { + IRDaikinESP ac(0); + IRsendTest irsend(0); + IRrecv irrecv(0); irsend.begin(); uint8_t expectedState[kDaikinStateLength] = { - 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, 0x11, - 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, + 0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, + 0x11, 0xDA, 0x27, 0x00, 0x42, 0x3A, 0x05, 0x93, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x3F, 0x3A, 0x00, 0xA0, 0x00, 0x0A, 0x25, 0x17, 0x01, 0x00, 0xC0, 0x00, 0x00, 0x32}; irsend.reset(); irsend.sendDaikin(expectedState); irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_TRUE(irrecv.decodeDaikin(&irsend.capture)); ASSERT_EQ(DAIKIN, irsend.capture.decode_type); ASSERT_EQ(kDaikinBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 29C, Fan: 10 (AUTO), Powerful: On, " + "Quiet: Off, Sensor: Off, Mold: Off, Comfort: Off, " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 6:10, Weekly Timer: On", ac.toString()); } // Test decoding a message captured from a real IR remote. @@ -1085,6 +1189,57 @@ TEST(TestDaikin2Class, CleanSetting) { ac.setClean(false); ASSERT_FALSE(ac.getClean()); } + + +TEST(TestDaikin2Class, Temperature) { + IRDaikin2 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + // Now try it with Cool mode, which should set the temp to kDaikin2MinCoolTemp + ASSERT_TRUE(kDaikinMinTemp + 1 < kDaikin2MinCoolTemp); + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikin2MinCoolTemp, ac.getTemp()); + ac.setTemp(kDaikin2MinCoolTemp - 1); + EXPECT_EQ(kDaikin2MinCoolTemp, ac.getTemp()); + ac.setTemp(kDaikin2MinCoolTemp + 1); + EXPECT_EQ(kDaikin2MinCoolTemp + 1, ac.getTemp()); + // Should be released from that requirement in other modes. + ac.setMode(kDaikinAuto); + ac.setTemp(kDaikin2MinCoolTemp - 1); + EXPECT_EQ(kDaikin2MinCoolTemp - 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + // Test Fresh Air settings. TEST(TestDaikin2Class, FreshAirSettings) { IRDaikin2 ac(0); @@ -1359,11 +1514,22 @@ TEST(TestDaikin2Class, KnownConstruction) { EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin2Bits); } -TEST(TestUtils, Misc) { - ASSERT_EQ("DAIKIN", typeToString(DAIKIN)); - ASSERT_TRUE(hasACState(DAIKIN)); - ASSERT_EQ("DAIKIN2", typeToString(DAIKIN2)); - ASSERT_TRUE(hasACState(DAIKIN2)); +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("DAIKIN", typeToString(decode_type_t::DAIKIN)); + ASSERT_EQ(decode_type_t::DAIKIN, strToDecodeType("DAIKIN")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN)); + + ASSERT_EQ("DAIKIN160", typeToString(decode_type_t::DAIKIN160)); + ASSERT_EQ(decode_type_t::DAIKIN160, strToDecodeType("DAIKIN160")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN160)); + + ASSERT_EQ("DAIKIN2", typeToString(decode_type_t::DAIKIN2)); + ASSERT_EQ(decode_type_t::DAIKIN2, strToDecodeType("DAIKIN2")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN2)); + + ASSERT_EQ("DAIKIN216", typeToString(decode_type_t::DAIKIN216)); + ASSERT_EQ(decode_type_t::DAIKIN216, strToDecodeType("DAIKIN216")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN216)); } // https://github.com/markszabo/IRremoteESP8266/issues/582#issuecomment-453863879 @@ -1451,3 +1617,482 @@ TEST(TestDecodeDaikin2, Issue582PowerfulEconoFix) { "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", ac.toString()); } + +// Tests for IRDaikin216 class. + +TEST(TestDaikin216Class, Power) { + IRDaikin216 ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestDaikin216Class, Temperature) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp - 1); + EXPECT_EQ(kDaikinMinTemp, ac.getTemp()); + + ac.setTemp(kDaikinMaxTemp + 1); + EXPECT_EQ(kDaikinMaxTemp, ac.getTemp()); + + ac.setTemp(kDaikinMinTemp + 1); + EXPECT_EQ(kDaikinMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestDaikin216Class, OperatingMode) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setMode(kDaikinAuto); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinCool); + EXPECT_EQ(kDaikinCool, ac.getMode()); + + ac.setMode(kDaikinHeat); + EXPECT_EQ(kDaikinHeat, ac.getMode()); + + ac.setMode(kDaikinDry); + EXPECT_EQ(kDaikinDry, ac.getMode()); + + ac.setMode(kDaikinFan); + EXPECT_EQ(kDaikinFan, ac.getMode()); + + ac.setMode(kDaikinFan + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(kDaikinAuto + 1); + EXPECT_EQ(kDaikinAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kDaikinAuto, ac.getMode()); +} + + +TEST(TestDaikin216Class, VaneSwing) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + ac.setSwingVertical(false); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingHorizontal(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_TRUE(ac.getSwingVertical()); + + ac.setSwingVertical(false); + EXPECT_FALSE(ac.getSwingHorizontal()); + EXPECT_FALSE(ac.getSwingVertical()); +} + +TEST(TestDaikin216Class, FanSpeed) { + IRDaikin216 ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax); + EXPECT_EQ(kDaikinFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kDaikinFanMax + 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanMax - 1); + EXPECT_EQ(kDaikinFanMax - 1, ac.getFan()); + + ac.setFan(kDaikinFanMin); + EXPECT_EQ(kDaikinFanMin, ac.getFan()); + + ac.setFan(kDaikinFanMin + 1); + EXPECT_EQ(kDaikinFanMin + 1, ac.getFan()); + + // Beyond Min should default to Auto. + ac.setFan(kDaikinFanMin - 1); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + + ac.setFan(kDaikinFanAuto); + EXPECT_EQ(kDaikinFanAuto, ac.getFan()); + + ac.setFan(kDaikinFanQuiet); + EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); +} + +TEST(TestDaikin216Class, QuietAndPowerful) { + IRDaikin216 ac(0); + ac.begin(); + + ac.setQuiet(false); + ac.setPowerful(false); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPowerful()); + + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); +} + +TEST(TestDaikin216Class, ExampleStates) { + IRDaikin216 ac(0); + ac.begin(); + // https://github.com/markszabo/IRremoteESP8266/pull/690#issuecomment-487770194 + uint8_t state[kDaikin216StateLength] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x21, 0xC0, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x53}; + ac.setRaw(state); + EXPECT_EQ( + "Power: On, Mode: 2 (DRY), Temp: 32C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Quiet: Off, Powerful: Off", + ac.toString()); +} + +TEST(TestDaikin216Class, ReconstructKnownState) { + IRDaikin216 ac(0); + ac.begin(); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint8_t expectedState[kDaikin216StateLength] = { + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + ac.setPower(false); + ac.setMode(kDaikinAuto); + ac.setTemp(19); + ac.setFan(kDaikinFanAuto); + ac.setSwingHorizontal(false); + ac.setSwingVertical(false); + ac.setQuiet(false); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Quiet: Off, Powerful: Off", + ac.toString()); + + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kDaikin216Bits); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/689 +TEST(TestDecodeDaikin216, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint16_t rawData[439] = { + 3402, 1770, 382, 1340, 382, 480, 382, 478, 382, 480, 380, 1342, 382, 478, + 356, 504, 382, 480, 380, 478, 384, 1342, 380, 480, 380, 1342, 382, 1342, + 382, 478, 382, 1340, 382, 1340, 384, 1340, 382, 1342, 382, 1340, 380, 480, + 382, 480, 382, 1296, 426, 480, 380, 480, 382, 480, 380, 480, 382, 480, + 382, 478, 382, 1342, 382, 1342, 382, 1340, 356, 1368, 382, 478, 382, 480, + 382, 478, 380, 480, 382, 480, 382, 480, 382, 478, 382, 480, 382, 478, 358, + 504, 382, 480, 380, 480, 382, 480, 382, 480, 380, 480, 382, 478, 382, 480, + 382, 478, 382, 480, 354, 506, 354, 506, 380, 480, 382, 480, 382, 480, 382, + 480, 380, 1342, 382, 480, 382, 480, 382, 478, 382, 478, 382, 478, 384, + 478, 382, 29652, 3426, 1772, 382, 1340, 382, 480, 380, 478, 382, 480, 382, + 1342, 382, 480, 382, 480, 382, 478, 356, 506, 382, 1342, 380, 480, 382, + 1340, 382, 1340, 382, 478, 356, 1366, 382, 1340, 384, 1340, 382, 1340, + 382, 1342, 382, 478, 382, 478, 382, 1340, 382, 478, 382, 478, 382, 478, + 382, 480, 382, 480, 384, 478, 358, 504, 382, 478, 382, 480, 382, 478, 382, + 480, 382, 480, 382, 478, 382, 480, 382, 478, 382, 478, 382, 478, 382, 478, + 384, 478, 382, 478, 360, 500, 358, 504, 382, 478, 382, 480, 382, 480, 382, + 478, 382, 478, 382, 1340, 382, 1342, 382, 480, 380, 480, 382, 1342, 382, + 478, 382, 480, 356, 506, 382, 478, 382, 480, 382, 480, 356, 506, 382, 478, + 382, 480, 382, 478, 382, 480, 382, 478, 382, 480, 380, 480, 380, 480, 382, + 1342, 382, 478, 382, 1342, 382, 480, 382, 480, 382, 478, 382, 478, 382, + 480, 382, 478, 382, 480, 356, 504, 384, 478, 382, 480, 382, 480, 380, 480, + 382, 478, 382, 480, 382, 480, 382, 478, 356, 504, 384, 478, 380, 480, 382, + 480, 382, 480, 382, 478, 356, 506, 382, 478, 382, 480, 380, 480, 382, 478, + 382, 480, 382, 478, 382, 480, 358, 504, 382, 478, 382, 478, 356, 504, 382, + 478, 382, 480, 382, 478, 382, 478, 382, 478, 382, 480, 380, 480, 382, 480, + 380, 480, 356, 506, 356, 504, 382, 480, 382, 478, 382, 478, 382, 478, 382, + 478, 382, 480, 382, 478, 382, 480, 382, 480, 382, 1340, 382, 1342, 382, + 478, 384, 478, 382, 478, 382, 480, 380, 480, 382, 478, 382, 480, 356, 506, + 382, 478, 382, 480, 382, 478, 356, 506, 380, 480, 382, 478, 382, 478, 382, + 478, 382, 480, 382, 480, 380, 480, 382, 1342, 382, 1340, 382, 480, 356, + 504, 382, 1342, 382}; // UNKNOWN E0E32232 + uint8_t expectedState[kDaikin216StateLength] = { + // 8 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + // 19 bytes + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 439, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN216, irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRDaikin216 ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (AUTO), " + "Swing (Horizontal): Off, Swing (Vertical): Off, " + "Quiet: Off, Powerful: Off", + ac.toString()); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/689 +TEST(TestDecodeDaikin216, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + uint8_t expectedState[kDaikin216StateLength] = { + // 8 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, + // 19 bytes + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x26, 0x00, 0xA0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x98}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin216(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN216, irsend.capture.decode_type); + ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDaikinClass, toCommon) { + IRDaikinESP ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setEcono(false); + ac.setMold(true); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN, 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_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + 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(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDaikin2Class, toCommon) { + IRDaikin2 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(kDaikin2SwingVHigh + 3); + ac.setSwingHorizontal(kDaikin2SwingHAuto); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setEcono(false); + ac.setMold(true); + ac.setLight(true); + ac.setPurify(true); + ac.setBeep(true); + ac.enableSleepTimer(6 * 60); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN2, 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_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_TRUE(ac.toCommon().beep); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kMiddle, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_EQ(6 * 60, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDaikin216Class, toCommon) { + IRDaikin216 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPowerful(true); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN216, 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_TRUE(ac.toCommon().turbo); + 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(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + 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); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/731 +TEST(TestDecodeDaikin160, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t rawData[327] = { + 5024, 2144, 342, 1786, 344, 706, 342, 706, 344, 706, 342, 1786, 342, 706, + 342, 708, 342, 708, 342, 708, 342, 1786, 342, 708, 342, 1786, 342, 1788, + 342, 708, 342, 1786, 344, 1786, 342, 1786, 342, 1786, 342, 1786, 342, 708, + 342, 708, 340, 1786, 344, 706, 342, 708, 342, 706, 342, 708, 342, 708, + 342, 706, 342, 1786, 342, 1788, 342, 1786, 342, 1786, 342, 1788, 342, 706, + 342, 1788, 342, 1786, 342, 706, 342, 706, 342, 708, 342, 708, 342, 708, + 342, 708, 342, 706, 342, 708, 342, 708, 342, 706, 342, 706, 342, 708, 342, + 1786, 342, 1786, 342, 1786, 342, 1788, 342, 706, 342, 706, 342, 706, 342, + 708, 342, 29442, 5022, 2146, 342, 1786, 342, 706, 342, 708, 342, 708, 342, + 1788, 342, 706, 342, 706, 342, 706, 342, 706, 342, 1788, 342, 708, 342, + 1786, 342, 1786, 342, 708, 342, 1786, 342, 1788, 342, 1786, 342, 1786, + 342, 1786, 342, 706, 342, 706, 342, 1786, 344, 706, 342, 706, 344, 706, + 342, 706, 342, 706, 342, 708, 342, 706, 342, 706, 342, 706, 342, 706, 342, + 1786, 342, 1786, 342, 706, 342, 706, 342, 1788, 342, 708, 342, 1786, 342, + 1786, 342, 706, 342, 706, 342, 706, 342, 708, 342, 1786, 342, 1786, 342, + 708, 342, 708, 342, 1786, 342, 706, 342, 706, 344, 706, 342, 1786, 342, + 708, 342, 706, 342, 706, 342, 708, 342, 706, 342, 708, 342, 706, 342, 708, + 342, 706, 342, 704, 344, 706, 342, 706, 344, 706, 342, 706, 342, 708, 342, + 706, 342, 706, 344, 706, 342, 706, 342, 706, 344, 1786, 342, 1786, 342, + 1786, 342, 1786, 342, 706, 342, 706, 344, 706, 342, 706, 342, 1786, 344, + 706, 342, 1786, 342, 706, 342, 708, 342, 706, 342, 706, 344, 706, 342, + 706, 342, 706, 342, 1786, 342, 706, 344, 706, 342, 706, 342, 708, 342, + 706, 342, 1788, 342, 1786, 342, 706, 344, 1786, 344, 706, 344, 1786, 342, + 708, 342}; // UNKNOWN 99CC993 + + uint8_t expectedState[kDaikin160StateLength] = { + // 7 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x0D, 0x00, 0x0F, + // 13 bytes + 0x11, 0xDA, 0x27, 0x00, 0xD3, 0x30, 0x11, + 0x00, 0x00, 0x1E, 0x0A, 0x08, 0x56}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 327, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN160, irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDecodeDaikin160, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + uint8_t expectedState[kDaikin160StateLength] = { + // 7 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x0D, 0x00, 0x0F, + // 13 bytes + 0x11, 0xDA, 0x27, 0x00, 0xD3, 0x30, 0x11, + 0x00, 0x00, 0x1E, 0x0A, 0x08, 0x56}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin160(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN160, irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} diff --git a/lib/IRremoteESP8266/test/ir_Denon_test.cpp b/lib/IRremoteESP8266/test/ir_Denon_test.cpp index 911fd75..bfb554b 100644 --- a/lib/IRremoteESP8266/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Denon_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendDenon, SendDataOnly) { irsend.reset(); irsend.sendDenon(0x2278); // Denon AVR Power On. (Sharp) EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -24,8 +25,9 @@ TEST(TestSendDenon, SendDataOnly) { irsend.reset(); // Denon Eco Mode On. (Panasonic/Kaseikyo) - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -43,8 +45,9 @@ TEST(TestSendDenon, SendNormalWithRepeats) { irsend.begin(); irsend.reset(); - irsend.sendDenon(0x2278, DENON_BITS, 1); // 1 repeat. + irsend.sendDenon(0x2278, kDenonBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -58,8 +61,9 @@ TEST(TestSendDenon, SendNormalWithRepeats) { "m260s780m260s780m260s780m260s780m260s1820m260s1820m260s1820" "m260s43602", irsend.outputStr()); - irsend.sendDenon(0x2278, DENON_BITS, 2); // 2 repeats. + irsend.sendDenon(0x2278, kDenonBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m260s780m260s1820m260s780m260s780m260s780m260s1820m260s780m260s780" "m260s1820m260s1820m260s1820m260s1820m260s780m260s780m260s780" "m260s43602" @@ -86,8 +90,9 @@ TEST(TestSendDenon, Send48BitWithRepeats) { irsend.begin(); irsend.reset(); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 1); // 1 repeat. + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits, 1); // 1 repeat. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -105,8 +110,9 @@ TEST(TestSendDenon, Send48BitWithRepeats) { "m432s1296m432s1296m432s1296m432s432m432s432m432s432m432s1296m432s1296" "m432s98928", irsend.outputStr()); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS, 2); // 2 repeats. + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits, 2); // 2 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s1296m432s432m432s1296m432s432m432s1296m432s432" "m432s432m432s1296m432s432m432s432m432s1296m432s1296m432s432m432s432" @@ -142,6 +148,7 @@ TEST(TestSendDenon, SendUnusualSize) { irsend.reset(); irsend.sendDenon(0x12, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s1820m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s780m260s1820m260s1820m260s780m260s1820" @@ -151,6 +158,7 @@ TEST(TestSendDenon, SendUnusualSize) { irsend.reset(); irsend.sendDenon(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s1296m432s432m432s432m432s1296m432s432" "m432s432m432s432m432s1296m432s1296m432s432m432s1296m432s432m432s432" @@ -177,9 +185,9 @@ TEST(TestDecodeDenon, NormalDecodeWithStrict) { irsend.sendDenon(0x2278); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenonBits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); + EXPECT_EQ(kDenonBits, irsend.capture.bits); EXPECT_EQ(0x2278, irsend.capture.value); EXPECT_EQ(0x2, irsend.capture.address); EXPECT_EQ(0x79, irsend.capture.command); @@ -200,11 +208,11 @@ TEST(TestDecodeDenon, NormalDecodeWithStrict) { // Normal Denon 48-bit message. (Panasonic/Kaseikyo) irsend.reset(); - irsend.sendDenon(0x2A4C028D6CE3, DENON_48_BITS); + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_48_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenon48Bits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_48_BITS, irsend.capture.bits); + EXPECT_EQ(kDenon48Bits, irsend.capture.bits); EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); EXPECT_EQ(0x2A4C, irsend.capture.address); EXPECT_EQ(0x028D6CE3, irsend.capture.command); @@ -227,9 +235,9 @@ TEST(TestDecodeDenon, DecodeGlobalCacheExample) { irsend.sendGC(gc_test_power, 67); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenonBits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); + EXPECT_EQ(kDenonBits, irsend.capture.bits); EXPECT_EQ(0x2278, irsend.capture.value); EXPECT_EQ(0x2, irsend.capture.address); EXPECT_EQ(0x79, irsend.capture.command); @@ -248,9 +256,9 @@ TEST(TestDecodeDenon, DecodeGlobalCacheExample) { irsend.sendGC(gc_test_eco, 103); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, DENON_48_BITS, true)); + ASSERT_TRUE(irrecv.decodeDenon(&irsend.capture, kDenon48Bits, true)); EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_48_BITS, irsend.capture.bits); + EXPECT_EQ(kDenon48Bits, irsend.capture.bits); EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); EXPECT_EQ(0x2A4C, irsend.capture.address); EXPECT_EQ(0x028D6CE3, irsend.capture.command); @@ -273,6 +281,6 @@ TEST(TestDecodeDenon, FailToDecodeNonDenonExample) { ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture)); ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, kDenonLegacyBits, false)); - ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, DENON_BITS, false)); - ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, DENON_48_BITS, false)); + ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, kDenonBits, false)); + ASSERT_FALSE(irrecv.decodeDenon(&irsend.capture, kDenon48Bits, false)); } diff --git a/lib/IRremoteESP8266/test/ir_Dish_test.cpp b/lib/IRremoteESP8266/test/ir_Dish_test.cpp index 0c58496..21286b7 100644 --- a/lib/IRremoteESP8266/test/ir_Dish_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Dish_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0x0); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -32,6 +33,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0x9C00); // Power on. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -50,6 +52,7 @@ TEST(TestSendDish, SendDataOnly) { irsend.reset(); irsend.sendDISH(0xFFFF); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700" "m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700m400s1700" @@ -74,6 +77,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.reset(); irsend.sendDISH(0x9C00, kDishBits, 0); // 0 repeats. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -83,6 +87,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.reset(); irsend.sendDISH(0x9C00, kDishBits, 1); // 1 repeat. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -94,6 +99,7 @@ TEST(TestSendDish, SendWithRepeats) { irsend.sendDISH(0x9C00, kDishBits, 2); // 2 repeats. EXPECT_EQ( + "f57600d50" "m400s6100" "m400s1700m400s2800m400s2800m400s1700m400s1700m400s1700m400s2800m400s2800" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" @@ -115,6 +121,7 @@ TEST(TestSendDish, SendUnusualSize) { irsend.reset(); irsend.sendDISH(0x0, 8); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800m400s2800" "m400s6100" @@ -129,6 +136,7 @@ TEST(TestSendDish, SendUnusualSize) { irsend.reset(); irsend.sendDISH(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f57600d50" "m400s6100" "m400s2800m400s2800m400s2800m400s1700m400s2800m400s2800m400s1700m400s2800" "m400s2800m400s2800m400s1700m400s1700m400s2800m400s1700m400s2800m400s2800" diff --git a/lib/IRremoteESP8266/test/ir_Electra_test.cpp b/lib/IRremoteESP8266/test/ir_Electra_test.cpp index df5dd7a..7d6d0c9 100644 --- a/lib/IRremoteESP8266/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Electra_test.cpp @@ -12,12 +12,13 @@ TEST(TestSendElectraAC, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); - uint8_t data[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t data[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.sendElectraAC(data); EXPECT_EQ( + "f38000d50" "m9166s4470" "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" @@ -46,9 +47,9 @@ TEST(TestDecodeElectraAC, SyntheticDecode) { // Synthesised Normal ElectraAC message. irsend.reset(); - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.sendElectraAC(expectedState); irsend.makeDecodeResult(); EXPECT_TRUE(irrecv.decode(&irsend.capture)); @@ -84,9 +85,9 @@ TEST(TestDecodeElectraAC, RealExampleDecode) { 662, 562, 642, 1686, 582, 570, 634, 566, 604, 576, 636, 566, 610, 578, 634, 1664, 584, 590, 660, 1636, 610, 1642, 664, 590, 610, 590, 636, 566, 634, 568, 686}; // UNKNOWN 9AD8CDB5 - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0xE1, 0x6F, 0x14, 0x06, - 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0xA0, 0xB0}; + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; irsend.reset(); irsend.sendRaw(rawData, 211, 38000); diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index 23fa3e7..b71a6a8 100644 --- a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp @@ -6,183 +6,177 @@ #include "ir_Fujitsu.h" #include "gtest/gtest.h" -template -::testing::AssertionResult ArraysMatch(const T (&expected)[size], - const T* actual) { - for (size_t i(0); i < size; ++i) { - if (expected[i] != actual[i]) { - int e = expected[i]; - int a = actual[i]; - return ::testing::AssertionFailure() << "array[" << i - << "] (" << std::hex << a << std::dec << ") != expected[" << i - << "] (" << std::hex << e << std::dec << ")"; - } - } - return ::testing::AssertionSuccess(); -} // Tests for Fujitsu A/C methods. // Test sending typical data only. TEST(TestIRFujitsuACClass, GetRawDefault) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); // AR-RAH2E - fujitsu.setCmd(kFujitsuAcCmdTurnOn); - fujitsu.setSwing(kFujitsuAcSwingBoth); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanHigh); - fujitsu.setTemp(24); + IRFujitsuAC ac(0); // 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_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Swing: Vert + Horiz, Command: N/A", + ac.toString()); uint8_t expected_ardb1[15] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x81, 0x01, 0x31, 0x00, 0x00, 0x00, 0x1D}; - fujitsu.setModel(ARDB1); - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Command: N/A", + ac.toString()); } TEST(TestIRFujitsuACClass, GetRawTurnOff) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setModel(ARRAH2E); - fujitsu.off(); + IRFujitsuAC ac(0); + ac.setModel(ARRAH2E); + ac.off(); uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort, fujitsu.getStateLength()); - EXPECT_EQ("Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: Off, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Swing: Vert + Horiz, Command: N/A", + ac.toString()); - fujitsu.setModel(ARDB1); + ac.setModel(ARDB1); uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: Off, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Command: N/A", + ac.toString()); } TEST(TestIRFujitsuACClass, GetRawStepHoriz) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.stepHoriz(); + IRFujitsuAC ac(0); + ac.stepHoriz(); uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; - EXPECT_TRUE(ArraysMatch(expected, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: Step vane horizontally", - fujitsu.toString()); + EXPECT_STATE_EQ(expected, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Swing: Vert + Horiz, Command: Step vane horizontally", + ac.toString()); } TEST(TestIRFujitsuACClass, GetRawStepVert) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setModel(ARRAH2E); - fujitsu.stepVert(); + IRFujitsuAC ac(0); + ac.setModel(ARRAH2E); + ac.stepVert(); uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLengthShort, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: Step vane vertically", - fujitsu.toString()); + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Swing: Vert + Horiz, Command: Step vane vertically", + ac.toString()); - fujitsu.setModel(ARDB1); - fujitsu.stepVert(); + ac.setModel(ARDB1); + ac.stepVert(); uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); EXPECT_EQ(kFujitsuAcStateLengthShort - 1, - fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: Step vane vertically", - fujitsu.toString()); + ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Command: Step vane vertically", + ac.toString()); } TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingHoriz); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanQuiet); - fujitsu.setTemp(25); + IRFujitsuAC ac(0); + 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_TRUE(ArraysMatch(expected, fujitsu.getRaw())); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 4 (QUIET), " - "Swing: Horiz, Command: N/A", - fujitsu.toString()); + EXPECT_STATE_EQ(expected, ac.getRaw(), 16 * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 25C, " + "Fan: 4 (QUIET), Swing: Horiz, Command: N/A", + ac.toString()); } TEST(TestIRFujitsuACClass, GetRawWithFan) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingHoriz); - fujitsu.setMode(kFujitsuAcModeFan); - fujitsu.setFanSpeed(kFujitsuAcFanMed); - fujitsu.setTemp(20); // temp doesn't matter for fan + IRFujitsuAC ac(0); + 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 - fujitsu.setModel(ARRAH2E); + ac.setModel(ARRAH2E); uint8_t expected_arrah2e[16] = { - 0x14, 0x63, 0x0, 0x10, 0x10, 0xFE, 0x9, 0x30, - 0x40, 0x3, 0x22, 0x0, 0x0, 0x0, 0x20, 0x4B}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 3 (FAN), Temp: 20C, Fan: 2 (MED), Swing: Horiz, " - "Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 3 (FAN), Temp: 20C, " + "Fan: 2 (MED), Swing: Horiz, Command: N/A", ac.toString()); - fujitsu.setModel(ARDB1); + ac.setModel(ARDB1); uint8_t expected_ardb1[15] = { - 0x14, 0x63, 0x0, 0x10, 0x10, 0xFC, 0x8, 0x30, - 0x40, 0x3, 0x22, 0x0, 0x0, 0x0, 0x6B}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 3 (FAN), Temp: 20C, Fan: 2 (MED), Swing: Horiz, " - "Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 3 (FAN), Temp: 20C, " + "Fan: 2 (MED), Command: N/A", ac.toString()); } TEST(TestIRFujitsuACClass, SetRaw) { - IRFujitsuAC fujitsu = IRFujitsuAC(0); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); + IRFujitsuAC ac(0); + 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_TRUE(ArraysMatch(expected_default_arrah2e, fujitsu.getRaw())); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " - "Swing: Vert + Horiz, Command: N/A", fujitsu.toString()); + EXPECT_STATE_EQ(expected_default_arrah2e, ac.getRaw(), + ac.getStateLength() * 8); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (HIGH), Swing: Vert + Horiz, Command: N/A", + 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}; - fujitsu.setRaw(new_state1, kFujitsuAcStateLength - 1); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_TRUE(ArraysMatch(new_state1, fujitsu.getRaw())); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 0 (AUTO), Command: N/A", ac.toString()); } TEST(TestSendFujitsuAC, GenerateMessage) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); irsend.begin(); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingBoth); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanHigh); - fujitsu.setTemp(24); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); - EXPECT_EQ(kFujitsuAcFanHigh, fujitsu.getFanSpeed()); - EXPECT_EQ(kFujitsuAcModeCool, fujitsu.getMode()); - EXPECT_EQ(24, fujitsu.getTemp()); - EXPECT_EQ(kFujitsuAcSwingBoth, fujitsu.getSwing()); - EXPECT_EQ(kFujitsuAcCmdStayOn, fujitsu.getCmd()); + 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(fujitsu.getRaw(), kFujitsuAcStateLength); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLength); EXPECT_EQ( + "f38000d50" "m3324s1574" "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" @@ -205,18 +199,19 @@ TEST(TestSendFujitsuAC, GenerateMessage) { } TEST(TestSendFujitsuAC, GenerateShortMessage) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); irsend.begin(); - fujitsu.off(); + ac.off(); - EXPECT_EQ(kFujitsuAcCmdTurnOff, fujitsu.getCmd()); + EXPECT_EQ(kFujitsuAcCmdTurnOff, ac.getCmd()); irsend.reset(); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); EXPECT_EQ( + "f38000d50" "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" @@ -229,15 +224,16 @@ TEST(TestSendFujitsuAC, GenerateShortMessage) { // Issue #275 TEST(TestSendFujitsuAC, Issue275) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); irsend.begin(); irsend.reset(); - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); EXPECT_EQ( + "f38000d50" // Header "m3324s1574" // 0 0 1 0 1 0 0 0 (0x28) @@ -272,6 +268,7 @@ TEST(TestSendFujitsuAC, Issue275) { 450, 1250, 450}; irsend.sendRaw(off, 115, 38); EXPECT_EQ( + "f38000d50" // Header "m3350s1650" // 0 0 1 0 1 0 0 0 (0x28) @@ -295,51 +292,51 @@ TEST(TestSendFujitsuAC, Issue275) { TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { IRsendTest irsend(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); + IRFujitsuAC ac(0); IRrecv irrecv(0); irsend.begin(); irsend.reset(); - fujitsu.setModel(ARRAH2E); - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); + 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_TRUE(ArraysMatch(expected_arrah2e, irsend.capture.state)); + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); irsend.reset(); - fujitsu.setModel(ARDB1); - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); + 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_TRUE(ArraysMatch(expected_ardb1, irsend.capture.state)); + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); } TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { IRsendTest irsend(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); + IRFujitsuAC ac(0); IRrecv irrecv(0); irsend.begin(); irsend.reset(); - fujitsu.setModel(ARRAH2E); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingVert); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanQuiet); - fujitsu.setTemp(18); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); - ASSERT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); + 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); @@ -347,34 +344,34 @@ TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { uint8_t expected_arrah2e[kFujitsuAcStateLength] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; - EXPECT_TRUE(ArraysMatch(expected_arrah2e, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " - "Swing: Vert, Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (QUIET), Swing: Vert, Command: N/A", ac.toString()); irsend.reset(); - fujitsu.setModel(ARDB1); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.getStateLength()); + 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, 0x14, 0x00, 0x00, 0x00, 0x9B}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " - "Swing: Vert, Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (QUIET), Command: N/A", ac.toString()); } TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { IRsendTest irsend(0); IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); + IRFujitsuAC ac(0); irsend.begin(); @@ -396,17 +393,17 @@ TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { 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_TRUE(ArraysMatch(expected, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLengthShort - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); + 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), Power: Off, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (AUTO), Command: N/A", ac.toString()); } TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { IRsendTest irsend(0); IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); + IRFujitsuAC ac(0); irsend.begin(); irsend.reset(); @@ -440,11 +437,11 @@ TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { uint8_t expected1[kFujitsuAcStateLength - 1] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; - EXPECT_TRUE(ArraysMatch(expected1, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 4 (QUIET), " - "Swing: Off, Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (QUIET), Command: N/A", ac.toString()); irsend.reset(); uint16_t rawData2[243] = { @@ -477,17 +474,17 @@ TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { uint8_t expected2[kFujitsuAcStateLength - 1] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - EXPECT_TRUE(ArraysMatch(expected2, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength - 1, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 0 (AUTO), Command: N/A", ac.toString()); } TEST(TestDecodeFujitsuAC, Issue414) { IRsendTest irsend(0); IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); + IRFujitsuAC ac(0); // Capture as supplied by arpmota uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, @@ -519,11 +516,11 @@ TEST(TestDecodeFujitsuAC, Issue414) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); - fujitsu.setRaw(irsend.capture.state, irsend.capture.bits / 8); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.getStateLength()); - EXPECT_EQ("Power: On, Mode: 4 (HEAT), Temp: 24C, Fan: 0 (AUTO), " - "Swing: Off, Command: N/A", fujitsu.toString()); + 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), Power: On, Mode: 4 (HEAT), Temp: 24C, " + "Fan: 0 (AUTO), Swing: Off, Command: N/A", ac.toString()); // Resend it using the state this time. irsend.reset(); @@ -532,8 +529,9 @@ TEST(TestDecodeFujitsuAC, Issue414) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( + "f38000d50" "m3324s1574" "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" @@ -553,3 +551,248 @@ TEST(TestDecodeFujitsuAC, Issue414) { "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" "m448s8100", irsend.outputStr()); } + +TEST(TestIRFujitsuACClass, toCommon) { + IRFujitsuAC ac(0); + 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(0); + 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), Power: Off, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (AUTO), Swing: 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(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + // 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), Power: On, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (AUTO), Swing: Off, Command: Powerful, Outside Quiet: 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), Power: On, Mode: 0 (AUTO), Temp: 16C, " + "Fan: 0 (AUTO), Swing: Off, Command: Economy, Outside Quiet: Off", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, OutsideQuiet) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + 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/markszabo/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), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 0 (AUTO), Swing: Off, Command: N/A", ac.toString()); + ac.setModel(fujitsu_ac_remote_model_t::ARREB1E); + EXPECT_EQ( + "Model: 3 (ARREB1E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 0 (AUTO), Swing: Off, Command: N/A, Outside Quiet: 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), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 0 (AUTO), Swing: Off, Command: N/A, Outside Quiet: On", + 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(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + 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), Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 1 (HIGH), " + "Command: Toggle horizontal swing", + 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(0); + IRrecv irrecv(0); + IRFujitsuAC ac(0); + + // 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), Power: On, Mode: 0 (AUTO), Temp: 24C, " + "Fan: 0 (AUTO), Swing: Off, Command: N/A", + ac.toString()); +} diff --git a/lib/IRremoteESP8266/test/ir_GICable_test.cpp b/lib/IRremoteESP8266/test/ir_GICable_test.cpp index b9bfce9..bad9bbd 100644 --- a/lib/IRremoteESP8266/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GICable_test.cpp @@ -12,6 +12,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.begin(); irsend.sendGICable(0); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200m550s2200" @@ -20,6 +21,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.outputStr()); irsend.sendGICable(0x8807); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -28,6 +30,7 @@ TEST(TestSendGICable, SendDataOnly) { irsend.outputStr()); irsend.sendGICable(0xFFFF); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" "m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400m550s4400" @@ -43,6 +46,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 0 repeats. irsend.sendGICable(0x8807, kGicableBits, 0); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -51,6 +55,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 1 repeat. irsend.sendGICable(0x8807, kGicableBits, 1); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" @@ -60,6 +65,7 @@ TEST(TestSendGICable, SendWithRepeats) { // Send a command with 3 repeats. irsend.sendGICable(0x8807, kGicableBits, 3); EXPECT_EQ( + "f39000d50" "m9000s4400" "m550s4400m550s2200m550s2200m550s2200m550s4400m550s2200m550s2200m550s2200" "m550s2200m550s2200m550s2200m550s2200m550s2200m550s4400m550s4400m550s4400" diff --git a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp index 16a556b..00742ae 100644 --- a/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp +++ b/lib/IRremoteESP8266/test/ir_GlobalCache_test.cpp @@ -29,6 +29,7 @@ TEST(TestSendGlobalCache, NonRepeatingCode) { EXPECT_EQ(0x4, irsend.capture.address); EXPECT_EQ(0x41, irsend.capture.command); EXPECT_EQ( + "f38000d50" "m8892s4472m546s572m546s546m546s1690m546s546m546s572m546s572" "m546s546m546s572m546s1690m546s1690m546s572m546s1690m546s1690" "m546s1690m546s1690m546s1690m546s1690m546s572m546s572m546s546" @@ -60,6 +61,7 @@ TEST(TestSendGlobalCache, RepeatCode) { EXPECT_EQ(0x4583, irsend.capture.address); EXPECT_EQ(0x11, irsend.capture.command); EXPECT_EQ( + "f38000d50" "m8866s4446m546s1664m546s1664m546s546m546s546m546s546m546s546" "m546s546m546s1664m546s1664m546s546m546s1664m546s546m546s546" "m546s546m546s1664m546s546m546s1664m546s546m546s546m546s546" diff --git a/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp new file mode 100644 index 0000000..5185eb4 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Goodweather_test.cpp @@ -0,0 +1,511 @@ +// Copyright 2019 David Conran + +#include "ir_Goodweather.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +TEST(TestIRUtils, Goodweather) { + ASSERT_EQ("GOODWEATHER", typeToString(decode_type_t::GOODWEATHER)); + ASSERT_EQ(decode_type_t::GOODWEATHER, strToDecodeType("GOODWEATHER")); + ASSERT_FALSE(hasACState(decode_type_t::GOODWEATHER)); +} + +// Tests for sendGoodweather(). + +// Test sending typical data only. +TEST(TestSendGoodweather, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendGoodweather(0x0); + EXPECT_EQ( + "f38000d50" + "m6800s6800" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600m640s1600" + "m640s580m640s580m640s580m640s580m640s580m640s580m640s580m640s580" + "m640s6800m640s100000", + irsend.outputStr()); + + irsend.reset(); +} + +// Tests for decodeGoodweather(). + +// Decode normal Goodweather messages. +TEST(TestDecodeGoodweather, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Normal (made-up value) Goodweather 48-bit message. + irsend.reset(); + irsend.sendGoodweather(0x1234567890AB); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0x1234567890AB, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + // Normal (Real) Goodweather 48-bit message. + irsend.reset(); + irsend.sendGoodweather(0xD5276A030000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5276A030000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); +} + +// Decode a real example of a Goodweather message. +// https://github.com/markszabo/IRremoteESP8266/issues/697#issuecomment-490209819 +TEST(TestDecodeGoodweather, RealExampleDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRGoodweatherAc ac(0); + irsend.begin(); + ac.begin(); + + irsend.reset(); + // Raw Goodweather 48-bit message. + uint16_t rawData_4624AB[197] = { + 6828, 6828, 732, 1780, 652, 1830, 652, 1806, 678, 1830, 652, 1806, 678, + 1830, 652, 1830, 652, 1834, 706, 518, 734, 508, 734, 514, 734, 510, 732, + 510, 732, 510, 732, 510, 732, 514, 732, 1776, 706, 1780, 628, 1854, 628, + 1832, 654, 1832, 654, 1856, 628, 1832, 634, 1876, 680, 536, 708, 536, 708, + 536, 706, 538, 706, 538, 706, 538, 706, 536, 680, 564, 680, 1828, 708, + 1758, 680, 1804, 680, 1828, 708, 1778, 732, 1754, 732, 1754, 732, 1756, + 732, 490, 658, 586, 658, 586, 658, 586, 658, 586, 658, 584, 658, 586, 658, + 586, 660, 1850, 704, 520, 658, 1828, 658, 1826, 658, 1826, 658, 586, 660, + 584, 684, 1826, 730, 490, 686, 1824, 660, 560, 710, 532, 710, 534, 712, + 1776, 712, 1774, 686, 560, 712, 1774, 712, 1798, 730, 492, 712, 1798, 684, + 1798, 678, 568, 730, 1756, 686, 1796, 686, 532, 712, 532, 712, 1796, 728, + 494, 712, 532, 738, 1772, 730, 492, 712, 532, 738, 506, 738, 1772, 660, + 582, 728, 1736, 712, 558, 710, 1750, 710, 558, 710, 510, 738, 1748, 738, + 508, 736, 1772, 684, 534, 736, 1772, 704, 518, 738, 1772, 660, 1824, 678, + 6770, 684}; // COOLIX 4624AB + irsend.sendRaw(rawData_4624AB, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD52462000000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 3 (LOW), " + "Turbo: -, Light: -, Sleep: -, Swing: 0 (Fast), Command: 0 (Power)", + ac.toString()); + +uint16_t rawData_FAD2BE31[197] = { + 6142, 7348, 570, 1612, 638, 1562, 620, 1584, 670, 1538, 566, 1638, 564, + 1610, 618, 1582, 642, 1542, 638, 498, 622, 518, 618, 496, 622, 518, 596, + 522, 596, 542, 618, 498, 618, 520, 594, 1590, 614, 1586, 618, 1588, 640, + 1592, 538, 1614, 612, 1584, 620, 1584, 616, 1592, 564, 546, 596, 540, 596, + 520, 620, 520, 594, 524, 618, 522, 650, 466, 616, 522, 670, 1532, 618, 1568, + 590, 1610, 618, 1612, 640, 1530, 594, 1586, 618, 1616, 590, 1586, 640, 472, + 618, 520, 672, 446, 618, 520, 646, 474, 616, 520, 622, 500, 614, 518, 624, + 1612, 560, 1616, 590, 1584, 620, 520, 646, 1540, 612, 518, 622, 516, 596, + 1586, 618, 518, 622, 498, 616, 520, 622, 1582, 616, 498, 620, 1582, 622, + 1586, 586, 528, 616, 1582, 622, 498, 616, 518, 624, 1582, 614, 1592, 568, + 544, 620, 1580, 648, 1542, 610, 520, 622, 1586, 666, 1536, 592, 518, 600, + 542, 594, 1592, 590, 544, 620, 498, 616, 518, 622, 1580, 620, 496, 620, + 1586, 618, 502, 610, 1584, 620, 518, 672, 446, 620, 1612, 592, 504, 608, + 1586, 618, 518, 646, 1540, 612, 520, 600, 1604, 622, 1582, 596, 7382, 566}; + // UNKNOWN FAD2BE31 + + irsend.reset(); + irsend.sendRaw(rawData_FAD2BE31, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD52668000000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: Off, Mode: 1 (COOL), Temp: 22C, Fan: 3 (LOW), Turbo: -, " + "Light: -, Sleep: -, Swing: 2 (Off), Command: 0 (Power)", + ac.toString()); + + uint16_t rawData_5453D3AD[197] = { + 6190, 7298, 668, 1542, 614, 1590, 590, 1582, 620, 1584, 566, 1624, 632, + 1592, 616, 1588, 638, 1538, 594, 520, 620, 520, 594, 522, 620, 520, 586, + 530, 618, 520, 640, 480, 616, 520, 642, 1544, 612, 1588, 622, 1576, 668, + 1540, 564, 1640, 592, 1582, 646, 1558, 670, 1536, 594, 518, 622, 520, 594, + 522, 620, 520, 566, 552, 618, 520, 614, 504, 618, 518, 666, 1520, 610, + 1586, 618, 1612, 636, 1568, 564, 1590, 614, 1584, 620, 1582, 666, 1542, + 614, 526, 590, 520, 596, 520, 622, 520, 566, 550, 620, 520, 588, 530, 618, + 520, 668, 1536, 594, 520, 646, 1558, 668, 452, 616, 1584, 642, 498, 566, + 550, 618, 1582, 668, 454, 612, 1582, 646, 496, 594, 1614, 666, 450, 662, + 1536, 584, 1600, 612, 520, 642, 1590, 588, 502, 616, 520, 588, 1600, 612, + 1586, 616, 520, 612, 1574, 610, 1584, 644, 496, 564, 1620, 636, 1562, 640, + 524, 560, 530, 616, 1582, 644, 498, 620, 494, 670, 472, 622, 1558, 616, + 520, 642, 1564, 594, 518, 646, 1558, 668, 454, 638, 494, 668, 1538, 616, + 498, 642, 1558, 670, 454, 636, 1560, 642, 496, 614, 1592, 616, 1584, 620, + 7350, 668}; // UNKNOWN 5453D3AD + + irsend.reset(); + irsend.sendRaw(rawData_5453D3AD, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5266A000000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 3 (LOW), Turbo: -, " + "Light: -, Sleep: -, Swing: 2 (Off), Command: 0 (Power)", + ac.toString()); + + uint16_t rawData_B2354FBB[197] = { + 6192, 7298, 592, 1616, 618, 1584, 620, 1558, 668, 1520, 636, 1562, 642, + 1584, 590, 1614, 542, 1634, 622, 494, 668, 454, 638, 494, 670, 454, 638, + 492, 646, 480, 636, 494, 672, 470, 622, 1560, 642, 1556, 672, 1534, 614, + 1572, 636, 1584, 622, 1582, 644, 1534, 596, 1586, 642, 494, 666, 454, 640, + 494, 668, 452, 640, 494, 668, 454, 638, 494, 670, 470, 620, 1562, 666, + 470, 644, 1546, 634, 1584, 618, 1584, 644, 1534, 640, 1548, 636, 1560, + 644, 520, 542, 1618, 638, 494, 670, 454, 636, 496, 670, 454, 634, 494, + 672, 470, 620, 1564, 640, 496, 642, 1562, 616, 520, 622, 1558, 668, 450, + 640, 494, 694, 1536, 566, 524, 644, 1558, 666, 456, 638, 1558, 644, 520, + 572, 1588, 638, 1558, 644, 494, 590, 1596, 638, 1584, 620, 1584, 644, 454, + 638, 1556, 672, 472, 620, 1562, 640, 1558, 646, 494, 644, 470, 646, 496, + 566, 1618, 638, 494, 668, 1534, 646, 468, 674, 468, 568, 550, 670, 1530, + 670, 454, 638, 1560, 644, 494, 622, 1582, 620, 494, 646, 496, 620, 1560, + 644, 494, 668, 1522, 610, 518, 674, 1532, 614, 504, 640, 1584, 642, 1562, + 616, 7332, 594}; // UNKNOWN B2354FBB + + irsend.reset(); + irsend.sendRaw(rawData_B2354FBB, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5286A020000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 3 (LOW), Turbo: -, " + "Light: -, Sleep: -, Swing: 2 (Off), Command: 2 (Temp Up)", + ac.toString()); + + uint16_t rawData_71DD9105[197] = { + 6190, 7296, 696, 1496, 634, 1562, 642, 1582, 640, 1564, 564, 1598, 638, + 1558, 646, 1560, 588, 1616, 618, 520, 620, 494, 622, 494, 646, 494, 620, + 496, 644, 494, 590, 528, 642, 494, 642, 1544, 638, 1584, 618, 1564, 804, + 1394, 620, 1564, 640, 1558, 644, 1586, 562, 1616, 620, 492, 672, 470, 622, + 494, 646, 494, 622, 494, 646, 494, 620, 498, 644, 492, 596, 520, 644, 494, + 592, 1596, 612, 1584, 642, 1560, 614, 1612, 594, 1584, 620, 1558, 646, + 1556, 644, 1562, 618, 520, 620, 494, 620, 494, 646, 494, 568, 548, 644, + 494, 616, 1570, 638, 494, 670, 1534, 568, 550, 646, 1556, 616, 526, 618, + 492, 672, 1532, 568, 550, 646, 1558, 640, 500, 618, 1560, 668, 470, 642, + 1548, 658, 1536, 642, 520, 588, 504, 644, 492, 644, 478, 642, 1582, 618, + 1586, 590, 506, 640, 1556, 646, 1584, 562, 1616, 620, 1558, 646, 1556, + 670, 454, 638, 492, 648, 1558, 642, 478, 644, 492, 590, 530, 858, 1342, + 642, 496, 618, 1564, 642, 492, 642, 1548, 636, 492, 648, 494, 622, 1562, + 642, 492, 644, 1562, 618, 520, 620, 1558, 644, 476, 640, 1558, 646, 1558, + 612, 7382, 594}; // UNKNOWN 71DD9105 + + irsend.reset(); + irsend.sendRaw(rawData_71DD9105, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD5276A030000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 23C, Fan: 3 (LOW), " + "Turbo: -, Light: -, Sleep: -, Swing: 2 (Off), Command: 3 (Temp Down)", + ac.toString()); + + uint16_t rawData_C4F9E573[199] = { + 6186, 7296, 648, 1558, 670, 1542, 612, 1584, 618, 1560, 668, 1534, 622, + 1566, 638, 1558, 646, 1584, 590, 506, 640, 492, 642, 480, 640, 494, 644, + 478, 640, 494, 668, 454, 614, 516, 648, 1560, 566, 1638, 618, 1584, 620, + 1556, 672, 1534, 620, 1564, 640, 1584, 618, 1586, 564, 528, 670, 468, 640, + 478, 642, 494, 644, 478, 640, 492, 670, 454, 638, 494, 670, 1560, 542, + 1636, 644, 468, 670, 1534, 620, 1586, 618, 1558, 646, 1556, 670, 1534, + 622, 492, 648, 494, 620, 1562, 642, 496, 642, 476, 642, 494, 696, 426, + 642, 492, 646, 1558, 568, 548, 644, 494, 642, 1564, 618, 1584, 620, 494, + 568, 548, 644, 1558, 644, 480, 636, 1584, 620, 1584, 644, 456, 634, 494, + 672, 1560, 540, 1638, 618, 494, 728, 1476, 592, 524, 646, 492, 616, 1572, + 638, 1560, 644, 492, 668, 1520, 638, 1562, 642, 494, 588, 1598, 638, 1560, + 642, 494, 622, 498, 642, 1556, 646, 478, 638, 492, 646, 494, 620, 1584, + 618, 522, 616, 1546, 612, 516, 648, 1556, 644, 476, 668, 468, 670, 1534, + 620, 494, 648, 1556, 670, 452, 640, 1558, 644, 496, 646, 1536, 616, 1582, + 646, 7326, 616, 41598, 230}; // UNKNOWN C4F9E573 + + irsend.reset(); + irsend.sendRaw(rawData_C4F9E573, 199, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::GOODWEATHER, irsend.capture.decode_type); + EXPECT_EQ(kGoodweatherBits, irsend.capture.bits); + EXPECT_EQ(0xD52666040000, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_FALSE(irsend.capture.repeat); + ac.setRaw(irsend.capture.value); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 3 (LOW), Turbo: -, Light: -, " + "Sleep: -, Swing: 1 (Slow), Command: 4 (Swing)", + ac.toString()); +} + + +TEST(TestGoodweatherAcClass, toCommon) { + IRGoodweatherAc ac(0); + ac.setPower(true); + ac.setMode(kGoodweatherCool); + ac.setTemp(20); + ac.setFan(kGoodweatherFanHigh); + ac.setSwing(kGoodweatherSwingFast); + ac.setTurbo(true); + ac.setLight(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::GOODWEATHER, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + 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_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + + +TEST(TestGoodweatherAcClass, Temperature) { + IRGoodweatherAc ac(0); + + ac.setTemp(kGoodweatherTempMin); + EXPECT_EQ(kGoodweatherTempMin, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMin + 1); + EXPECT_EQ(kGoodweatherTempMin + 1, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMax); + EXPECT_EQ(kGoodweatherTempMax, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMin - 1); + EXPECT_EQ(kGoodweatherTempMin, ac.getTemp()); + + ac.setTemp(kGoodweatherTempMax + 1); + EXPECT_EQ(kGoodweatherTempMax, ac.getTemp()); + + ac.setTemp(23); + EXPECT_EQ(23, ac.getTemp()); + + ac.setTemp(27); + EXPECT_EQ(27, ac.getTemp()); + + ac.setTemp(22); + EXPECT_EQ(22, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(0); + EXPECT_EQ(kGoodweatherTempMin, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kGoodweatherTempMax, ac.getTemp()); +} + +TEST(TestGoodweatherAcClass, OperatingMode) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setMode(kGoodweatherAuto); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + EXPECT_EQ(kGoodweatherCool, ac.getMode()); + + ac.setMode(kGoodweatherHeat); + EXPECT_EQ(kGoodweatherHeat, ac.getMode()); + + ac.setMode(kGoodweatherFan); // Should set fan speed to High. + EXPECT_EQ(kGoodweatherFan, ac.getMode()); + + ac.setMode(kGoodweatherDry); + EXPECT_EQ(kGoodweatherDry, ac.getMode()); + + ac.setMode(kGoodweatherHeat + 1); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + EXPECT_EQ(kGoodweatherCool, ac.getMode()); + + ac.setMode(kGoodweatherAuto - 1); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + ac.setMode(255); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); + + ac.setMode(kGoodweatherCool); + ac.setMode(0); + EXPECT_EQ(kGoodweatherAuto, ac.getMode()); +} + +TEST(TestGoodweatherAcClass, Power) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_EQ(false, ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_EQ(false, ac.getPower()); + + ac.on(); + EXPECT_TRUE(ac.getPower()); +} + +TEST(TestGoodweatherAcClass, Light) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_EQ(false, ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestGoodweatherAcClass, Turbo) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + ac.setTurbo(false); + EXPECT_EQ(false, ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestGoodweatherAcClass, Sleep) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_EQ(false, ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestGoodweatherAcClass, FanSpeed) { + IRGoodweatherAc ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kGoodweatherFanAuto, ac.getFan()); + + ac.setFan(kGoodweatherFanLow); + EXPECT_EQ(kGoodweatherFanLow, ac.getFan()); + ac.setFan(kGoodweatherFanMed); + EXPECT_EQ(kGoodweatherFanMed, ac.getFan()); + ac.setFan(kGoodweatherFanHigh); + EXPECT_EQ(kGoodweatherFanHigh, ac.getFan()); + ac.setFan(kGoodweatherFanAuto); + EXPECT_EQ(kGoodweatherFanAuto, ac.getFan()); + + // Beyond Low should default to Auto. + ac.setFan(kGoodweatherFanLow + 1); + EXPECT_EQ(kGoodweatherFanAuto, ac.getFan()); +} + + +TEST(TestGoodweatherAcClass, SwingSpeed) { + IRGoodweatherAc ac(0); + ac.begin(); + + // Unexpected value should default to Off. + ac.setSwing(255); + EXPECT_EQ(kGoodweatherSwingOff, ac.getSwing()); + + ac.setSwing(kGoodweatherSwingSlow); + EXPECT_EQ(kGoodweatherSwingSlow, ac.getSwing()); + ac.setSwing(kGoodweatherSwingOff); + EXPECT_EQ(kGoodweatherSwingOff, ac.getSwing()); + ac.setSwing(kGoodweatherSwingFast); + EXPECT_EQ(kGoodweatherSwingFast, ac.getSwing()); + + // Beyond Off should default to Off. + ac.setSwing(kGoodweatherSwingOff + 1); + EXPECT_EQ(kGoodweatherSwingOff, ac.getSwing()); +} + +TEST(TestGoodweatherAcClass, Command) { + IRGoodweatherAc ac(0); + ac.begin(); + + ac.setCommand(kGoodweatherCmdMode); + EXPECT_EQ(kGoodweatherCmdMode, ac.getCommand()); + // Unexpected value should not change anything. + ac.setCommand(255); + EXPECT_EQ(kGoodweatherCmdMode, ac.getCommand()); + + ac.setCommand(kGoodweatherCmdPower); + EXPECT_EQ(kGoodweatherCmdPower, ac.getCommand()); + ac.setCommand(kGoodweatherCmdLight); + EXPECT_EQ(kGoodweatherCmdLight, ac.getCommand()); + + // Beyond Light should be ignored. + ac.setCommand(kGoodweatherCmdLight + 1); + EXPECT_EQ(kGoodweatherCmdLight, ac.getCommand()); +} diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index 6c7a1f6..58b2ba8 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -20,7 +20,8 @@ TEST(TestSendGreeChars, SendData) { irsend.reset(); irsend.sendGree(gree_code); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -42,7 +43,8 @@ TEST(TestSendGreeUint64, SendData) { irsend.reset(); irsend.sendGree(0x1234567890ABCDEF); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -69,7 +71,8 @@ TEST(TestSendGreeChars, SendWithRepeats) { irsend.sendGree(gree_code, kGreeStateLength, 1); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -81,7 +84,7 @@ TEST(TestSendGreeChars, SendWithRepeats) { "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600" "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600" "m620s19000" - "m9000s4000" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -103,7 +106,8 @@ TEST(TestSendGreeUint64, SendWithRepeats) { irsend.reset(); irsend.sendGree(0x1234567890ABCDEF, kGreeBits, 1); EXPECT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -115,7 +119,7 @@ TEST(TestSendGreeUint64, SendWithRepeats) { "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600" "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600" "m620s19000" - "m9000s4000" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -146,7 +150,8 @@ TEST(TestSendGreeChars, SendUnexpectedSizes) { irsend.reset(); irsend.sendGree(gree_long_code, kGreeStateLength + 1); ASSERT_EQ( - "m9000s4000" + "f38000d50" + "m9000s4500" "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540" "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540" "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540" @@ -486,7 +491,7 @@ TEST(TestDecodeGree, NormalSynthetic) { EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits); } -// Decode a synthetic Gree message. +// Decode a real Gree message. TEST(TestDecodeGree, NormalRealExample) { IRsendTest irsend(4); IRrecv irrecv(4); @@ -525,3 +530,36 @@ TEST(TestDecodeGree, NormalRealExample) { "Swing Vertical Pos: 2", irgree.toString()); } + +TEST(TestGreeClass, toCommon) { + IRGreeAC ac(0); + ac.setPower(true); + ac.setMode(kGreeCool); + ac.setTemp(20); + ac.setFan(kGreeFanMax); + ac.setSwingVertical(false, kGreeSwingUp); + ac.setTurbo(true); + ac.setXFan(true); + ac.setLight(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::GREE, 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_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index 11848e0..bc4895f 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -19,6 +19,7 @@ TEST(TestSendHaierAC, SendDataOnly) { irsend.reset(); irsend.sendHaierAC(haier_zero); EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s650" @@ -37,6 +38,7 @@ TEST(TestSendHaierAC, SendDataOnly) { irsend.reset(); irsend.sendHaierAC(haier_test); EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" @@ -62,6 +64,7 @@ TEST(TestSendHaierAC, SendWithRepeats) { irsend.reset(); irsend.sendHaierAC(haier_test, kHaierACStateLength, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m3000s3000m3000s4300" "m520s1650m520s650m520s1650m520s650m520s650m520s1650m520s650m520s1650" "m520s650m520s650m520s650m520s650m520s650m520s650m520s650m520s1650" @@ -374,7 +377,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.setSleep(true); haier.setCurrTime(615); // 10:15am EXPECT_EQ( - "Command: 8 (Sleep), Mode: 3 (HEAT), Temp: 21C, Fan: 3 (MAX), " + "Command: 8 (Sleep), Mode: 1 (COOL), Temp: 21C, Fan: 3 (MAX), " "Swing: 3 (Chg), Sleep: On, Health: On, " "Current Time: 10:15, On Timer: Off, Off Timer: Off", haier.toString()); @@ -383,7 +386,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.setCommand(kHaierAcCmdOn); EXPECT_EQ( - "Command: 1 (On), Mode: 2 (DRY), Temp: 21C, Fan: 2, " + "Command: 1 (On), Mode: 1 (COOL), Temp: 21C, Fan: 2, " "Swing: 3 (Chg), Sleep: On, Health: On, " "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", haier.toString()); @@ -393,18 +396,18 @@ TEST(TestHaierACClass, MessageConstuction) { EXPECT_EQ( "Command: 2 (Mode), Mode: 3 (HEAT), Temp: 21C, Fan: 2, " "Swing: 3 (Chg), Sleep: On, Health: On, " - "Current Time: 10:15, On Timer: 13:52, Off Timer: 18:45", + "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", haier.toString()); haier.setTemp(25); EXPECT_EQ( "Command: 6 (Temp Up), Mode: 3 (HEAT), Temp: 25C, Fan: 2, " "Swing: 3 (Chg), Sleep: On, Health: On, " - "Current Time: 10:15, On Timer: 13:52, Off Timer: 18:45", + "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", haier.toString()); uint8_t expectedState[kHaierACStateLength] = {0xA5, 0x96, 0xEA, 0xCF, 0x32, - 0x2D, 0x0D, 0x74, 0xD4}; + 0xED, 0x6D, 0x54, 0xD4}; EXPECT_STATE_EQ(expectedState, haier.getRaw(), kHaierACBits); // Check that the checksum is valid. @@ -828,7 +831,7 @@ TEST(TestDecodeHaierAC, RealExample1) { IRHaierAC haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Command: 1 (On), Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), " + "Command: 1 (On), Mode: 1 (COOL), Temp: 16C, Fan: 0 (AUTO), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Current Time: 00:01, On Timer: Off, Off Timer: Off", haier.toString()); @@ -870,7 +873,7 @@ TEST(TestDecodeHaierAC, RealExample2) { IRHaierAC haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Command: 6 (Temp Up), Mode: 0 (AUTO), Temp: 22C, Fan: 0 (AUTO), " + "Command: 6 (Temp Up), Mode: 1 (COOL), Temp: 22C, Fan: 0 (AUTO), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Current Time: 00:01, On Timer: Off, Off Timer: Off", haier.toString()); @@ -912,7 +915,7 @@ TEST(TestDecodeHaierAC, RealExample3) { IRHaierAC haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Command: 12 (Health), Mode: 0 (AUTO), Temp: 30C, Fan: 0 (AUTO), " + "Command: 12 (Health), Mode: 1 (COOL), Temp: 30C, Fan: 0 (AUTO), " "Swing: 0 (Off), Sleep: Off, Health: On, " "Current Time: 00:09, On Timer: Off, Off Timer: Off", haier.toString()); @@ -987,3 +990,154 @@ TEST(TestDecodeHaierAC_YRW02, RealExample) { " Health: On", haier.toString()); } + +// Default state of the remote needed to include hidden data. +// Ref: https://github.com/markszabo/IRremoteESP8266/issues/668 +TEST(TestHaierAcIssues, Issue668) { + IRHaierAC ac(0); + IRHaierAC acText(1); + IRrecv irrecv(0); + ac.begin(); + + // Turn on the AC. + ac._irsend.reset(); + char expected_on[] = + "Command: 1 (On), Mode: 1 (COOL), Temp: 25C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + // State taken from real capture: + // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + uint8_t expected_on_state[9] = { + 0xA5, 0x91, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x42}; + ac.setMode(kHaierAcCool); + ac.setCommand(kHaierAcCmdOn); + EXPECT_EQ(expected_on, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_on_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_on, acText.toString()); + + // Set the temp to 25C + ac._irsend.reset(); + ac.setTemp(25); + EXPECT_EQ(expected_on, ac.toString()); + ASSERT_EQ(25, ac.getTemp()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_on_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_on, acText.toString()); + + // Increase the temp by 1. + ac._irsend.reset(); + char expected_temp_plus_one[] = + "Command: 6 (Temp Up), Mode: 1 (COOL), Temp: 26C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + // State taken from real capture: + // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + uint8_t expected_temp_plus_one_state[9] = { + 0xA5, 0xA6, 0x20, 0x00, 0x0C, 0xC0, 0x20, 0x00, 0x57}; + ASSERT_EQ(25, ac.getTemp()); + ac.setTemp(ac.getTemp() + 1); + ASSERT_EQ(26, ac.getTemp()); + EXPECT_EQ(expected_temp_plus_one, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expected_temp_plus_one_state, + ac._irsend.capture.state, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_temp_plus_one, acText.toString()); + + // Decrease the temp by 1. + ac._irsend.reset(); + char expected_temp_minus_one[] = + "Command: 7 (Temp Down), Mode: 1 (COOL), Temp: 25C, Fan: 0 (AUTO), " + "Swing: 0 (Off), Sleep: Off, Health: Off, Current Time: 00:00, " + "On Timer: Off, Off Timer: Off"; + ASSERT_EQ(26, ac.getTemp()); + ac.setTemp(ac.getTemp() - 1); + ASSERT_EQ(25, ac.getTemp()); + EXPECT_EQ(expected_temp_minus_one, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(HAIER_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kHaierACBits, ac._irsend.capture.bits); + acText.setRaw(ac._irsend.capture.state); + EXPECT_EQ(expected_temp_minus_one, acText.toString()); +} + +TEST(TestHaierACClass, toCommon) { + IRHaierAC ac(0); + ac.setCommand(kHaierAcCmdOn); + ac.setMode(kHaierAcCool); + ac.setTemp(20); + ac.setFan(kHaierAcFanHigh); + ac.setSwing(kHaierAcSwingChg); + ac.setHealth(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::HAIER_AC, 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_TRUE(ac.toCommon().filter); + 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(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestHaierACYRW02Class, toCommon) { + IRHaierACYRW02 ac(0); + ac.setPower(true); + ac.setMode(kHaierAcYrw02Cool); + ac.setTemp(20); + ac.setFan(kHaierAcYrw02FanHigh); + ac.setSwing(kHaierAcYrw02SwingTop); + ac.setHealth(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::HAIER_AC_YRW02, 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_TRUE(ac.toCommon().filter); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp index de0a4a2..0d9b2f4 100644 --- a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp @@ -22,6 +22,7 @@ TEST(TestSendHitachiAC, SendData) { irsend.reset(); irsend.sendHitachiAC(hitachi_code); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -68,6 +69,7 @@ TEST(TestSendHitachiAC, SendWithRepeats) { irsend.sendHitachiAC(hitachi_code, kHitachiAcStateLength, 1); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -151,6 +153,7 @@ TEST(TestSendHitachiAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendHitachiAC(hitachi_long_code, kHitachiAcStateLength + 1); ASSERT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -511,6 +514,7 @@ TEST(TestSendHitachiAC1, SendData) { irsend.reset(); irsend.sendHitachiAC1(hitachi_code); EXPECT_EQ( + "f38000d50" "m3400s3400" "m400s1250m400s500m400s1250m400s1250m400s500m400s500m400s1250m400s500" "m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s1250m400s500" @@ -585,6 +589,7 @@ TEST(TestSendHitachiAC2, SendData) { irsend.reset(); irsend.sendHitachiAC2(hitachi_code); EXPECT_EQ( + "f38000d50" "m3300s1700" "m400s1250m400s500m400s500m400s500m400s500m400s500m400s500m400s500" "m400s500m400s500m400s500m400s500m400s1250m400s500m400s500m400s500" @@ -763,3 +768,33 @@ TEST(TestDecodeHitachiAC2, NormalRealExample) { ASSERT_EQ(kHitachiAc2Bits, irsend.capture.bits); EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, kHitachiAc2Bits); } + +TEST(TestIRHitachiAcClass, toCommon) { + IRHitachiAc ac(0); + ac.setPower(true); + ac.setMode(kHitachiAcCool); + ac.setTemp(20); + ac.setFan(kHitachiAcFanHigh); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + // Now test it. + ASSERT_EQ(decode_type_t::HITACHI_AC, 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_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(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Inax_test.cpp b/lib/IRremoteESP8266/test/ir_Inax_test.cpp new file mode 100644 index 0000000..b182cce --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Inax_test.cpp @@ -0,0 +1,119 @@ +// Copyright 2019 crankyoldgit (David Conran) + +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" +#include "gtest/gtest.h" + + +// General housekeeping +TEST(TestInax, Housekeeping) { + ASSERT_EQ("INAX", typeToString(INAX)); + ASSERT_FALSE(hasACState(INAX)); +} + +// Tests for sendInax(). +// Test sending typical data only. +TEST(TestSendInax, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendInax(0x5C32CD); // Small flush. + EXPECT_EQ( + "f38000d50" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000", + irsend.outputStr()); + + irsend.reset(); +} + +// Test sending with different repeats. +TEST(TestSendInax, SendWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendInax(0x5C32CD, kInaxBits, 0); // 0 repeats. + EXPECT_EQ( + "f38000d50" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000", + irsend.outputStr()); + irsend.sendInax(0x5C32CD, kInaxBits, 2); // 2 repeats. + EXPECT_EQ( + "f38000d50" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000" + "m9000s4500" + "m560s560m560s1675m560s560m560s1675m560s1675m560s1675m560s560m560s560" + "m560s560m560s560m560s1675m560s1675m560s560m560s560m560s1675m560s560" + "m560s1675m560s1675m560s560m560s560m560s1675m560s1675m560s560m560s1675" + "m560s40000", + irsend.outputStr()); +} + +// Tests for decodeInax(). + +// Decode normal Inax messages. +TEST(TestDecodeInax, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Normal Inax 24-bit message. + irsend.reset(); + irsend.sendInax(0x5C32CD); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(INAX, irsend.capture.decode_type); + EXPECT_EQ(kInaxBits, irsend.capture.bits); + EXPECT_EQ(0x5C32CD, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); +} + +// Decode real example via Issue #704 +TEST(TestDecodeInax, DecodeExamples) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // Inax Small Flush from Issue #309 + uint16_t smallFlushRawData[51] = { + 8996, 4474, 568, 556, 560, 1676, 568, 556, 562, 1676, 562, 1678, 566, + 1674, 566, 558, 560, 560, 566, 556, 566, 556, 560, 1678, 562, 1676, 566, + 556, 562, 560, 564, 1672, 566, 556, 562, 1676, 562, 1678, 562, 560, 564, + 558, 564, 1674, 560, 1678, 564, 560, 566, 1670, 562}; + + irsend.sendRaw(smallFlushRawData, 51, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(INAX, irsend.capture.decode_type); + EXPECT_EQ(kInaxBits, irsend.capture.bits); + EXPECT_EQ(0x5C32CD, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266/test/ir_JVC_test.cpp b/lib/IRremoteESP8266/test/ir_JVC_test.cpp index c899fa8..3cd99c3 100644 --- a/lib/IRremoteESP8266/test/ir_JVC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_JVC_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendJVC, SendDataOnly) { irsend.reset(); irsend.sendJVC(0xC2B8); // JVC VCR Power On. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -29,6 +30,7 @@ TEST(TestSendJVC, SendWithRepeats) { irsend.reset(); irsend.sendJVC(0xC2B8, kJvcBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -39,6 +41,7 @@ TEST(TestSendJVC, SendWithRepeats) { irsend.outputStr()); irsend.sendJVC(0xC2B8, kJvcBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s1725m525s1725m525s525m525s525m525s525m525s525m525s1725m525s525" "m525s1725m525s525m525s1725m525s1725m525s1725m525s525m525s525m525s525" @@ -60,6 +63,7 @@ TEST(TestSendJVC, SendUnusualSize) { irsend.reset(); irsend.sendJVC(0x0, 8); EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s525m525s525m525s525m525s525m525s525m525s525m525s525m525s525" "m525s38475", @@ -68,6 +72,7 @@ TEST(TestSendJVC, SendUnusualSize) { irsend.reset(); irsend.sendJVC(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d33" "m8400s4200" "m525s525m525s525m525s525m525s1725m525s525m525s525m525s1725m525s525" "m525s525m525s525m525s1725m525s1725m525s525m525s1725m525s525m525s525" diff --git a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp index 001f8bc..e0ff6cb 100644 --- a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp @@ -21,6 +21,7 @@ TEST(TestSendKelvinator, SendDataOnly) { irsend.reset(); irsend.sendKelvinator(kelv_code); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -61,6 +62,7 @@ TEST(TestSendKelvinator, SendWithRepeats) { irsend.sendKelvinator(kelv_code, kKelvinatorStateLength, 1); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -131,6 +133,7 @@ TEST(TestSendKelvinator, SendUnexpectedSizes) { // extra data. irsend.sendKelvinator(kelv_long_code, 17); ASSERT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s510m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -472,6 +475,7 @@ TEST(TestKelvinatorClass, MessageConstuction) { irsend.reset(); irsend.sendKelvinator(irkelv.getRaw()); EXPECT_EQ( + "f38000d50" "m9010s4505" "m680s1530m680s510m680s510m680s1530m680s1530m680s510m680s1530m680s510" "m680s1530m680s1530m680s510m680s1530m680s510m680s510m680s510m680s510" @@ -516,3 +520,39 @@ TEST(TestDecodeKelvinator, NormalSynthetic) { ASSERT_EQ(kKelvinatorBits, irsend.capture.bits); EXPECT_STATE_EQ(kelv_code, irsend.capture.state, kKelvinatorBits); } + +TEST(TestKelvinatorClass, toCommon) { + IRKelvinatorAC ac(0); + ac.setPower(true); + ac.setMode(kKelvinatorCool); + ac.setTemp(20); + ac.setFan(kKelvinatorFanMax); + ac.setIonFilter(true); + ac.setXFan(true); + ac.setQuiet(false); + ac.setTurbo(true); + ac.setLight(true); + ac.setSwingHorizontal(false); + ac.setSwingVertical(true); + + // Now test it. + ASSERT_EQ(decode_type_t::KELVINATOR, 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_TRUE(ac.toCommon().filter); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().light); + 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(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_LG_test.cpp b/lib/IRremoteESP8266/test/ir_LG_test.cpp index 30ca07e..2925494 100644 --- a/lib/IRremoteESP8266/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266/test/ir_LG_test.cpp @@ -30,6 +30,7 @@ TEST(TestSendLG, SendDataOnly) { irsend.reset(); irsend.sendLG(0x4B4AE51); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" @@ -41,6 +42,7 @@ TEST(TestSendLG, SendDataOnly) { irsend.reset(); irsend.sendLG(0xB4B4AE51, kLg32Bits); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" @@ -59,6 +61,7 @@ TEST(TestSendLG, SendWithRepeats) { irsend.reset(); irsend.sendLG(0x4B4AE51, kLgBits, 1); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s1600m550s550m550s550" "m550s1600m550s550m550s1600m550s1600m550s550m550s1600m550s550m550s550" @@ -71,6 +74,7 @@ TEST(TestSendLG, SendWithRepeats) { irsend.reset(); irsend.sendLG(0xB4B4AE51, kLg32Bits, 1); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" @@ -90,6 +94,7 @@ TEST(TestSendLG, SendUnusualSize) { irsend.reset(); irsend.sendLG(0x0, 31); EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" @@ -101,6 +106,7 @@ TEST(TestSendLG, SendUnusualSize) { irsend.reset(); irsend.sendLG(0x0, 64); EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -373,6 +379,7 @@ TEST(TestSendLG2, SendDataOnly) { irsend.reset(); irsend.sendLG2(0x880094D); EXPECT_EQ( + "f38000d50" "m3200s9850" "m550s1600m550s550m550s550m550s550m550s1600m550s550m550s550m550s550" "m550s550m550s550m550s550m550s550m550s550m550s550m550s550m550s550" @@ -460,6 +467,7 @@ TEST(TestDecodeLG, Issue620) { EXPECT_EQ(0x872, irsend.capture.command); // The following seems to match the rawData above. EXPECT_EQ( + "f38000d50" "m8500s4250" "m550s1600m550s550m550s550m550s550m550s1600" "m550s550m550s550m550s550m550s550m550s550" @@ -470,4 +478,3 @@ TEST(TestDecodeLG, Issue620) { "s55550", irsend.outputStr()); } - diff --git a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp index 041109f..bad724f 100644 --- a/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lasertag_test.cpp @@ -20,6 +20,7 @@ TEST(TestSendLasertag, SendDataOnly) { irsend.reset(); irsend.sendLasertag(0x1); // Red 1 EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333" "s333m333s333m333s333m333s666m333s100000", irsend.outputStr()); @@ -27,6 +28,7 @@ TEST(TestSendLasertag, SendDataOnly) { irsend.reset(); irsend.sendLasertag(0x2); // Red 2 EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333" "m333s333m333s333m333s333m333s666m666s100333", irsend.outputStr()); @@ -38,6 +40,7 @@ TEST(TestSendLasertag, SendDataOnly) { EXPECT_EQ( // m364s364m332s336m384s276m332s364m332s304m416s584 // m692s724m640s360m304s332m392s612m380 + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666" "m666s666m666s333m333s333m333s666m333s100000", irsend.outputStr()); @@ -49,6 +52,7 @@ TEST(TestSendLasertag, SendDataOnly) { EXPECT_EQ( // m332s308m412s280m360s336m332s304m444s248m332s644 // m744s612m696s692m668s636m360 + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666" "m666s666m666s666m666s666m333s100000", irsend.outputStr()); @@ -61,6 +65,7 @@ TEST(TestSendLasertag, SendDataWithRepeat) { irsend.reset(); irsend.sendLasertag(0x1, kLasertagBits, 1); // Red 1, one repeat. EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" "m333s333m333s333m333s333m333s666m333s100000" "m333s333m333s333m333s333m333s333m333s333m333s333m333s333m333s333" @@ -70,6 +75,7 @@ TEST(TestSendLasertag, SendDataWithRepeat) { irsend.reset(); irsend.sendLasertag(0x52, kLasertagBits, 2); // Green 2, two repeats. EXPECT_EQ( + "f36000d25" "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" "m333s666m666s100333" "m333s333m333s333m333s333m333s333m333s333m333s666m666s666m666s333" @@ -86,7 +92,8 @@ TEST(TestSendLasertag, SmallestMessageSize) { irsend.reset(); irsend.sendLasertag(0x1555); // Alternating bit pattern will be the smallest. // i.e. 7 actual 'mark' pulses, which is a rawlen of 13. - EXPECT_EQ("m0s333m666s666m666s666m666s666m666s666m666s666m666s666m333s100000", + EXPECT_EQ("f36000d25" + "m0s333m666s666m666s666m666s666m666s666m666s666m666s666m333s100000", irsend.outputStr()); } diff --git a/lib/IRremoteESP8266/test/ir_Lego_test.cpp b/lib/IRremoteESP8266/test/ir_Lego_test.cpp new file mode 100644 index 0000000..4e859b1 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Lego_test.cpp @@ -0,0 +1,196 @@ +// Copyright 2019 David Conran + +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestLego, Housekeeping) { + ASSERT_EQ("LEGOPF", typeToString(LEGOPF)); + ASSERT_FALSE(hasACState(LEGOPF)); // Uses uint64_t, not uint8_t*. +} + +// Tests for sendLego(). + +// Test sending typical data only. +TEST(TestSendLegoPf, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x1234); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472", irsend.outputStr()); + + irsend.reset(); + irsend.send(LEGOPF, 0x1234, kLegoPfBits); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472", irsend.outputStr()); +} + +// Test sending typical repeat data. +TEST(TestSendLegoPf, SendDataWithRepeats) { + IRsendTest irsend(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x1234, kLegoPfBits, 1); + EXPECT_EQ( + "f38000d50" + "m0s32000" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s70472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472" + "m158s1026" + "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s150472", irsend.outputStr()); + + irsend.reset(); + irsend.sendLegoPf(0x2345, kLegoPfBits, 2); + EXPECT_EQ( + "f38000d50" + "m0s16000" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s70182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s70182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182" + "m158s1026" + "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" + "m158s263m158s553m158s263m158s263m158s263m158s553m158s263m158s553" + "m158s182182", irsend.outputStr()); + + irsend.reset(); + irsend.sendLegoPf(0x3456, kLegoPfBits, 7); + EXPECT_EQ( + "f38000d50" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s69892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s69892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892" + "m158s1026" + "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" + "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" + "m158s213892", irsend.outputStr()); +} + +// Tests for decodeLego(). + +// Decode normal "synthetic" messages. +TEST(TestDecodeLegoPf, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x000F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x000F, irsend.capture.value); + EXPECT_EQ(1, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + irsend.reset(); + irsend.sendLegoPf(0x100E); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x100E, irsend.capture.value); + EXPECT_EQ(2, irsend.capture.address); + EXPECT_EQ(0, irsend.capture.command); + + irsend.reset(); + irsend.sendLegoPf(0x221E); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ(0x221E, irsend.capture.value); + EXPECT_EQ(3, irsend.capture.address); + EXPECT_EQ(0x21, irsend.capture.command); + + // Test a bad LRC is not matched. + irsend.reset(); + irsend.sendLegoPf(0x001F); // LRC should be 0xE, not 0xF. + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + EXPECT_NE(LEGOPF, irsend.capture.decode_type); +} + +// Decode normal "synthetic" message with releats. +TEST(TestDecodeLegoPf, SyntheticDecodeWithRepeat) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + irsend.sendLegoPf(0x330F, kLegoPfBits, 1); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LEGOPF, irsend.capture.decode_type); + EXPECT_EQ(kLegoPfBits, irsend.capture.bits); + EXPECT_EQ(0x330F, irsend.capture.value); + EXPECT_EQ(4, irsend.capture.address); + EXPECT_EQ(0x30, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp index 6c99b99..d682967 100644 --- a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp @@ -11,17 +11,19 @@ TEST(TestSendLutron, SendDataOnly) { IRsendTest irsend(0); irsend.begin(); irsend.sendLutron(0); - EXPECT_EQ("m2288s230080", irsend.outputStr()); + EXPECT_EQ("f40000d40m2288s230080", irsend.outputStr()); irsend.sendLutron(0xAAAAAAAAA); // Longest possible sequence. (I think) EXPECT_EQ( + "f40000d40" "m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288" "m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288m2288s2288" "m2288s2288m2288s2288m2288s2288m2288s152288", irsend.outputStr()); irsend.sendLutron(0x7FFFFFFFF); - EXPECT_EQ("m82368s150000", irsend.outputStr()); + EXPECT_EQ("f40000d40m82368s150000", irsend.outputStr()); irsend.sendLutron(0x7F88BD120); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440", irsend.outputStr()); @@ -34,12 +36,14 @@ TEST(TestSendLutron, SendWithRepeats) { // Send a command with 0 repeats. irsend.sendLutron(0x7F88BD120, kLutronBits, 0); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440", irsend.outputStr()); // Send a command with 1 repeat. irsend.sendLutron(0x7F88BD120, kLutronBits, 1); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" @@ -48,6 +52,7 @@ TEST(TestSendLutron, SendWithRepeats) { // Send a command with 3 repeats. irsend.sendLutron(0x7F88BD120, kLutronBits, 3); EXPECT_EQ( + "f40000d40" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" "s161440" "m20592s6864m2288s6864m2288s2288m9152s2288m2288s6864m2288s4576m2288" diff --git a/lib/IRremoteESP8266/test/ir_MWM_test.cpp b/lib/IRremoteESP8266/test/ir_MWM_test.cpp index 9ecd0ea..2ca69ac 100644 --- a/lib/IRremoteESP8266/test/ir_MWM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MWM_test.cpp @@ -36,6 +36,7 @@ TEST(TestSendMWM, SendDataOnly) { */ irsend.sendMWM(test1, sizeof(test1), 0); EXPECT_EQ( + "f38000d25" "m834s834m417s417m834s834" "m417s417m834s834m1251s417" "m2085s417m1251s417" @@ -65,6 +66,7 @@ TEST(TestSendMWM, SendDataOnly) { }; irsend.sendMWM(test2, sizeof(test2), 0); EXPECT_EQ( + "f38000d25" "m417s417m834s834m834s834" "m834s834m834s417m834s417" "m834s834m834s834m417s417" diff --git a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp index e1c3da8..bbc5f33 100644 --- a/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Magiquest_test.cpp @@ -26,6 +26,7 @@ TEST(TestSendMagiQuest, SendDataOnly) { irsend.reset(); irsend.sendMagiQuest(0x0); EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" @@ -37,6 +38,7 @@ TEST(TestSendMagiQuest, SendDataOnly) { irsend.reset(); irsend.sendMagiQuest(0x123456789ABC); EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" @@ -55,6 +57,7 @@ TEST(TestSendMagiQuest, SendWithRepeats) { irsend.reset(); irsend.sendMagiQuest(0x12345678ABCD, kMagiquestBits, 2); // two repeats. EXPECT_EQ( + "f36000d50" "m280s850m280s850m280s850m280s850m280s850m280s850m280s850m280s850" "m280s850m280s850m280s850m580s600m280s850m280s850m580s600m280s850" "m280s850m280s850m580s600m580s600m280s850m580s600m280s850m280s850" diff --git a/lib/IRremoteESP8266/test/ir_Midea_test.cpp b/lib/IRremoteESP8266/test/ir_Midea_test.cpp index 5d5f5e9..8f49360 100644 --- a/lib/IRremoteESP8266/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Midea_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0x0); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -36,6 +37,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0x55AA55AA55AA); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -57,6 +59,7 @@ TEST(TestSendMidea, SendDataOnly) { irsend.reset(); irsend.sendMidea(0xFFFFFFFFFFFF); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -84,6 +87,7 @@ TEST(TestSendMidea, SendWithRepeats) { irsend.reset(); irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -119,6 +123,7 @@ TEST(TestSendMidea, SendWithRepeats) { irsend.outputStr()); irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560" @@ -178,6 +183,7 @@ TEST(TestSendMidea, SendUnusualSize) { irsend.reset(); irsend.sendMidea(0x0, 8); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s5600" @@ -189,6 +195,7 @@ TEST(TestSendMidea, SendUnusualSize) { irsend.reset(); irsend.sendMidea(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560" "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" @@ -649,3 +656,31 @@ TEST(TestDecodeMidea, DecodeRealExample) { EXPECT_EQ(kMideaBits, irsend.capture.bits); EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value); } + +TEST(TestMideaACClass, toCommon) { + IRMideaAC ac(0); + ac.setPower(true); + ac.setMode(kMideaACCool); + ac.setTemp(20, true); + ac.setFan(kMideaACFanHigh); + // Now test it. + ASSERT_EQ(decode_type_t::MIDEA, 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_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + // Unsupported. + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp new file mode 100644 index 0000000..f6bc49f --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp @@ -0,0 +1,920 @@ +// Copyright 2019 David Conran + +#include "ir_MitsubishiHeavy.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// General housekeeping +TEST(TestMitsubishiHeavy, Housekeeping) { + ASSERT_EQ("MITSUBISHI_HEAVY_88", typeToString(MITSUBISHI_HEAVY_88)); + ASSERT_TRUE(hasACState(MITSUBISHI_HEAVY_88)); + ASSERT_EQ("MITSUBISHI_HEAVY_152", typeToString(MITSUBISHI_HEAVY_152)); + ASSERT_TRUE(hasACState(MITSUBISHI_HEAVY_152)); +} + +// Tests for IRMitsubishiHeavy152Ac class. + +TEST(TestMitsubishiHeavy152AcClass, Power) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestMitsubishiHeavy152AcClass, Temperature) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyCool); + + ac.setTemp(0); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp - 1); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp + 1); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(19); + EXPECT_EQ(19, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestMitsubishiHeavy152AcClass, OperatingMode) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyAuto); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(kMitsubishiHeavyCool); + EXPECT_EQ(kMitsubishiHeavyCool, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat); + EXPECT_EQ(kMitsubishiHeavyHeat, ac.getMode()); + + ac.setMode(kMitsubishiHeavyDry); + EXPECT_EQ(kMitsubishiHeavyDry, ac.getMode()); + + ac.setMode(kMitsubishiHeavyFan); + EXPECT_EQ(kMitsubishiHeavyFan, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat + 1); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); +} + + +TEST(TestMitsubishiHeavy152AcClass, Filter) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); +} + +TEST(TestMitsubishiHeavy152AcClass, Turbo) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestMitsubishiHeavy152AcClass, Econo) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestMitsubishiHeavy152AcClass, 3D) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); + + ac.set3D(false); + EXPECT_FALSE(ac.get3D()); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); +} + +TEST(TestMitsubishiHeavy152AcClass, Night) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setNight(true); + EXPECT_TRUE(ac.getNight()); + + ac.setNight(false); + EXPECT_FALSE(ac.getNight()); + + ac.setNight(true); + EXPECT_TRUE(ac.getNight()); +} + +TEST(TestMitsubishiHeavy152AcClass, Clean) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); +} + +TEST(TestMitsubishiHeavy152AcClass, FanSpeed) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + ac.setFan(kMitsubishiHeavy152FanLow); + EXPECT_EQ(kMitsubishiHeavy152FanLow, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanAuto); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + + ac.setFan(255); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax); + EXPECT_EQ(kMitsubishiHeavy152FanMax, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax + 1); + EXPECT_EQ(kMitsubishiHeavy152FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanMax - 1); + EXPECT_EQ(kMitsubishiHeavy152FanMax - 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanLow + 1); + EXPECT_EQ(kMitsubishiHeavy152FanLow + 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanEcono); + EXPECT_EQ(kMitsubishiHeavy152FanEcono, ac.getFan()); + + ac.setFan(kMitsubishiHeavy152FanTurbo); + EXPECT_EQ(kMitsubishiHeavy152FanTurbo, ac.getFan()); +} + +TEST(TestMitsubishiHeavy152AcClass, VerticalSwing) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + EXPECT_EQ(kMitsubishiHeavy152SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest); + EXPECT_EQ(kMitsubishiHeavy152SwingVHighest, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingVHighest + 1, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); + + // Out of bounds. + ac.setSwingVertical(255); + EXPECT_EQ(kMitsubishiHeavy152SwingVOff, ac.getSwingVertical()); +} + +TEST(TestMitsubishiHeavy152AcClass, HorizontalSwing) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ(kMitsubishiHeavy152SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); + EXPECT_EQ(kMitsubishiHeavy152SwingHLeftMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHLeftMax + 1, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax); + EXPECT_EQ(kMitsubishiHeavy152SwingHRightMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax - 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHRightMax - 1, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHOff); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHOff + 1); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(255); + EXPECT_EQ(kMitsubishiHeavy152SwingHOff, ac.getSwingHorizontal()); +} + +TEST(TestMitsubishiHeavy152AcClass, Checksums) { + IRMitsubishiHeavy152Ac ac(0); + ac.begin(); + + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + EXPECT_TRUE(IRMitsubishiHeavy152Ac::validChecksum(expected)); + + // Screw up the "checksum" to test it fails. + expected[kMitsubishiHeavy152StateLength - 1] = 0x55; + EXPECT_FALSE(IRMitsubishiHeavy152Ac::validChecksum(expected)); + // getting the after getRaw() should repair it. + ac.setRaw(expected); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_TRUE(IRMitsubishiHeavy152Ac::validChecksum(ac.getRaw())); +} + +TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { + IRMitsubishiHeavy152Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(kMitsubishiHeavyMinTemp); + ac.setFan(kMitsubishiHeavy152FanMax); + ac.setFilter(true); + ac.setNight(true); + ac.setTurbo(false); + ac.setSilent(true); + ac.setEcono(false); + ac.set3D(true); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: On, Turbo: Off, " + "Econo: Off, Night: On, Filter: On, 3D: On, Clean: Off", + ac.toString()); + + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(kMitsubishiHeavyMaxTemp); + ac.setFilter(true); + ac.setNight(false); + ac.setTurbo(true); + ac.setEcono(false); + ac.setSilent(false); + ac.set3D(false); + ac.setSwingVertical(kMitsubishiHeavy152SwingVLowest); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); + + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 8 (Turbo), " + "Swing (V): 5 (Lowest), Swing (H): 1 (Max Left), Silent: Off, Turbo: On, " + "Econo: Off, Night: Off, Filter: On, 3D: Off, Clean: Off", + ac.toString()); + + ac.setClean(true); + ac.setEcono(true); + ac.setMode(kMitsubishiHeavyAuto); + ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); + + EXPECT_EQ( + "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 6 (Econo), " + "Swing (V): 6 (Off), Swing (H): 1 (Max Left), Silent: Off, " + "Turbo: Off, Econo: On, Night: Off, Filter: On, 3D: Off, Clean: On", + ac.toString()); + + ac.setClean(false); + ac.setTemp(25); + ac.setEcono(false); + ac.setMode(kMitsubishiHeavyDry); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftRight); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 6 (Off), Swing (H): 7 (Left Right), Silent: Off, " + "Turbo: Off, Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +TEST(TestMitsubishiHeavy152AcClass, ReconstructKnownExample) { + IRMitsubishiHeavy152Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(24); + ac.setFan(kMitsubishiHeavy152FanMax); + ac.setFilter(true); + ac.setNight(false); + ac.setTurbo(false); + ac.setSilent(false); + ac.setEcono(false); + ac.set3D(false); + ac.setClean(false); + ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + EXPECT_STATE_EQ(expected, ac.getRaw(), kMitsubishiHeavy152Bits); +} + +// Tests for IRMitsubishiHeavy88Ac class. + +TEST(TestMitsubishiHeavy88AcClass, Power) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestMitsubishiHeavy88AcClass, Temperature) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyCool); + + ac.setTemp(0); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMinTemp - 1); + EXPECT_EQ(kMitsubishiHeavyMinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishiHeavyMaxTemp + 1); + EXPECT_EQ(kMitsubishiHeavyMaxTemp, ac.getTemp()); + + ac.setTemp(19); + EXPECT_EQ(19, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestMitsubishiHeavy88AcClass, OperatingMode) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setMode(kMitsubishiHeavyAuto); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(kMitsubishiHeavyCool); + EXPECT_EQ(kMitsubishiHeavyCool, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat); + EXPECT_EQ(kMitsubishiHeavyHeat, ac.getMode()); + + ac.setMode(kMitsubishiHeavyDry); + EXPECT_EQ(kMitsubishiHeavyDry, ac.getMode()); + + ac.setMode(kMitsubishiHeavyFan); + EXPECT_EQ(kMitsubishiHeavyFan, ac.getMode()); + + ac.setMode(kMitsubishiHeavyHeat + 1); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kMitsubishiHeavyAuto, ac.getMode()); +} + +TEST(TestMitsubishiHeavy88AcClass, Turbo) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestMitsubishiHeavy88AcClass, Econo) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestMitsubishiHeavy88AcClass, 3D) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); + + ac.set3D(false); + EXPECT_FALSE(ac.get3D()); + + ac.set3D(true); + EXPECT_TRUE(ac.get3D()); +} + +TEST(TestMitsubishiHeavy88AcClass, Clean) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); + + ac.setClean(false); + EXPECT_FALSE(ac.getClean()); + + ac.setClean(true); + EXPECT_TRUE(ac.getClean()); +} + +TEST(TestMitsubishiHeavy88AcClass, FanSpeed) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + ac.setFan(kMitsubishiHeavy88FanLow); + EXPECT_EQ(kMitsubishiHeavy88FanLow, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanAuto); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + + ac.setFan(255); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh); + EXPECT_EQ(kMitsubishiHeavy88FanHigh, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh + 1); + EXPECT_EQ(kMitsubishiHeavy88FanAuto, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanHigh - 1); + EXPECT_EQ(kMitsubishiHeavy88FanHigh - 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanLow + 1); + EXPECT_EQ(kMitsubishiHeavy88FanLow + 1, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanEcono); + EXPECT_EQ(kMitsubishiHeavy88FanEcono, ac.getFan()); + + ac.setFan(kMitsubishiHeavy88FanTurbo); + EXPECT_EQ(kMitsubishiHeavy88FanTurbo, ac.getFan()); +} + +TEST(TestMitsubishiHeavy88AcClass, VerticalSwing) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); + EXPECT_EQ(kMitsubishiHeavy88SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest); + EXPECT_EQ(kMitsubishiHeavy88SwingVHighest, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); + + // Out of bounds. + ac.setSwingVertical(255); + EXPECT_EQ(kMitsubishiHeavy88SwingVOff, ac.getSwingVertical()); +} + +TEST(TestMitsubishiHeavy88AcClass, HorizontalSwing) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHAuto); + EXPECT_EQ(kMitsubishiHeavy88SwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); + EXPECT_EQ(kMitsubishiHeavy88SwingHLeftMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax); + EXPECT_EQ(kMitsubishiHeavy88SwingHRightMax, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax - 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHOff); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHOff + 1); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(255); + EXPECT_EQ(kMitsubishiHeavy88SwingHOff, ac.getSwingHorizontal()); +} + +TEST(TestMitsubishiHeavy88AcClass, Checksums) { + IRMitsubishiHeavy88Ac ac(0); + ac.begin(); + + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + + uint8_t expected[kMitsubishiHeavy88StateLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26, 0x48, 0xB7, 0x00, 0xFF, 0x8A, 0x75}; + EXPECT_TRUE(IRMitsubishiHeavy88Ac::validChecksum(expected)); + + // Screw up the "checksum" to test it fails. + expected[kMitsubishiHeavy88StateLength - 1] = 0x55; + EXPECT_FALSE(IRMitsubishiHeavy88Ac::validChecksum(expected)); + // getting the after getRaw() should repair it. + ac.setRaw(expected); + EXPECT_TRUE(ac.validChecksum(ac.getRaw())); + EXPECT_TRUE(IRMitsubishiHeavy88Ac::validChecksum(ac.getRaw())); +} + +TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { + IRMitsubishiHeavy88Ac ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 0 (Off), " + "Turbo: Off, Econo: Off, 3D: Off, Clean: Off", + ac.toString()); + ac.on(); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(kMitsubishiHeavyMinTemp); + ac.setFan(kMitsubishiHeavy88FanHigh); + ac.setTurbo(false); + ac.setEcono(false); + ac.set3D(true); + ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (High), " + "Swing (V): 16 (Auto), Swing (H): 200 (3D), " + "Turbo: Off, Econo: Off, 3D: On, Clean: Off", + ac.toString()); + + ac.setMode(kMitsubishiHeavyHeat); + ac.setTemp(kMitsubishiHeavyMaxTemp); + ac.setTurbo(true); + ac.setEcono(false); + ac.set3D(false); + ac.setSwingVertical(kMitsubishiHeavy88SwingVLowest); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); + + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 6 (Turbo), " + "Swing (V): 26 (Lowest), Swing (H): 4 (Max Left), Turbo: On, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); + + ac.setClean(true); + ac.setEcono(true); + ac.setMode(kMitsubishiHeavyAuto); + ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); + + EXPECT_EQ( + "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 7 (Econo), " + "Swing (V): 0 (Off), Swing (H): 4 (Max Left), Turbo: Off, Econo: On, " + "3D: Off, Clean: On", + ac.toString()); + + ac.setClean(false); + ac.setTemp(25); + ac.setEcono(false); + ac.setMode(kMitsubishiHeavyDry); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftRight); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); +} + +// Tests for decodeMitsubishiHeavy(). + +// Decode a real MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + uint16_t rawData[307] = { + 3136, 1638, 364, 428, 366, 1224, 362, 432, 364, 430, 364, 1226, 362, 432, + 364, 1224, 366, 428, 366, 430, 366, 1224, 362, 1228, 362, 1228, 362, 432, + 364, 1224, 364, 432, 364, 1226, 364, 1224, 366, 1226, 364, 428, 364, 430, + 364, 430, 364, 432, 366, 1226, 364, 1224, 364, 430, 364, 1226, 364, 428, + 364, 1224, 368, 1224, 364, 428, 364, 430, 366, 430, 364, 1158, 430, 432, + 366, 1222, 366, 430, 366, 430, 364, 1226, 364, 1224, 364, 1224, 364, 1224, + 366, 1224, 364, 430, 364, 430, 364, 1228, 362, 1226, 364, 1226, 366, 1222, + 366, 430, 364, 430, 364, 1224, 366, 1224, 364, 430, 364, 430, 364, 432, + 364, 430, 364, 428, 364, 430, 364, 430, 366, 1226, 362, 1154, 434, 1228, + 364, 1226, 362, 1226, 364, 1226, 364, 1228, 362, 1226, 362, 432, 364, 430, + 364, 428, 364, 430, 364, 430, 364, 1228, 362, 1228, 362, 432, 364, 1224, + 368, 1224, 364, 1226, 362, 1226, 364, 1226, 366, 428, 366, 430, 364, 1224, + 364, 430, 366, 430, 366, 430, 364, 430, 364, 430, 364, 1226, 364, 1226, + 366, 1224, 366, 1224, 366, 1226, 364, 1224, 366, 1224, 366, 1224, 366, + 428, 364, 430, 366, 428, 364, 430, 364, 430, 366, 428, 364, 430, 364, 432, + 364, 1226, 364, 1226, 364, 1226, 364, 1228, 364, 1222, 370, 1222, 362, + 1228, 362, 1226, 362, 430, 364, 430, 364, 430, 364, 432, 364, 428, 364, + 432, 364, 428, 364, 430, 366, 1226, 362, 1224, 364, 1226, 364, 1226, 364, + 1226, 362, 1226, 366, 1224, 366, 1224, 364, 430, 364, 432, 364, 428, 364, + 432, 364, 428, 364, 430, 366, 430, 364, 430, 364, 1226, 362, 1226, 364, + 1224, 366, 1226, 362, 1228, 364, 1224, 366, 1224, 364, 430, 364, 432, 364, + 428, 364, 430, 364, 430, 364, 430, 366, 430, 364, 430, 338, 1252, 362 + }; // UNKNOWN 5138D49D + + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a Synthetic MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x0C, 0xF3, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + irsend.reset(); + irsend.sendMitsubishiHeavy152(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a real MitsubishiHeavy 152Bit message. +TEST(TestDecodeMitsubishiHeavy, ZmsRealExample2) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy152Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy152StateLength] = { + 0xAD, 0x51, 0x3C, 0xE5, 0x1A, 0x04, 0xFB, 0x07, + 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x80, 0x7F}; + + // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + uint16_t rawData[307] = { + 3196, 1580, 398, 390, 404, 1190, 400, 390, 402, 390, 402, 1192, 402, 388, + 402, 1192, 400, 390, 402, 392, 402, 1192, 400, 1188, 400, 1188, 400, 390, + 404, 1192, 398, 392, 400, 1192, 402, 1188, 400, 1190, 402, 388, 402, 392, + 404, 392, 402, 392, 404, 1188, 400, 1190, 398, 392, 404, 1188, 398, 392, + 402, 1192, 398, 1190, 400, 390, 404, 390, 402, 392, 404, 1188, 398, 392, + 404, 1190, 400, 392, 400, 394, 402, 1192, 398, 1190, 398, 1192, 398, 1190, + 400, 1190, 398, 392, 402, 1192, 398, 1190, 398, 1190, 398, 1192, 396, + 1192, 398, 396, 400, 394, 398, 1194, 396, 394, 400, 394, 398, 396, 398, + 396, 400, 402, 390, 394, 402, 392, 398, 396, 398, 1194, 396, 1194, 398, + 1192, 398, 1192, 396, 1194, 396, 1192, 396, 1196, 398, 1190, 398, 392, + 402, 392, 402, 394, 398, 394, 400, 394, 400, 1192, 398, 1192, 400, 390, + 402, 1190, 398, 1190, 398, 1192, 402, 1188, 398, 1190, 400, 390, 402, 392, + 402, 1190, 400, 390, 404, 390, 402, 394, 402, 392, 402, 390, 404, 1190, + 400, 1188, 400, 1190, 400, 1190, 402, 1188, 402, 1188, 400, 1188, 402, + 1190, 400, 388, 402, 394, 404, 392, 404, 388, 404, 390, 404, 392, 402, + 394, 402, 390, 402, 1190, 402, 1186, 402, 1190, 400, 1190, 398, 1190, 402, + 1186, 402, 1190, 400, 1188, 400, 390, 404, 392, 404, 390, 402, 392, 402, + 392, 400, 394, 402, 392, 402, 394, 400, 1192, 400, 1190, 400, 1188, 400, + 1192, 400, 1186, 402, 1190, 400, 1190, 400, 1188, 402, 388, 402, 390, 404, + 392, 402, 392, 402, 392, 402, 392, 404, 392, 402, 392, 404, 1190, 400, + 1190, 398, 1190, 400, 1190, 400, 1190, 400, 1188, 400, 1188, 400, 392, + 402, 392, 404, 390, 402, 392, 402, 392, 402, 392, 402, 390, 402, 392, 402, + 1192, 398}; // UNKNOWN A650F2C1 + + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_152, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: Off, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "Swing (V): 0 (Auto), Swing (H): 0 (Auto), Silent: Off, Turbo: Off, " + "Econo: Off, Night: Off, Filter: Off, 3D: Off, Clean: Off", + ac.toString()); +} + +// Decode a Synthetic MitsubishiHeavy 88 Bit message. +TEST(TestDecodeMitsubishiHeavy, ZjsSyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + IRMitsubishiHeavy88Ac ac(0); + irsend.begin(); + + uint8_t expected[kMitsubishiHeavy88StateLength] = { + 0xAD, 0x51, 0x3C, 0xD9, 0x26, 0x48, 0xB7, 0x00, 0xFF, 0x8A, 0x75}; + + irsend.reset(); + irsend.sendMitsubishiHeavy88(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_HEAVY_88, irsend.capture.decode_type); + ASSERT_EQ(kMitsubishiHeavy88Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " + "3D: Off, Clean: Off", + ac.toString()); +} + +TEST(TestMitsubishiHeavy152AcClass, toCommon) { + IRMitsubishiHeavy152Ac ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(20); + ac.setFan(kMitsubishiHeavy152FanLow); + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax); + ac.setTurbo(false); + ac.setEcono(true); + ac.setClean(true); + ac.setFilter(true); + ac.setSilent(true); + ac.setNight(true); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_HEAVY_152, 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_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kRightMax, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestMitsubishiHeavy88AcClass, toCommon) { + IRMitsubishiHeavy88Ac ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(20); + ac.setFan(kMitsubishiHeavy88FanLow); + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax); + ac.setTurbo(false); + ac.setEcono(true); + ac.setClean(true); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_HEAVY_88, 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_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kRightMax, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().clean); + // Unsupported. + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index 7b8eb21..09930b1 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -17,6 +17,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0xE242); EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -28,6 +29,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0x0); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s34080" @@ -39,6 +41,7 @@ TEST(TestSendMitsubishi, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi(0x4321); EXPECT_EQ( + "f33000d50" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s2100" "m300s900m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100" "m300s28080" @@ -56,6 +59,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 0); // 0 repeat. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080", @@ -64,6 +68,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 1); // 1 repeat. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -73,6 +78,7 @@ TEST(TestSendMitsubishi, SendWithRepeats) { irsend.outputStr()); irsend.sendMitsubishi(0xE242, kMitsubishiBits, 2); // 2 repeats. EXPECT_EQ( + "f33000d50" "m300s2100m300s2100m300s2100m300s900m300s900m300s900m300s2100m300s900" "m300s900m300s2100m300s900m300s900m300s900m300s900m300s2100m300s900" "m300s28080" @@ -93,6 +99,7 @@ TEST(TestSendMitsubishi, SendUnusualSize) { irsend.reset(); irsend.sendMitsubishi(0x0, 8); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" "m300s43680" "m300s900m300s900m300s900m300s900m300s900m300s900m300s900m300s900" @@ -102,6 +109,7 @@ TEST(TestSendMitsubishi, SendUnusualSize) { irsend.reset(); irsend.sendMitsubishi(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f33000d50" "m300s900m300s900m300s900m300s2100m300s900m300s900m300s2100m300s900" "m300s900m300s900m300s2100m300s2100m300s900m300s2100m300s900m300s900" "m300s900m300s2100m300s900m300s2100m300s900m300s2100m300s2100m300s900" @@ -305,6 +313,7 @@ TEST(TestSendMitsubishiAC, SendDataOnly) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_code); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -360,6 +369,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { irsend.sendMitsubishiAC(mitsub_code, kMitsubishiACStateLength, 0); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -385,6 +395,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_code, kMitsubishiACStateLength, 2); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -466,6 +477,7 @@ TEST(TestSendMitsubishiAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendMitsubishiAC(mitsub_long_code, 19); ASSERT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -665,6 +677,7 @@ TEST(TestMitsubishiACClass, MessageConstuction) { irsend.reset(); irsend.sendMitsubishiAC(mitsub.getRaw()); EXPECT_EQ( + "f38000d50" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -984,6 +997,7 @@ TEST(TestSendMitsubishi2, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi2(0xF82); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" @@ -999,6 +1013,7 @@ TEST(TestSendMitsubishi2, SendDataOnly) { irsend.reset(); irsend.sendMitsubishi2(0x0); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520" "m560s4200" @@ -1020,6 +1035,7 @@ TEST(TestSendMitsubishi2, Repeats) { irsend.reset(); irsend.sendMitsubishi2(0xF82, kMitsubishiBits, 0); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" @@ -1030,6 +1046,7 @@ TEST(TestSendMitsubishi2, Repeats) { irsend.reset(); irsend.sendMitsubishi2(0xF82, kMitsubishiBits, 2); EXPECT_EQ( + "f33000d50" "m8400s4200" "m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560" "m560s4200" @@ -1116,3 +1133,32 @@ TEST(TestDecodeMitsubishi2, DecodeRealExample) { EXPECT_EQ(0xF, irsend.capture.address); EXPECT_EQ(0x82, irsend.capture.command); } + +TEST(TestMitsubishiACClass, toCommon) { + IRMitsubishiAC ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiAcCool); + ac.setTemp(20); + ac.setFan(kMitsubishiAcFanSilent); + ac.setVane(kMitsubishiAcVaneAuto); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_AC, 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_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_TRUE(ac.toCommon().quiet); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_NEC_test.cpp b/lib/IRremoteESP8266/test/ir_NEC_test.cpp index 6b84b0e..c881b7b 100644 --- a/lib/IRremoteESP8266/test/ir_NEC_test.cpp +++ b/lib/IRremoteESP8266/test/ir_NEC_test.cpp @@ -12,6 +12,7 @@ TEST(TestSendNEC, SendDataOnly) { irsend.begin(); irsend.sendNEC(0); EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -20,6 +21,7 @@ TEST(TestSendNEC, SendDataOnly) { irsend.outputStr()); irsend.sendNEC(0xAA00FF55); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -33,15 +35,17 @@ TEST(TestSendNEC, SendSmallData) { IRsendTest irsend(4); irsend.begin(); irsend.sendNEC(0xA, 4); // Send only 4 data bits. - EXPECT_EQ("m8960s4480m560s1680m560s560m560s1680m560s560m560s87360", + EXPECT_EQ("f38000d33m8960s4480m560s1680m560s560m560s1680m560s560m560s87360", irsend.outputStr()); irsend.sendNEC(0, 8); // Send only 8 data bits. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0x1234567890ABCDEF, 64); // Send 64 data bits. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s1680m560s560m560s560m560s560m560s1680m560s1680m560s560" "m560s1680m560s560m560s560m560s560m560s1680m560s560m560s1680" @@ -61,17 +65,20 @@ TEST(TestSendNEC, SendWithRepeats) { irsend.begin(); irsend.sendNEC(0, 8, 0); // Send a command with 0 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s85120", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 1); // Send a command with 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s80640" "m8960s2240m560s96320", irsend.outputStr()); irsend.sendNEC(0xAA, 8, 3); // Send a command with 3 repeats. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s560m560s1680m560s560m560s1680m560s560" "m560s1680m560s560m560s80640" "m8960s2240m560s96320" diff --git a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp index 4a4ea05..fff2423 100644 --- a/lib/IRremoteESP8266/test/ir_Nikai_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Nikai_test.cpp @@ -13,6 +13,7 @@ TEST(TestSendNikai, SendDataOnly) { irsend.reset(); irsend.sendNikai(0xD5F2A); // Nikai TV Power Off. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" @@ -31,6 +32,7 @@ TEST(TestSendNikai, SendWithRepeats) { irsend.reset(); irsend.sendNikai(0xD5F2A, kNikaiBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" @@ -44,6 +46,7 @@ TEST(TestSendNikai, SendWithRepeats) { irsend.outputStr()); irsend.sendNikai(0xD5F2A, kNikaiBits, 2); // 2 repeat. EXPECT_EQ( + "f38000d33" "m4000s4000" "m500s2000m500s2000m500s2000m500s2000m500s1000m500s1000m500s2000" "m500s1000m500s2000m500s1000m500s2000m500s1000m500s1000m500s1000" diff --git a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp index a1d8a79..27d1555 100644 --- a/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Panasonic_test.cpp @@ -32,6 +32,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0x0); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" @@ -45,6 +46,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -58,6 +60,7 @@ TEST(TestSendPanasonic64, SendDataOnly) { irsend.reset(); irsend.sendPanasonic64(0xFFFFFFFFFFFF); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" "m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296m432s1296" @@ -77,6 +80,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 0); // 0 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -90,6 +94,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.reset(); irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 1); // 1 repeat. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -110,6 +115,7 @@ TEST(TestSendPanasonic64, SendWithRepeats) { irsend.sendPanasonic64(0x40040190ED7C, kPanasonicBits, 2); // 2 repeats. EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -145,6 +151,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { irsend.reset(); irsend.sendPanasonic64(0x0, 8); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s432m432s432m432s432m432s432m432s432" "m432s150768", @@ -153,6 +160,7 @@ TEST(TestSendPanasonic64, SendUnusualSize) { irsend.reset(); irsend.sendPanasonic64(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s432m432s432m432s1296m432s432m432s432m432s1296m432s432" "m432s432m432s432m432s1296m432s1296m432s432m432s1296m432s432m432s432" @@ -483,6 +491,7 @@ TEST(TestSendPanasonicAC, SendDataOnly) { 0x00, 0x06, 0x60, 0x00, 0x00, 0x80, 0x00, 0x06, 0x83}; irsend.sendPanasonicAC(state); EXPECT_EQ( + "f36700d50" "m3456s1728" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -1133,3 +1142,36 @@ TEST(TestDecodePanasonicAC, CkpModelSpecifics) { EXPECT_EQ(kPanasonicCkp, pana.getModel()); EXPECT_STATE_EQ(ckpPowerfulOn, pana.getRaw(), kPanasonicAcBits); } + +TEST(TestIRPanasonicAcClass, toCommon) { + IRPanasonicAc ac(0); + ac.setModel(panasonic_ac_remote_model_t::kPanasonicDke); + ac.setPower(true); + ac.setMode(kPanasonicAcCool); + ac.setTemp(20); + ac.setFan(kPanasonicAcFanMax); + ac.setSwingVertical(kPanasonicAcSwingVAuto); + ac.setSwingHorizontal(kPanasonicAcSwingHMiddle); + ac.setPowerful(true); + ac.setQuiet(false); + // Now test it. + ASSERT_EQ(decode_type_t::PANASONIC_AC, ac.toCommon().protocol); + ASSERT_EQ(panasonic_ac_remote_model_t::kPanasonicDke, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + 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(stdAc::swingh_t::kMiddle, ac.toCommon().swingh); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().quiet); + // Unsupported. + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp index b78469a..36d61c7 100644 --- a/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pioneer_test.cpp @@ -13,6 +13,7 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.begin(); irsend.sendPioneer(0); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -28,6 +29,7 @@ TEST(TestSendPioneer, SendDataOnly) { irsend.outputStr()); irsend.sendPioneer(0x55FF00AAAA00FF55); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680" "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" @@ -136,6 +138,7 @@ TEST(TestDecodePioneer, SyntheticPioneerMessage) { irsend.reset(); irsend.sendPioneer(0x659A857AF50A3DC2, 64, 0); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680" "m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560" diff --git a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp index e52c6dd..513f985 100644 --- a/lib/IRremoteESP8266/test/ir_Pronto_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Pronto_test.cpp @@ -58,7 +58,8 @@ TEST(TestSendPronto, MoreDataThanNeededInNormal) { uint16_t pronto_test[8] = {0x0000, 0x0067, 0x0001, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004}; irsend.sendPronto(pronto_test, 8); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. } TEST(TestSendPronto, MoreDataThanNeededInRepeat) { @@ -70,7 +71,8 @@ TEST(TestSendPronto, MoreDataThanNeededInRepeat) { uint16_t pronto_test[8] = {0x0000, 0x0067, 0x0000, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004}; irsend.sendPronto(pronto_test, 8); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. } TEST(TestSendPronto, MoreDataThanNeededInBoth) { @@ -82,9 +84,11 @@ TEST(TestSendPronto, MoreDataThanNeededInBoth) { uint16_t pronto_test[10] = {0x0000, 0x0067, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004, 0x5, 0x6}; irsend.sendPronto(pronto_test, 10); - EXPECT_EQ("m25s50", irsend.outputStr()); // Only send the data required. + EXPECT_EQ("f40244d50m25s50", + irsend.outputStr()); // Only send the data required. irsend.sendPronto(pronto_test, 10, 1); - EXPECT_EQ("m25s50m75s100", irsend.outputStr()); // Only the data required. + EXPECT_EQ("f40244d50m25s50m75s100", + irsend.outputStr()); // Only the data required. } TEST(TestSendPronto, ShortestValidCodeThatSendsNothing) { @@ -134,6 +138,7 @@ TEST(TestSendPronto, NonRepeatingCode) { EXPECT_EQ(0x1, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s27650" @@ -160,6 +165,7 @@ TEST(TestSendPronto, NonRepeatingCode) { EXPECT_EQ(0x1, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m600s600m600s27650" @@ -201,6 +207,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { EXPECT_EQ(0x1A, irsend.capture.address); EXPECT_EQ(0x24AE, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" @@ -218,6 +225,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForSony) { EXPECT_EQ(0x1A, irsend.capture.address); EXPECT_EQ(0x24AE, irsend.capture.command); EXPECT_EQ( + "f40244d50" "m2400s600" "m600s600m1200s600m1200s600m1200s600m600s600m1200s600m600s600m600s600" "m1200s600m600s600m1200s600m1200s600m1200s600m600s600m600s600m1200s600" @@ -265,6 +273,7 @@ TEST(TestSendPronto, RepeatSequenceOnlyForPanasonic) { EXPECT_EQ(0x4004, irsend.capture.address); EXPECT_EQ(0x1007C7D, irsend.capture.command); EXPECT_EQ( + "f36682d50" "m3456s1701" "m432s432m432s1296m432s432m432s432m432s432m432s432m432s432m432s432" "m432s432m432s432m432s432m432s432m432s432m432s1296m432s432m432s432" @@ -305,6 +314,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" @@ -324,6 +334,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" @@ -344,6 +355,7 @@ TEST(TestSendPronto, NormalPlusRepeatSequence) { EXPECT_EQ(0x18, irsend.capture.address); EXPECT_EQ(0x8, irsend.capture.command); EXPECT_EQ( + "f38028d50" "m8892s4446" "m546s546m546s546m546s546m546s1664m546s1664m546s546m546s546m546s546" "m546s1664m546s1664m546s1664m546s546m546s546m546s1664m546s1664m546s1664" diff --git a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp index e8aa9bf..da9fa02 100644 --- a/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RC5_RC6_test.cpp @@ -79,6 +79,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x0, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); @@ -86,6 +87,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x1AAA, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m889s889m1778s1778m1778s1778m1778s1778" "m1778s1778m1778s1778m1778s90664", irsend.outputStr()); @@ -93,6 +95,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); @@ -100,6 +103,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x3FFF, kRC5Bits); EXPECT_EQ( + "f36000d25" "m889s889m889s889m889s889m889s889m889s889m889s889m889s889" "m889s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); @@ -107,6 +111,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x0, kRC5XBits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s90664", irsend.outputStr()); @@ -114,6 +119,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x1AAA, kRC5XBits); EXPECT_EQ( + "f36000d25" "m1778s1778m1778s1778m1778s1778m1778" "s1778m1778s1778m1778s1778m1778s90664", irsend.outputStr()); @@ -121,6 +127,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x175, kRC5XBits); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s89775", irsend.outputStr()); @@ -128,6 +135,7 @@ TEST(TestSendRC5, SendDataOnly) { irsend.reset(); irsend.sendRC5(0x3FFF, kRC5XBits); EXPECT_EQ( + "f36000d25" "m1778s1778m889s889m889s889m889s889m889s889m889s889m889" "s889m889s889m889s889m889s889m889s889m889s889m889s89775", irsend.outputStr()); @@ -141,6 +149,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits, 1); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -150,6 +159,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5Bits, 2); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -161,6 +171,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x175, kRC5XBits, 1); EXPECT_EQ( + "f36000d25" "m889s889m1778s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m889s889m1778s889m889s889m889s1778m1778s1778" @@ -170,6 +181,7 @@ TEST(TestSendRC5, SendWithRepeats) { irsend.reset(); irsend.sendRC5(0x1175, kRC5XBits, 2); EXPECT_EQ( + "f36000d25" "m1778s889m889s889m889s889m889s1778m1778s1778" "m889s889m889s889m1778s1778m1778s1778m889s90664" "m1778s889m889s889m889s889m889s1778m1778s1778" @@ -444,6 +456,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444s888m888s444m444s444m444s444" "m444s444m444s444m444s444m444s444m444s444m444s444m444s444" @@ -454,6 +467,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x1FFFF); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m1332s888m444s444m444s444m444s444" "m444s444m444s444m444s444m444s444m444s444m444s444m444s444" @@ -464,6 +478,7 @@ TEST(TestSendRC6, SendMode0DataOnly) { irsend.reset(); irsend.sendRC6(0x15555); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m1332s1332m888s888m888s888" "m888s888m888s888m888s888m888s888m888s888m888" @@ -479,6 +494,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0x0, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -492,6 +508,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xFFFFFFFFF, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s444m444s444" "m888s888" @@ -505,6 +522,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xAAAAAAAAAA, kRC6_36Bits); EXPECT_EQ( + "f36000d33" "m2664s888m444s444m444s888m888" "s1332m1332" "s888m888s888m888s888m888s888m888s888m888s888m888s888m888s888m888s888m888" @@ -514,6 +532,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.reset(); irsend.sendRC6(0xC800F740C, kRC6_36Bits); // Xbox 360 OnOff code EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s888m444" "s888m1332" @@ -526,6 +545,7 @@ TEST(TestSendRC6, Send36BitDataOnly) { irsend.sendRC6(irsend.toggleRC6(0xC800F740C, kRC6_36Bits), kRC6_36Bits); // Xbox 360 OnOff code (toggled) EXPECT_EQ( + "f36000d33" "m2664s888" "m444s444m444s444m444s888m444" "s888m1332" @@ -544,6 +564,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -554,6 +575,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 1); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -569,6 +591,7 @@ TEST(TestSendRC6, SendMode0WithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6Mode0Bits, 2); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -595,6 +618,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 0); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -607,6 +631,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 1); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" @@ -626,6 +651,7 @@ TEST(TestSendRC6, Send36BitWithRepeats) { irsend.reset(); irsend.sendRC6(0x175, kRC6_36Bits, 2); EXPECT_EQ( + "f36000d33" "m2664s888" "m444s888m444s444m444s444m444" "s888m888" diff --git a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp index 028dbd8..22306a5 100644 --- a/lib/IRremoteESP8266/test/ir_RCMM_test.cpp +++ b/lib/IRremoteESP8266/test/ir_RCMM_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendRCMM, SendDataOnly) { irsend.reset(); irsend.sendRCMM(0xe0a600); EXPECT_EQ( + "f36000d33" "m416s277" "m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -22,6 +23,7 @@ TEST(TestSendRCMM, SendDataOnly) { irsend.reset(); irsend.sendRCMM(0x28e0a600UL, 32); EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -37,6 +39,7 @@ TEST(TestSendRCMM, SendWithRepeats) { irsend.reset(); irsend.sendRCMM(0x28e0a600, 32, 2); // 2 repeats. EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" @@ -60,6 +63,7 @@ TEST(TestSendRCMM, SendUnusualSize) { irsend.reset(); irsend.sendRCMM(0xE0, 8); EXPECT_EQ( + "f36000d33" "m416s277" "m166s777m166s611m166s277m166s277" "m166s24313", @@ -67,6 +71,7 @@ TEST(TestSendRCMM, SendUnusualSize) { irsend.reset(); irsend.sendRCMM(0x28e0a60000UL, 40); EXPECT_EQ( + "f36000d33" "m416s277" "m166s277m166s611m166s611m166s277m166s777m166s611m166s277m166s277" "m166s611m166s611m166s444m166s611m166s277m166s277m166s277m166s277" diff --git a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp index 390e86e..050de27 100644 --- a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp @@ -1,5 +1,6 @@ // Copyright 2017, 2018, 2019 David Conran +#include #include "ir_Samsung.h" #include "IRrecv.h" #include "IRrecv_test.h" @@ -24,6 +25,7 @@ TEST(TestSendSamsung, SendDataOnly) { irsend.reset(); irsend.sendSAMSUNG(0xE0E09966); // Samsung TV Power On. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -43,6 +45,7 @@ TEST(TestSendSamsung, SendWithRepeats) { irsend.reset(); irsend.sendSAMSUNG(0xE0E09966, kSamsungBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -58,6 +61,7 @@ TEST(TestSendSamsung, SendWithRepeats) { irsend.outputStr()); irsend.sendSAMSUNG(0xE0E09966, kSamsungBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d33" "m4480s4480" "m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s560" @@ -305,6 +309,7 @@ TEST(TestSendSamsungAC, SendDataOnly) { 0x01, 0x02, 0xAF, 0x71, 0x00, 0x15, 0xF0}; irsend.sendSamsungAC(data); EXPECT_EQ( + "f38000d50" "m690s17844" "m3086s8864" "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" @@ -338,6 +343,7 @@ TEST(TestSendSamsungAC, SendExtendedData) { 0x01, 0x02, 0xFF, 0x71, 0x80, 0x11, 0xC0}; irsend.sendSamsungAC(data, kSamsungAcExtendedStateLength); EXPECT_EQ( + "f38000d50" "m690s17844" "m3086s8864" "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" @@ -494,15 +500,87 @@ TEST(TestIRSamsungAcClass, SetAndGetFan) { } TEST(TestIRSamsungAcClass, SetAndGetQuiet) { - IRSamsungAc samsung(0); - samsung.setQuiet(false); - EXPECT_FALSE(samsung.getQuiet()); - samsung.setFan(kSamsungAcFanHigh); - samsung.setQuiet(true); - EXPECT_TRUE(samsung.getQuiet()); - EXPECT_EQ(kSamsungAcFanAuto, samsung.getFan()); - samsung.setQuiet(false); - EXPECT_FALSE(samsung.getQuiet()); + IRSamsungAc ac(0); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + ac.setFan(kSamsungAcFanHigh); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + // Actual quiet on & off states from: + // https://github.com/markszabo/IRremoteESP8266/issues/734#issuecomment-500071419 + uint8_t on[14] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x20, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(on, 14); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + uint8_t off[14] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(off, 14); + EXPECT_FALSE(ac.getQuiet()); +} + + +TEST(TestIRSamsungAcClass, SetAndGetPowerful) { + IRSamsungAc ac(0); + ac.setFan(kSamsungAcFanMed); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanMed, ac.getFan()); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + + // Actual powerful on & off states from: + // https://github.com/markszabo/IRremoteESP8266/issues/734#issuecomment-500120270 + uint8_t on[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xA2, 0xFE, 0x77, 0x00, 0x1F, 0xF0}; + ac.setRaw(on, kSamsungAcStateLength); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 7 (TURBO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: On", ac.toString()); + + uint8_t off[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(off, kSamsungAcStateLength); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", ac.toString()); +} + +TEST(TestIRSamsungAcClass, QuietAndPowerfulAreMutuallyExclusive) { + IRSamsungAc ac(0); + ac.setQuiet(false); + ac.setPowerful(false); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); } TEST(TestIRSamsungAcClass, ChecksumCalculation) { @@ -546,7 +624,7 @@ TEST(TestIRSamsungAcClass, HumanReadable) { IRSamsungAc samsung(0); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (LOW), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); samsung.setTemp(kSamsungAcMaxTemp); samsung.setMode(kSamsungAcHeat); @@ -557,12 +635,18 @@ TEST(TestIRSamsungAcClass, HumanReadable) { samsung.setClean(true); EXPECT_EQ( "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 5 (HIGH), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off", + "Beep: On, Clean: On, Quiet: Off, Powerful: Off", samsung.toString()); samsung.setQuiet(true); EXPECT_EQ( "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " - "Beep: On, Clean: On, Quiet: On", + "Beep: On, Clean: On, Quiet: On, Powerful: Off", + samsung.toString()); + samsung.setQuiet(false); + samsung.setPowerful(true); + EXPECT_EQ( + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 7 (TURBO), Swing: Off, " + "Beep: On, Clean: On, Quiet: Off, Powerful: On", samsung.toString()); } @@ -666,7 +750,7 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) { samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (LOW), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -715,7 +799,7 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) { samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -774,7 +858,7 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) { samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -834,7 +918,7 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) { samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( "Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -881,7 +965,7 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) { samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 4 (HEAT), Temp: 17C, Fan: 0 (AUTO), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -928,7 +1012,7 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { samsung.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -986,7 +1070,7 @@ TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { samsung.setRaw(irsend.capture.state, irsend.capture.bits / 8); EXPECT_EQ( "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -997,6 +1081,7 @@ TEST(TestSendSamsung36, SendDataOnly) { irsend.reset(); irsend.sendSamsung36(0); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1008,6 +1093,7 @@ TEST(TestSendSamsung36, SendDataOnly) { irsend.outputStr()); irsend.sendSamsung36(0x400E00FF); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1034,6 +1120,7 @@ TEST(TestSendSamsung36, SendWithRepeats) { irsend.reset(); irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1053,6 +1140,7 @@ TEST(TestSendSamsung36, SendWithRepeats) { irsend.outputStr()); irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1119,3 +1207,239 @@ TEST(TestDecodeSamsung36, SyntheticExample) { EXPECT_EQ(0xE00FF, irsend.capture.command); EXPECT_EQ(0x400, irsend.capture.address); } + +// https://github.com/markszabo/IRremoteESP8266/issues/604 +TEST(TestIRSamsungAcClass, Issue604SendPowerHack) { + IRSamsungAc ac(0); + ac.begin(); + + std::string freqduty = "f38000d50"; + + std::string poweron = + "m690s17844" + "m3086s8864" + "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s436m586s1432" + "m586s1432m586s1432m586s1432m586s1432m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s2886" + "m3086s8864" + "m586s1432m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s1432m586s1432" + "m586s1432m586s1432m586s1432m586s1432m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s2886" + "m3086s8864" + "m586s1432m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s436m586s1432m586s1432m586s1432" + "m586s436m586s1432m586s1432m586s1432m586s1432m586s1432m586s1432m586s1432" + "m586s1432m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s1432" + "m586s1432m586s436m586s436m586s436m586s1432m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s100000"; + std::string settings = + "m690s17844" + "m3086s8864" + "m586s436m586s1432m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s436m586s1432" + "m586s1432m586s1432m586s1432m586s1432m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s2886" + "m3086s8864" + "m586s1432m586s436m586s436m586s436m586s436m586s436m586s436m586s436" + "m586s436m586s1432m586s436m586s436m586s1432m586s436m586s1432m586s1432" + "m586s436m586s1432m586s1432m586s1432m586s436m586s1432m586s436m586s1432" + "m586s1432m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s436" + "m586s1432m586s436m586s436m586s1432m586s1432m586s436m586s436m586s436" + "m586s436m586s436m586s436m586s436m586s1432m586s1432m586s1432m586s1432" + "m586s100000"; + std::string text = "Power: On, Mode: 1 (COOL), Temp: 23C, Fan: 4 (MED), " + "Swing: On, Beep: Off, Clean: Off, Quiet: Off, " + "Powerful: Off"; + // Don't do a setPower()/on()/off() as that will trigger the special message. + // So it should only be the normal "settings" message. + ac.setTemp(23); + ac.setMode(kSamsungAcCool); + ac.setFan(kSamsungAcFanMed); + ac.send(); + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Now trigger a special power message by using a power method. + ac.on(); + ac.send(); // This should result in two messages. 1 x extended + 1 x normal. + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + poweron + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Subsequent sending should be just the "settings" message. + ac.send(); + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Now trigger a special power message by using a power method (again). + ac.setPower(true); + ac.send(); // This should result in two messages. 1 x extended + 1 x normal. + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + poweron + settings, ac._irsend.outputStr()); +} + +TEST(TestIRSamsungAcClass, toCommon) { + IRSamsungAc ac(0); + ac.setPower(true); + ac.setMode(kSamsungAcCool); + ac.setTemp(20); + ac.setFan(kSamsungAcFanAuto); + ac.setSwing(true); + ac.setBeep(true); + ac.setClean(true); + ac.setQuiet(true); + // Now test it. + ASSERT_EQ(decode_type_t::SAMSUNG_AC, 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_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().beep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeSamsungAC, Issue734QuietSetting) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // QUIET MODE ON data from: + // https://github.com/markszabo/IRremoteESP8266/issues/734#issuecomment-499791618 + uint16_t quietOn[233] = { + 624, 17360, 3076, 8902, 520, 476, 520, 1472, 520, 476, 520, 474, 520, 476, + 520, 476, 520, 474, 522, 476, 520, 478, 518, 1476, 516, 500, 494, 502, + 548, 448, 546, 450, 544, 452, 522, 1468, 520, 1474, 520, 1472, 520, 1472, + 520, 1472, 520, 476, 520, 476, 518, 478, 516, 480, 516, 500, 496, 500, + 494, 502, 550, 446, 546, 450, 544, 452, 524, 472, 522, 474, 518, 476, 520, + 476, 520, 474, 522, 474, 520, 474, 520, 476, 520, 474, 520, 476, 518, 478, + 518, 480, 516, 480, 516, 502, 494, 502, 548, 1444, 524, 472, 522, 472, + 520, 474, 518, 478, 518, 476, 520, 476, 520, 1472, 520, 1470, 520, 1472, + 520, 1474, 516, 2980, 2998, 8980, 498, 1498, 548, 448, 526, 470, 544, 452, + 524, 472, 520, 474, 520, 476, 520, 476, 520, 476, 520, 1472, 520, 474, + 520, 476, 520, 1474, 518, 1476, 516, 1496, 496, 1498, 548, 446, 546, 1446, + 524, 1468, 518, 1474, 520, 1472, 520, 1472, 520, 1472, 520, 1474, 518, + 1476, 518, 480, 516, 500, 496, 528, 520, 1446, 544, 1446, 524, 1470, 518, + 476, 520, 476, 520, 474, 520, 476, 520, 474, 520, 476, 520, 474, 520, 476, + 520, 476, 518, 1476, 516, 482, 514, 502, 548, 448, 548, 1442, 544, 452, + 522, 474, 518, 476, 518, 476, 520, 476, 520, 474, 520, 476, 520, 1472, + 520, 1470, 522, 1474, 518, 1476, 536}; + + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x20, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + + irsend.sendRaw(quietOn, 233, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc ac(0); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off", + ac.toString()); + + // Make sure the ac class state is in something wildly different first. + ac.stateReset(); + ac.setPower(false); + ac.setMode(kSamsungAcAuto); + ac.setTemp(30); + ac.setSwing(true); + ac.setBeep(true); + ac.setClean(true); + ac.setQuiet(false); + // See if we can build the state from scratch. + ac.setPower(true); + ac.setMode(kSamsungAcCool); + ac.setTemp(16); + ac.setSwing(false); + ac.setBeep(false); + ac.setClean(false); + ac.setQuiet(true); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off", + ac.toString()); + // Check it matches the known good/expected state. + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kSamsungAcBits); +} + +TEST(TestDecodeSamsungAC, Issue734PowerfulOff) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // 1st Powerful off data from: + // https://github.com/markszabo/IRremoteESP8266/issues/734#issuecomment-500114580 + uint16_t powerfulOff[233] = { + 652, 17336, 3078, 8910, 562, 456, 546, 1448, 550, 446, 552, 444, 552, 444, + 550, 446, 550, 446, 552, 446, 550, 446, 552, 1440, 550, 446, 550, 446, + 550, 1470, 478, 518, 502, 492, 536, 1458, 542, 1450, 552, 1440, 552, 1442, + 552, 1442, 550, 446, 550, 446, 550, 446, 552, 444, 550, 446, 550, 446, + 550, 472, 524, 472, 480, 516, 510, 488, 538, 458, 542, 452, 548, 448, 550, + 446, 550, 446, 550, 444, 552, 444, 552, 444, 552, 444, 552, 444, 552, 444, + 552, 444, 550, 446, 550, 446, 550, 472, 524, 472, 482, 514, 510, 486, 536, + 460, 542, 454, 546, 450, 550, 446, 552, 1442, 552, 1442, 550, 1442, 552, + 1440, 508, 2994, 3030, 8932, 552, 1638, 450, // <= (was 356) + // Above hack due to poor data. + 470, 526, 470, 506, 492, 510, + 486, 542, 454, 544, 450, 550, 446, 554, 444, 550, 1442, 550, 444, 550, + 446, 550, 1442, 552, 1440, 550, 1442, 550, 1470, 524, 470, 480, 1512, 512, + 1480, 546, 1448, 550, 1442, 552, 1442, 552, 1442, 550, 1440, 552, 1440, + 552, 446, 550, 444, 552, 444, 550, 1468, 484, 1510, 512, 1482, 544, 452, + 550, 446, 552, 442, 554, 444, 552, 444, 554, 442, 554, 442, 552, 444, 554, + 442, 554, 1440, 552, 444, 554, 442, 554, 468, 528, 1466, 508, 488, 512, + 484, 544, 450, 550, 446, 554, 442, 556, 442, 554, 442, 554, 1438, 554, + 1438, 554, 1438, 554, 1438, 562}; // UNKNOWN 7B551B62}; + + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + + irsend.sendRaw(powerfulOff, 233, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeSamsungAC(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc ac(0); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (AUTO), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index 14c1c7d..165e29f 100644 --- a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp @@ -27,6 +27,7 @@ TEST(TestEncodeSanyoLC7461, SendDataOnly) { irsend.reset(); irsend.sendSanyoLC7461(0x1D8113F00FF); EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" @@ -45,6 +46,7 @@ TEST(TestEncodeSanyoLC7461, SendWithRepeats) { irsend.reset(); irsend.sendSanyoLC7461(0x1D8113F00FF, kSanyoLC7461Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m8960s4480" "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" diff --git a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp index 8481a46..28b104d 100644 --- a/lib/IRremoteESP8266/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sharp_test.cpp @@ -1,5 +1,8 @@ // Copyright 2017 David Conran +#include "ir_Sharp.h" +#include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -47,6 +50,7 @@ TEST(TestSendSharp, SendDataOnly) { irsend.reset(); irsend.sendSharp(0x11, 0x52); EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -64,6 +68,7 @@ TEST(TestSendSharp, SendWithRepeats) { irsend.reset(); irsend.sendSharp(0x11, 0x52, kSharpBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -87,6 +92,7 @@ TEST(TestSendSharp, SendUnusualSize) { irsend.reset(); irsend.sendSharp(0x0, 0x0, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s1820m260s1820m260s1820m260s780m260s1820" @@ -96,6 +102,7 @@ TEST(TestSendSharp, SendUnusualSize) { irsend.reset(); irsend.sendSharp(0x0, 0x0, 16); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s780m260s780" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" @@ -115,6 +122,7 @@ TEST(TestSendSharpRaw, SendDataOnly) { irsend.reset(); irsend.sendSharpRaw(0x454A); EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -132,6 +140,7 @@ TEST(TestSendSharpRaw, SendWithRepeats) { irsend.reset(); irsend.sendSharpRaw(0x454A, kSharpBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d33" "m260s1820m260s780m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s1820m260s780m260s780m260s1820m260s780m260s1820m260s780" "m260s43602" @@ -155,6 +164,7 @@ TEST(TestSendSharpRaw, SendUnusualSize) { irsend.reset(); irsend.sendSharpRaw(0x2, 8); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" "m260s1820m260s1820m260s1820m260s1820m260s1820m260s1820m260s780m260s1820" @@ -164,6 +174,7 @@ TEST(TestSendSharpRaw, SendUnusualSize) { irsend.reset(); irsend.sendSharpRaw(0x2, 16); EXPECT_EQ( + "f38000d33" "m260s780m260s780m260s780m260s780m260s780m260s780m260s780m260s780" "m260s780m260s780m260s780m260s780m260s780m260s780m260s1820m260s780" "m260s43602" @@ -352,3 +363,345 @@ TEST(TestDecodeSharp, FailToDecodeNonSharpExample) { ASSERT_FALSE(irrecv.decodeSharp(&irsend.capture)); ASSERT_FALSE(irrecv.decodeSharp(&irsend.capture, kSharpBits, false)); } + +// https://github.com/markszabo/IRremoteESP8266/issues/638#issue-421064165 +TEST(TestDecodeSharpAc, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // cool-auto-27.txt + uint16_t rawData[211] = { + 3804, 1892, 466, 486, 466, 1388, 466, 486, 466, 1386, 468, 486, 468, 1388, + 466, 486, 466, 1386, 468, 488, 466, 1388, 466, 488, 466, 1386, 468, 1388, + 466, 486, 466, 1388, 466, 486, 468, 1384, 468, 1388, 468, 1388, 466, 1388, + 466, 486, 468, 484, 468, 1386, 468, 1386, 468, 486, 466, 486, 468, 486, + 466, 488, 466, 1388, 466, 486, 466, 486, 468, 486, 466, 488, 466, 488, + 466, 1386, 468, 1388, 466, 486, 468, 486, 466, 1388, 464, 1388, 466, 1386, + 468, 486, 466, 486, 468, 486, 466, 1388, 468, 1384, 470, 486, 466, 486, + 468, 486, 468, 1386, 468, 486, 468, 486, 468, 486, 468, 1388, 466, 486, + 466, 486, 466, 486, 466, 488, 466, 486, 468, 486, 468, 486, 468, 486, 466, + 486, 466, 486, 466, 488, 466, 486, 466, 486, 466, 1388, 466, 486, 468, + 486, 466, 486, 468, 486, 468, 486, 466, 486, 466, 488, 466, 486, 466, 486, + 466, 488, 466, 486, 468, 1386, 468, 486, 466, 486, 466, 1390, 464, 488, + 466, 486, 468, 486, 468, 486, 466, 486, 466, 486, 466, 486, 468, 486, 468, + 486, 466, 486, 466, 1386, 468, 1390, 466, 1388, 466, 1388, 468, 486, 466, + 486, 468, 486, 466, 486, 466, 486, 466, 1390, 464, 486, 414}; + // UNKNOWN F2B82C78 + uint8_t expectedState[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCC, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x41}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 211, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SHARP_AC, irsend.capture.decode_type); + ASSERT_EQ(kSharpAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSharpAc ac(0); + ac.begin(); + ac.setRaw(irsend.capture.state); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 27C, Fan: 2 (AUTO)", + ac.toString()); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/638#issue-421064165 +TEST(TestDecodeSharpAc, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + // cool-auto-27.txt + uint8_t expectedState[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCC, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x41}; + + irsend.begin(); + irsend.reset(); + irsend.sendSharpAc(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SHARP_AC, irsend.capture.decode_type); + ASSERT_EQ(kSharpAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestIRUtils, Sharp) { + ASSERT_EQ("SHARP", typeToString(decode_type_t::SHARP)); + ASSERT_EQ(decode_type_t::SHARP, strToDecodeType("SHARP")); + ASSERT_FALSE(hasACState(decode_type_t::SHARP)); +} + +TEST(TestIRUtils, SharpAc) { + ASSERT_EQ("SHARP_AC", typeToString(decode_type_t::SHARP_AC)); + ASSERT_EQ(decode_type_t::SHARP_AC, strToDecodeType("SHARP_AC")); + ASSERT_TRUE(hasACState(decode_type_t::SHARP_AC)); +} + +// Tests for IRSharpAc class. + +TEST(TestSharpAcClass, Power) { + IRSharpAc ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestSharpAcClass, Checksum) { + uint8_t state[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCC, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x41}; + EXPECT_EQ(0x4, IRSharpAc::calcChecksum(state)); + EXPECT_TRUE(IRSharpAc::validChecksum(state)); + // Change the state so it is not valid. + state[3] = 0; + EXPECT_FALSE(IRSharpAc::validChecksum(state)); +} + +TEST(TestSharpAcClass, Temperature) { + IRSharpAc ac(0); + ac.begin(); + ac.setMode(kSharpAcCool); // Cool mode doesn't have temp restrictions. + + ac.setTemp(0); + EXPECT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kSharpAcMaxTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMinTemp); + EXPECT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMaxTemp); + EXPECT_EQ(kSharpAcMaxTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMinTemp - 1); + EXPECT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMaxTemp + 1); + EXPECT_EQ(kSharpAcMaxTemp, ac.getTemp()); + + ac.setTemp(kSharpAcMinTemp + 1); + EXPECT_EQ(kSharpAcMinTemp + 1, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestSharpAcClass, OperatingMode) { + IRSharpAc ac(0); + ac.begin(); + + ac.setTemp(25); + ac.setMode(kSharpAcAuto); + EXPECT_EQ(kSharpAcAuto, ac.getMode()); + + ac.setMode(kSharpAcCool); + EXPECT_EQ(kSharpAcCool, ac.getMode()); + + ac.setMode(kSharpAcHeat); + EXPECT_EQ(kSharpAcHeat, ac.getMode()); + + ac.setMode(kSharpAcDry); + EXPECT_EQ(kSharpAcDry, ac.getMode()); + ASSERT_EQ(kSharpAcMinTemp, ac.getTemp()); // Dry mode restricts the temp. + ac.setTemp(25); + ASSERT_EQ(kSharpAcMinTemp, ac.getTemp()); + + ac.setMode(kSharpAcDry + 1); + EXPECT_EQ(kSharpAcAuto, ac.getMode()); + + ac.setMode(kSharpAcCool); + EXPECT_EQ(kSharpAcCool, ac.getMode()); + // We are no longer restricted. + ac.setTemp(25); + ASSERT_EQ(25, ac.getTemp()); + + ac.setMode(255); + EXPECT_EQ(kSharpAcAuto, ac.getMode()); +} + + +TEST(TestSharpAcClass, FanSpeed) { + IRSharpAc ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(0); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanMax); + EXPECT_EQ(kSharpAcFanMax, ac.getFan()); + + // Beyond Max should default to Auto. + ac.setFan(kSharpAcFanMax + 1); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanMed); + EXPECT_EQ(kSharpAcFanMed, ac.getFan()); + + ac.setFan(kSharpAcFanMin); + EXPECT_EQ(kSharpAcFanMin, ac.getFan()); + + ac.setFan(kSharpAcFanAuto - 1); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanMax + 1); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); + + ac.setFan(kSharpAcFanAuto); + EXPECT_EQ(kSharpAcFanAuto, ac.getFan()); +} + +TEST(TestSharpAcClass, ReconstructKnownState) { + IRSharpAc ac(0); + ac.begin(); + + uint8_t on_auto_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + ac.on(); + ac.setMode(kSharpAcAuto); + ac.setTemp(kSharpAcMinTemp); + ac.setFan(kSharpAcFanAuto); + EXPECT_STATE_EQ(on_auto_auto, ac.getRaw(), kSharpAcBits); + EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 15C, Fan: 2 (AUTO)", + ac.toString()); + + uint8_t cool_auto_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x51}; + ac.stateReset(); + ac.on(); + ac.setMode(kSharpAcCool); + ac.setTemp(28); + ac.setFan(kSharpAcFanAuto); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 2 (AUTO)", + ac.toString()); + EXPECT_STATE_EQ(cool_auto_28, ac.getRaw(), kSharpAcBits); +} + +// https://github.com/markszabo/IRremoteESP8266/issues/638#issue-421064165 +TEST(TestSharpAcClass, KnownStates) { + IRSharpAc ac(0); + ac.begin(); + + uint8_t off_auto_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x21, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x31}; + ASSERT_TRUE(ac.validChecksum(off_auto_auto)); + ac.setRaw(off_auto_auto); + EXPECT_EQ("Power: Off, Mode: 0 (AUTO), Temp: 15C, Fan: 2 (AUTO)", + ac.toString()); + uint8_t on_auto_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + ASSERT_TRUE(ac.validChecksum(on_auto_auto)); + ac.setRaw(on_auto_auto); + EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 15C, Fan: 2 (AUTO)", + ac.toString()); + uint8_t cool_auto_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0, + 0x51}; + ASSERT_TRUE(ac.validChecksum(cool_auto_28)); + ac.setRaw(cool_auto_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 2 (AUTO)", + ac.toString()); + uint8_t cool_fan1_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x42, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x21}; + ASSERT_TRUE(ac.validChecksum(cool_fan1_28)); + ac.setRaw(cool_fan1_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 4 (MIN)", + ac.toString()); + uint8_t cool_fan2_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x32, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x51}; + ASSERT_TRUE(ac.validChecksum(cool_fan2_28)); + ac.setRaw(cool_fan2_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 3 (MED)", + ac.toString()); + uint8_t cool_fan3_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x52, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x31}; + ASSERT_TRUE(ac.validChecksum(cool_fan3_28)); + ac.setRaw(cool_fan3_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 5 (HIGH)", + ac.toString()); + uint8_t cool_fan4_28[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x72, 0x00, 0x08, 0x80, 0x05, 0xE0, + 0x11}; + ASSERT_TRUE(ac.validChecksum(cool_fan4_28)); + ac.setRaw(cool_fan4_28); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 7 (MAX)", + ac.toString()); + /* Unsupported / Not yet reverse engineered. + uint8_t cool_fan4_28_ion_on[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x61, 0x72, 0x08, 0x08, 0x80, 0x00, 0xE4, + 0xD1}; + ASSERT_TRUE(ac.validChecksum(cool_fan4_28_ion_on)); + ac.setRaw(cool_fan4_28_ion_on); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 7 (MAX)", + ac.toString()); + uint8_t cool_fan4_28_eco1[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x61, 0x72, 0x18, 0x08, 0x80, 0x00, 0xE8, + 0x01}; + ASSERT_TRUE(ac.validChecksum(cool_fan4_28_eco1)); + ac.setRaw(cool_fan4_28_eco1); + EXPECT_EQ("Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 7 (MAX)", + ac.toString()); */ + uint8_t dry_auto[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x31, 0x23, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x11}; + ASSERT_TRUE(ac.validChecksum(dry_auto)); + ac.setRaw(dry_auto); + EXPECT_EQ("Power: On, Mode: 3 (DRY), Temp: 15C, Fan: 2 (AUTO)", + ac.toString()); +} + +TEST(TestSharpAcClass, toCommon) { + IRSharpAc ac(0); + ac.setPower(true); + ac.setMode(kSharpAcCool); + ac.setTemp(20); + ac.setFan(kSharpAcFanMax); + // Now test it. + ASSERT_EQ(decode_type_t::SHARP_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp index 22d9ead..f1f41d9 100644 --- a/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sherwood_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendSherwood, SendDataOnly) { irsend.reset(); irsend.sendSherwood(0xC1A28877); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" @@ -31,6 +32,7 @@ TEST(TestSendSherwood, SendDataWithRepeats) { irsend.reset(); irsend.sendSherwood(0xC1A28877, 32, 2); EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" @@ -50,6 +52,7 @@ TEST(TestSendSherwood, SendDataWithZeroRepeats) { irsend.sendSherwood(0xC1A28877, 32, 0); // Should have a single NEC repeat, as we always send one. EXPECT_EQ( + "f38000d33" "m8960s4480m560s1680m560s1680m560s560m560s560m560s560m560s560" "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560" "m560s560m560s1680m560s560m560s1680m560s560m560s560m560s560" diff --git a/lib/IRremoteESP8266/test/ir_Sony_test.cpp b/lib/IRremoteESP8266/test/ir_Sony_test.cpp index c79ff61..35c3287 100644 --- a/lib/IRremoteESP8266/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sony_test.cpp @@ -15,6 +15,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0); // We expect three 20-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s600m600s600m600s600m600s600" "m600s600m600s600m600s600m600s600m600s18600" @@ -30,6 +31,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0x240C, kSony20Bits); // We expect three 20-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" @@ -45,6 +47,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0x240C, kSony15Bits); // We expect three 15-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m1200s600m600s600m600s600m1200s600m600s600" "m600s600m600s600m600s600m600s600m600s600m1200s600m1200s600m600s600" "m600s22200" @@ -60,6 +63,7 @@ TEST(TestSendSony, SendDataOnly) { irsend.sendSony(0xA90, kSony12Bits); // We expect three 15-bit commands to be sent. EXPECT_EQ( + "f40000d33" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" "m600s600m1200s600m600s600m600s600m600s600m600s25800" "m2400s600m1200s600m600s600m1200s600m600s600m1200s600m600s600" @@ -77,12 +81,14 @@ TEST(TestSendSony, SendWithDiffRepeats) { irsend.reset(); irsend.sendSony(0x240C, kSony20Bits, 0); // Send a command with 0 repeats. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200", irsend.outputStr()); irsend.sendSony(0x240C, kSony20Bits, 1); // Send a command with 1 repeat. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" @@ -92,6 +98,7 @@ TEST(TestSendSony, SendWithDiffRepeats) { irsend.outputStr()); irsend.sendSony(0x240C, kSony20Bits, 3); // Send a command with 3 repeats. EXPECT_EQ( + "f40000d33" "m2400s600m600s600m600s600m600s600m600s600m600s600m600s600m1200s600" "m600s600m600s600m1200s600m600s600m600s600m600s600m600s600m600s600" "m600s600m1200s600m1200s600m600s600m600s16200" diff --git a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp index 3d8bec3..8432cf9 100644 --- a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp @@ -64,7 +64,10 @@ TEST(TestDecodeTcl112Ac, DecodeRealExample) { IRTcl112Ac ac(0); ac.setRaw(irsend.capture.state); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 24C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); } // Decode a synthetic Tcl112Ac A/C example from Issue #619 @@ -103,13 +106,25 @@ TEST(TestTcl112AcClass, Temperature) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBC}; IRTcl112Ac ac(0); ac.setRaw(temp16C); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 16C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); ac.setRaw(temp16point5C); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 16.5C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16.5C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); ac.setRaw(temp19point5C); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 19.5C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 19.5C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); ac.setRaw(temp31C); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 31C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 31C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); ac.setTemp(kTcl112AcTempMin); EXPECT_EQ(kTcl112AcTempMin, ac.getTemp()); @@ -158,8 +173,10 @@ TEST(TestTcl112AcClass, OperatingMode) { ac.setMode(kTcl112AcHeat); EXPECT_EQ(kTcl112AcHeat, ac.getMode()); - ac.setMode(kTcl112AcFan); + ac.setFan(kTcl112AcFanAuto); + ac.setMode(kTcl112AcFan); // Should set fan speed to High. EXPECT_EQ(kTcl112AcFan, ac.getMode()); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); ac.setMode(kTcl112AcDry); EXPECT_EQ(kTcl112AcDry, ac.getMode()); @@ -185,7 +202,10 @@ TEST(TestTcl112AcClass, OperatingMode) { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0x48}; ac.setRaw(automode); - EXPECT_EQ("Power: On, Mode: 8 (AUTO), Temp: 24C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 8 (AUTO), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); } TEST(TestTcl112AcClass, Power) { @@ -211,16 +231,21 @@ TEST(TestTcl112AcClass, Power) { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB}; ac.setRaw(on); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 16C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); const uint8_t off[kTcl112AcStateLength] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x03, 0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0xCB}; ac.setRaw(off); - EXPECT_EQ("Power: Off, Mode: 3 (COOL), Temp: 24C", ac.toString()); + EXPECT_EQ( + "Power: Off, Mode: 3 (COOL), Temp: 24C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); } - TEST(TestTcl112AcClass, Checksum) { uint8_t temp16C[kTcl112AcStateLength] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, @@ -231,9 +256,15 @@ TEST(TestTcl112AcClass, Checksum) { IRTcl112Ac ac(0); EXPECT_EQ(0xCB, ac.calcChecksum(temp16C)); ac.setRaw(temp16C); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 16C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 16C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); ac.setRaw(temp31C); - EXPECT_EQ("Power: On, Mode: 3 (COOL), Temp: 31C", ac.toString()); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 31C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); EXPECT_EQ(0xBC, ac.calcChecksum(temp31C)); EXPECT_TRUE(IRTcl112Ac::validChecksum(temp16C)); @@ -245,3 +276,187 @@ TEST(TestTcl112AcClass, Checksum) { EXPECT_TRUE(ac.validChecksum(ac.getRaw())); EXPECT_EQ(0xBC, ac.calcChecksum(ac.getRaw())); } + +TEST(TestTcl112AcClass, Econo) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + ac.setEcono(false); + EXPECT_EQ(false, ac.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + +TEST(TestTcl112AcClass, Health) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setHealth(true); + EXPECT_TRUE(ac.getHealth()); + ac.setHealth(false); + EXPECT_EQ(false, ac.getHealth()); + ac.setHealth(true); + EXPECT_TRUE(ac.getHealth()); +} + +TEST(TestTcl112AcClass, Light) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_EQ(false, ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestTcl112AcClass, SwingHorizontal) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); + ac.setSwingHorizontal(false); + EXPECT_EQ(false, ac.getSwingHorizontal()); + ac.setSwingHorizontal(true); + EXPECT_TRUE(ac.getSwingHorizontal()); +} + +TEST(TestTcl112AcClass, SwingVertical) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); + ac.setSwingVertical(false); + EXPECT_EQ(false, ac.getSwingVertical()); + ac.setSwingVertical(true); + EXPECT_TRUE(ac.getSwingVertical()); +} + +TEST(TestTcl112AcClass, Turbo) { + IRTcl112Ac ac(0); + ac.begin(); + + ac.setFan(kTcl112AcFanLow); + ac.setSwingHorizontal(false); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + EXPECT_FALSE(ac.getSwingVertical()); + EXPECT_EQ(kTcl112AcFanLow, ac.getFan()); + + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_TRUE(ac.getSwingVertical()); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestTcl112AcClass, FanSpeed) { + IRTcl112Ac ac(0); + ac.begin(); + + // Unexpected value should default to Auto. + ac.setFan(255); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); + + ac.setFan(kTcl112AcFanLow); + EXPECT_EQ(kTcl112AcFanLow, ac.getFan()); + ac.setFan(kTcl112AcFanMed); + EXPECT_EQ(kTcl112AcFanMed, ac.getFan()); + ac.setFan(kTcl112AcFanHigh); + EXPECT_EQ(kTcl112AcFanHigh, ac.getFan()); + ac.setFan(kTcl112AcFanAuto); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); + + // Beyond High should default to Auto. + ac.setFan(kTcl112AcFanHigh + 1); + EXPECT_EQ(kTcl112AcFanAuto, ac.getFan()); +} + + +TEST(TestTcl112AcClass, toCommon) { + IRTcl112Ac ac(0); + ac.setPower(true); + ac.setMode(kTcl112AcCool); + ac.setTemp(20); + ac.setFan(kTcl112AcFanHigh); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setTurbo(true); + ac.setHealth(true); + ac.setEcono(true); + ac.setLight(true); + // Now test it. + ASSERT_EQ(decode_type_t::TCL112AC, 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_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(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_TRUE(ac.toCommon().filter); + // Unsupported. + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeTcl112Ac, Issue744) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[227] = { + 3164, 1532, 584, 1082, 472, 1068, 580, 244, 602, 264, 542, 328, 530, 1034, + 586, 262, 540, 326, 508, 1064, 582, 1082, 490, 328, 532, 1032, 586, 262, + 544, 352, 478, 1060, 584, 1082, 486, 328, 502, 1058, 588, 1084, 472, 344, + 530, 250, 600, 1086, 492, 322, 530, 258, 594, 1082, 494, 318, 510, 344, + 530, 248, 600, 262, 544, 326, 504, 296, 578, 252, 598, 260, 550, 318, 506, + 344, 530, 250, 600, 258, 546, 318, 508, 342, 532, 254, 596, 236, 606, 266, + 524, 1066, 580, 242, 602, 266, 542, 1054, 574, 246, 604, 262, 550, 1088, + 530, 1034, 588, 262, 542, 328, 504, 296, 582, 238, 606, 262, 546, 322, + 508, 342, 530, 250, 602, 260, 544, 1052, 572, 252, 600, 260, 546, 320, + 506, 344, 530, 254, 596, 264, 578, 268, 552, 316, 528, 256, 598, 260, 578, + 272, 520, 372, 476, 294, 582, 240, 604, 266, 542, 328, 502, 294, 582, 238, + 604, 268, 540, 322, 506, 346, 530, 244, 604, 260, 542, 354, 478, 298, 580, + 240, 604, 262, 542, 326, 506, 342, 530, 250, 600, 260, 548, 318, 506, 344, + 530, 250, 600, 260, 546, 320, 528, 322, 530, 254, 598, 262, 548, 316, 468, + 380, 532, 250, 600, 260, 546, 1092, 500, 300, 578, 246, 602, 1082, 474, + 346, 530, 248, 602, 260, 542, 1054, 570, 1090, 524}; // UNKNOWN 3338FACE + + uint8_t expectedState[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC4}; + + irsend.sendRaw(rawData, 227, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL112AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl112AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRTcl112Ac ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 23C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266/test/ir_Teco_test.cpp b/lib/IRremoteESP8266/test/ir_Teco_test.cpp index 7e639e2..8dd47f0 100644 --- a/lib/IRremoteESP8266/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teco_test.cpp @@ -23,13 +23,14 @@ TEST(TestSendTeco, SendDataOnly) { irsend.reset(); irsend.sendTeco(0x250002BC9); EXPECT_EQ( + "f38000d50" "m9000s4440" "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" "m620s580m620s1650m620s580" - "m620s1000000", + "m620s100000", irsend.outputStr()); } @@ -41,27 +42,28 @@ TEST(TestSendTeco, SendWithRepeats) { irsend.reset(); irsend.sendTeco(0x250002BC9, kTecoBits, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m9000s4440" "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" "m620s580m620s1650m620s580" - "m620s1000000" + "m620s100000" "m9000s4440" "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" "m620s580m620s1650m620s580" - "m620s1000000" + "m620s100000" "m9000s4440" "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" "m620s580m620s580m620s580m620s580m620s580m620s580m620s580m620s580" "m620s580m620s580m620s580m620s580m620s1650m620s580m620s1650m620s580" "m620s580m620s1650m620s580" - "m620s1000000", + "m620s100000", irsend.outputStr()); } @@ -354,3 +356,34 @@ TEST(TestDecodeTeco, RealNormalExample) { "Swing: On", ac.toString()); } + + +TEST(TestTecoACClass, toCommon) { + IRTecoAc ac(0); + ac.setPower(true); + ac.setMode(kTecoCool); + ac.setTemp(20); + ac.setFan(kTecoFanHigh); + ac.setSwing(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::TECO, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + 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(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp index b5e1e07..0977043 100644 --- a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp @@ -18,6 +18,7 @@ TEST(TestSendToshibaAC, SendDataOnly) { irsend.reset(); irsend.sendToshibaAC(toshiba_code); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -54,6 +55,7 @@ TEST(TestSendToshibaAC, SendWithRepeats) { irsend.sendToshibaAC(toshiba_code, kToshibaACStateLength, 0); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -70,6 +72,7 @@ TEST(TestSendToshibaAC, SendWithRepeats) { irsend.reset(); irsend.sendToshibaAC(toshiba_code, kToshibaACStateLength, 2); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -122,6 +125,7 @@ TEST(TestSendToshibaAC, SendUnexpectedSizes) { irsend.reset(); irsend.sendToshibaAC(toshiba_long_code, kToshibaACStateLength + 1); ASSERT_EQ( + "f38000d50" "m4400s4300" "m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623" "m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472" @@ -380,6 +384,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -415,6 +420,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -450,6 +456,7 @@ TEST(TestToshibaACClass, MessageConstuction) { irsend.reset(); irsend.sendToshibaAC(toshiba.getRaw()); EXPECT_EQ( + "f38000d50" "m4400s4300" "m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472" "m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623" @@ -662,3 +669,31 @@ TEST(TestDecodeToshibaAC, RealExamples) { // sending the power off message. EXPECT_EQ(kToshibaAcHeat, toshiba.getMode()); } + +TEST(TestToshibaACClass, toCommon) { + IRToshibaAC ac(0); + ac.setPower(true); + ac.setMode(kToshibaAcCool); + ac.setTemp(20); + ac.setFan(kToshibaAcFanMax); + // Now test it. + ASSERT_EQ(decode_type_t::TOSHIBA_AC, 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_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + // Unsupported. + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp new file mode 100644 index 0000000..ec1794d --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Trotec_test.cpp @@ -0,0 +1,179 @@ +// Copyright 2019 David Conran +#include "ir_Trotec.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + + +TEST(TestTrotecESPClass, toCommon) { + IRTrotecESP ac(0); + ac.setPower(true); + ac.setMode(kTrotecCool); + ac.setTemp(20); + ac.setSpeed(kTrotecFanHigh); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::TROTEC, ac.toCommon().protocol); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestTrotecESPClass, MessageConstructon) { + IRTrotecESP ac(0); + ac.setPower(true); + ac.setTemp(20); + ac.setMode(kTrotecCool); + ac.setSpeed(kTrotecFanMed); + ac.setSleep(true); + + uint8_t expected[kTrotecStateLength] = { + 0x12, 0x34, 0x29, 0x82, 0x00, 0x00, 0x00, 0x00, 0xAB}; + EXPECT_STATE_EQ(expected, ac.getRaw(), kTrotecBits); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan Speed: 2 (Med), Sleep: On", + ac.toString()); +} + +// Tests for sendTrotec(). + +// Test sending typical data only. +TEST(TestSendTrotec, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kTrotecStateLength] = { + 0x12, 0x34, 0x29, 0x82, 0x00, 0x00, 0x00, 0x00, 0xAB}; + + irsend.sendTrotec(data); + EXPECT_EQ( + "f36000d50" + "m5952s7364" + "m592s592m592s1560m592s592m592s592m592s1560m592s592m592s592m592s592" + "m592s592m592s592m592s1560m592s592m592s1560m592s1560m592s592m592s592" + "m592s1560m592s592m592s592m592s1560m592s592m592s1560m592s592m592s592" + "m592s592m592s1560m592s592m592s592m592s592m592s592m592s592m592s1560" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s592m592s592m592s592m592s592m592s592m592s592m592s592m592s592" + "m592s1560m592s1560m592s592m592s1560m592s592m592s1560m592s592m592s1560" + "m592s6184" + "m592s1500", + irsend.outputStr()); +} + +// Tests for decodeTrotec(). +// Decode normal Trotec messages. + +TEST(TestDecodeTrotec, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal Trotec message. + irsend.reset(); + uint8_t expectedState[kTrotecStateLength] = { + 0x12, 0x34, 0x29, 0x82, 0x00, 0x00, 0x00, 0x00, 0xAB}; + irsend.sendTrotec(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::TROTEC, irsend.capture.decode_type); + EXPECT_EQ(kTrotecBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRTrotecESP ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan Speed: 2 (Med), Sleep: On", + ac.toString()); +} + + +TEST(TestTrotecESPClass, SetAndGetTemp) { + IRTrotecESP ac(0); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kTrotecMinTemp); + EXPECT_EQ(kTrotecMinTemp, ac.getTemp()); + ac.setTemp(kTrotecMaxTemp); + EXPECT_EQ(kTrotecMaxTemp, ac.getTemp()); + ac.setTemp(kTrotecMinTemp - 1); + EXPECT_EQ(kTrotecMinTemp, ac.getTemp()); + ac.setTemp(kTrotecMaxTemp + 1); + EXPECT_EQ(kTrotecMaxTemp, ac.getTemp()); +} + +TEST(TestTrotecESPClass, SetAndGetMode) { + IRTrotecESP ac(0); + + ac.setMode(kTrotecFan); + EXPECT_EQ(kTrotecFan, ac.getMode()); + ac.setMode(kTrotecCool); + EXPECT_EQ(kTrotecCool, ac.getMode()); + ac.setMode(kTrotecAuto); + EXPECT_EQ(kTrotecAuto, ac.getMode()); + ac.setMode(kTrotecDry); + EXPECT_EQ(kTrotecDry, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kTrotecAuto, ac.getMode()); +} + +TEST(TestTrotecESPClass, SetAndGetFan) { + IRTrotecESP ac(0); + + ac.setSpeed(kTrotecFanHigh); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); + ac.setSpeed(kTrotecFanLow); + EXPECT_EQ(kTrotecFanLow, ac.getSpeed()); + ac.setSpeed(kTrotecFanMed); + EXPECT_EQ(kTrotecFanMed, ac.getSpeed()); + ac.setSpeed(kTrotecFanHigh); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); + ASSERT_NE(7, kTrotecFanHigh); + // Now try some unexpected value. + ac.setSpeed(7); + EXPECT_EQ(kTrotecFanHigh, ac.getSpeed()); +} + +TEST(TestTrotecESPClass, Sleep) { + IRTrotecESP ac(0); + ac.setSleep(false); + ASSERT_FALSE(ac.getSleep()); + ac.setSleep(true); + ASSERT_TRUE(ac.getSleep()); + ac.setSleep(false); + ASSERT_FALSE(ac.getSleep()); +} + +TEST(TestTrotecESPClass, Power) { + IRTrotecESP ac(0); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); + ac.setPower(true); + ASSERT_TRUE(ac.getPower()); + ac.setPower(false); + ASSERT_FALSE(ac.getPower()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("TROTEC", typeToString(decode_type_t::TROTEC)); + ASSERT_EQ(decode_type_t::TROTEC, strToDecodeType("TROTEC")); + ASSERT_TRUE(hasACState(decode_type_t::TROTEC)); +} diff --git a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp index 823b92f..aefaadb 100644 --- a/lib/IRremoteESP8266/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Vestel_test.cpp @@ -17,6 +17,7 @@ TEST(TestSendVestelAc, SendDataOnly) { irsend.reset(); irsend.sendVestelAc(0x0F00D9001FEF201ULL); EXPECT_EQ( + "f38000d50" "m3110s9066" "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" @@ -37,6 +38,7 @@ TEST(TestSendVestelAc, SendWithRepeats) { irsend.reset(); irsend.sendVestelAc(0x0F00D9001FEF201ULL, kVestelAcBits, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m3110s9066" "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" "m520s480m520s1535m520s480m520s480m520s1535m520s1535m520s1535m520s1535" @@ -509,3 +511,34 @@ TEST(TestDecodeVestelAc, Housekeeping) { ASSERT_EQ("VESTEL_AC", typeToString(VESTEL_AC)); ASSERT_FALSE(hasACState(VESTEL_AC)); // Uses uint64_t, not uint8_t*. } + +TEST(TestVestelAcClass, toCommon) { + IRVestelAc ac(0); + ac.setPower(true); + ac.setMode(kVestelAcCool); + ac.setTemp(20); + ac.setFan(kVestelAcFanHigh); + ac.setSwing(true); + ac.setTurbo(true); + ac.setIon(true); + // Now test it. + ASSERT_EQ(decode_type_t::VESTEL_AC, 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_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_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().filter); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp index f0c4ebe..489d9d8 100644 --- a/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whirlpool_test.cpp @@ -19,6 +19,7 @@ TEST(TestSendWhirlpoolAC, SendDataOnly) { irsend.sendWhirlpoolAC(data); EXPECT_EQ( + "f38000d50" "m8950s4484" "m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533m597s1649" "m597s533m597s1649m597s1649m597s533m597s533m597s533m597s533m597s533" @@ -582,3 +583,36 @@ TEST(TestIRWhirlpoolAcClass, MessageConstruction) { ac.toString()); EXPECT_STATE_EQ(expectedState, ac.getRaw(), kWhirlpoolAcBits); } + +TEST(TestIRWhirlpoolAcClass, toCommon) { + IRWhirlpoolAc ac(0); + ac.setModel(whirlpool_ac_remote_model_t::DG11J13A); + ac.setPowerToggle(true); + ac.setMode(kWhirlpoolAcCool); + ac.setTemp(18); + ac.setFan(kWhirlpoolAcFanHigh); + ac.setSwing(true); + ac.setSuper(true); + ac.setLight(true); + ac.setSleep(false); + // Now test it. + ASSERT_EQ(decode_type_t::WHIRLPOOL_AC, ac.toCommon().protocol); + ASSERT_EQ(whirlpool_ac_remote_model_t::DG11J13A, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(18, ac.toCommon().degrees); + 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_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_EQ(-1, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp index 748a4c9..92ced5c 100644 --- a/lib/IRremoteESP8266/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Whynter_test.cpp @@ -14,6 +14,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0x0); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" @@ -25,6 +26,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0xFFFFFFFF); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" "m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150m750s2150" @@ -36,6 +38,7 @@ TEST(TestSendWhynter, SendDataOnly) { irsend.reset(); irsend.sendWhynter(0x87654321); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -53,6 +56,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 0); // 0 repeats. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -64,6 +68,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -81,6 +86,7 @@ TEST(TestSendWhynter, SendWithRepeats) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s2150m750s750m750s750m750s750m750s750m750s2150m750s2150m750s2150" "m750s750m750s2150m750s2150m750s750m750s750m750s2150m750s750m750s2150" @@ -110,6 +116,7 @@ TEST(TestSendWhynter, SendUnusualSize) { irsend.reset(); irsend.sendWhynter(0x0, 8); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s750m750s750m750s750m750s750m750s750" "m750s88050", @@ -118,6 +125,7 @@ TEST(TestSendWhynter, SendUnusualSize) { irsend.reset(); irsend.sendWhynter(0x1234567890ABCDEF, 64); EXPECT_EQ( + "f38000d50" "m750s750m2850s2850" "m750s750m750s750m750s750m750s2150m750s750m750s750m750s2150m750s750" "m750s750m750s750m750s2150m750s2150m750s750m750s2150m750s750m750s750" diff --git a/lib/IRremoteESP8266/tools/Makefile b/lib/IRremoteESP8266/tools/Makefile index 4f3e33e..129279e 100644 --- a/lib/IRremoteESP8266/tools/Makefile +++ b/lib/IRremoteESP8266/tools/Makefile @@ -14,13 +14,14 @@ USER_DIR = ../src # Where to find test code. TEST_DIR = ../test +INCLUDES = -I$(USER_DIR) -I$(TEST_DIR) # Flags passed to the preprocessor. # Set Google Test's header directory as a system directory, such that # the compiler doesn't generate warnings in Google Test headers. CPPFLAGS += -DUNIT_TEST # Flags passed to the C++ compiler. -CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 all : gc_decode mode2_decode @@ -28,7 +29,7 @@ run_tests : all failed=""; \ for py_unittest in *_test.py; do \ echo "RUNNING: $${py_unittest}"; \ - python ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ + python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ done; \ if [ -n "$${failed}" ]; then \ echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \ @@ -48,25 +49,28 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \ ir_Pronto.o ir_GlobalCache.o ir_Nikai.o ir_Toshiba.o ir_Midea.o \ ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o ir_Hitachi.o \ ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o ir_Pioneer.o \ - ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o + ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o \ + ir_MitsubishiHeavy.o ir_Goodweather.o ir_Inax.o ir_Argo.o \ + ir_Trotec.o # Common object files COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o $(PROTOCOLS) # Common dependencies COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \ - $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h + $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h \ + $(TEST_DIR)/IRsend_test.h # Common test dependencies COMMON_TEST_DEPS = $(COMMON_DEPS) $(TEST_DIR)/IRsend_test.h gc_decode.o : gc_decode.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -I$(TEST_DIR) -c gc_decode.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c gc_decode.cpp gc_decode : $(COMMON_OBJ) gc_decode.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ mode2_decode.o : mode2_decode.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -I$(TEST_DIR) -c mode2_decode.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c mode2_decode.cpp mode2_decode : $(COMMON_OBJ) mode2_decode.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ @@ -83,7 +87,6 @@ IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP82 IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP8266.h $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp - ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp @@ -97,10 +100,13 @@ ir_Sony.o : $(USER_DIR)/ir_Sony.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sony.cpp ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Samsung.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Samsung.cpp ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Kelvinator.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Kelvinator.cpp + +ir_Inax.o : $(USER_DIR)/ir_Inax.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Inax.cpp ir_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp @@ -112,19 +118,22 @@ ir_LG.o : $(USER_DIR)/ir_LG.h $(USER_DIR)/ir_LG.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_LG.cpp ir_Mitsubishi.o : $(USER_DIR)/ir_Mitsubishi.h $(USER_DIR)/ir_Mitsubishi.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Mitsubishi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Mitsubishi.cpp + +ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_MitsubishiHeavy.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_MitsubishiHeavy.cpp ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Fujitsu.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Fujitsu.cpp -ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sharp.cpp +ir_Sharp.o : $(USER_DIR)/ir_Sharp.h $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Sharp.cpp ir_RC5_RC6.o : $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_RC5_RC6.cpp ir_Panasonic.o : $(USER_DIR)/ir_Panasonic.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Panasonic.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Panasonic.cpp ir_Dish.o : $(USER_DIR)/ir_Dish.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Dish.cpp @@ -133,7 +142,7 @@ ir_Whynter.o : $(USER_DIR)/ir_Whynter.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whynter.cpp ir_Coolix.o : $(USER_DIR)/ir_Coolix.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Coolix.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Coolix.cpp ir_Aiwa.o : $(USER_DIR)/ir_Aiwa.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Aiwa.cpp @@ -145,10 +154,10 @@ ir_Sanyo.o : $(USER_DIR)/ir_Sanyo.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Sanyo.cpp ir_Daikin.o : $(USER_DIR)/ir_Daikin.cpp $(USER_DIR)/ir_Daikin.h $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Daikin.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Daikin.cpp ir_Gree.o : $(USER_DIR)/ir_Gree.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Gree.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Gree.cpp ir_Pronto.o : $(USER_DIR)/ir_Pronto.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pronto.cpp @@ -157,10 +166,10 @@ ir_Nikai.o : $(USER_DIR)/ir_Nikai.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Nikai.cpp ir_Toshiba.o : $(USER_DIR)/ir_Toshiba.h $(USER_DIR)/ir_Toshiba.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Toshiba.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Toshiba.cpp ir_Midea.o : $(USER_DIR)/ir_Midea.cpp $(COMMON_DEPS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Midea.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Midea.cpp ir_Magiquest.o : $(USER_DIR)/ir_Magiquest.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Magiquest.cpp @@ -172,16 +181,16 @@ ir_Carrier.o : $(USER_DIR)/ir_Carrier.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Carrier.cpp ir_Haier.o : $(USER_DIR)/ir_Haier.cpp $(USER_DIR)/ir_Haier.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Haier.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Haier.cpp ir_Hitachi.o : $(USER_DIR)/ir_Hitachi.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Hitachi.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Hitachi.cpp ir_GICable.o : $(USER_DIR)/ir_GICable.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_GICable.cpp ir_Whirlpool.o : $(USER_DIR)/ir_Whirlpool.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Whirlpool.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Whirlpool.cpp ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp @@ -196,10 +205,22 @@ ir_MWM.o : $(USER_DIR)/ir_MWM.cpp $(USER_DIR)/ir_RC5_RC6.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_MWM.cpp ir_Vestel.o : $(USER_DIR)/ir_Vestel.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Vestel.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Vestel.cpp ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Teco.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Teco.cpp ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(USER_DIR)/ir_Tcl.h $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Tcl.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Tcl.cpp + +ir_Trotec.o : $(USER_DIR)/ir_Trotec.cpp $(USER_DIR)/ir_Trotec.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Trotec.cpp + +ir_Lego.o : $(USER_DIR)/ir_Lego.cpp $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp + +ir_Argo.o : $(USER_DIR)/ir_Argo.cpp $(USER_DIR)/ir_Argo.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Argo.cpp + +ir_Goodweather.o : $(USER_DIR)/ir_Goodweather.cpp $(USER_DIR)/ir_Goodweather.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Goodweather.cpp diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py index b23cdb4..8a2e457 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py @@ -8,7 +8,7 @@ import argparse import sys -class RawIRMessage(object): +class RawIRMessage(): """Basic analyse functions & structure for raw IR messages.""" # pylint: disable=too-many-instance-attributes @@ -62,7 +62,7 @@ class RawIRMessage(object): def _usec_compare(self, seen, expected): """Compare two usec values and see if they match within a subtractive margin.""" - return seen <= expected and seen > expected - self.margin + return expected - self.margin < seen <= expected def _usec_compares(self, usecs, expecteds): """Compare a usec value to a list of values and return True @@ -160,7 +160,7 @@ class RawIRMessage(object): def avg_list(items): """Return the average of a list of numbers.""" if items: - return sum(items) / len(items) + return int(sum(items) / len(items)) return 0 @@ -293,7 +293,7 @@ def decode_data(message, defines, function_code, output=sys.stdout): output.write("kHdrSpace+") function_code.append(" space(kHdrSpace);") elif message.is_bit_mark(usec) and count % 2: - if state != "HS" and state != "BS": + if state not in ("HS", "BS"): output.write("kBitMark(UNEXPECTED)") state = "BM" elif message.is_zero_space(usec): diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py index 681ff55..5d5504f 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data_test.py @@ -1,6 +1,6 @@ -#!/usr/bin/python +#!/usr/bin/python3 """Unit tests for auto_analyse_raw_data.py""" -import StringIO +from io import StringIO import unittest import auto_analyse_raw_data as analyse @@ -12,7 +12,7 @@ class TestRawIRMessage(unittest.TestCase): def test_display_binary(self): """Test the display_binary() method.""" - output = StringIO.StringIO() + output = StringIO() message = analyse.RawIRMessage(100, [8000, 4000, 500, 500, 500], output, False) self.assertEqual(output.getvalue(), '') @@ -52,8 +52,8 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_dump_constants_simple(self): """Simple tests for the dump_constants() function.""" - ignore = StringIO.StringIO() - output = StringIO.StringIO() + ignore = StringIO() + output = StringIO() defs = [] message = analyse.RawIRMessage(200, [ 7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, @@ -75,8 +75,8 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_dump_constants_aircon(self): """More complex tests for the dump_constants() function.""" - ignore = StringIO.StringIO() - output = StringIO.StringIO() + ignore = StringIO() + output = StringIO() defs = [] message = analyse.RawIRMessage(200, [ 9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, @@ -111,7 +111,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): self.assertEqual(analyse.convert_rawdata("0"), [0]) with self.assertRaises(ValueError) as context: analyse.convert_rawdata("") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data contains a non-numeric value of ''.") # Single parenthesis @@ -132,13 +132,13 @@ class TestAutoAnalyseRawData(unittest.TestCase): # Bad parentheses with self.assertRaises(ValueError) as context: analyse.convert_rawdata("}10{") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data not parsible due to parentheses placement.") # Non base-10 values with self.assertRaises(ValueError) as context: analyse.convert_rawdata("10, 20, foo, bar, 30") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data contains a non-numeric value of 'foo'.") # A messy usual "good" case. @@ -155,7 +155,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): """Tests for the parse_and_report() function.""" # Without code generation. - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[139] = {9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, 556, 650, 584, 626, 560, 644, 580, 628, 1680, @@ -210,7 +210,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): 'Total Nr. of suspected bits: 67\n') # With code generation. - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[37] = {7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494, @@ -294,7 +294,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): """Tests for unusual Space Gaps in parse_and_report() function.""" # Tests for unusual Gaps. (Issue #482) - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[272] = {3485, 3512, 864, 864, 864, 2620, 864, 864, 864, 2620, 864, 2620, 864, 2620, 864, 2620, 864, 2620, 864, 864, @@ -466,7 +466,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_reduce_list(self): """Tests for the reduce_list method.""" - ignore = StringIO.StringIO() + ignore = StringIO() message = analyse.RawIRMessage(200, [ 7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494, 3978, 494, 520, 494, 520, 494, 520, 494, diff --git a/lib/IRremoteESP8266/tools/scrape_supported_devices.py b/lib/IRremoteESP8266/tools/scrape_supported_devices.py new file mode 100644 index 0000000..8953490 --- /dev/null +++ b/lib/IRremoteESP8266/tools/scrape_supported_devices.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +"""Generate SupportedProtocols.md by scraping source code files""" +import pathlib +import argparse +import sys +import re +import time + +CODE_URL = "https://github.com/markszabo/IRremoteESP8266/blob/master/src/ir_" + +BRAND_MODEL = re.compile(r"Brand: *(?P.+), *Model: *(?P.+)") +ENUMS = re.compile(r"enum \w+ {(.+?)};", re.DOTALL) +ENUM_ENTRY = re.compile(r"^\s+(\w+)", re.MULTILINE) +DECODED_PROTOCOLS = re.compile(r".*results->decode_type *=.*?(\w+);") +AC_FN = re.compile(r"ir_(.+).h") + +ALL_FN = re.compile(r"ir_(.+).(h|cpp)") + +EXCLUDED_PROTOCOLS = ["UNKNOWN", "UNUSED", "kLastDecodeType"] + +MARKDOWN_HEADER = """""".format(time.asctime()) + + +def getallprotocols(): + """Return all protocls configured in IRremoteESP8266.h + """ + irremote = ARGS.directory / "IRremoteESP8266.h" + enums = getenums(irremote) + if not enums: + errorexit("Error getting ENUMS from IRremoteESP8266.h") + return enums + + +def getdecodedprotocols(): + """All protocols that include decoding support""" + ret = set() + for path in ARGS.directory.iterdir(): + if path.suffix != ".cpp": + continue + matches = DECODED_PROTOCOLS.finditer(path.open().read()) + for match in matches: + protocol = match.group(1) + if protocol not in EXCLUDED_PROTOCOLS: + ret.add(protocol) + return ret + + +def getallacs(): + """All supported A/C codes""" + ret = {} + for path in ARGS.directory.iterdir(): + match = AC_FN.match(path.name) + if not match: + continue + acprotocol = match.group(1) + rawmodels = getenums(path) + models = set() + for model in rawmodels: + model = model.upper() + model = model.replace("K{}".format(acprotocol.upper()), "") + if model and model not in EXCLUDED_PROTOCOLS: + models.add(model) + ret[acprotocol] = models + return ret + + +def getalldevices(): + """All devices and associated branding and model information (if available) + """ + allcodes = {} + fnnomatch = set() + fnmatch = set() + for path in ARGS.directory.iterdir(): + match = ALL_FN.match(path.name) + if not match: + continue + supports = extractsupports(path) + if supports: + fnmatch.add(path.stem) + else: + fnnomatch.add(path.stem) + protocol = match.group(1) + for brand, model in supports: + protocolbrand = (protocol, brand) + allcodes[protocolbrand] = allcodes.get(protocolbrand, list()) + [model] + nosupports = fnnomatch - fnmatch + for fnprotocol in nosupports: + allcodes[(fnprotocol[3:], "Unknown")] = [] + return allcodes, nosupports + + +def getenums(path): + """Returns the keys for the first enum type in path + """ + enums = ENUMS.search(path.open().read()) + ret = set() + if not enums: + return ret + for enum in ENUM_ENTRY.finditer(enums.group(1)): + enum = enum.group(1) + if enum in EXCLUDED_PROTOCOLS: + continue + ret.add(enum) + return ret + + +ARGS = None + + +def initargs(): + """Init the command line arguments""" + global ARGS # pylint: disable=global-statement + parser = argparse.ArgumentParser() + parser.add_argument( + "-s", + "--stdout", + help="output to stdout rather than SupportedProtocols.md", + action="store_true", + ) + parser.add_argument("-v", + "--verbose", + help="increase output verbosity", + action="store_true") + parser.add_argument( + "-a", + "--alert", + help="alert if a file does not have a supports section", + action="store_true", + ) + parser.add_argument( + "directory", + nargs="?", + help="directory of the source git checkout", + default=None, + ) + ARGS = parser.parse_args() + if ARGS.directory is None: + src = pathlib.Path("../src") + if not src.is_dir(): + src = pathlib.Path("./src") + else: + src = pathlib.Path(ARGS.directory) / "src" + if not src.is_dir(): + errorexit("Directory not valid: {}".format(str(src))) + ARGS.directory = src + return ARGS + + +def errorexit(msg): + """Print an error and exit on critical error""" + sys.stderr.write("{}\n".format(msg)) + sys.exit(1) + + +def extractsupports(path): + """Extract all of the Supports: sections and associated brands and models + """ + supports = [] + insupports = False + for line in path.open(): + if not line.startswith("//"): + continue + line = line[2:].strip() + if line == "Supports:": + insupports = True + continue + if insupports: + match = BRAND_MODEL.match(line) + if match: + supports.append((match.group("brand"), match.group("model"))) + else: + insupports = False + continue + return supports + + +def makeurl(txt, path): + """Make a Markup URL from given filename""" + return "[{}]({})".format(txt, CODE_URL + path) + + +def outputprotocols(fout, protocols): + """For a given protocol set, sort and output the markdown""" + protocols = list(protocols) + protocols.sort() + for protocol in protocols: + fout.write("- {}\n".format(protocol)) + + +def main(): + """Standard boiler plate""" + initargs() + if ARGS.verbose: + print("Looking for files in: {}".format(str(ARGS.directory.resolve()))) + if ARGS.stdout: + fout = sys.stdout + else: + foutpath = ARGS.directory / "../SupportedProtocols.md" + foutpath = foutpath.resolve() + if ARGS.verbose: + print("Output path: {}".format(str(foutpath))) + fout = foutpath.open("w") + decodedprotocols = getdecodedprotocols() + sendonly = getallprotocols() - decodedprotocols + allacs = getallacs() + + allcodes, nosupports = getalldevices() + allbrands = list(allcodes.keys()) + allbrands.sort() + + fout.write(MARKDOWN_HEADER) + fout.write("\n# IR Protocols supported by this library\n\n") + fout.write( + "| Protocol | Brand | Model | A/C Model | Detailed A/C Support |\n") + fout.write("| --- | --- | --- | --- | --- |\n") + + for protocolbrand in allbrands: + protocol, brand = protocolbrand + codes = allcodes[protocolbrand] + codes.sort() + if protocol in allacs: + acmodels = list(allacs[protocol]) + acmodels.sort() + acsupport = "Yes" + brand = makeurl(brand, protocol + ".h") + else: + acmodels = [] + acsupport = "-" + + fout.write("| {} | **{}** | {} | {} | {} |\n".format( + makeurl(protocol, protocol + ".cpp"), + brand, + "
".join(codes), + "
".join(acmodels), + acsupport, + )) + + fout.write("\n\n## Send only protocols:\n\n") + outputprotocols(fout, sendonly) + + fout.write("\n\n## Send & decodable protocols:\n\n") + outputprotocols(fout, decodedprotocols) + + if ARGS.alert: + nosupports = list(nosupports) + nosupports.sort() + print("The following files had no supports section:") + for path in nosupports: + print("\t{}".format(path)) + + +if __name__ == "__main__": + main()