From 46f779cb3c785813276aabafa098d29db36abcc8 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Tue, 16 Apr 2019 09:23:58 +0300 Subject: [PATCH 01/15] [IR TX RX] Library updates - New Protocols --- lib/IRremoteESP8266/.travis.yml | 1 + .../examples/ControlSamsungAC/platformio.ini | 2 + .../examples/IRGCSendDemo/platformio.ini | 2 + .../examples/IRGCTCPServer/platformio.ini | 2 + .../examples/IRMQTTServer/IRMQTTServer.ino | 16 + .../examples/IRMQTTServer/platformio.ini | 3 + .../examples/IRServer/platformio.ini | 2 + .../examples/IRrecvDemo/platformio.ini | 2 + .../examples/IRrecvDump/platformio.ini | 2 + .../examples/IRrecvDumpV2/IRrecvDumpV2.ino | 13 + .../examples/IRrecvDumpV2/platformio.ini | 2 + .../examples/IRsendDemo/platformio.ini | 2 + .../examples/IRsendProntoDemo/platformio.ini | 2 + .../JVCPanasonicSendDemo/platformio.ini | 2 + .../examples/LGACSend/platformio.ini | 2 + .../examples/TurnOnArgoAC/platformio.ini | 2 + .../examples/TurnOnDaikinAC/platformio.ini | 2 + .../examples/TurnOnFujitsuAC/platformio.ini | 2 + .../TurnOnKelvinatorAC/platformio.ini | 2 + .../TurnOnMitsubishiAC/platformio.ini | 2 + .../TurnOnMitsubishiHeavyAc.ino | 72 ++ .../TurnOnMitsubishiHeavyAc/platformio.ini | 19 + .../examples/TurnOnPanasonicAC/platformio.ini | 19 + .../examples/TurnOnToshibaAC/platformio.ini | 2 + .../examples/TurnOnTrotecAC/platformio.ini | 2 + lib/IRremoteESP8266/platformio.ini | 3 + lib/IRremoteESP8266/src/IRac.cpp | 879 ++++++++++++++ lib/IRremoteESP8266/src/IRac.h | 206 ++++ lib/IRremoteESP8266/src/IRrecv.cpp | 6 + lib/IRremoteESP8266/src/IRrecv.h | 4 + lib/IRremoteESP8266/src/IRremoteESP8266.h | 15 +- lib/IRremoteESP8266/src/IRsend.h | 56 +- lib/IRremoteESP8266/src/IRtimer.cpp | 2 +- lib/IRremoteESP8266/src/IRutils.cpp | 32 +- lib/IRremoteESP8266/src/ir_Argo.cpp | 34 + lib/IRremoteESP8266/src/ir_Argo.h | 14 +- lib/IRremoteESP8266/src/ir_Coolix.cpp | 32 + lib/IRremoteESP8266/src/ir_Coolix.h | 22 +- lib/IRremoteESP8266/src/ir_Daikin.cpp | 86 +- lib/IRremoteESP8266/src/ir_Daikin.h | 20 +- lib/IRremoteESP8266/src/ir_Fujitsu.cpp | 34 + lib/IRremoteESP8266/src/ir_Fujitsu.h | 11 +- lib/IRremoteESP8266/src/ir_Gree.cpp | 51 + lib/IRremoteESP8266/src/ir_Gree.h | 15 +- lib/IRremoteESP8266/src/ir_Haier.cpp | 100 ++ lib/IRremoteESP8266/src/ir_Haier.h | 22 +- lib/IRremoteESP8266/src/ir_Hitachi.cpp | 36 +- lib/IRremoteESP8266/src/ir_Hitachi.h | 11 +- lib/IRremoteESP8266/src/ir_Kelvinator.cpp | 17 + lib/IRremoteESP8266/src/ir_Kelvinator.h | 10 +- lib/IRremoteESP8266/src/ir_Midea.cpp | 33 + lib/IRremoteESP8266/src/ir_Midea.h | 9 +- lib/IRremoteESP8266/src/ir_Mitsubishi.cpp | 46 + lib/IRremoteESP8266/src/ir_Mitsubishi.h | 12 +- .../src/ir_MitsubishiHeavy.cpp | 1014 +++++++++++++++++ lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h | 264 +++++ lib/IRremoteESP8266/src/ir_Panasonic.cpp | 67 ++ lib/IRremoteESP8266/src/ir_Panasonic.h | 11 +- lib/IRremoteESP8266/src/ir_Samsung.cpp | 60 +- lib/IRremoteESP8266/src/ir_Samsung.h | 13 +- lib/IRremoteESP8266/src/ir_Tcl.cpp | 32 + lib/IRremoteESP8266/src/ir_Tcl.h | 11 +- lib/IRremoteESP8266/src/ir_Teco.cpp | 32 + lib/IRremoteESP8266/src/ir_Teco.h | 11 +- lib/IRremoteESP8266/src/ir_Toshiba.cpp | 33 + lib/IRremoteESP8266/src/ir_Toshiba.h | 9 +- lib/IRremoteESP8266/src/ir_Trotec.cpp | 117 +- lib/IRremoteESP8266/src/ir_Trotec.h | 35 +- lib/IRremoteESP8266/src/ir_Vestel.cpp | 47 +- lib/IRremoteESP8266/src/ir_Vestel.h | 13 +- lib/IRremoteESP8266/src/ir_Whirlpool.cpp | 32 + lib/IRremoteESP8266/src/ir_Whirlpool.h | 10 +- lib/IRremoteESP8266/test/IRac_test.cpp | 757 ++++++++++++ lib/IRremoteESP8266/test/IRsend_test.h | 2 +- lib/IRremoteESP8266/test/Makefile | 158 +-- .../test/ir_MitsubishiHeavy_test.cpp | 851 ++++++++++++++ lib/IRremoteESP8266/tools/Makefile | 49 +- 77 files changed, 5429 insertions(+), 196 deletions(-) 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/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_MitsubishiHeavy.cpp create mode 100644 lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h create mode 100644 lib/IRremoteESP8266/test/IRac_test.cpp create mode 100644 lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp diff --git a/lib/IRremoteESP8266/.travis.yml b/lib/IRremoteESP8266/.travis.yml index e34084ba9a..e9b30d1fb1 100644 --- a/lib/IRremoteESP8266/.travis.yml +++ b/lib/IRremoteESP8266/.travis.yml @@ -42,6 +42,7 @@ script: - 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 # Also check the tools programs compile. - (cd tools; make all) diff --git a/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/ControlSamsungAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRGCSendDemo/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRGCTCPServer/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino index 145ef65963..80e01bc4d2 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -624,6 +624,8 @@ void handleRoot() { "" "" // Default "" + "" + "" "" "" "" @@ -721,6 +723,12 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, 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; @@ -857,6 +865,14 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, 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)); diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini index 27b44ddca5..243822c5f1 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini @@ -4,6 +4,7 @@ src_dir=. [common] build_flags = -DMQTT_MAX_PACKET_SIZE=512 +lib_ldf_mode = chain+ lib_deps_builtin = lib_deps_external = PubSubClient @@ -13,6 +14,7 @@ lib_deps_external = platform = espressif8266 framework = arduino board = nodemcuv2 +lib_ldf_mode = ${common.lib_ldf_mode} build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} @@ -22,6 +24,7 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRServer/platformio.ini b/lib/IRremoteESP8266/examples/IRServer/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRServer/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDemo/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDump/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino index 9e8c3e199e..9d3808d17d 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,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); diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRsendDemo/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRsendProntoDemo/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini +++ b/lib/IRremoteESP8266/examples/JVCPanasonicSendDemo/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266/examples/LGACSend/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/LGACSend/platformio.ini +++ b/lib/IRremoteESP8266/examples/LGACSend/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnArgoAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnDaikinAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino new file mode 100644 index 0000000000..2ad2d7bc30 --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino @@ -0,0 +1,72 @@ +/* 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. +*/ +#ifndef UNIT_TEST +#include +#endif +#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 0000000000..ec84f22f3b --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnMitsubishiHeavyAc/platformio.ini @@ -0,0 +1,19 @@ +[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} diff --git a/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/platformio.ini new file mode 100644 index 0000000000..ec84f22f3b --- /dev/null +++ b/lib/IRremoteESP8266/examples/TurnOnPanasonicAC/platformio.ini @@ -0,0 +1,19 @@ +[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} diff --git a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnToshibaAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini index eeb8d1f2eb..ec84f22f3b 100644 --- a/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini +++ b/lib/IRremoteESP8266/examples/TurnOnTrotecAC/platformio.ini @@ -6,11 +6,13 @@ 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} diff --git a/lib/IRremoteESP8266/platformio.ini b/lib/IRremoteESP8266/platformio.ini index 63c3781e11..b6020c165c 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,7 @@ 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} diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp new file mode 100644 index 0000000000..6c071a6cd7 --- /dev/null +++ b/lib/IRremoteESP8266/src/IRac.cpp @@ -0,0 +1,879 @@ +// 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 + +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRremoteESP8266.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_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; } + +#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); + switch (mode) { + case stdAc::opmode_t::kCool: + ac->setCoolMode(kArgoCoolOn); + break; + case stdAc::opmode_t::kHeat: + ac->setHeatMode(kArgoHeatOn); + break; + case stdAc::opmode_t::kDry: + ac->setCoolMode(kArgoCoolHum); + break; + default: // No idea how to set Fan mode. + ac->setCoolMode(kArgoCoolAuto); + } + 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_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) { + ac->setModel(model); + 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 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. + if (!on) ac->off(); + ac->send(); +} +#endif // SEND_FUJITSU_AC + +#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 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 sendOnOffHack) { + if (sendOnOffHack) { + // Use a hack to for the unit on or off. + // See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 + if (on) + ac->sendOn(); + else + ac->sendOff(); + } + 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); + if (turbo) ac->setFan(kSamsungAcFanTurbo); + // 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_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 degreees 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 uint16_t model, + const bool on, 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 = (degrees - 32.0) * (5.0 / 9.0); + + // 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_DAIKIN2 +#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); + break; + } +#endif // SEND_FUJITSU_AC +#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_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. +} diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h new file mode 100644 index 0000000000..fcc8c1bfec --- /dev/null +++ b/lib/IRremoteESP8266/src/IRac.h @@ -0,0 +1,206 @@ +#ifndef IRAC_H_ +#define IRAC_H_ + +// Copyright 2019 David Conran + +#ifndef UNIT_TEST +#include +#endif +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "ir_Argo.h" +#include "ir_Coolix.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.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_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); + bool sendAc(const decode_type_t vendor, const uint16_t model, + const bool on, 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); +#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_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); +#endif // SEND_FUJITSU_AC +#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 sendOnOffHack = true); +#endif // SEND_SAMSUNG_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 +}; +#endif // IRAC_H_ diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp index 6ee56bf2ce..9a52151ca4 100644 --- a/lib/IRremoteESP8266/src/IRrecv.cpp +++ b/lib/IRremoteESP8266/src/IRrecv.cpp @@ -513,6 +513,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { 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_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index fbc896c3f1..988367ecba 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -181,6 +181,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, diff --git a/lib/IRremoteESP8266/src/IRremoteESP8266.h b/lib/IRremoteESP8266/src/IRremoteESP8266.h index bfdff5c481..cd7c1c37e9 100644 --- a/lib/IRremoteESP8266/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266/src/IRremoteESP8266.h @@ -219,13 +219,16 @@ #define DECODE_LEGOPF true #define SEND_LEGOPF true +#define DECODE_MITSUBISHIHEAVY true +#define SEND_MITSUBISHIHEAVY true + #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ - DECODE_VESTEL_AC || DECODE_TCL112AC) + DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -303,6 +306,8 @@ enum decode_type_t { SAMSUNG36, TCL112AC, LEGOPF, + MITSUBISHI_HEAVY_88, + MITSUBISHI_HEAVY_152, // 60 }; // Message lengths & required repeat values @@ -375,6 +380,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; @@ -502,6 +513,7 @@ 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 @@ -509,5 +521,6 @@ const uint8_t kVestelAcBits = 56; // See: https://github.com/markszabo/IRremoteESP8266/issues/667 #define F(x) x #endif // F +#endif // UNIT_TEST #endif // IRREMOTEESP8266_H_ diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index 28fe75c5d4..c20b514178 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -31,6 +31,46 @@ const uint16_t kMaxAccurateUsecDelay = 16383; // Usecs to wait between messages we don't know the proper gap time. const uint32_t kDefaultMessageGap = 100000; + +namespace stdAc { + enum class opmode_t { + 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, + }; +}; // namespace stdAc + // Classes class IRsend { public: @@ -99,9 +139,9 @@ class IRsend { 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, @@ -191,6 +231,16 @@ class IRsend { uint16_t nbytes = kMitsubishiACStateLength, 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); diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp index 029637cbb9..b3136abca1 100644 --- a/lib/IRremoteESP8266/src/IRtimer.cpp +++ b/lib/IRremoteESP8266/src/IRtimer.cpp @@ -7,7 +7,7 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. -extern uint32_t _IRtimer_unittest_now; +uint32_t _IRtimer_unittest_now = 0; #endif // UNIT_TEST // This class performs a simple time in useconds since instantiated. diff --git a/lib/IRremoteESP8266/src/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp index 03fac833c9..85a4e34425 100644 --- a/lib/IRremoteESP8266/src/IRutils.cpp +++ b/lib/IRremoteESP8266/src/IRutils.cpp @@ -192,6 +192,12 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case 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 = F("MWM"); break; @@ -296,6 +302,8 @@ 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: @@ -353,15 +361,16 @@ std::string resultToSourceCode(const decode_results *results) { } 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); @@ -379,7 +388,7 @@ std::string resultToSourceCode(const decode_results *results) { 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 += F("};\n"); #endif // DECODE_AC @@ -424,15 +433,16 @@ std::string resultToTimingInfo(const decode_results *results) { if (i % 2 == 0) 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 += F("\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 += F("\n"); + output += '\n'; return output; } @@ -470,7 +480,7 @@ std::string resultToHumanReadableBasic(const decode_results *results) { // Show Encoding standard output += F("Encoding : "); output += typeToString(results->decode_type, results->repeat); - output += F("\n"); + output += '\n'; // Show Code & length output += F("Code : "); diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp index 8246b2615f..d6711acd38 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266/src/ir_Argo.cpp @@ -228,3 +228,37 @@ void IRArgoAC::setRoomTemp(uint8_t temp) { argo[3] += temp << 5; // Append to bit 5,6,7 argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 } + +// 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; + } +} diff --git a/lib/IRremoteESP8266/src/ir_Argo.h b/lib/IRremoteESP8266/src/ir_Argo.h index 0174d619eb..883c2ddfdb 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -6,6 +6,10 @@ #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + // ARGO Ulisse DCI @@ -55,7 +59,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 @@ -118,13 +122,19 @@ class IRArgoAC { void setRoomTemp(uint8_t temp); uint8_t* getRaw(); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); +#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 // Attributes uint8_t set_temp; diff --git a/lib/IRremoteESP8266/src/ir_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp index f7b5f62a34..2659a1d881 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266/src/ir_Coolix.cpp @@ -305,6 +305,38 @@ 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 the internal state into a human readable string. #ifdef ARDUINO String IRCoolixAC::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index fdc060aa38..d85db98d7d 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -14,6 +14,9 @@ #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 @@ -30,11 +33,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 @@ -120,17 +123,22 @@ class IRCoolixAC { bool getZoneFollow(); uint32_t getRaw(); void setRaw(const uint32_t new_code); - + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO 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 4a18d5b3d5..2628e37b1a 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.cpp +++ b/lib/IRremoteESP8266/src/ir_Daikin.cpp @@ -16,6 +16,9 @@ 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 @@ -601,8 +604,41 @@ void IRDaikinESP::setCommand(uint32_t value) { setCurrentTime(value); } -#if DECODE_DAIKIN +// 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(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; + } +} + +#if DECODE_DAIKIN void addbit(bool val, unsigned char data[]) { uint8_t curbit = data[kDaikinCurBit]; uint8_t curindex = data[kDaikinCurIndex]; @@ -1182,6 +1218,54 @@ 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) { + 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 IRDaikin2::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 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 the internal state into a human readable string. #ifdef ARDUINO String IRDaikin2::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index b629205e05..7eddd849d2 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -12,6 +12,9 @@ #include "IRrecv.h" #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Option to disable the additional Daikin debug info to conserve memory #define DAIKIN_DEBUG false @@ -229,6 +232,8 @@ class IRDaikinESP { void setCommand(uint32_t value); static bool validChecksum(uint8_t state[], const uint16_t length = kDaikinStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); static String renderTime(uint16_t timemins); @@ -236,8 +241,13 @@ class IRDaikinESP { 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 daikin[kDaikinStateLength]; void stateReset(); @@ -245,7 +255,6 @@ class IRDaikinESP { 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; }; // Class to emulate a Daikin ARC477A1 remote. @@ -317,6 +326,9 @@ class IRDaikin2 { void setCommand(uint32_t value); static bool validChecksum(uint8_t state[], const uint16_t length = kDaikin2StateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); #ifdef ARDUINO String toString(); static String renderTime(uint16_t timemins); @@ -324,15 +336,19 @@ class IRDaikin2 { 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(); - IRsend _irsend; }; #endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp index 611b0d22f0..de1b97e87f 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp @@ -281,6 +281,7 @@ void IRFujitsuAC::setMode(uint8_t mode) { } uint8_t IRFujitsuAC::getMode() { return _mode; } + // Set the requested swing operation mode of the a/c unit. void IRFujitsuAC::setSwing(uint8_t swingMode) { switch (_model) { @@ -319,6 +320,39 @@ bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { 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 the internal state into a human readable string. #ifdef ARDUINO String IRFujitsuAC::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index d5dc837a46..78a4f89517 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -13,6 +13,9 @@ #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 @@ -99,15 +102,21 @@ class IRFujitsuAC { uint8_t getStateLength(); static bool validChecksum(uint8_t* state, uint16_t length); bool getPower(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#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; diff --git a/lib/IRremoteESP8266/src/ir_Gree.cpp b/lib/IRremoteESP8266/src/ir_Gree.cpp index 4d9c57d864..949342dbd2 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266/src/ir_Gree.cpp @@ -305,6 +305,57 @@ uint8_t IRGreeAC::getSwingVerticalPosition() { return remote_state[4] & kGreeSwingPosMask; } + +// 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 the internal state into a human readable string. #ifdef ARDUINO String IRGreeAC::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index 489cefbf09..c3c5916dc4 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // GGGG RRRRRR EEEEEEE EEEEEEE // GG GG RR RR EE EE @@ -44,6 +47,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; @@ -108,7 +113,9 @@ class IRGreeAC { void setSwingVertical(const bool automatic, const uint8_t position); bool getSwingVerticalAuto(); uint8_t getSwingVerticalPosition(); - + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); uint8_t* getRaw(); void setRaw(uint8_t new_code[]); static bool validChecksum(const uint8_t state[], @@ -118,13 +125,17 @@ class IRGreeAC { #else std::string toString(); #endif +#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; }; #endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp index 768548c264..2572aa2859 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -312,6 +312,55 @@ std::string IRHaierAC::timeToString(const uint16_t nr_mins) { 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 the internal state into a human readable string. #ifdef ARDUINO String IRHaierAC::toString() { @@ -636,6 +685,57 @@ 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 the internal state into a human readable string. #ifdef ARDUINO String IRHaierACYRW02::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index f4a0d32959..8f7b351965 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // HH HH AAA IIIII EEEEEEE RRRRRR // HH HH AAAAA III EE RR RR @@ -223,6 +226,10 @@ class IRHaierAC { void setRaw(uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); + #ifdef ARDUINO String toString(); static String timeToString(const uint16_t nr_mins); @@ -230,14 +237,18 @@ class IRHaierAC { 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(); static uint16_t getTime(const uint8_t ptr[]); static void setTime(uint8_t ptr[], const uint16_t nr_mins); - IRsend _irsend; }; class IRHaierACYRW02 { @@ -281,17 +292,24 @@ class IRHaierACYRW02 { void setRaw(uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACYRW02StateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t position); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint8_t remote_state[kHaierACYRW02StateLength]; void stateReset(); void checksum(); - IRsend _irsend; }; #endif // IR_HAIER_H_ diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp index a7b9b670a9..b88189f4a0 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266/src/ir_Hitachi.cpp @@ -106,7 +106,7 @@ void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, } #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 @@ -257,6 +257,40 @@ 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 the internal state into a human readable string. #ifdef ARDUINO String IRHitachiAc::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.h b/lib/IRremoteESP8266/src/ir_Hitachi.h index efc775ebc5..532717447d 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint8_t kHitachiAcAuto = 2; @@ -59,17 +62,23 @@ class IRHitachiAc { const uint16_t length = kHitachiAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kHitachiAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#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_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp index 473beb49af..c69f4cb8ad 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp @@ -19,6 +19,7 @@ #ifndef ARDUINO #include #endif +#include "IRac.h" #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" @@ -347,6 +348,22 @@ bool IRKelvinatorAC::getTurbo() { return ((remote_state[2] & kKelvinatorTurbo) != 0); } +// 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 the internal state into a human readable string. #ifdef ARDUINO String IRKelvinatorAC::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index 2cc4e5a370..ce830c70af 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // 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 @@ -163,18 +166,23 @@ class IRKelvinatorAC { const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2); static bool validChecksum(const uint8_t state[], const uint16_t length = kKelvinatorStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#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; }; #endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp index 2671116be3..8d5d9494fd 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266/src/ir_Midea.cpp @@ -257,6 +257,39 @@ void IRMideaAC::checksum() { 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 the internal state into a human readable string. #ifdef ARDUINO String IRMideaAC::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index c749b32121..ab14eb252e 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // MM MM IIIII DDDDD EEEEEEE AAA // MMM MMM III DD DD EE AAAAA @@ -85,6 +88,8 @@ class IRMideaAC { static bool validChecksum(const uint64_t state); void setSleep(const bool state); bool getSleep(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else @@ -93,11 +98,13 @@ class IRMideaAC { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint64_t remote_state; void checksum(); 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 266dc78f70..ca9bef5d98 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp @@ -615,6 +615,52 @@ void IRMitsubishiAC::setTimer(uint8_t timer) { remote_state[13] = timer & 0b111; } +// 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; + } +} + #ifdef ARDUINO String IRMitsubishiAC::timeToString(uint64_t time) { String result = ""; diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index 0d897bc263..c8dca5dbcd 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -12,6 +12,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // 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 @@ -89,13 +92,21 @@ class IRMitsubishiAC { void setStopClock(uint8_t clock); uint8_t getTimer(); void setTimer(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); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif #ifdef ARDUINO String timeToString(uint64_t time); #else @@ -103,7 +114,6 @@ class IRMitsubishiAC { #endif uint8_t remote_state[kMitsubishiACStateLength]; void checksum(); - IRsend _irsend; }; #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 0000000000..9048124d4b --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp @@ -0,0 +1,1014 @@ +// 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() { _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 the internal state into a human readable string. +#ifdef ARDUINO +String IRMitsubishiHeavy152Ac::toString(void) { + String result = ""; +#else +std::string IRMitsubishiHeavy152Ac::toString(void) { + std::string result = ""; +#endif // ARDUINO + 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() { _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 the internal state into a human readable string. +#ifdef ARDUINO +String IRMitsubishiHeavy88Ac::toString(void) { + String result = ""; +#else +std::string IRMitsubishiHeavy88Ac::toString(void) { + std::string result = ""; +#endif // ARDUINO + 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 0000000000..bcd85c6e01 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h @@ -0,0 +1,264 @@ +// Copyright 2019 David Conran + +#ifndef IR_MITSUBISHIHEAVY_H_ +#define IR_MITSUBISHIHEAVY_H_ + +#ifndef UNIT_TEST +#include +#else +#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); +#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); +#ifdef ARDUINO + String toString(void); +#else // ARDUINO + std::string toString(void); +#endif // ARDUINO +#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(); +}; + +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); +#ifdef ARDUINO + String toString(void); +#else // ARDUINO + std::string toString(void); +#endif // ARDUINO +#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(); +}; +#endif // IR_MITSUBISHIHEAVY_H_ diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp index b2f61e3c5f..47aa51c96d 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266/src/ir_Panasonic.cpp @@ -618,6 +618,73 @@ std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { 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 the internal state into a human readable string. #ifdef ARDUINO String IRPanasonicAc::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.h b/lib/IRremoteESP8266/src/ir_Panasonic.h index fc31dbcaf5..1a7b4e1144 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266/src/ir_Panasonic.h @@ -12,6 +12,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // 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 @@ -123,6 +126,10 @@ class IRPanasonicAc { const bool enable = true); void cancelOffTimer(); bool isOffTimerEnabled(); + 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); #ifdef ARDUINO String toString(); static String timeToString(const uint16_t mins_since_midnight); @@ -133,6 +140,9 @@ class IRPanasonicAc { #ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; #endif uint8_t remote_state[kPanasonicAcStateLength]; uint8_t _swingh; @@ -140,7 +150,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_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp index 27990acef5..7e54d17df6 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266/src/ir_Samsung.cpp @@ -301,7 +301,8 @@ bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, // // 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. @@ -385,9 +386,7 @@ void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { if (calcchecksum) checksum(); _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. @@ -407,6 +406,28 @@ 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() { @@ -556,6 +577,39 @@ void IRSamsungAc::setQuiet(const bool state) { } } +// 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 the internal state into a human readable string. #ifdef ARDUINO String IRSamsungAc::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Samsung.h b/lib/IRremoteESP8266/src/ir_Samsung.h index e8bcc4cec1..9df427c6ff 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -14,6 +14,9 @@ #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 @@ -66,6 +69,8 @@ class IRSamsungAc { 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); #endif // SEND_SAMSUNG_AC void begin(); void on(); @@ -93,17 +98,23 @@ class IRSamsungAc { const uint16_t length = kSamsungAcStateLength); static uint8_t calcChecksum(const uint8_t state[], const uint16_t length = kSamsungAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#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]; void checksum(const uint16_t length = kSamsungAcStateLength); - IRsend _irsend; }; #endif // IR_SAMSUNG_H_ diff --git a/lib/IRremoteESP8266/src/ir_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp index b6533d3340..79fb23cf1a 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.cpp +++ b/lib/IRremoteESP8266/src/ir_Tcl.cpp @@ -259,6 +259,38 @@ 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 the internal state into a human readable string. #ifdef ARDUINO String IRTcl112Ac::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index 4a2c1a3f7e..a1595451d1 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -10,6 +10,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants const uint16_t kTcl112AcHdrMark = 3000; @@ -80,17 +83,23 @@ class IRTcl112Ac { 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); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#ifndef UNIT_TEST private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif uint8_t remote_state[kTcl112AcStateLength]; void stateReset(); void checksum(const uint16_t length = kTcl112AcStateLength); - IRsend _irsend; }; #endif // IR_TCL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp index 9c3d87aa3e..779bf8f8ff 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.cpp +++ b/lib/IRremoteESP8266/src/ir_Teco.cpp @@ -136,6 +136,38 @@ 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 the internal state into a human readable string. #ifdef ARDUINO String IRTecoAc::toString(void) { diff --git a/lib/IRremoteESP8266/src/ir_Teco.h b/lib/IRremoteESP8266/src/ir_Teco.h index 9a020f9730..65a0050ae2 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.h +++ b/lib/IRremoteESP8266/src/ir_Teco.h @@ -10,6 +10,9 @@ #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 +123,22 @@ class IRTecoAc { uint64_t getRaw(void); void setRaw(const uint64_t new_code); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO 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 17ba06696a..a82a2fb24d 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266/src/ir_Toshiba.cpp @@ -233,6 +233,39 @@ void IRToshibaAC::setMode(uint8_t mode) { } } +// 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 the internal state into a human readable string. #ifdef ARDUINO String IRToshibaAC::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h index a6ea6c66e2..03b461add8 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -11,6 +11,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA // TTT OO OO SS HH HH III BB B AAAAA @@ -65,6 +68,8 @@ class IRToshibaAC { uint8_t* getRaw(); static bool validChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else @@ -73,13 +78,15 @@ class IRToshibaAC { #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 77d84eef41..b5c15e7fdb 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.cpp +++ b/lib/IRremoteESP8266/src/ir_Trotec.cpp @@ -1,6 +1,7 @@ // Copyright 2017 stufisher #include "ir_Trotec.h" +#include #include "IRremoteESP8266.h" #include "IRutils.h" @@ -41,24 +42,22 @@ void IRTrotecESP::begin() { _irsend.begin(); } #if SEND_TROTEC void IRTrotecESP::send(const uint16_t repeat) { checksum(); - _irsend.sendTrotec(trotec, kTrotecStateLength, repeat); + _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; + for (uint8_t i = 2; i < 8; i++) sum += remote_state[i]; + remote_state[8] = sum & 0xFF; } void IRTrotecESP::stateReset() { - for (uint8_t i = 2; i < kTrotecStateLength; i++) trotec[i] = 0x0; + for (uint8_t i = 2; i < kTrotecStateLength; i++) remote_state[i] = 0x0; - trotec[0] = kTrotecIntro1; - trotec[1] = kTrotecIntro2; + remote_state[0] = kTrotecIntro1; + remote_state[1] = kTrotecIntro2; setPower(false); setTemp(kTrotecDefTemp); @@ -68,60 +67,96 @@ void IRTrotecESP::stateReset() { uint8_t* IRTrotecESP::getRaw() { checksum(); - return trotec; + return remote_state; } -void IRTrotecESP::setPower(bool state) { - if (state) - trotec[2] |= (kTrotecOn << 3); +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() { 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() { return (remote_state[2] & 0b00110000) >> 4; } + +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::getMode() { return trotec[2] & 0x03; } +uint8_t IRTrotecESP::getMode() { return remote_state[2] & 0b00000011; } -void IRTrotecESP::setTemp(uint8_t temp) { - if (temp < kTrotecMinTemp) - temp = kTrotecMinTemp; - else if (temp > kTrotecMaxTemp) - temp = kTrotecMaxTemp; - - trotec[3] = (trotec[3] & 0x80) | (temp - kTrotecMinTemp); +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() { return trotec[3] & 0x7f; } +uint8_t IRTrotecESP::getTemp() { + return (remote_state[3] & 0b01111111) + kTrotecMinTemp; +} void IRTrotecESP::setSleep(bool sleep) { if (sleep) - trotec[3] |= (kTrotecSleepOn << 7); + remote_state[3] |= kTrotecSleepBit; else - trotec[3] &= ~(kTrotecSleepOn << 7); + remote_state[3] &= ~kTrotecSleepBit; } -bool IRTrotecESP::getSleep(void) { return trotec[3] & (kTrotecSleepOn << 7); } +bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } -void IRTrotecESP::setTimer(uint8_t timer) { - if (timer > kTrotecMaxTimer) timer = kTrotecMaxTimer; +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; +} - if (timer) { - trotec[5] |= (kTrotecTimerOn << 6); - trotec[6] = timer; - } else { - trotec[5] &= ~(kTrotecTimerOn << 6); - trotec[6] = 0; +uint8_t IRTrotecESP::getTimer() { 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; } } -uint8_t IRTrotecESP::getTimer() { return trotec[6]; } +// 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; + } +} diff --git a/lib/IRremoteESP8266/src/ir_Trotec.h b/lib/IRremoteESP8266/src/ir_Trotec.h index 846381b7e5..dfbc26c074 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -5,6 +5,9 @@ #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // Constants // Byte 0 @@ -19,8 +22,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 +33,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 +51,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 { @@ -62,31 +62,38 @@ class IRTrotecESP { #endif // SEND_TROTEC void begin(); - void setPower(bool state); - uint8_t getPower(); + void setPower(const bool state); + bool getPower(); - void setTemp(uint8_t temp); + void setTemp(const uint8_t celsius); uint8_t getTemp(); - void setSpeed(uint8_t fan); + void setSpeed(const uint8_t fan); uint8_t getSpeed(); uint8_t getMode(); - void setMode(uint8_t mode); + void setMode(const uint8_t mode); bool getSleep(); void setSleep(bool sleep); uint8_t getTimer(); - void setTimer(uint8_t timer); + void setTimer(const uint8_t timer); uint8_t* getRaw(); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); +#ifndef UNIT_TEST + private: - uint8_t trotec[kTrotecStateLength]; + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kTrotecStateLength]; void stateReset(); void checksum(); - IRsend _irsend; }; #endif // IR_TROTEC_H_ diff --git a/lib/IRremoteESP8266/src/ir_Vestel.cpp b/lib/IRremoteESP8266/src/ir_Vestel.cpp index 34ed993be9..1fbb822cf6 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.cpp +++ b/lib/IRremoteESP8266/src/ir_Vestel.cpp @@ -58,8 +58,8 @@ IRVestelAc::IRVestelAc(uint16_t pin) : _irsend(pin) { stateReset(); } // Reset the state of the remote to a known good state/sequence. void IRVestelAc::stateReset() { // 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; } @@ -93,13 +93,19 @@ void IRVestelAc::setRaw(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. @@ -398,6 +404,39 @@ bool IRVestelAc::isTimeCommand() { 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 the internal state into a human readable string. #ifdef ARDUINO String IRVestelAc::toString() { diff --git a/lib/IRremoteESP8266/src/ir_Vestel.h b/lib/IRremoteESP8266/src/ir_Vestel.h index 5c93ead3ef..ab04e8b35a 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.h +++ b/lib/IRremoteESP8266/src/ir_Vestel.h @@ -13,6 +13,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL // VV VV EE S TT EE LL @@ -100,6 +103,8 @@ 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: @@ -149,18 +154,24 @@ class IRVestelAc { bool isTimerActive(void); void setTimerActive(const bool on); static uint8_t calcChecksum(const uint64_t state); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif +#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; }; #endif // IR_VESTEL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp index 343fffb0cf..048c1a1eb9 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp @@ -397,6 +397,38 @@ void IRWhirlpoolAc::setCommand(const uint8_t code) { remote_state[kWhirlpoolAcCommandPos] = code; } +// 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; + } +} + #ifdef ARDUINO String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { String result = ""; diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.h b/lib/IRremoteESP8266/src/ir_Whirlpool.h index 211e30f97f..9604d025c5 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.h @@ -14,6 +14,9 @@ #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif // 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 @@ -129,19 +132,22 @@ class IRWhirlpoolAc { const uint16_t length = kWhirlpoolAcStateLength); static bool validChecksum(uint8_t state[], const uint16_t length = kWhirlpoolAcStateLength); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); #ifdef ARDUINO String toString(); #else std::string toString(); #endif - #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); diff --git a/lib/IRremoteESP8266/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp new file mode 100644 index 0000000000..1932704ba6 --- /dev/null +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -0,0 +1,757 @@ +// Copyright 2019 David Conran + +#include "ir_Argo.h" +#include "ir_Daikin.h" +#include "ir_Fujitsu.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_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(1, 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()); +} + +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, Eye: Off, Mold: On, Swing (Horizontal): Off, " + "Swing (Vertical): Off, Current Time: 0:00, On Time: Off, Off Time: Off"; + + 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, Fujitsu) { + IRFujitsuAC ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "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 + ASSERT_EQ(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(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 + ASSERT_EQ(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(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: 3 (HEAT), 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"; + + 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 the Hack 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 the Hack 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"; + ASSERT_EQ(expected_on, 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); + + 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()); +} + +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( + "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()); +} diff --git a/lib/IRremoteESP8266/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index 6d9fe51b81..abe29a0de3 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -17,7 +17,7 @@ #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 { diff --git a/lib/IRremoteESP8266/test/Makefile b/lib/IRremoteESP8266/test/Makefile index 15ee8d4634..9a64aaaaa7 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,8 @@ 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_Lego_test + ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test ir_Lego_test IRac_test \ + ir_MitsubishiHeavy_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -80,7 +82,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_Lego.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 # All the IR Protocol header files. PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ @@ -93,6 +96,7 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Daikin.h \ $(USER_DIR)/ir_Kelvinator.h \ $(USER_DIR)/ir_Mitsubishi.h \ + $(USER_DIR)/ir_MitsubishiHeavy.h \ $(USER_DIR)/ir_NEC.h \ $(USER_DIR)/ir_Samsung.h \ $(USER_DIR)/ir_Trotec.h \ @@ -104,12 +108,12 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Tcl.h \ $(USER_DIR)/ir_Teco.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 +144,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 +156,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 +165,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 +192,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 +201,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 +210,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 +237,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 +246,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,25 +255,34 @@ 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 $@ @@ -269,7 +291,7 @@ ir_Sharp.o : $(USER_DIR)/ir_Sharp.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -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 +300,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 +318,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 +327,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 +345,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 +354,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 +363,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 +390,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 +399,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 +426,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 +435,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 +444,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 +471,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 +489,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 +498,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 +507,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 +516,34 @@ 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 $@ @@ -530,7 +552,13 @@ 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) -I$(USER_DIR) -c ir_Lego_test.cpp + $(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_Trotec.o : $(USER_DIR)/ir_Trotec.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Trotec.cpp diff --git a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp new file mode 100644 index 0000000000..340a040782 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp @@ -0,0 +1,851 @@ +// 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()); +} diff --git a/lib/IRremoteESP8266/tools/Makefile b/lib/IRremoteESP8266/tools/Makefile index b426b2af41..08488949c6 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 @@ -48,25 +49,27 @@ 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_Lego.o + ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o ir_Lego.o \ + ir_MitsubishiHeavy.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 +86,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 +99,10 @@ 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_JVC.o : $(USER_DIR)/ir_JVC.cpp $(COMMON_DEPS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_JVC.cpp @@ -112,10 +114,13 @@ 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 @@ -124,7 +129,7 @@ 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 +138,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 +150,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 +162,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 +177,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,13 +201,13 @@ 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_Lego.o : $(USER_DIR)/ir_Lego.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lego.cpp From 2c3a9f64bfe4a8c9e93828c376f02f49f966b999 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Tue, 16 Apr 2019 09:27:22 +0300 Subject: [PATCH 02/15] [IR TX RX] Plugin Updates --- src/_P016_IR.ino | 27 ++++-------- src/_P035_IRTX.ino | 104 ++++++++++++++++++++++++++++----------------- 2 files changed, 75 insertions(+), 56 deletions(-) diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index 3f5aeb663c..d238a16b3c 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -4,6 +4,7 @@ //####################################################################################################### // Uncomment the following define to enable the extended decoding of AC messages (20K bytes in flash) +// Also for using standardised common arguments for controlling all deeply supported A/C units (Plugin 035) // Example of those messages: "Mesg Desc.: Power: On, Fan: 5 (AUTO), Mode: 3 (HEAT), Temp: 22C, Zone Follow: Off, Sensor Temp: Ignored" //#define P016_Extended_Decoding @@ -14,23 +15,13 @@ #include #include -#ifdef P016_Extended_Decoding // 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 +#ifdef P035_Standardized_AC_Commands //They use the same library, so enable it for both plugins if already enabled in the other. +#define P016_Extended_Decoding +#endif + +#ifdef P016_Extended_Decoding // The following are needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units +#include +#define P035_Standardized_AC_Commands #endif #define PLUGIN_016 @@ -146,7 +137,7 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string) int irPin = CONFIG_PIN1; if (irReceiver == 0 && irPin != -1) { - serialPrintln(F("IR RX Init")); + serialPrintln(F("INIT: IR RX")); irReceiver = new IRrecv(irPin, kCaptureBufferSize, P016_TIMEOUT, true); irReceiver->setUnknownThreshold(kMinUnknownSize); // Ignore messages with less than minimum on or off pulses. irReceiver->enableIRIn(); // Start the receiver diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index 7f20982edf..579c0d51ec 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -9,8 +9,19 @@ #include #include -IRsend *Plugin_035_irSender=nullptr; +// Uncomment the following define to enable the extended decoding of AC messages (20K bytes in flash) (Plugin 016) +// Also for using standardised common arguments for controlling all deeply supported A/C units +//#define P035_Standardized_AC_Commands + +#ifdef P016_Extended_Decoding //They use the same library, so enable it for both plugins if already enabled in the other. +#define P035_Standardized_AC_Commands +#endif +#ifdef P035_Standardized_AC_Commands // The following are needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units +#include +#endif + +IRsend *Plugin_035_irSender=nullptr; #define PLUGIN_035 #define PLUGIN_ID_035 35 @@ -55,7 +66,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { addRowLabel(F("Command")); - addHtml(F("IRSENT,[PROTOCOL],[DATA],[BITS optional],[REPEATS optional]
BITS and REPEATS are optional and default to 0")); + addHtml(F("IRSEND,[PROTOCOL],[DATA],[BITS optional],[REPEATS optional]
BITS and REPEATS are optional and default to 0")); success = true; break; @@ -270,39 +281,42 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) IrBits = 0; //Leave it to 0 for default protocol bits if (GetArgv(string.c_str(), TmpStr1, 5)) IrRepeat = str2int(TmpStr1.c_str()); // Nr. of times the message is to be repeated - if (IrType.equals(F("aiwa_rc_t501"))) sendIRCode(AIWA_RC_T501,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("carrier_ac"))) sendIRCode(CARRIER_AC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("coolix"))) sendIRCode(COOLIX,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("denon"))) sendIRCode(DENON,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("dish"))) sendIRCode(DISH,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("gicable"))) sendIRCode(GICABLE,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("jvc"))) sendIRCode(JVC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lasertag"))) sendIRCode(LASERTAG,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("legopf"))) sendIRCode(LEGOPF,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lg"))) sendIRCode(LG,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lg2"))) sendIRCode(LG2,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lutron"))) sendIRCode(LUTRON,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("magiquest"))) sendIRCode(MAGIQUEST,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("midea"))) sendIRCode(MIDEA,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("mitsubishi"))) sendIRCode(MITSUBISHI,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("mitsubishi2"))) sendIRCode(MITSUBISHI2,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("nikai"))) sendIRCode(NIKAI,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("nec"))) sendIRCode(NEC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("panasonic"))) sendIRCode(PANASONIC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("pioneer"))) sendIRCode(PIONEER,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rc5x"))) sendIRCode(RC5X,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rc5"))) sendIRCode(RC5,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rc6"))) sendIRCode(RC6,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rcmm"))) sendIRCode(RCMM,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("samsung"))) sendIRCode(SAMSUNG,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("samsung36"))) sendIRCode(SAMSUNG36,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sanyo_lc7461"))) sendIRCode(SANYO_LC7461,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sharp"))) sendIRCode(SHARP,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sherwood"))) sendIRCode(SHERWOOD,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sony"))) sendIRCode(SONY,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("teco"))) sendIRCode(TECO,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("vestel_ac"))) sendIRCode(VESTEL_AC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("whynter"))) sendIRCode(WHYNTER,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("aiwa_rc_t501"))) sendIRCode(AIWA_RC_T501,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("carrier_ac"))) sendIRCode(CARRIER_AC,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("coolix"))) sendIRCode(COOLIX,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("denon"))) sendIRCode(DENON,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("dish"))) sendIRCode(DISH,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("gicable"))) sendIRCode(GICABLE,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("jvc"))) sendIRCode(JVC,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("lasertag"))) sendIRCode(LASERTAG,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("legopf"))) sendIRCode(LEGOPF,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("lg"))) sendIRCode(LG,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("lg2"))) sendIRCode(LG2,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("lutron"))) sendIRCode(LUTRON,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("magiquest"))) sendIRCode(MAGIQUEST,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("midea"))) sendIRCode(MIDEA,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("mitsubishi"))) sendIRCode(MITSUBISHI,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("mitsubishi2"))) sendIRCode(MITSUBISHI2,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("nikai"))) sendIRCode(NIKAI,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("nec"))) sendIRCode(NEC,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("panasonic"))) sendIRCode(PANASONIC,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("pioneer"))) sendIRCode(PIONEER,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("rc5x"))) sendIRCode(RC5X,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("rc5"))) sendIRCode(RC5,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("rc6"))) sendIRCode(RC6,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("rcmm"))) sendIRCode(RCMM,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("samsung"))) sendIRCode(SAMSUNG,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("samsung36"))) sendIRCode(SAMSUNG36,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("sanyo_lc7461"))) sendIRCode(SANYO_LC7461,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("sharp"))) sendIRCode(SHARP,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("sherwood"))) sendIRCode(SHERWOOD,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("sony"))) sendIRCode(SONY,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("teco"))) sendIRCode(TECO,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("vestel_ac"))) sendIRCode(VESTEL_AC,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("whynter"))) sendIRCode(WHYNTER,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("mitsubishi_heavy_88"))) sendIRCode(MITSUBISHI_HEAVY_88,IrCode,IrBits,IrRepeat); + if (IrType.equals(F("mitsubishi_heavy_152"))) sendIRCode(MITSUBISHI_HEAVY_152,IrCode,IrBits,IrRepeat); + //if (IrType.equals(F("raw"))) parseStringAndSendRaw(Plugin_035_irSender, code_str); //too big String is needed for this, //also conflicts with the keyword RAW (for the encoding) and RAW as in the library meanning of the timmings information. @@ -341,10 +355,10 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) #endif } break; - } - } + } //PLUGIN_WRITE END + } // SWITCH END return success; -} +} // Plugin_035 END boolean addErrorTrue() { addLog(LOG_LEVEL_ERROR, F("RAW2: Invalid encoding!")); @@ -659,6 +673,12 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String& str) { 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; @@ -795,6 +815,14 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String& str) { Plugin_035_irSender->sendMitsubishiAC(reinterpret_cast(state)); break; #endif +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: // 59 + Plugin_035_irSender->sendMitsubishiHeavy88(reinterpret_cast(state)); + break; + case MITSUBISHI_HEAVY_152: // 60 + Plugin_035_irSender->sendMitsubishiHeavy152(reinterpret_cast(state)); + break; +#endif // SEND_MITSUBISHIHEAVY #if SEND_TROTEC case TROTEC: Plugin_035_irSender->sendTrotec(reinterpret_cast(state)); From 8b7207430e0aca3883b1d390a3b00afd2e4dc489 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Sat, 27 Apr 2019 09:43:54 +0300 Subject: [PATCH 03/15] [IR TX RX] Lib Updates --- lib/IRremoteESP8266/.travis.yml | 1 + .../examples/IRMQTTServer/IRMQTTServer.h | 258 ++ .../examples/IRMQTTServer/IRMQTTServer.ino | 2066 +++++++++++++---- .../examples/IRMQTTServer/platformio.ini | 13 +- lib/IRremoteESP8266/src/IRac.cpp | 224 +- lib/IRremoteESP8266/src/IRac.h | 41 +- lib/IRremoteESP8266/src/IRremoteESP8266.h | 2 + lib/IRremoteESP8266/src/IRsend.h | 23 +- lib/IRremoteESP8266/src/IRtimer.cpp | 31 +- lib/IRremoteESP8266/src/IRtimer.h | 12 + lib/IRremoteESP8266/src/IRutils.cpp | 211 +- lib/IRremoteESP8266/src/IRutils.h | 8 +- lib/IRremoteESP8266/src/ir_Haier.cpp | 9 +- lib/IRremoteESP8266/src/ir_MWM.cpp | 4 +- lib/IRremoteESP8266/test/IRac_test.cpp | 80 + lib/IRremoteESP8266/test/IRutils_test.cpp | 20 + lib/IRremoteESP8266/test/ir_Haier_test.cpp | 90 +- src/define_plugin_sets.h | 6 + 18 files changed, 2595 insertions(+), 504 deletions(-) create mode 100644 lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h diff --git a/lib/IRremoteESP8266/.travis.yml b/lib/IRremoteESP8266/.travis.yml index e9b30d1fb1..ae2d9fe3c7 100644 --- a/lib/IRremoteESP8266/.travis.yml +++ b/lib/IRremoteESP8266/.travis.yml @@ -15,6 +15,7 @@ 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 diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h new file mode 100644 index 0000000000..85ac0e1d31 --- /dev/null +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h @@ -0,0 +1,258 @@ +/* + * 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. + +// 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. e.g. 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 + +// --------------------- 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) + +// ----------------------- 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 update is always passworded. +// If you do not set a password, Firmware OTA 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 -------------------------------- +// 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 + +// ------------------------ Advanced Usage Only -------------------------------- +// Change if you need multiple independent send gpio/topics. +const uint8_t gpioTable[] = { + IR_LED, // Default GPIO. e.g. ir_server/send or ir_server/send_0 + // Uncomment the following as needed. + // NOTE: Remember to disable DEBUG if you are using one of the serial pins. + // 5, // GPIO 5 / D1 e.g. ir_server/send_1 + // 14, // GPIO 14 / D5 e.g. ir_server/send_2 + // 16, // GPIO 16 / D0 e.g. ir_server/send_3 +}; + +#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_CLOCK "clock" +#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" + +// 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 ----------------------------------- +// Disable debug output if any of the IR pins are on the TX (D1) pin. +// Note: This is a crude method to catch the common use cases. +// See `isSerialGpioUsedByIr()` for the better method. +#if (IR_LED != 1 && IR_RX != 1) +#ifndef DEBUG +#define DEBUG true // Change to 'false' to disable all serial output. +#endif // DEBUG +#else // (IR_LED != 1 && IR_RX != 1) +#undef DEBUG +#define DEBUG false +#endif + +// ----------------- End of User Configuration Section ------------------------- + +// Constants +#define _MY_VERSION_ "v1.0.0-gamma" + +const uint8_t kSendTableSize = sizeof(gpioTable); +// 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"; + +#if MQTT_ENABLE +const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds. +const uint32_t kStatListenPeriodMs = 5 * 1000; // mSeconds + +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 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 commonAcState_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 listOfSendGpios(void); +bool hasUnsafeHTMLChars(String input); +String htmlMenu(void); +void handleRoot(void); +String addJsReloadUrl(const String url, const uint16_t timeout_s, + const bool notify); +void handleExamples(void); +String boolToString(const bool value); +String opmodeToString(const stdAc::opmode_t mode); +String fanspeedToString(const stdAc::fanspeed_t speed); +String swingvToString(const stdAc::swingv_t swingv); +String swinghToString(const stdAc::swingh_t swingh); +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); +commonAcState_t updateClimate(commonAcState_t current, const String str, + const String prefix, const String payload); +bool cmpClimate(const commonAcState_t a, const commonAcState_t b); +bool sendClimate(const commonAcState_t prev, const commonAcState_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR); +#endif // EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_ diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino index 80e01bc4d2..f8ac49c48c 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -1,34 +1,40 @@ /* * 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 ESP8266 GPIO4 (D2) if + * you want to send IR messages. + * A compatible IR RX modules SHOULD be connected to ESP8266 GPIO14 (D5) + * if you want to capture & decode IR nessages. + * See 'IR_LED' & 'IR_RX' in IRMQTTServer.h. + * + * 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.x and < 6) * - PubSubClient (https://pubsubclient.knolleary.net/) + * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) * 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. @@ -37,15 +43,16 @@ * The ESP8266 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 ESP8266 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:///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 @@ -156,33 +268,17 @@ * 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 #include #include #include @@ -192,122 +288,22 @@ #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. e.g. 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. -#define MQTTlog MQTTprefix "/log" // Topic we send log messages to. -#define MQTTstatus MQTTprefix "/status" // Topic for the Last Will & Testament. -#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. - -// ------------------------ Advanced Usage Only -------------------------------- - -// Change if you need multiple independent send gpio/topics. -const uint8_t gpioTable[] = { - IR_LED, // Default GPIO. e.g. ir_server/send or ir_server/send_0 - // Uncomment the following as needed. - // NOTE: Remember to disable DEBUG if you are using one of the serial pins. - // 5, // GPIO 5 / D1 e.g. ir_server/send_1 - // 14, // GPIO 14 / D5 e.g. ir_server/send_2 - // 16, // GPIO 16 / D0 e.g. ir_server/send_3 -}; - -#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. - -// ----------------- End of User Configuration Section ------------------------- - // Globals -#define _MY_VERSION_ "v0.9.1" -// HTML arguments we will parse for IR code information. -#define argType "type" -#define argData "code" -#define argBits "bits" -#define argRepeat "repeats" - -// Text for Last Will & Testament status messages. -#define LWT_ONLINE "Online" -#define LWT_OFFLINE "Offline" - ESP8266WebServer server(kHttpPort); #ifdef IR_RX IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); @@ -316,15 +312,19 @@ decode_results capture; // Somewhere to store inbound IR messages. MDNSResponder mdns; 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. +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. +IRsend *IrSendTable[kSendTableSize]; #ifdef IR_RX String lastIrReceived = "None"; @@ -332,33 +332,190 @@ uint32_t lastIrReceivedTime = 0; uint32_t irRecvCounter = 0; #endif // IR_RX +// Climate stuff +commonAcState_t climate; +commonAcState_t climate_prev; +IRac commonAc(gpioTable[0]); +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; -// 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); -const uint8_t kSendTableSize = sizeof(gpioTable); -IRsend *IrSendTable[kSendTableSize]; - +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 uint8_t kSerialTxGpio = 1; // The GPIO serial output is sent too. + // Note: *DOES NOT* control Serial output. + // Ensure we are not trodding on anything IR related. +#ifdef IR_RX + if (IR_RX == kSerialTxGpio) + return true; // Serial port is in use by IR capture. Abort. +#endif // IR_RX + for (uint8_t i = 0; i < kSendTableSize; i++) + if (gpioTable[i] == 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; +} + +void saveWifiConfig(void) { + debug("Saving the wifi config."); + 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 (SPIFFS.begin()) { + 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."); + } + SPIFFS.end(); + } +} + +void loadWifiConfigFile(void) { + debug("Trying to mount SPIFFS"); + if (SPIFFS.begin()) { + debug("mounted 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); + debug("Recovered Json fields."); + } 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(); + } else { + debug("Failed to mount SPIFFS"); + } +} + +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"; @@ -368,30 +525,7 @@ 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"; } // Return a string containing the comma separated list of sending gpios. @@ -399,111 +533,53 @@ String listOfSendGpios(void) { String result = String(gpioTable[0]); if (kSendTableSize > 1) result += " (default)"; for (uint8_t i = 1; i < kSendTableSize; i++) { - result += ", " + String(gpioTable[1]); - } - return result; -} - -// Return a string containing the comma separated list of MQTT command topics. -String listOfCommandTopics(void) { - String result = MQTTcommand; - for (uint8_t i = 0; i < kSendTableSize; i++) { - result += ", " MQTTcommand "_" + String(gpioTable[1]); + result += ", " + String(gpioTable[i]); } return result; } -// 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 htmlMenu(void) { + return F( + "
" + "" + "" + "" + "" + "" + "
" + "
"); } // 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 = + String html = F( "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(s): " + listOfSendGpios() + "
" - "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(s): " + listOfCommandTopics() + "
" - "Acknowledgements topic: " MQTTack "
" -#ifdef IR_RX - "IR Received topic: " MQTTrecv "
" -#endif // IR_RX - "Log topic: " MQTTlog "
" - "LWT topic: " MQTTstatus "
" - "QoS: " + String(QOS) + "
" - "Last MQTT command seen: " + lastMqttCmdTopic + " : " + - // 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)

" - "

" + "
" _MY_VERSION_ "
"); + html += htmlMenu(); + html += F( "

Send a simple IR message

" "

" "Type: " @@ -569,6 +645,42 @@ void handleRoot() { " " "
" "

" + "

Send a complex (Air Conditioner) IR message

" + "

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

" "

Send an IRremote Raw IR message

" "

" "" @@ -606,78 +718,587 @@ void handleRoot() { "size='2' maxlength='2'>" " " "
" - "

" - "

Send an Air Conditioner IR message

" - "

" - "Type: " - "" - " State code: 0x" - "" - " " - "
" - "
"; + "
"); + 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 = F( + "IR MQTT examples" + "" + "

ESP8266 IR MQTT Server

" + "
" _MY_VERSION_ "
"); + 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)

" + "

"); + server.send(200, "text/html", html); +} + +String boolToString(const bool value) { + return value ? F("on") : F("off"); +} + + +String 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 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 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 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"); + } +} + +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 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; +} + +// Admin web page +void handleAirCon(void) { + String html = F( + "AirCon control" + "" + "

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) + + "
" + "" + "
"; + // Display the current settings. + html += F(""); + 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 + commonAcState_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 + // Update the old climate state with the new one. + climate = result; + // Redirect back to the aircon page. + String html = F( + "Update Aircon" + "" + "

Aircon updated!

"); + html += addJsReloadUrl("/aircon", 2, false); + html += F(""); + server.send(200, "text/html", html); +} + +// Admin web page +void handleAdmin(void) { + String html = F( + "IR MQTT server admin" + "" + "

Administration

"); + html += htmlMenu(); + html += F( + "

Special commands

" +#if MQTT_ENABLE + " " + "Send a Climate MQTT discovery message to Home Assistant.

" +#endif // MQTT_ENABLE + " A simple reboot of the ESP8266. " + "ie. No changes

" + " Warning: " + "Resets the device back to original settings. " + "ie. Goes back to AP/Setup mode.
"); #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 += F("OTA firmware is disabled until you set a password. " + "You will need to wipe & reset to set one." + "

"); 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 += F(""); + server.send(200, "text/html", html); +} + +// Info web page +void handleInfo(void) { + String html = + "IR MQTT server info" + "" + "

Information

"; + 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_ "
" + "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" + "IR Send GPIO(s): " + listOfSendGpios() + "
" + "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 + "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 + "
" +#ifdef 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(gpioTable[0]) + "
" + "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 + + "(" 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_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" + "State topics: " + MqttClimateStat + + "(" 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_CLOCK "|" KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS ")
" +#endif // MQTT_ENABLE + "

" + // Page footer + "

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

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

Resetting the WiFiManager config back to defaults.

" - "

Device restarting. Try connecting in a few seconds.

" + "

Device restarting. Try connecting in a few seconds.

" + + addJsReloadUrl("/", 10, true) + ""); - // Do the reset. + // Do the reset. +#if MQTT_ENABLE + mqttLog("Wiping all saved config settings."); +#endif // MQTT_ENABLE + debug("Trying to mount SPIFFS"); + if (SPIFFS.begin()) { + 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 /quitquitquit."); + return server.requestAuthentication(); + } +#endif + server.send(200, "text/html", + "Rebooting" + "" + "

Device restarting.

" + "

Try connecting in a few seconds.

" + + addJsReloadUrl("/", 15, true) + + ""); +#if MQTT_ENABLE + mqttLog("Reboot requested"); +#endif // MQTT_ENABLE + // Do the reset. + delay(1000); ESP.restart(); delay(1000); } @@ -827,7 +1448,8 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, 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. @@ -1151,38 +1773,45 @@ bool parseStringAndSendRaw(IRsend *irsend, const String str) { #endif // SEND_RAW // 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(IrSendTable[0], ir_type, data, data_str.c_str(), nbits, repeat); - handleRoot(); + String html = F( + "Send IR command" + "" + "

IR command sent!

"); + html += addJsReloadUrl("/", 2, true); + html += F(""); + server.send(200, "text/html", html); } -void handleNotFound() { +void handleNotFound(void) { String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); @@ -1196,27 +1825,138 @@ void handleNotFound() { server.send(404, "text/plain", message); } -void setup_wifi() { +void setup_wifi(void) { delay(10); + loadWifiConfigFile(); // 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(); 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) { + saveWifiConfig(); + } + 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(ESP.getChipId(), HEX); +#endif // MQTT_ENABLE } void setup(void) { + // 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; + // Initialise all the IR transmitters. for (uint8_t i = 0; i < kSendTableSize; i++) { IrSendTable[i] = new IRsend(gpioTable[i]); @@ -1235,67 +1975,111 @@ void setup(void) { #endif // IR_RX #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()) { + // 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."); + } #endif // DEBUG setup_wifi(); // Wait a bit for things to settle. - delay(1500); + delay(500); lastReconnectAttempt = 0; - if (mdns.begin(HOSTNAME, WiFi.localIP())) { + if (mdns.begin(Hostname, WiFi.localIP())) { debug("MDNS responder started"); } // Setup the root web page. server.on("/", handleRoot); + // Setup the examples web page. + server.on("/examples", handleExamples); // Setup the page to handle web-based IR codes. server.on("/ir", handleIr); + // Setup the aircon page. + server.on("/aircon", handleAirCon); + // Setup the aircon update page. + server.on("/aircon/set", handleAirConSet); + // Setup the info page. + server.on("/info", handleInfo); + // Setup the admin page. + server.on("/admin", handleAdmin); // Setup a reset page to cause WiFiManager information to be reset. server.on("/reset", handleReset); + // Reboot url + server.on("/quitquitquit", handleReboot); +#if MQTT_ENABLE + // MQTT Discovery url + server.on("/send_discovery", 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", + "Updating firmware." + "" + "

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("/", 20, true) + + ""); + 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) { WiFiUDP::stopAll(); - debug("Update: " + upload.filename); + debug("Update:"); + debug(upload.filename.c_str()); uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; 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(); @@ -1315,42 +2099,66 @@ void setup(void) { void subscribing(const String topic_name) { // subscription to topic for receiving data with QoS. if (mqtt_client.subscribe(topic_name.c_str(), QOS)) - debug("Subscription OK to " + topic_name); + debug("Subscription OK to:"); + else + debug("Subscription FAILED to:"); + debug(topic_name.c_str()); +} + +// 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("Subscription FAILED to " + topic_name); + debug("FAILED to unsubscribe from:"); + debug(topic_name.c_str()); } -bool reconnect() { +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, MQTTstatus, QOS, true, - LWT_OFFLINE); - else - connected = mqtt_client.connect(mqtt_clientid.c_str(), MQTTstatus, QOS, - true, LWT_OFFLINE); + 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(MQTTlog, "(Re)Connected"); - debug("reconnected."); + mqttLog("(Re)Connected."); // Update Last Will & Testament to say we are back online. - mqtt_client.publish(MQTTstatus, LWT_ONLINE, true); + mqtt_client.publish(MqttLwt.c_str(), kLwtOnline, true); + mqttSentCounter++; // Subscribing to topic(s) - subscribing(MQTTcommand); + subscribing(MqttSend); for (uint8_t i = 0; i < kSendTableSize; i++) { - subscribing(String(MQTTcommand "_") + String(static_cast(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) } @@ -1358,6 +2166,191 @@ 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 < kSendTableSize; 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", + "Sending MQTT Discovery message" + "" + "

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("/", 15, true) + + ""); + sendMQTTDiscovery(MqttDiscovery.c_str()); +} + +void doBroadcast(TimerMs *timer, const uint32_t interval, + const commonAcState_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) { + char* tok_ptr; + uint64_t code = 0; + uint16_t nbits = 0; + uint16_t repeat = 0; + uint8_t channel = 0; // Default to the first channel. e.g. "*_0" + + 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"); + commonAcState_t updated = updateClimate( + climate, topic_name, MqttClimateCmnd, callback_str); + sendClimate(climate, updated, MqttClimateStat, + true, false, false); + 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 < kSendTableSize; 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(gpioTable[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); + + // 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( + IrSendTable[channel], ir_type, code, + callback_str.substring(callback_str.indexOf(",") + 1).c_str(), + nbits, repeat); +} + +// 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)); + // 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) { @@ -1381,28 +2374,47 @@ void loop(void) { lastReconnectAttempt = 0; wasConnected = true; if (boot) { - mqtt_client.publish(MQTTlog, "IR Server just booted"); + mqttLog("IR Server just booted"); boot = false; } else { - String text = "IR Server just (re)connected to MQTT. " - "Lost connection about " + timeSince(lastConnectedTime); - mqtt_client.publish(MQTTlog, text.c_str()); + mqttLog("IR Server 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); + } + 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 // Check if an IR code has been received via the IR RX module. #if REPORT_UNKNOWNS if (irrecv.decode(&capture)) { -#else +#else // REPORT_UNKNOWNS if (irrecv.decode(&capture) && capture.decode_type != UNKNOWN) { #endif // REPORT_UNKNOWNS lastIrReceivedTime = millis(); @@ -1428,10 +2440,12 @@ void loop(void) { if (!hasACState(capture.decode_type)) lastIrReceived += "," + String(capture.bits); #if MQTT_ENABLE - mqtt_client.publish(MQTTrecv, lastIrReceived.c_str()); -#endif + mqtt_client.publish(MqttRecv.c_str(), lastIrReceived.c_str()); + mqttSentCounter++; +#endif // MQTT_ENABLE irRecvCounter++; - debug("Incoming IR message sent to MQTT: " + lastIrReceived); + debug("Incoming IR message sent to MQTT:"); + debug(lastIrReceived.c_str()); } #endif // IR_RX delay(100); @@ -1472,9 +2486,9 @@ bool sendIRCode(IRsend *irsend, int const ir_type, uint64_t const code, char const * code_str, uint16_t bits, uint16_t repeat) { // 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. @@ -1760,7 +2774,7 @@ bool sendIRCode(IRsend *irsend, int const ir_type, } lastSendTime = millis(); // Release the lock. - ir_lock = false; + lockIr = false; // Indicate that we sent the message or not. if (success) { @@ -1769,7 +2783,8 @@ bool sendIRCode(IRsend *irsend, int const ir_type, } 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 || @@ -1781,108 +2796,215 @@ bool sendIRCode(IRsend *irsend, int const ir_type, #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) + ",R" + + String(repeat) + "," + + 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) + "," + + 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) + "," + + uint64ToString(code, 16) + + "," + String(bits) + "," + + 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; - uint8_t channel = 0; // Default to the first channel. e.g. "*_0" - - debug("Receiving data by MQTT topic " + topic_name); - // Check if a specific channel was requested by looking for a "*_[0-9]" suffix - for (int i = 0; i < kSendTableSize; i++) { - debug("Checking if " + topic_name + " ends with _" + String(i)); - if (topic_name.endsWith("_" + String(i))) { - channel = i; - debug("It does!"); - break; - } - } - - debug("Using transmit channel " + String(static_cast(channel)) + - " / GPIO " + String(static_cast(gpioTable[channel]))); - // 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). - lastMqttCmdTopic = topic_name; - 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); + mqttSentCounter++; + return mqtt_client.publish(topic.c_str(), String(num).c_str(), retain); +#else // MQTT_ENABLE + return true; +#endif // MQTT_ENABLE +} - free(callback_c_str); +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 +} - // send received MQTT value by IR signal - lastSendSucceeded = sendIRCode( - IrSendTable[channel], ir_type, code, - callback_str.substring(callback_str.indexOf(",") + 1).c_str(), - nbits, repeat); +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 } -// Callback function, when we 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); +commonAcState_t updateClimate(commonAcState_t current, const String str, + const String prefix, const String payload) { + commonAcState_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(); + else if (str.equals(prefix + KEY_CLOCK)) + result.clock = value.toInt(); + return result; +} - // Conversion to a printable string - payload_copy[length] = '\0'; - String callback_string = String(reinterpret_cast(payload_copy)); - String topic_name = String(reinterpret_cast(topic)); +// Compare two AirCon states (climates). +// Returns: True if they differ, False if they don't. +bool cmpClimate(const commonAcState_t a, const commonAcState_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; +} - // launch the function to treat received data - receivingMQTT(topic_name, callback_string); +bool sendClimate(const commonAcState_t prev, const commonAcState_t next, + const String topic_prefix, const bool retain, + const bool forceMQTT, const bool forceIR) { + bool diff = false; + bool success = true; - // Free the memory - free(payload_copy); + 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 ? 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, + fanspeedToString(next.fanspeed), retain); + } + if (prev.swingv != next.swingv || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGV, + swingvToString(next.swingv), retain); + } + if (prev.swingh != next.swingh || forceMQTT) { + diff = true; + success &= sendString(topic_prefix + KEY_SWINGH, + 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 ((diff && !forceMQTT) || forceIR) { + debug("Sending common A/C state via IR."); + 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 (lastClimateSucceeded) hasClimateBeenSent = true; + success &= lastClimateSucceeded; + lastClimateIr.reset(); + irClimateCounter++; + sendReqCounter++; + } + return success; } -#endif // MQTT_ENABLE diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini index 243822c5f1..243b36a99f 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini @@ -3,12 +3,13 @@ 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 [env:nodemcuv2] platform = espressif8266 @@ -29,3 +30,13 @@ build_flags = ${common.build_flags} lib_deps = ${common.lib_deps_builtin} ${common.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.lib_deps_builtin} + ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp index 6c071a6cd7..5bc66763cc 100644 --- a/lib/IRremoteESP8266/src/IRac.cpp +++ b/lib/IRremoteESP8266/src/IRac.cpp @@ -9,6 +9,7 @@ #include #endif +#include #ifndef ARDUINO #include #endif @@ -35,6 +36,79 @@ 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_FUJITSU_AC + case decode_type_t::FUJITSU_AC: +#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_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, @@ -653,8 +727,8 @@ void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, // 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 uint16_t model, - const bool on, const stdAc::opmode_t mode, +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, @@ -663,11 +737,13 @@ bool IRac::sendAc(const decode_type_t vendor, const uint16_t model, const bool beep, const int16_t sleep, const int16_t clock) { // Convert the temperature to Celsius. float degC; + bool on = power; if (celsius) degC = degrees; else degC = (degrees - 32.0) * (5.0 / 9.0); - + // 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 @@ -877,3 +953,145 @@ bool IRac::sendAc(const decode_type_t vendor, const uint16_t model, } 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; +} diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h index fcc8c1bfec..5cd562bc6b 100644 --- a/lib/IRremoteESP8266/src/IRac.h +++ b/lib/IRremoteESP8266/src/IRac.h @@ -33,14 +33,27 @@ class IRac { public: explicit IRac(uint8_t pin); - bool sendAc(const decode_type_t vendor, const uint16_t model, - const bool on, const stdAc::opmode_t mode, const float degrees, + 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); #ifndef UNIT_TEST private: @@ -202,5 +215,27 @@ class IRac { const bool turbo, const bool light, const int16_t sleep = -1, const int16_t clock = -1); #endif // SEND_WHIRLPOOL_AC -}; +}; // IRac class + +// 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; +} commonAcState_t; #endif // IRAC_H_ diff --git a/lib/IRremoteESP8266/src/IRremoteESP8266.h b/lib/IRremoteESP8266/src/IRremoteESP8266.h index cd7c1c37e9..02f2dcf10b 100644 --- a/lib/IRremoteESP8266/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266/src/IRremoteESP8266.h @@ -308,6 +308,8 @@ enum decode_type_t { LEGOPF, MITSUBISHI_HEAVY_88, MITSUBISHI_HEAVY_152, // 60 + // Add new entries before this one, and update it to point to the last entry. + kLastDecodeType = MITSUBISHI_HEAVY_152, }; // Message lengths & required repeat values diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index c20b514178..1c6582e489 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -34,20 +34,21 @@ const uint32_t kDefaultMessageGap = 100000; namespace stdAc { enum class opmode_t { - kAuto = 0, - kCool = 1, - kHeat = 2, - kDry = 3, - kFan = 4, + 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, + kAuto = 0, + kMin = 1, + kLow = 2, + kMedium = 3, + kHigh = 4, + kMax = 5, }; enum class swingv_t { diff --git a/lib/IRremoteESP8266/src/IRtimer.cpp b/lib/IRremoteESP8266/src/IRtimer.cpp index b3136abca1..4173d763ba 100644 --- a/lib/IRremoteESP8266/src/IRtimer.cpp +++ b/lib/IRremoteESP8266/src/IRtimer.cpp @@ -8,11 +8,11 @@ #ifdef UNIT_TEST // Used to help simulate elapsed time in unit tests. 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 baca1cf74d..d00e1d0fad 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 85a4e34425..9cb5305d90 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 @@ -81,6 +82,208 @@ 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 *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, "DAIKIN2")) + return decode_type_t::DAIKIN2; + 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, "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, "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, "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. +#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. +String htmlEscape(const String unescaped) { + String result = ""; +#else +std::string htmlEscape(const std::string unescaped) { + std::string result = ""; +#endif + 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. @@ -95,10 +298,6 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { std::string result = ""; #endif switch (protocol) { - default: - case UNKNOWN: - result = F("UNKNOWN"); - break; case UNUSED: result = F("UNUSED"); break; @@ -282,6 +481,10 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case WHYNTER: result = F("WHYNTER"); break; + case UNKNOWN: + default: + result = F("UNKNOWN"); + break; } if (isRepeat) result += F(" (Repeat)"); return result; diff --git a/lib/IRremoteESP8266/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index ff045e911a..0d0b677b57 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -24,7 +24,8 @@ 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 +String htmlEscape(const String unescaped); +#else // ARDUINO std::string uint64ToString(uint64_t input, uint8_t base = 10); std::string typeToString(const decode_type_t protocol, const bool isRepeat = false); @@ -32,7 +33,8 @@ 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 +std::string htmlEscape(const std::string unescaped); +#endif // ARDUINO 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); @@ -42,5 +44,5 @@ uint16_t countBits(const uint8_t *start, const uint16_t length, 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); #endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp index 2572aa2859..f76bb3447a 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -45,7 +45,7 @@ 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) { @@ -104,7 +104,10 @@ bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { void IRHaierAC::stateReset() { 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; + remote_state[6] = 0x20; setTemp(kHaierAcDefTemp); setFan(kHaierAcFanAuto); @@ -894,7 +897,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_MWM.cpp b/lib/IRremoteESP8266/src/ir_MWM.cpp index a75e99e3a5..61eac49e27 100644 --- a/lib/IRremoteESP8266/src/ir_MWM.cpp +++ b/lib/IRremoteESP8266/src/ir_MWM.cpp @@ -105,7 +105,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 +129,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/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp index 1932704ba6..0b5e270412 100644 --- a/lib/IRremoteESP8266/test/IRac_test.cpp +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -755,3 +755,83 @@ TEST(TestIRac, Whirlpool) { 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)); +} diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 57eceed860..4a49076497 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -395,3 +395,23 @@ 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(""<'>&")); +} diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index 11848e00a3..008cfd0204 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -404,7 +404,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.toString()); uint8_t expectedState[kHaierACStateLength] = {0xA5, 0x96, 0xEA, 0xCF, 0x32, - 0x2D, 0x0D, 0x74, 0xD4}; + 0xED, 0x2D, 0x74, 0xB4}; EXPECT_STATE_EQ(expectedState, haier.getRaw(), kHaierACBits); // Check that the checksum is valid. @@ -987,3 +987,91 @@ 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: 0 (AUTO), 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.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: 0 (AUTO), 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: 0 (AUTO), 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()); +} diff --git a/src/define_plugin_sets.h b/src/define_plugin_sets.h index adcab50db3..0941f12b43 100644 --- a/src/define_plugin_sets.h +++ b/src/define_plugin_sets.h @@ -185,12 +185,18 @@ To create/register a plugin, you have to : // Disable all settings like these when not needed: // #define DECODE_TOSHIBA_AC true // #define SEND_TOSHIBA_AC true +// The following are needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units +// #define P016_P035_Extended_AC + #ifdef PLUGIN_BUILD_IR #define PLUGIN_DESCR "IR" #define USES_P016 // IR #define USES_P035 // IRTX #endif +#ifdef P016_P035_Extended_AC +#include +#endif /******************************************************************************\ * Devices ******************************************************************** From eb5efacfba62cf2ff3f5a3230a07f09d0e241891 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Sat, 27 Apr 2019 09:47:48 +0300 Subject: [PATCH 04/15] [IR TX RX] Code refactoring Lib updates gave me some ideas --- src/_P016_IR.ino | 13 +----- src/_P035_IRTX.ino | 90 ++++++++-------------------------------- src/define_plugin_sets.h | 2 +- 3 files changed, 21 insertions(+), 84 deletions(-) diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index d238a16b3c..a9813ebab6 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -15,15 +15,6 @@ #include #include -#ifdef P035_Standardized_AC_Commands //They use the same library, so enable it for both plugins if already enabled in the other. -#define P016_Extended_Decoding -#endif - -#ifdef P016_Extended_Decoding // The following are needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units -#include -#define P035_Standardized_AC_Commands -#endif - #define PLUGIN_016 #define PLUGIN_ID_016 16 #define PLUGIN_NAME_016 "Communication - TSOP4838" @@ -197,7 +188,7 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string) //addLog(LOG_LEVEL_DEBUG,(String(F("IR: RAW TIMINGS: ")) + resultToSourceCode(&results))); // Output the results as RAW source code //not showing up nicely in the web log } -#ifdef P016_Extended_Decoding +#ifdef P016_P035_Extended_AC // Display any extra A/C info if we have it. // Display the human readable state of an A/C message if we can. String description = ""; @@ -331,7 +322,7 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string) // If we got a human-readable description of the message, display it. if (description != "") addLog(LOG_LEVEL_INFO, description); -#endif // Extended Messages +#endif // P016_P035_Extended_AC unsigned long IRcode = results.value; UserVar[event->BaseVarIndex] = (IRcode & 0xFFFF); diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index 579c0d51ec..5304c1fe1e 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -9,18 +9,6 @@ #include #include -// Uncomment the following define to enable the extended decoding of AC messages (20K bytes in flash) (Plugin 016) -// Also for using standardised common arguments for controlling all deeply supported A/C units -//#define P035_Standardized_AC_Commands - -#ifdef P016_Extended_Decoding //They use the same library, so enable it for both plugins if already enabled in the other. -#define P035_Standardized_AC_Commands -#endif - -#ifdef P035_Standardized_AC_Commands // The following are needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units -#include -#endif - IRsend *Plugin_035_irSender=nullptr; #define PLUGIN_035 @@ -281,67 +269,25 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) IrBits = 0; //Leave it to 0 for default protocol bits if (GetArgv(string.c_str(), TmpStr1, 5)) IrRepeat = str2int(TmpStr1.c_str()); // Nr. of times the message is to be repeated - if (IrType.equals(F("aiwa_rc_t501"))) sendIRCode(AIWA_RC_T501,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("carrier_ac"))) sendIRCode(CARRIER_AC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("coolix"))) sendIRCode(COOLIX,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("denon"))) sendIRCode(DENON,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("dish"))) sendIRCode(DISH,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("gicable"))) sendIRCode(GICABLE,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("jvc"))) sendIRCode(JVC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lasertag"))) sendIRCode(LASERTAG,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("legopf"))) sendIRCode(LEGOPF,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lg"))) sendIRCode(LG,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lg2"))) sendIRCode(LG2,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("lutron"))) sendIRCode(LUTRON,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("magiquest"))) sendIRCode(MAGIQUEST,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("midea"))) sendIRCode(MIDEA,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("mitsubishi"))) sendIRCode(MITSUBISHI,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("mitsubishi2"))) sendIRCode(MITSUBISHI2,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("nikai"))) sendIRCode(NIKAI,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("nec"))) sendIRCode(NEC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("panasonic"))) sendIRCode(PANASONIC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("pioneer"))) sendIRCode(PIONEER,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rc5x"))) sendIRCode(RC5X,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rc5"))) sendIRCode(RC5,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rc6"))) sendIRCode(RC6,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("rcmm"))) sendIRCode(RCMM,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("samsung"))) sendIRCode(SAMSUNG,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("samsung36"))) sendIRCode(SAMSUNG36,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sanyo_lc7461"))) sendIRCode(SANYO_LC7461,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sharp"))) sendIRCode(SHARP,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sherwood"))) sendIRCode(SHERWOOD,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("sony"))) sendIRCode(SONY,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("teco"))) sendIRCode(TECO,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("vestel_ac"))) sendIRCode(VESTEL_AC,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("whynter"))) sendIRCode(WHYNTER,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("mitsubishi_heavy_88"))) sendIRCode(MITSUBISHI_HEAVY_88,IrCode,IrBits,IrRepeat); - if (IrType.equals(F("mitsubishi_heavy_152"))) sendIRCode(MITSUBISHI_HEAVY_152,IrCode,IrBits,IrRepeat); - - - //if (IrType.equals(F("raw"))) parseStringAndSendRaw(Plugin_035_irSender, code_str); //too big String is needed for this, + //if (IrType.equals(F("raw"))) parseStringAndSendRaw(Plugin_035_irSender, code_str); //too big String is needed for this, //also conflicts with the keyword RAW (for the encoding) and RAW as in the library meanning of the timmings information. - if (IrType.equals(F("gc"))) parseStringAndSendGC(ircodestr); //Needs testing - if (IrType.equals(F("pronto"))) parseStringAndSendPronto(ircodestr, 0); //Needs testing - if (IrType.equals(F("mitsubishi_ac"))) parseStringAndSendAirCon(MITSUBISHI_AC, ircodestr); - if (IrType.equals(F("fujitsu_ac"))) parseStringAndSendAirCon(FUJITSU_AC, ircodestr); - if (IrType.equals(F("kelvinator"))) parseStringAndSendAirCon(KELVINATOR, ircodestr); - if (IrType.equals(F("daikin"))) parseStringAndSendAirCon(DAIKIN, ircodestr); - if (IrType.equals(F("daikin2"))) parseStringAndSendAirCon(DAIKIN2, ircodestr); - if (IrType.equals(F("gree"))) parseStringAndSendAirCon(GREE, ircodestr); - if (IrType.equals(F("argo"))) parseStringAndSendAirCon(ARGO, ircodestr); - if (IrType.equals(F("trotec"))) parseStringAndSendAirCon(TROTEC, ircodestr); - if (IrType.equals(F("toshiba_ac"))) parseStringAndSendAirCon(TOSHIBA_AC, ircodestr); - if (IrType.equals(F("haier_ac"))) parseStringAndSendAirCon(HAIER_AC, ircodestr); - if (IrType.equals(F("haier_ac_yrw02"))) parseStringAndSendAirCon(HAIER_AC_YRW02, ircodestr); - if (IrType.equals(F("hitachi_ac"))) parseStringAndSendAirCon(HITACHI_AC, ircodestr); - if (IrType.equals(F("hitachi_ac1"))) parseStringAndSendAirCon(HITACHI_AC1, ircodestr); - if (IrType.equals(F("hitachi_ac2"))) parseStringAndSendAirCon(HITACHI_AC2, ircodestr); - if (IrType.equals(F("electra_ac"))) parseStringAndSendAirCon(ELECTRA_AC, ircodestr); - if (IrType.equals(F("panasonic_ac"))) parseStringAndSendAirCon(PANASONIC_AC, ircodestr); - if (IrType.equals(F("samsung_ac"))) parseStringAndSendAirCon(SAMSUNG_AC, ircodestr); - if (IrType.equals(F("whirlpool_ac"))) parseStringAndSendAirCon(WHIRLPOOL_AC, ircodestr); - if (IrType.equals(F("mwm"))) parseStringAndSendAirCon(MWM, ircodestr); - if (IrType.equals(F("tcl112ac"))) parseStringAndSendAirCon(TCL112AC, ircodestr); + // if (IrType.equals(F("gc"))) parseStringAndSendGC(ircodestr); //Needs testing + // if (IrType.equals(F("pronto"))) parseStringAndSendPronto(ircodestr, 0); //Needs testing + + if (IrType.equals(F("mitsubishi_ac")) || IrType.equals(F("fujitsu_ac")) || IrType.equals(F("kelvinator")) + || IrType.equals(F("daikin")) || IrType.equals(F("daikin2")) || IrType.equals(F("gree")) + || IrType.equals(F("argo")) || IrType.equals(F("trotec")) || IrType.equals(F("toshiba_ac")) + || IrType.equals(F("haier_ac")) || IrType.equals(F("haier_ac_yrw02")) || IrType.equals(F("hitachi_ac")) + || IrType.equals(F("hitachi_ac1")) || IrType.equals(F("hitachi_ac2")) || IrType.equals(F("electra_ac")) + || IrType.equals(F("panasonic_ac")) || IrType.equals(F("samsung_ac")) || IrType.equals(F("whirlpool_ac")) + || IrType.equals(F("mwm")) || IrType.equals(F("tcl112ac"))) + {parseStringAndSendAirCon(strToDecodeType(IrType.c_str()), ircodestr); } + else { + sendIRCode(strToDecodeType(IrType.c_str()),IrCode,IrBits,IrRepeat); + } + + + } } addLog(LOG_LEVEL_INFO, String(F("IRTX: IR Code Sent: ")) + IrType_orig); diff --git a/src/define_plugin_sets.h b/src/define_plugin_sets.h index 0941f12b43..04c57f9913 100644 --- a/src/define_plugin_sets.h +++ b/src/define_plugin_sets.h @@ -185,7 +185,7 @@ To create/register a plugin, you have to : // Disable all settings like these when not needed: // #define DECODE_TOSHIBA_AC true // #define SEND_TOSHIBA_AC true -// The following are needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units +// The following define is needed for extended decoding of A/C Messages and or using standardised common arguments for controlling all deeply supported A/C units // #define P016_P035_Extended_AC #ifdef PLUGIN_BUILD_IR From 80880f72a6c9c1a1126aa044bd49849736c243b7 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Sat, 27 Apr 2019 15:24:10 +0300 Subject: [PATCH 05/15] [IR RX TX] More Refactoring --- src/_P035_IRTX.ino | 348 +++++++++++++++++---------------------------- 1 file changed, 127 insertions(+), 221 deletions(-) diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index 5304c1fe1e..42a158574a 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -88,7 +88,7 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) int argIndex = cmdCode.indexOf(','); if (argIndex) cmdCode = cmdCode.substring(0, argIndex); - if (cmdCode.equalsIgnoreCase(F("IRSEND")) && Plugin_035_irSender != 0) + if ((cmdCode.equalsIgnoreCase(F("IRSEND")) || cmdCode.equalsIgnoreCase(F("IRSENDAC"))) && Plugin_035_irSender != 0) { success = true; #ifdef PLUGIN_016 @@ -252,14 +252,14 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) //addLog(LOG_LEVEL_INFO, log); //sprintf_P(log, PSTR("IR Params2: RAW Code:%s"), IrRaw.c_str()); //addLog(LOG_LEVEL_INFO, log); - } else { + } else if (cmdCode.equalsIgnoreCase(F("IRSEND"))) { uint16_t IrRepeat=0; // unsigned long IrSecondCode=0UL; String ircodestr; if (GetArgv(string.c_str(), TmpStr1, 2)) { IrType = TmpStr1; IrType_orig = TmpStr1; - IrType.toLowerCase(); // To lower case to inprove compare speed + IrType.toUpperCase(); //strToDecodeType assumes all capital letters } if (GetArgv(string.c_str(), ircodestr, 3)) { IrCode = strtoull(ircodestr.c_str(), NULL, 16); @@ -269,25 +269,32 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) IrBits = 0; //Leave it to 0 for default protocol bits if (GetArgv(string.c_str(), TmpStr1, 5)) IrRepeat = str2int(TmpStr1.c_str()); // Nr. of times the message is to be repeated - //if (IrType.equals(F("raw"))) parseStringAndSendRaw(Plugin_035_irSender, code_str); //too big String is needed for this, - //also conflicts with the keyword RAW (for the encoding) and RAW as in the library meanning of the timmings information. - // if (IrType.equals(F("gc"))) parseStringAndSendGC(ircodestr); //Needs testing - // if (IrType.equals(F("pronto"))) parseStringAndSendPronto(ircodestr, 0); //Needs testing - - if (IrType.equals(F("mitsubishi_ac")) || IrType.equals(F("fujitsu_ac")) || IrType.equals(F("kelvinator")) - || IrType.equals(F("daikin")) || IrType.equals(F("daikin2")) || IrType.equals(F("gree")) - || IrType.equals(F("argo")) || IrType.equals(F("trotec")) || IrType.equals(F("toshiba_ac")) - || IrType.equals(F("haier_ac")) || IrType.equals(F("haier_ac_yrw02")) || IrType.equals(F("hitachi_ac")) - || IrType.equals(F("hitachi_ac1")) || IrType.equals(F("hitachi_ac2")) || IrType.equals(F("electra_ac")) - || IrType.equals(F("panasonic_ac")) || IrType.equals(F("samsung_ac")) || IrType.equals(F("whirlpool_ac")) - || IrType.equals(F("mwm")) || IrType.equals(F("tcl112ac"))) - {parseStringAndSendAirCon(strToDecodeType(IrType.c_str()), ircodestr); } - else { - sendIRCode(strToDecodeType(IrType.c_str()),IrCode,IrBits,IrRepeat); - } - + sendIRCode(strToDecodeType(IrType.c_str()),IrCode,ircodestr.c_str(),IrBits,IrRepeat); } + if (cmdCode.equalsIgnoreCase(F("IRSENDAC"))) { //Preliminary-test code for the standardized AC commands. + + // decode_type_t vendor, + // uint16_t model, //The specific model of A/C if applicable. + // bool on, //POWER ON or OFF + // stdAc::opmode_t mode, //What operating mode should the unit perform? e.g. Cool, Heat etc. + // float degrees, //What temperature should the unit be set to? + // bool celsius, //Use degreees Celsius, otherwise Fahrenheit. + // stdAc::fanspeed_t fan, //Fan Speed setting + // stdAc::swingv_t swingv, //Vertical swing setting + // stdAc::swingh_t swingh, //Horizontal Swing setting + // bool quiet, //Quiet setting ON or OFF + // bool turbo, //Turbo setting ON or OFF + // bool econo, //Economy setting ON or OFF + // bool light, //Light setting ON or OFF + // bool filter, //Filter setting ON or OFF + // bool clean, //Clean setting ON or OFF + // bool beep, //Beep setting ON or OFF + // int16_t sleep = -1, //Nr. of mins of sleep mode, or use sleep mode. (<= 0 means off.) + // int16_t clock //Nr. of mins past midnight to set the clock to. (< 0 means off.) + + //Plugin_035_irACSender = new IRac(irPin); + // ac.sendAc(GREE,); } addLog(LOG_LEVEL_INFO, String(F("IRTX: IR Code Sent: ")) + IrType_orig); @@ -328,33 +335,32 @@ boolean addErrorTrue() { // Returns: // bool: Successfully sent or not. bool sendIRCode(int const ir_type, - uint64_t const code, uint16_t bits, + uint64_t const code, char const * code_str, uint16_t bits, uint16_t repeat) { - // Create a pseudo-lock so we don't try to send two codes at the same time. - bool success = true; // Assume success. + IRsend *irsend = Plugin_035_irSender; - // send the IR message. + // send the IR message. switch (ir_type) { #if SEND_RC5 case RC5: // 1 if (bits == 0) bits = kRC5Bits; - Plugin_035_irSender->sendRC5(code, bits, repeat); + irsend->sendRC5(code, bits, repeat); break; #endif #if SEND_RC6 case RC6: // 2 if (bits == 0) bits = kRC6Mode0Bits; - Plugin_035_irSender->sendRC6(code, bits, repeat); + irsend->sendRC6(code, bits, repeat); break; #endif #if SEND_NEC case NEC: // 3 if (bits == 0) bits = kNECBits; - Plugin_035_irSender->sendNEC(code, bits, repeat); + irsend->sendNEC(code, bits, repeat); break; #endif #if SEND_SONY @@ -362,42 +368,42 @@ bool sendIRCode(int const ir_type, if (bits == 0) bits = kSony12Bits; repeat = std::max(repeat, kSonyMinRepeat); - Plugin_035_irSender->sendSony(code, bits, repeat); + irsend->sendSony(code, bits, repeat); break; #endif #if SEND_PANASONIC case PANASONIC: // 5 if (bits == 0) bits = kPanasonicBits; - Plugin_035_irSender->sendPanasonic64(code, bits, repeat); + irsend->sendPanasonic64(code, bits, repeat); break; #endif #if SEND_JVC case JVC: // 6 if (bits == 0) bits = kJvcBits; - Plugin_035_irSender->sendJVC(code, bits, repeat); + irsend->sendJVC(code, bits, repeat); break; #endif #if SEND_SAMSUNG case SAMSUNG: // 7 if (bits == 0) bits = kSamsungBits; - Plugin_035_irSender->sendSAMSUNG(code, bits, repeat); + irsend->sendSAMSUNG(code, bits, repeat); break; #endif #if SEND_SAMSUNG36 case SAMSUNG36: // 56 if (bits == 0) bits = kSamsung36Bits; - Plugin_035_irSender->sendSamsung36(code, bits, repeat); + irsend->sendSamsung36(code, bits, repeat); break; #endif #if SEND_WHYNTER case WHYNTER: // 8 if (bits == 0) bits = kWhynterBits; - Plugin_035_irSender->sendWhynter(code, bits, repeat); + irsend->sendWhynter(code, bits, repeat); break; #endif #if SEND_AIWA_RC_T501 @@ -405,14 +411,14 @@ bool sendIRCode(int const ir_type, if (bits == 0) bits = kAiwaRcT501Bits; repeat = std::max(repeat, kAiwaRcT501MinRepeats); - Plugin_035_irSender->sendAiwaRCT501(code, bits, repeat); + irsend->sendAiwaRCT501(code, bits, repeat); break; #endif #if SEND_LG case LG: // 10 if (bits == 0) bits = kLgBits; - Plugin_035_irSender->sendLG(code, bits, repeat); + irsend->sendLG(code, bits, repeat); break; #endif #if SEND_MITSUBISHI @@ -420,7 +426,7 @@ bool sendIRCode(int const ir_type, if (bits == 0) bits = kMitsubishiBits; repeat = std::max(repeat, kMitsubishiMinRepeat); - Plugin_035_irSender->sendMitsubishi(code, bits, repeat); + irsend->sendMitsubishi(code, bits, repeat); break; #endif #if SEND_DISH @@ -428,28 +434,49 @@ bool sendIRCode(int const ir_type, if (bits == 0) bits = kDishBits; repeat = std::max(repeat, kDishMinRepeat); - Plugin_035_irSender->sendDISH(code, bits, repeat); + irsend->sendDISH(code, bits, repeat); break; #endif #if SEND_SHARP case SHARP: // 14 if (bits == 0) bits = kSharpBits; - Plugin_035_irSender->sendSharpRaw(code, bits, repeat); + irsend->sendSharpRaw(code, bits, repeat); break; #endif #if SEND_COOLIX case COOLIX: // 15 if (bits == 0) bits = kCoolixBits; - Plugin_035_irSender->sendCOOLIX(code, bits, repeat); + irsend->sendCOOLIX(code, bits, repeat); + break; +#endif + case DAIKIN: // 16 + case DAIKIN2: // 53 + case KELVINATOR: // 18 + case MITSUBISHI_AC: // 20 + case GREE: // 24 + case ARGO: // 27 + case TROTEC: // 28 + case TOSHIBA_AC: // 32 + case FUJITSU_AC: // 33 + case HAIER_AC: // 38 + case HAIER_AC_YRW02: // 44 + case HITACHI_AC: // 40 + case HITACHI_AC1: // 41 + case HITACHI_AC2: // 42 + case WHIRLPOOL_AC: // 45 + case SAMSUNG_AC: // 46 + case ELECTRA_AC: // 48 + case PANASONIC_AC: // 49 + case MWM: // 52 + success = parseStringAndSendAirCon(ir_type, code_str); break; -#endif #if SEND_DENON case DENON: // 17 if (bits == 0) bits = DENON_BITS; - Plugin_035_irSender->sendDenon(code, bits, repeat); + irsend->sendDenon(code, bits, repeat); break; #endif #if SEND_SHERWOOD @@ -457,63 +484,78 @@ bool sendIRCode(int const ir_type, if (bits == 0) bits = kSherwoodBits; repeat = std::max(repeat, kSherwoodMinRepeat); - Plugin_035_irSender->sendSherwood(code, bits, repeat); + irsend->sendSherwood(code, bits, repeat); break; #endif #if SEND_RCMM case RCMM: // 21 if (bits == 0) bits = kRCMMBits; - Plugin_035_irSender->sendRCMM(code, bits, repeat); + irsend->sendRCMM(code, bits, repeat); break; #endif #if SEND_SANYO case SANYO_LC7461: // 22 if (bits == 0) bits = kSanyoLC7461Bits; - Plugin_035_irSender->sendSanyoLC7461(code, bits, repeat); + irsend->sendSanyoLC7461(code, bits, repeat); break; #endif #if SEND_RC5 case RC5X: // 23 if (bits == 0) bits = kRC5XBits; - Plugin_035_irSender->sendRC5(code, bits, repeat); + irsend->sendRC5(code, bits, repeat); + break; +#endif +#if SEND_PRONTO + case PRONTO: // 25 + // success = parseStringAndSendPronto(irsend, code_str, repeat); break; #endif #if SEND_NIKAI case NIKAI: // 29 if (bits == 0) bits = kNikaiBits; - Plugin_035_irSender->sendNikai(code, bits, repeat); + irsend->sendNikai(code, bits, repeat); + break; +#endif +#if SEND_RAW + case RAW: // 30 + //success = parseStringAndSendRaw(irsend, code_str); + break; +#endif +#if SEND_GLOBALCACHE + case GLOBALCACHE: // 31 + // success = parseStringAndSendGC(irsend, code_str); break; #endif #if SEND_MIDEA case MIDEA: // 34 if (bits == 0) bits = kMideaBits; - Plugin_035_irSender->sendMidea(code, bits, repeat); + irsend->sendMidea(code, bits, repeat); break; #endif #if SEND_MAGIQUEST case MAGIQUEST: // 35 if (bits == 0) bits = kMagiquestBits; - Plugin_035_irSender->sendMagiQuest(code, bits, repeat); + irsend->sendMagiQuest(code, bits, repeat); break; #endif #if SEND_LASERTAG case LASERTAG: // 36 if (bits == 0) bits = kLasertagBits; - Plugin_035_irSender->sendLasertag(code, bits, repeat); + irsend->sendLasertag(code, bits, repeat); break; #endif #if SEND_CARRIER_AC case CARRIER_AC: // 37 if (bits == 0) bits = kCarrierAcBits; - Plugin_035_irSender->sendCarrierAC(code, bits, repeat); + irsend->sendCarrierAC(code, bits, repeat); break; #endif #if SEND_MITSUBISHI2 @@ -521,7 +563,7 @@ bool sendIRCode(int const ir_type, if (bits == 0) bits = kMitsubishiBits; repeat = std::max(repeat, kMitsubishiMinRepeat); - Plugin_035_irSender->sendMitsubishi2(code, bits, repeat); + irsend->sendMitsubishi2(code, bits, repeat); break; #endif #if SEND_GICABLE @@ -529,49 +571,49 @@ bool sendIRCode(int const ir_type, if (bits == 0) bits = kGicableBits; repeat = std::max(repeat, kGicableMinRepeat); - Plugin_035_irSender->sendGICable(code, bits, repeat); + irsend->sendGICable(code, bits, repeat); break; #endif #if SEND_LUTRON case LUTRON: // 47 if (bits == 0) bits = kLutronBits; - Plugin_035_irSender->sendLutron(code, bits, repeat); + irsend->sendLutron(code, bits, repeat); break; #endif #if SEND_PIONEER case PIONEER: // 50 if (bits == 0) bits = kPioneerBits; - Plugin_035_irSender->sendPioneer(code, bits, repeat); + irsend->sendPioneer(code, bits, repeat); break; #endif #if SEND_LG case LG2: // 51 if (bits == 0) bits = kLgBits; - Plugin_035_irSender->sendLG2(code, bits, repeat); + irsend->sendLG2(code, bits, repeat); break; #endif #if SEND_VESTEL_AC case VESTEL_AC: // 54 if (bits == 0) bits = kVestelAcBits; - Plugin_035_irSender->sendVestelAc(code, bits, repeat); + irsend->sendVestelAc(code, bits, repeat); break; #endif #if SEND_TECO case TECO: // 55 if (bits == 0) bits = kTecoBits; - Plugin_035_irSender->sendTeco(code, bits, repeat); + irsend->sendTeco(code, bits, repeat); break; #endif #if SEND_LEGOPF case LEGOPF: // 58 if (bits == 0) bits = kLegoPfBits; - Plugin_035_irSender->sendLegoPf(code, bits, repeat); + irsend->sendLegoPf(code, bits, repeat); break; #endif default: @@ -586,8 +628,9 @@ bool sendIRCode(int const ir_type, // Args: // irType: Nr. of the protocol we need to send. // str: A hexadecimal string containing the state to be sent. -bool parseStringAndSendAirCon(const uint16_t irType, const String& str) { - uint8_t strOffset = 0; +bool parseStringAndSendAirCon(const uint16_t irType, const String str) { +IRsend *irsend = Plugin_035_irSender; +uint8_t strOffset = 0; uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. uint16_t stateSize = 0; @@ -723,7 +766,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. @@ -738,110 +782,110 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String& str) { switch (irType) { #if SEND_KELVINATOR case KELVINATOR: - Plugin_035_irSender->sendKelvinator(reinterpret_cast(state)); + irsend->sendKelvinator(reinterpret_cast(state)); break; #endif #if SEND_TOSHIBA_AC case TOSHIBA_AC: - Plugin_035_irSender->sendToshibaAC(reinterpret_cast(state)); + irsend->sendToshibaAC(reinterpret_cast(state)); break; #endif #if SEND_DAIKIN case DAIKIN: - Plugin_035_irSender->sendDaikin(reinterpret_cast(state)); + irsend->sendDaikin(reinterpret_cast(state)); break; #endif #if SEND_DAIKIN2 case DAIKIN2: - Plugin_035_irSender->sendDaikin2(reinterpret_cast(state)); + irsend->sendDaikin2(reinterpret_cast(state)); break; #endif #if SEND_MITSUBISHI_AC case MITSUBISHI_AC: - Plugin_035_irSender->sendMitsubishiAC(reinterpret_cast(state)); + irsend->sendMitsubishiAC(reinterpret_cast(state)); break; #endif #if SEND_MITSUBISHIHEAVY case MITSUBISHI_HEAVY_88: // 59 - Plugin_035_irSender->sendMitsubishiHeavy88(reinterpret_cast(state)); + irsend->sendMitsubishiHeavy88(reinterpret_cast(state)); break; case MITSUBISHI_HEAVY_152: // 60 - Plugin_035_irSender->sendMitsubishiHeavy152(reinterpret_cast(state)); + irsend->sendMitsubishiHeavy152(reinterpret_cast(state)); break; #endif // SEND_MITSUBISHIHEAVY #if SEND_TROTEC case TROTEC: - Plugin_035_irSender->sendTrotec(reinterpret_cast(state)); + irsend->sendTrotec(reinterpret_cast(state)); break; #endif #if SEND_ARGO case ARGO: - Plugin_035_irSender->sendArgo(reinterpret_cast(state)); + irsend->sendArgo(reinterpret_cast(state)); break; #endif #if SEND_GREE case GREE: - Plugin_035_irSender->sendGree(reinterpret_cast(state)); + irsend->sendGree(reinterpret_cast(state)); break; #endif #if SEND_FUJITSU_AC case FUJITSU_AC: - Plugin_035_irSender->sendFujitsuAC(reinterpret_cast(state), stateSize); + irsend->sendFujitsuAC(reinterpret_cast(state), stateSize); break; #endif #if SEND_HAIER_AC case HAIER_AC: - Plugin_035_irSender->sendHaierAC(reinterpret_cast(state)); + irsend->sendHaierAC(reinterpret_cast(state)); break; #endif #if SEND_HAIER_AC_YRW02 case HAIER_AC_YRW02: - Plugin_035_irSender->sendHaierACYRW02(reinterpret_cast(state)); + irsend->sendHaierACYRW02(reinterpret_cast(state)); break; #endif #if SEND_HITACHI_AC case HITACHI_AC: - Plugin_035_irSender->sendHitachiAC(reinterpret_cast(state)); + irsend->sendHitachiAC(reinterpret_cast(state)); break; #endif #if SEND_HITACHI_AC1 case HITACHI_AC1: - Plugin_035_irSender->sendHitachiAC1(reinterpret_cast(state)); + irsend->sendHitachiAC1(reinterpret_cast(state)); break; #endif #if SEND_HITACHI_AC2 case HITACHI_AC2: - Plugin_035_irSender->sendHitachiAC2(reinterpret_cast(state)); + irsend->sendHitachiAC2(reinterpret_cast(state)); break; #endif #if SEND_WHIRLPOOL_AC case WHIRLPOOL_AC: - Plugin_035_irSender->sendWhirlpoolAC(reinterpret_cast(state)); + irsend->sendWhirlpoolAC(reinterpret_cast(state)); break; #endif #if SEND_SAMSUNG_AC case SAMSUNG_AC: - Plugin_035_irSender->sendSamsungAC(reinterpret_cast(state), stateSize); + irsend->sendSamsungAC(reinterpret_cast(state), stateSize); break; #endif #if SEND_ELECTRA_AC case ELECTRA_AC: - Plugin_035_irSender->sendElectraAC(reinterpret_cast(state)); + irsend->sendElectraAC(reinterpret_cast(state)); break; #endif #if SEND_PANASONIC_AC case PANASONIC_AC: - Plugin_035_irSender->sendPanasonicAC(reinterpret_cast(state)); + irsend->sendPanasonicAC(reinterpret_cast(state)); break; #endif #if SEND_MWM case MWM: - Plugin_035_irSender->sendMWM(reinterpret_cast(state), stateSize); + irsend->sendMWM(reinterpret_cast(state), stateSize); break; #endif #if SEND_TCL112AC case TCL112AC: - Plugin_035_irSender->sendTcl112Ac(reinterpret_cast(state)); + irsend->sendTcl112Ac(reinterpret_cast(state)); break; #endif default: @@ -851,144 +895,6 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String& str) { return true; // We were successful as far as we can tell. } - -#if SEND_PRONTO -// Parse a Pronto Hex String/code and send it. -// Args: -// 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, -// 0018,0018,0030,0018,0030,0018,0030,0018,0018,0018,0018,0018, -// 0030,0018,0018,0018,0018,0018,0030,0018,0018,03f6" -// or -// "0000,0067,0000,0015,0060,0018". i.e. without the Repeat value -// Requires at least PRONTO_MIN_LENGTH comma-separated values. -// sendPronto() only supports raw pronto code types, thus so does this. -// repeats: Nr. of times the message is to be repeated. -// This value is ignored if an embeddd repeat is found in str. -void parseStringAndSendPronto(const String& str, uint16_t repeats) { - uint16_t count; - uint16_t *code_array; - int16_t index = -1; - uint16_t start_from = 0; - - // Find out how many items there are in the string. - count = countValuesInStr(str, ','); - - // Check if we have the optional embedded repeats value in the code string. - if (str.startsWith("R") || str.startsWith("r")) { - // Grab the first value from the string, as it is the nr. of repeats. - index = str.indexOf(',', start_from); - repeats = str.substring(start_from + 1, index).toInt(); // Skip the 'R'. - start_from = index + 1; - count--; // We don't count the repeats value as part of the code array. - } - - // We need at least PRONTO_MIN_LENGTH values for the code part. - if (count < PRONTO_MIN_LENGTH) return; - - // Now we know how many there are, allocate the memory to store them all. - code_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); - - // Rest of the string are values for the code array. - // Now convert the hex strings to integers and place them in code_array. - count = 0; - do { - index = str.indexOf(',', start_from); - // Convert the hexadecimal value string to an unsigned integer. - code_array[count] = strtoul(str.substring(start_from, index).c_str(), - NULL, 16); - start_from = index + 1; - count++; - } while (index != -1); - - Plugin_035_irSender->sendPronto(code_array, count, repeats); // All done. Send it. - free(code_array); // Free up the memory allocated. -} -#endif // SEND_PRONTO -//#if SEND_RAW -//// Parse an IRremote Raw Hex String/code and send it. -//// Args: -//// 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. -//void parseStringAndSendRaw(const String& str) { -// uint16_t count; -// uint16_t freq = 38000; // Default to 38kHz. -// uint16_t *raw_array; -// -// // Find out how many items there are in the string. -// count = countValuesInStr(str, ','); -// -// // We expect the frequency as the first comma separated value, so we need at -// // least two values. If not, bail out. -// if (count < 2) return; -// count--; // We don't count the frequency value as part of the raw array. -// -// // Now we know how many there are, allocate the memory to store them all. -// raw_array = newCodeArray(count); -// -// // Grab the first value from the string, as it is the frequency. -// int16_t index = str.indexOf(',', 0); -// freq = str.substring(0, index).toInt(); -// uint16_t start_from = index + 1; -// // Rest of the string are values for the raw array. -// // Now convert the strings to integers and place them in raw_array. -// count = 0; -// do { -// index = str.indexOf(',', start_from); -// raw_array[count] = str.substring(start_from, index).toInt(); -// start_from = index + 1; -// count++; -// } while (index != -1); -// -// Plugin_035_irSender->sendRaw(raw_array, count, freq); // All done. Send it. -// free(raw_array); // Free up the memory allocated. -//} -//#endif // SEND_RAW -#if SEND_GLOBALCACHE -// Parse a GlobalCache String/code and send it. -// Args: -// 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, -// 20,20,20,20,20,20,20,20,20,20,20,20,20,63,20,20,20,63,20,63,20, -// 63,20,63,20,63,20,63,20,1798" -// Note: The leading "1:1,1," of normal GC codes should be removed. -void parseStringAndSendGC(const String& str) { - uint16_t count; - uint16_t *code_array; - String tmp_str; - - // Remove the leading "1:1,1," if present. - if (str.startsWith("1:1,1,")) - tmp_str = str.substring(6); - else - tmp_str = str; - - // Find out how many items there are in the string. - count = countValuesInStr(tmp_str, ','); - - // Now we know how many there are, allocate the memory to store them all. - code_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); - - // Now convert the strings to integers and place them in code_array. - count = 0; - uint16_t start_from = 0; - int16_t index = -1; - do { - index = tmp_str.indexOf(',', start_from); - code_array[count] = tmp_str.substring(start_from, index).toInt(); - start_from = index + 1; - count++; - } while (index != -1); - - Plugin_035_irSender->sendGC(code_array, count); // All done. Send it. - free(code_array); // Free up the memory allocated. -} -#endif // SEND_GLOBALCACHE - // Count how many values are in the String. // Args: // str: String containing the values. From 853c85e3e6ebaf15899ead31de0a54f6d8944536 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Fri, 7 Jun 2019 10:27:17 +0300 Subject: [PATCH 06/15] [IR TX RX] Update IR Lib --- lib/IRremoteESP8266/.gitignore | 1 + lib/IRremoteESP8266/README.md | 4 +- lib/IRremoteESP8266/ReleaseNotes.md | 42 + .../examples/IRMQTTServer/IRMQTTServer.h | 44 +- .../examples/IRMQTTServer/IRMQTTServer.ino | 470 +++++- .../examples/IRMQTTServer/platformio.ini | 2 +- .../examples/IRrecvDumpV2/IRrecvDumpV2.ino | 41 +- .../examples/TurnOnArgoAC/TurnOnArgoAC.ino | 2 +- .../TurnOnFujitsuAC/TurnOnFujitsuAC.ino | 6 +- lib/IRremoteESP8266/keywords.txt | 263 +++- lib/IRremoteESP8266/library.json | 2 +- lib/IRremoteESP8266/library.properties | 2 +- lib/IRremoteESP8266/src/IRac.cpp | 194 ++- lib/IRremoteESP8266/src/IRac.h | 49 +- lib/IRremoteESP8266/src/IRrecv.cpp | 26 +- lib/IRremoteESP8266/src/IRrecv.h | 85 +- lib/IRremoteESP8266/src/IRremoteESP8266.h | 48 +- lib/IRremoteESP8266/src/IRsend.cpp | 8 + lib/IRremoteESP8266/src/IRsend.h | 149 +- lib/IRremoteESP8266/src/IRutils.cpp | 100 +- lib/IRremoteESP8266/src/IRutils.h | 29 +- lib/IRremoteESP8266/src/ir_Argo.cpp | 399 +++-- lib/IRremoteESP8266/src/ir_Argo.h | 146 +- lib/IRremoteESP8266/src/ir_Coolix.cpp | 49 + lib/IRremoteESP8266/src/ir_Coolix.h | 3 + lib/IRremoteESP8266/src/ir_Daikin.cpp | 1400 +++++++++++------ lib/IRremoteESP8266/src/ir_Daikin.h | 269 +++- lib/IRremoteESP8266/src/ir_Denon.cpp | 8 +- lib/IRremoteESP8266/src/ir_Fujitsu.cpp | 424 +++-- lib/IRremoteESP8266/src/ir_Fujitsu.h | 82 +- lib/IRremoteESP8266/src/ir_Goodweather.cpp | 499 ++++++ lib/IRremoteESP8266/src/ir_Goodweather.h | 140 ++ lib/IRremoteESP8266/src/ir_Gree.cpp | 128 +- lib/IRremoteESP8266/src/ir_Gree.h | 52 +- lib/IRremoteESP8266/src/ir_Haier.cpp | 242 ++- lib/IRremoteESP8266/src/ir_Haier.h | 97 +- lib/IRremoteESP8266/src/ir_Hitachi.cpp | 96 +- lib/IRremoteESP8266/src/ir_Hitachi.h | 31 +- lib/IRremoteESP8266/src/ir_Inax.cpp | 98 ++ lib/IRremoteESP8266/src/ir_Kelvinator.cpp | 237 +-- lib/IRremoteESP8266/src/ir_Kelvinator.h | 65 +- lib/IRremoteESP8266/src/ir_LG.cpp | 6 +- lib/IRremoteESP8266/src/ir_Midea.cpp | 109 +- lib/IRremoteESP8266/src/ir_Midea.h | 35 +- lib/IRremoteESP8266/src/ir_Mitsubishi.cpp | 185 ++- lib/IRremoteESP8266/src/ir_Mitsubishi.h | 66 +- .../src/ir_MitsubishiHeavy.cpp | 145 +- lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h | 13 +- lib/IRremoteESP8266/src/ir_Panasonic.cpp | 190 ++- lib/IRremoteESP8266/src/ir_Panasonic.h | 59 +- lib/IRremoteESP8266/src/ir_Samsung.cpp | 171 +- lib/IRremoteESP8266/src/ir_Samsung.h | 46 +- lib/IRremoteESP8266/src/ir_Sharp.cpp | 412 ++++- lib/IRremoteESP8266/src/ir_Sharp.h | 99 ++ lib/IRremoteESP8266/src/ir_Tcl.cpp | 71 +- lib/IRremoteESP8266/src/ir_Tcl.h | 11 +- lib/IRremoteESP8266/src/ir_Teco.cpp | 58 +- lib/IRremoteESP8266/src/ir_Teco.h | 3 + lib/IRremoteESP8266/src/ir_Toshiba.cpp | 146 +- lib/IRremoteESP8266/src/ir_Toshiba.h | 37 +- lib/IRremoteESP8266/src/ir_Trotec.cpp | 236 ++- lib/IRremoteESP8266/src/ir_Trotec.h | 42 +- lib/IRremoteESP8266/src/ir_Vestel.cpp | 159 +- lib/IRremoteESP8266/src/ir_Vestel.h | 35 +- lib/IRremoteESP8266/src/ir_Whirlpool.cpp | 243 +-- lib/IRremoteESP8266/src/ir_Whirlpool.h | 53 +- lib/IRremoteESP8266/test/IRac_test.cpp | 124 +- lib/IRremoteESP8266/test/IRrecv_test.cpp | 8 +- lib/IRremoteESP8266/test/IRsend_test.cpp | 106 +- lib/IRremoteESP8266/test/IRsend_test.h | 20 + lib/IRremoteESP8266/test/IRutils_test.cpp | 73 + lib/IRremoteESP8266/test/Makefile | 42 +- 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 | 36 + lib/IRremoteESP8266/test/ir_Daikin_test.cpp | 1146 ++++++++++---- lib/IRremoteESP8266/test/ir_Denon_test.cpp | 40 +- lib/IRremoteESP8266/test/ir_Dish_test.cpp | 8 + lib/IRremoteESP8266/test/ir_Electra_test.cpp | 1 + lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp | 615 +++++--- 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 | 88 +- 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 | 5 + 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 | 69 + .../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 | 127 ++ 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 | 36 +- lib/IRremoteESP8266/test/ir_Teco_test.cpp | 33 + 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 | 19 +- 119 files changed, 10437 insertions(+), 2699 deletions(-) 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_Sharp.h 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_Trotec_test.cpp diff --git a/lib/IRremoteESP8266/.gitignore b/lib/IRremoteESP8266/.gitignore index 23e21ca3e3..1b1e81bb88 100644 --- a/lib/IRremoteESP8266/.gitignore +++ b/lib/IRremoteESP8266/.gitignore @@ -10,6 +10,7 @@ ## Build environments # Platformio +**/.pio/ **/.pioenvs/ **/.piolibdeps/ **/.clang_complete diff --git a/lib/IRremoteESP8266/README.md b/lib/IRremoteESP8266/README.md index 03c09a2b13..1eaaa21b44 100644 --- a/lib/IRremoteESP8266/README.md +++ b/lib/IRremoteESP8266/README.md @@ -8,8 +8,8 @@ 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. -## 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.0 Now Available +Version 2.6.0 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. diff --git a/lib/IRremoteESP8266/ReleaseNotes.md b/lib/IRremoteESP8266/ReleaseNotes.md index ed7c71005c..98416a12af 100644 --- a/lib/IRremoteESP8266/ReleaseNotes.md +++ b/lib/IRremoteESP8266/ReleaseNotes.md @@ -1,5 +1,47 @@ # Release Notes +## _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/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h index 85ac0e1d31..7567950477 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h @@ -78,6 +78,12 @@ const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. #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 @@ -94,6 +100,26 @@ const uint16_t kMinUnknownSize = 2 * 10; #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 -------------------------------- // Change if you need multiple independent send gpio/topics. const uint8_t gpioTable[] = { @@ -155,7 +181,7 @@ const uint8_t kPasswordLength = 20; // ----------------- End of User Configuration Section ------------------------- // Constants -#define _MY_VERSION_ "v1.0.0-gamma" +#define _MY_VERSION_ "v1.1.1-beta" const uint8_t kSendTableSize = sizeof(gpioTable); // JSON stuff @@ -173,6 +199,10 @@ const char* kHttpPassKey = "http_pass"; #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 * kCommandDelimiter = ","; +const char kPauseChar = 'P'; void mqttCallback(char* topic, byte* payload, unsigned int length); String listOfCommandTopics(void); @@ -185,7 +215,7 @@ 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 commonAcState_t state, const bool retain, + const stdAc::state_t state, const bool retain, const bool force); #endif // MQTT_ENABLE bool isSerialGpioUsedByIr(void); @@ -249,10 +279,12 @@ 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); -commonAcState_t updateClimate(commonAcState_t current, const String str, +stdAc::state_t updateClimate(stdAc::state_t current, const String str, const String prefix, const String payload); -bool cmpClimate(const commonAcState_t a, const commonAcState_t b); -bool sendClimate(const commonAcState_t prev, const commonAcState_t next, +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 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 f8ac49c48c..272abe2d25 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -102,6 +102,19 @@ * 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. @@ -325,6 +338,7 @@ 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. IRsend *IrSendTable[kSendTableSize]; +String lastClimateSource; #ifdef IR_RX String lastIrReceived = "None"; @@ -333,8 +347,8 @@ uint32_t irRecvCounter = 0; #endif // IR_RX // Climate stuff -commonAcState_t climate; -commonAcState_t climate_prev; +stdAc::state_t climate; +stdAc::state_t climate_prev; IRac commonAc(gpioTable[0]); TimerMs lastClimateIr = TimerMs(); // When we last sent the IR Climate mesg. uint32_t irClimateCounter = 0; // How many have we sent? @@ -590,6 +604,8 @@ void handleRoot(void) { "" "" "" + "" + "" "" "" "" @@ -652,6 +668,7 @@ void handleRoot(void) { "" "" "" + "" "" "" "" @@ -666,6 +683,7 @@ void handleRoot(void) { "" "" "" + "" "" "" "" @@ -1060,7 +1078,7 @@ void handleAirConSet(void) { return server.requestAuthentication(); } #endif - commonAcState_t result = climate; + 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)); @@ -1071,6 +1089,7 @@ void handleAirConSet(void) { #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. @@ -1208,6 +1227,7 @@ void handleInfo(void) { "

Climate Information

" "

" "IR Send GPIO: " + String(gpioTable[0]) + "
" + "Last update source: " + lastClimateSource + "
" "Total sent: " + String(irClimateCounter) + "
" "Last send: " + String(hasClimateBeenSent ? (String(lastClimateSucceeded ? "Ok" : "FAILED") + @@ -1333,11 +1353,28 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, 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 DAIKIN2: stateSize = kDaikin2StateLength; break; + case DAIKIN216: + stateSize = kDaikin216StateLength; + break; case ELECTRA_AC: stateSize = kElectraAcStateLength; break; @@ -1412,6 +1449,9 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, // 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 @@ -1482,6 +1522,11 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, 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)); @@ -1550,6 +1595,11 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, 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)); @@ -1653,7 +1703,6 @@ bool parseStringAndSendGC(IRsend *irsend, const String str) { start_from = index + 1; count++; } while (index != -1); - irsend->sendGC(code_array, count); // All done. Send it. free(code_array); // Free up the memory allocated. if (count > 0) @@ -1956,6 +2005,7 @@ void setup(void) { climate.sleep = -1; // Off climate.clock = -1; // Don't set. climate_prev = climate; + lastClimateSource = F("None"); // Initialise all the IR transmitters. for (uint8_t i = 0; i < kSendTableSize; i++) { @@ -2201,7 +2251,7 @@ void handleSendMqttDiscovery(void) { } void doBroadcast(TimerMs *timer, const uint32_t interval, - const commonAcState_t state, const bool retain, + const stdAc::state_t state, const bool retain, const bool force) { if (force || (!lockMqttBroadcast && timer->elapsed() > interval)) { debug("Sending MQTT stat update broadcast."); @@ -2213,7 +2263,6 @@ void doBroadcast(TimerMs *timer, const uint32_t interval, } 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; @@ -2232,10 +2281,11 @@ void receivingMQTT(String const topic_name, String const callback_str) { if (topic_name.startsWith(MqttClimate)) { if (topic_name.startsWith(MqttClimateCmnd)) { debug("It's a climate command topic"); - commonAcState_t updated = updateClimate( + stdAc::state_t updated = updateClimate( climate, topic_name, MqttClimateCmnd, callback_str); - sendClimate(climate, updated, MqttClimateStat, - true, false, false); + 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"); @@ -2261,33 +2311,61 @@ void receivingMQTT(String const topic_name, String const callback_str) { debug("MQTT Payload (raw):"); debug(callback_c_str); - // 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); + // 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); } - // 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( - IrSendTable[channel], ir_type, code, - callback_str.substring(callback_str.indexOf(",") + 1).c_str(), - nbits, repeat); } // Callback function, when we receive an MQTT value on the topics @@ -2403,6 +2481,7 @@ void loop(void) { 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. } @@ -2418,7 +2497,7 @@ void loop(void) { if (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) { @@ -2438,14 +2517,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); + lastIrReceived += kCommandDelimiter[0] + String(capture.bits); #if MQTT_ENABLE mqtt_client.publish(MqttRecv.c_str(), lastIrReceived.c_str()); mqttSentCounter++; -#endif // MQTT_ENABLE - irRecvCounter++; debug("Incoming IR message sent to MQTT:"); debug(lastIrReceived.c_str()); +#endif // MQTT_ENABLE + irRecvCounter++; +#if USE_DECODED_AC_SETTINGS + if (decodeCommonAc(&capture)) lastClimateSource = F("IR"); +#endif // USE_DECODED_AC_SETTINGS } #endif // IR_RX delay(100); @@ -2492,6 +2574,10 @@ bool sendIRCode(IRsend *irsend, int const ir_type, bool success = true; // Assume success. + // Turn off IR capture if we need to. +#if defined (IR_RX) && DISABLE_CAPTURE_WHILE_TRANSMITTING + irrecv.disableIRIn(); // Stop the IR receiver +#endif // defined (IR_RX) && DISABLE_CAPTURE_WHILE_TRANSMITTING // send the IR message. switch (ir_type) { #if SEND_RC5 @@ -2530,6 +2616,14 @@ bool sendIRCode(IRsend *irsend, int const ir_type, 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) @@ -2605,6 +2699,7 @@ bool sendIRCode(IRsend *irsend, int const ir_type, #endif case DAIKIN: // 16 case DAIKIN2: // 53 + case DAIKIN216: // 61 case KELVINATOR: // 18 case MITSUBISHI_AC: // 20 case GREE: // 24 @@ -2619,6 +2714,7 @@ bool sendIRCode(IRsend *irsend, int const ir_type, 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 @@ -2627,7 +2723,7 @@ bool sendIRCode(IRsend *irsend, int const ir_type, #if SEND_DENON case DENON: // 17 if (bits == 0) - bits = DENON_BITS; + bits = kDenonBits; irsend->sendDenon(code, bits, repeat); break; #endif @@ -2768,10 +2864,21 @@ bool sendIRCode(IRsend *irsend, int const ir_type, 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; } + // Turn IR capture back on if we need to. +#if defined (IR_RX) && DISABLE_CAPTURE_WHILE_TRANSMITTING + irrecv.enableIRIn(); // Restart the receiver +#endif // defined (IR_RX) && DISABLE_CAPTURE_WHILE_TRANSMITTING lastSendTime = millis(); // Release the lock. lockIr = false; @@ -2796,11 +2903,14 @@ bool sendIRCode(IRsend *irsend, int const ir_type, #if MQTT_ENABLE if (success) { if (ir_type == PRONTO && repeat > 0) - mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + ",R" + - String(repeat) + "," + + 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.c_str(), (String(ir_type) + "," + + mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + + kCommandDelimiter[0] + String(code_str)).c_str()); mqttSentCounter++; } @@ -2811,9 +2921,12 @@ bool sendIRCode(IRsend *irsend, int const ir_type, debug(("Repeats: " + String(repeat)).c_str()); #if MQTT_ENABLE if (success) { - mqtt_client.publish(MqttAck.c_str(), (String(ir_type) + "," + - uint64ToString(code, 16) - + "," + String(bits) + "," + + 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++; } @@ -2858,9 +2971,9 @@ bool sendFloat(const String topic, const float_t temp, const bool retain) { #endif // MQTT_ENABLE } -commonAcState_t updateClimate(commonAcState_t current, const String str, +stdAc::state_t updateClimate(stdAc::state_t current, const String str, const String prefix, const String payload) { - commonAcState_t result = current; + stdAc::state_t result = current; String value = payload; value.toUpperCase(); if (str.equals(prefix + KEY_PROTOCOL)) @@ -2902,7 +3015,7 @@ commonAcState_t updateClimate(commonAcState_t current, const String str, // Compare two AirCon states (climates). // Returns: True if they differ, False if they don't. -bool cmpClimate(const commonAcState_t a, const commonAcState_t b) { +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 || @@ -2911,9 +3024,10 @@ bool cmpClimate(const commonAcState_t a, const commonAcState_t b) { a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep; } -bool sendClimate(const commonAcState_t prev, const commonAcState_t next, +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 forceMQTT, const bool forceIR, + const bool enableIR) { bool diff = false; bool success = true; @@ -2993,13 +3107,21 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, else debug("NO difference in common A/C state detected."); // Only send an IR message if we need to. - if ((diff && !forceMQTT) || forceIR) { + if (enableIR && ((diff && !forceMQTT) || forceIR)) { debug("Sending common A/C state via IR."); + // Turn IR capture off if we need to. +#if defined (IR_RX) && DISABLE_CAPTURE_WHILE_TRANSMITTING + irrecv.disableIRIn(); // Stop the IR receiver +#endif // defined (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); + // Turn IR capture back on if we need to. +#if defined (IR_RX) && DISABLE_CAPTURE_WHILE_TRANSMITTING + irrecv.enableIRIn(); // Restart the receiver +#endif // defined (IR_RX) && DISABLE_CAPTURE_WHILE_TRANSMITTING if (lastClimateSucceeded) hasClimateBeenSent = true; success &= lastClimateSucceeded; lastClimateIr.reset(); @@ -3008,3 +3130,247 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, } return success; } + +#if USE_DECODED_AC_SETTINGS && defined (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"); + switch (decode->decode_type) { +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_ARGO +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(IR_LED); + 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(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN216 +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(IR_LED); + 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(IR_LED); + 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(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(IR_LED); + 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(IR_LED); + 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(IR_LED); + 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(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_KELVINATOR +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(IR_LED); + 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(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + IRPanasonicAc ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_SHARP_AC +#if DECODE_TCL112AC + case decode_type_t::TCL112AC: { + IRTcl112Ac ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_TCL112AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(IR_LED); + 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(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(IR_LED); + ac.setRaw(decode->state); + state = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(IR_LED); + 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(IR_LED); + 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 && defined (IR_RX) diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini index 243b36a99f..b77af187cd 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/platformio.ini @@ -9,7 +9,7 @@ lib_deps_builtin = lib_deps_external = PubSubClient WifiManager@0.14 - ArduinoJson + ArduinoJson@<6.0 [env:nodemcuv2] platform = espressif8266 diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino index 9d3808d17d..c7d8e4848d 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -26,9 +26,11 @@ #include #include // The following are only needed for extended decoding of A/C Messages +#include #include #include #include +#include #include #include #include @@ -38,9 +40,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -118,8 +122,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); @@ -134,6 +145,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); @@ -174,6 +192,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); @@ -209,6 +241,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); diff --git a/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino index 3993d11510..8e6e183ef2 100644 --- a/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnArgoAC/TurnOnArgoAC.ino @@ -44,7 +44,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/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino index 823a3f4856..783b998530 100644 --- a/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino @@ -30,11 +30,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/keywords.txt b/lib/IRremoteESP8266/keywords.txt index 3ab4e939b7..a498c5d61c 100644 --- a/lib/IRremoteESP8266/keywords.txt +++ b/lib/IRremoteESP8266/keywords.txt @@ -23,6 +23,7 @@ IRArgoAC KEYWORD1 IRCoolixAC KEYWORD1 IRDaikin2 KEYWORD1 +IRDaikin216 KEYWORD1 IRDaikinESP KEYWORD1 IRFujitsuAC KEYWORD1 IRGreeAC KEYWORD1 @@ -32,6 +33,8 @@ IRHitachiAc KEYWORD1 IRKelvinatorAC KEYWORD1 IRMideaAC KEYWORD1 IRMitsubishiAC KEYWORD1 +IRMitsubishiHeavy152Ac KEYWORD1 +IRMitsubishiHeavy88Ac KEYWORD1 IRPanasonicAc KEYWORD1 IRSamsungAc KEYWORD1 IRTcl112Ac KEYWORD1 @@ -40,9 +43,11 @@ 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,7 +60,7 @@ _delayMicroseconds KEYWORD2 _setMode KEYWORD2 _setTemp KEYWORD2 add KEYWORD2 -addbit KEYWORD2 +argo KEYWORD2 begin KEYWORD2 buildFromState KEYWORD2 buildState KEYWORD2 @@ -68,15 +73,19 @@ 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 decodeCOOLIX KEYWORD2 @@ -84,6 +93,7 @@ decodeCarrierAC KEYWORD2 decodeDISH KEYWORD2 decodeDaikin KEYWORD2 decodeDaikin2 KEYWORD2 +decodeDaikin216 KEYWORD2 decodeDenon KEYWORD2 decodeElectraAC KEYWORD2 decodeFujitsuAC KEYWORD2 @@ -97,6 +107,7 @@ decodeJVC KEYWORD2 decodeKelvinator KEYWORD2 decodeLG KEYWORD2 decodeLasertag KEYWORD2 +decodeLegoPf KEYWORD2 decodeLutron KEYWORD2 decodeMWM KEYWORD2 decodeMagiQuest KEYWORD2 @@ -104,6 +115,7 @@ decodeMidea KEYWORD2 decodeMitsubishi KEYWORD2 decodeMitsubishi2 KEYWORD2 decodeMitsubishiAC KEYWORD2 +decodeMitsubishiHeavy KEYWORD2 decodeNEC KEYWORD2 decodeNikai KEYWORD2 decodePanasonic KEYWORD2 @@ -150,15 +162,18 @@ 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 @@ -169,6 +184,7 @@ getEye KEYWORD2 getEyeAuto KEYWORD2 getFan KEYWORD2 getFanSpeed KEYWORD2 +getFilter KEYWORD2 getFlap KEYWORD2 getFreshAir KEYWORD2 getFreshAirHigh KEYWORD2 @@ -198,6 +214,7 @@ getRClevel KEYWORD2 getRaw KEYWORD2 getSensor KEYWORD2 getSensorTemp KEYWORD2 +getSilent KEYWORD2 getSleep KEYWORD2 getSleepTime KEYWORD2 getSleepTimerEnabled KEYWORD2 @@ -221,16 +238,23 @@ getVane KEYWORD2 getXFan KEYWORD2 getZoneFollow KEYWORD2 getiFeel 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,10 +263,16 @@ matchAtLeast KEYWORD2 matchData KEYWORD2 matchMark KEYWORD2 matchSpace KEYWORD2 +midea KEYWORD2 +mitsubishi KEYWORD2 +mitsubishiHeavy152 KEYWORD2 +mitsubishiHeavy88 KEYWORD2 +mode) KEYWORD2 off KEYWORD2 on KEYWORD2 -printState KEYWORD2 -readbits KEYWORD2 +opmode_t KEYWORD2 +panasonic KEYWORD2 +position) KEYWORD2 recoverSavedState KEYWORD2 renderTime KEYWORD2 reset KEYWORD2 @@ -252,7 +282,9 @@ resultToSourceCode KEYWORD2 resultToTimingInfo KEYWORD2 resume KEYWORD2 reverseBits KEYWORD2 +samsung KEYWORD2 send KEYWORD2 +sendAc KEYWORD2 sendAiwaRCT501 KEYWORD2 sendArgo KEYWORD2 sendCOOLIX KEYWORD2 @@ -260,6 +292,7 @@ sendCarrierAC KEYWORD2 sendDISH KEYWORD2 sendDaikin KEYWORD2 sendDaikin2 KEYWORD2 +sendDaikin216 KEYWORD2 sendData KEYWORD2 sendDenon KEYWORD2 sendElectraAC KEYWORD2 @@ -279,6 +312,7 @@ sendKelvinator KEYWORD2 sendLG KEYWORD2 sendLG2 KEYWORD2 sendLasertag KEYWORD2 +sendLegoPf KEYWORD2 sendLutron KEYWORD2 sendMWM KEYWORD2 sendMagiQuest KEYWORD2 @@ -286,8 +320,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 @@ -313,13 +351,14 @@ 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 @@ -329,6 +368,7 @@ setEye KEYWORD2 setEyeAuto KEYWORD2 setFan KEYWORD2 setFanSpeed KEYWORD2 +setFilter KEYWORD2 setFlap KEYWORD2 setFreshAir KEYWORD2 setFreshAirHigh KEYWORD2 @@ -357,6 +397,7 @@ setRoomTemp KEYWORD2 setSensor KEYWORD2 setSensorTemp KEYWORD2 setSensorTempRaw KEYWORD2 +setSilent KEYWORD2 setSleep KEYWORD2 setSpeed KEYWORD2 setStartClock KEYWORD2 @@ -377,20 +418,32 @@ setXFan KEYWORD2 setZoneFollow KEYWORD2 setiFeel KEYWORD2 space KEYWORD2 +speed) KEYWORD2 stateReset KEYWORD2 stepHoriz KEYWORD2 stepVert KEYWORD2 +strToBool KEYWORD2 +strToModel KEYWORD2 sumBytes KEYWORD2 +swingh_t KEYWORD2 +swingv) KEYWORD2 +swingv_t KEYWORD2 +tcl112 KEYWORD2 +teco KEYWORD2 ticksHigh KEYWORD2 ticksLow KEYWORD2 timeToString KEYWORD2 toString KEYWORD2 toggleRC5 KEYWORD2 toggleRC6 KEYWORD2 +toshiba KEYWORD2 +trotec KEYWORD2 typeToString KEYWORD2 uint64ToString KEYWORD2 updateSavedState KEYWORD2 validChecksum KEYWORD2 +vestel KEYWORD2 +whirlpool KEYWORD2 xorBytes KEYWORD2 ####################################### @@ -404,9 +457,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 @@ -431,10 +484,10 @@ COOLIX LITERAL1 COOLIX_BITS LITERAL1 DAIKIN 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 @@ -451,6 +504,7 @@ DECODE_CARRIER_AC LITERAL1 DECODE_COOLIX LITERAL1 DECODE_DAIKIN LITERAL1 DECODE_DAIKIN2 LITERAL1 +DECODE_DAIKIN216 LITERAL1 DECODE_DENON LITERAL1 DECODE_DISH LITERAL1 DECODE_ELECTRA_AC LITERAL1 @@ -467,12 +521,14 @@ DECODE_HITACHI_AC2 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 @@ -643,6 +699,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 +743,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 @@ -723,6 +782,7 @@ SEND_CARRIER_AC LITERAL1 SEND_COOLIX LITERAL1 SEND_DAIKIN LITERAL1 SEND_DAIKIN2 LITERAL1 +SEND_DAIKIN216 LITERAL1 SEND_DENON LITERAL1 SEND_DISH LITERAL1 SEND_ELECTRA_AC LITERAL1 @@ -738,12 +798,14 @@ SEND_HITACHI_AC2 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 @@ -804,7 +866,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 @@ -845,6 +906,7 @@ kArgoMinTemp LITERAL1 kArgoOneSpace LITERAL1 kArgoStateLength LITERAL1 kArgoZeroSpace LITERAL1 +kAuto LITERAL1 kCarrierAcBitMark LITERAL1 kCarrierAcBits LITERAL1 kCarrierAcGap LITERAL1 @@ -853,6 +915,7 @@ kCarrierAcHdrSpace LITERAL1 kCarrierAcMinRepeat LITERAL1 kCarrierAcOneSpace LITERAL1 kCarrierAcZeroSpace LITERAL1 +kCool LITERAL1 kCoolixAuto LITERAL1 kCoolixBitMark LITERAL1 kCoolixBitMarkTicks LITERAL1 @@ -902,6 +965,30 @@ kCoolixUnknown LITERAL1 kCoolixZeroSpace LITERAL1 kCoolixZeroSpaceTicks LITERAL1 kCoolixZoneFollowMask LITERAL1 +kDaikin216BitMark LITERAL1 +kDaikin216Bits LITERAL1 +kDaikin216ByteFan LITERAL1 +kDaikin216ByteMode LITERAL1 +kDaikin216BytePower 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 +997,7 @@ kDaikin2BitFreshAir LITERAL1 kDaikin2BitFreshAirHigh LITERAL1 kDaikin2BitMark LITERAL1 kDaikin2BitMold LITERAL1 +kDaikin2BitPower LITERAL1 kDaikin2BitPurify LITERAL1 kDaikin2BitSleepTimer LITERAL1 kDaikin2Bits LITERAL1 @@ -921,6 +1009,7 @@ kDaikin2HdrSpace LITERAL1 kDaikin2LeaderMark LITERAL1 kDaikin2LeaderSpace LITERAL1 kDaikin2LightMask LITERAL1 +kDaikin2MinCoolTemp LITERAL1 kDaikin2OneSpace LITERAL1 kDaikin2Section1Length LITERAL1 kDaikin2Section2Length LITERAL1 @@ -939,6 +1028,7 @@ kDaikinAuto LITERAL1 kDaikinBeepLoud LITERAL1 kDaikinBeepOff LITERAL1 kDaikinBeepQuiet LITERAL1 +kDaikinBitComfort LITERAL1 kDaikinBitEcono LITERAL1 kDaikinBitEye LITERAL1 kDaikinBitMark LITERAL1 @@ -950,15 +1040,29 @@ kDaikinBitPowerful LITERAL1 kDaikinBitSensor LITERAL1 kDaikinBitSilent 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 kDaikinCool LITERAL1 kDaikinCurBit LITERAL1 kDaikinCurIndex LITERAL1 @@ -973,6 +1077,7 @@ kDaikinFirstHeader64 LITERAL1 kDaikinGap LITERAL1 kDaikinHdrMark LITERAL1 kDaikinHdrSpace LITERAL1 +kDaikinHeaderLength LITERAL1 kDaikinHeat LITERAL1 kDaikinLightBright LITERAL1 kDaikinLightDim LITERAL1 @@ -981,8 +1086,12 @@ 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 @@ -1019,6 +1128,7 @@ kDishRptSpaceTicks LITERAL1 kDishTick LITERAL1 kDishZeroSpace LITERAL1 kDishZeroSpaceTicks LITERAL1 +kDry LITERAL1 kDutyDefault LITERAL1 kDutyMax LITERAL1 kElectraAcBitMark LITERAL1 @@ -1029,6 +1139,7 @@ kElectraAcMessageGap LITERAL1 kElectraAcOneSpace LITERAL1 kElectraAcStateLength LITERAL1 kElectraAcZeroSpace LITERAL1 +kFan LITERAL1 kFnvBasis32 LITERAL1 kFnvPrime32 LITERAL1 kFooter LITERAL1 @@ -1089,8 +1200,10 @@ kGreeCool LITERAL1 kGreeDefaultRepeat LITERAL1 kGreeDry LITERAL1 kGreeFan LITERAL1 +kGreeFanAuto LITERAL1 kGreeFanMask LITERAL1 kGreeFanMax LITERAL1 +kGreeFanMin LITERAL1 kGreeHdrMark LITERAL1 kGreeHdrSpace LITERAL1 kGreeHeat LITERAL1 @@ -1191,6 +1304,9 @@ kHaierAcYrw02TurboLow LITERAL1 kHaierAcYrw02TurboOff LITERAL1 kHaierAcZeroSpace LITERAL1 kHeader LITERAL1 +kHeat LITERAL1 +kHigh LITERAL1 +kHighest LITERAL1 kHitachiAc1Bits LITERAL1 kHitachiAc1HdrMark LITERAL1 kHitachiAc1HdrSpace LITERAL1 @@ -1292,6 +1408,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 +1449,8 @@ kLgRptSpaceTicks LITERAL1 kLgTick LITERAL1 kLgZeroSpace LITERAL1 kLgZeroSpaceTicks LITERAL1 +kLow LITERAL1 +kLowest LITERAL1 kLutronBits LITERAL1 kLutronDelta LITERAL1 kLutronGap LITERAL1 @@ -1346,8 +1474,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 +1515,7 @@ kMideaTick LITERAL1 kMideaTolerance LITERAL1 kMideaZeroSpace LITERAL1 kMideaZeroSpaceTicks LITERAL1 +kMin LITERAL1 kMitsubishi2BitMark LITERAL1 kMitsubishi2HdrMark LITERAL1 kMitsubishi2HdrSpace LITERAL1 @@ -1420,6 +1552,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 +1681,7 @@ kNikaiTick LITERAL1 kNikaiZeroSpace LITERAL1 kNikaiZeroSpaceTicks LITERAL1 kNoRepeat LITERAL1 +kOff LITERAL1 kPanasonicAcAuto LITERAL1 kPanasonicAcBits LITERAL1 kPanasonicAcChecksumInit LITERAL1 @@ -1585,6 +1803,8 @@ kRcmmRptLengthTicks LITERAL1 kRcmmTick LITERAL1 kRcmmTolerance LITERAL1 kRepeat LITERAL1 +kRight LITERAL1 +kRightMax LITERAL1 kSamsung36Bits LITERAL1 kSamsungACSectionLength LITERAL1 kSamsungAcAuto LITERAL1 @@ -1707,12 +1927,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 @@ -1792,14 +2023,12 @@ 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 +kTrotecTimerBit LITERAL1 kTrotecZeroMark LITERAL1 kTrotecZeroSpace LITERAL1 kUnknownThreshold LITERAL1 @@ -1837,9 +2066,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 7d5b7c2c03..95867de1db 100644 --- a/lib/IRremoteESP8266/library.json +++ b/lib/IRremoteESP8266/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.5.6", + "version": "2.6.0", "keywords": "infrared, ir, remote, esp8266", "description": "Send and receive infrared signals with multiple protocols (ESP8266)", "repository": diff --git a/lib/IRremoteESP8266/library.properties b/lib/IRremoteESP8266/library.properties index 4c7e413843..f122067c53 100644 --- a/lib/IRremoteESP8266/library.properties +++ b/lib/IRremoteESP8266/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.5.6 +version=2.6.0 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) diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp index 5bc66763cc..9cb1262018 100644 --- a/lib/IRremoteESP8266/src/IRac.cpp +++ b/lib/IRremoteESP8266/src/IRac.cpp @@ -15,6 +15,7 @@ #endif #include "IRsend.h" #include "IRremoteESP8266.h" +#include "IRutils.h" #include "ir_Argo.h" #include "ir_Coolix.h" #include "ir_Daikin.h" @@ -27,6 +28,7 @@ #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" @@ -51,9 +53,15 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #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 @@ -85,6 +93,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #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 @@ -115,19 +126,7 @@ void IRac::argo(IRArgoAC *ac, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool turbo, const int16_t sleep) { ac->setPower(on); - switch (mode) { - case stdAc::opmode_t::kCool: - ac->setCoolMode(kArgoCoolOn); - break; - case stdAc::opmode_t::kHeat: - ac->setHeatMode(kArgoHeatOn); - break; - case stdAc::opmode_t::kDry: - ac->setCoolMode(kArgoCoolHum); - break; - default: // No idea how to set Fan mode. - ac->setCoolMode(kArgoCoolAuto); - } + ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); ac->setFlap(ac->convertSwingV(swingv)); @@ -242,33 +241,101 @@ void IRac::daikin2(IRDaikin2 *ac, } #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 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->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 Turbo setting available. - // No Light setting available. + 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 Clean setting available. // No Beep setting available. - // No Sleep setting available. + // No Quiet setting available. // No Clock setting available. - if (!on) ac->off(); + ac->setPower(on); ac->send(); } -#endif // SEND_FUJITSU_AC +#endif // SEND_GOODWEATHER #if SEND_GREE void IRac::gree(IRGreeAC *ac, @@ -287,6 +354,7 @@ void IRac::gree(IRGreeAC *ac, 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. @@ -526,16 +594,9 @@ void IRac::samsung(IRSamsungAc *ac, 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 sendOnOffHack) { - if (sendOnOffHack) { - // Use a hack to for the unit on or off. - // See: https://github.com/markszabo/IRremoteESP8266/issues/604#issuecomment-475020036 - if (on) - ac->sendOn(); - else - ac->sendOff(); - } - ac->setPower(on); + 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)); @@ -556,6 +617,31 @@ void IRac::samsung(IRSamsungAc *ac, } #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, @@ -711,7 +797,7 @@ void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, // 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 degreees Celsius, otherwise Fahrenheit. +// 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. @@ -737,11 +823,11 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, const bool beep, const int16_t sleep, const int16_t clock) { // Convert the temperature to Celsius. float degC; - bool on = power; if (celsius) degC = degrees; else - degC = (degrees - 32.0) * (5.0 / 9.0); + 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. @@ -780,17 +866,34 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, light, econo, filter, clean, beep, sleep, clock); break; } -#endif // SEND_DAIKIN2 +#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); + 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: { @@ -892,6 +995,15 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, 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: { diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h index 5cd562bc6b..60dd1be795 100644 --- a/lib/IRremoteESP8266/src/IRac.h +++ b/lib/IRremoteESP8266/src/IRac.h @@ -14,6 +14,7 @@ #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" @@ -23,6 +24,7 @@ #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" @@ -91,13 +93,29 @@ class IRac { 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 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, @@ -174,8 +192,13 @@ class IRac { 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 sendOnOffHack = true); + 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, @@ -216,26 +239,4 @@ class IRac { const int16_t sleep = -1, const int16_t clock = -1); #endif // SEND_WHIRLPOOL_AC }; // IRac class - -// 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; -} commonAcState_t; #endif // IRAC_H_ diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp index 9a52151ca4..3957ad5f45 100644 --- a/lib/IRremoteESP8266/src/IRrecv.cpp +++ b/lib/IRremoteESP8266/src/IRrecv.cpp @@ -335,7 +335,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 +402,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; @@ -519,6 +523,26 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { 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_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 988367ecba..68f7f37dbc 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -156,6 +156,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); @@ -204,21 +208,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, @@ -226,8 +236,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, @@ -238,7 +249,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 @@ -246,8 +257,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, @@ -266,17 +282,27 @@ 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_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); @@ -293,6 +319,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); @@ -307,12 +338,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, @@ -320,7 +353,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, @@ -332,7 +366,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, @@ -344,16 +379,18 @@ 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, diff --git a/lib/IRremoteESP8266/src/IRremoteESP8266.h b/lib/IRremoteESP8266/src/IRremoteESP8266.h index 02f2dcf10b..36962d28ae 100644 --- a/lib/IRremoteESP8266/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266/src/IRremoteESP8266.h @@ -50,7 +50,7 @@ #endif // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.5.6" +#define _IRREMOTEESP8266_VERSION_ "2.6.0" // 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 +117,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 +132,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 +144,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 @@ -222,13 +231,17 @@ #define DECODE_MITSUBISHIHEAVY true #define SEND_MITSUBISHIHEAVY true +#define DECODE_DAIKIN216 true +#define SEND_DAIKIN216 true + #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ - DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY) + DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ + DECODE_DAIKIN216 || DECODE_SHARP_AC) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -308,8 +321,12 @@ enum decode_type_t { LEGOPF, MITSUBISHI_HEAVY_88, MITSUBISHI_HEAVY_152, // 60 + DAIKIN216, + SHARP_AC, + GOODWEATHER, + INAX, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = MITSUBISHI_HEAVY_152, + kLastDecodeType = INAX, }; // Message lengths & required repeat values @@ -319,20 +336,25 @@ 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 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 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; @@ -345,6 +367,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; @@ -361,6 +385,8 @@ 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; @@ -420,6 +446,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; @@ -436,6 +465,7 @@ 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; @@ -451,7 +481,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 diff --git a/lib/IRremoteESP8266/src/IRsend.cpp b/lib/IRremoteESP8266/src/IRsend.cpp index 5d0a762bb0..7421b60475 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; @@ -533,6 +536,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); diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index 1c6582e489..91b95ec420 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -70,6 +70,28 @@ namespace stdAc { 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 @@ -131,9 +153,9 @@ 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, @@ -152,21 +174,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 @@ -183,13 +211,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, @@ -246,6 +275,10 @@ class IRsend { void sendFujitsuAC(unsigned char data[], uint16_t nbytes, 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 @@ -255,13 +288,19 @@ class IRsend { 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_DAIKIN2 void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength, 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); @@ -272,25 +311,32 @@ class IRsend { void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength, 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, @@ -310,37 +356,38 @@ 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, @@ -352,9 +399,9 @@ class IRsend { 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, @@ -375,8 +422,8 @@ 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, @@ -397,8 +444,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/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp index 9cb5305d90..2f87be658e 100644 --- a/lib/IRremoteESP8266/src/IRutils.cpp +++ b/lib/IRremoteESP8266/src/IRutils.cpp @@ -57,6 +57,11 @@ std::string uint64ToString(uint64_t input, uint8_t base) { // 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; @@ -89,7 +94,7 @@ void serialPrintUint64(uint64_t input, uint8_t base) { // str: An upper-case C-style string. // Returns: // A decode_type_t enum. -decode_type_t strToDecodeType(const char *str) { +decode_type_t strToDecodeType(const char * const str) { if (!strcmp(str, "UNKNOWN")) return decode_type_t::UNKNOWN; else if (!strcmp(str, "UNUSED")) @@ -106,6 +111,8 @@ decode_type_t strToDecodeType(const char *str) { return decode_type_t::DAIKIN; 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")) @@ -118,6 +125,8 @@ decode_type_t strToDecodeType(const char *str) { 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")) @@ -130,6 +139,8 @@ decode_type_t strToDecodeType(const char *str) { 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")) @@ -190,6 +201,8 @@ decode_type_t strToDecodeType(const char *str) { 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")) @@ -319,6 +332,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case DAIKIN2: result = F("DAIKIN2"); break; + case DAIKIN216: + result = F("DAIKIN216"); + break; case DENON: result = F("DENON"); break; @@ -337,6 +353,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case GLOBALCACHE: result = F("GLOBALCACHE"); break; + case GOODWEATHER: + result = F("GOODWEATHER"); + break; case GREE: result = F("GREE"); break; @@ -355,6 +374,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case HITACHI_AC2: result = F("HITACHI_AC2"); break; + case INAX: + result = F("INAX"); + break; case JVC: result = F("JVC"); break; @@ -454,6 +476,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case SHARP: result = F("SHARP"); break; + case SHARP_AC: + result = F("SHARP_AC"); + break; case SHERWOOD: result = F("SHERWOOD"); break; @@ -493,8 +518,10 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { // 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 DAIKIN2: + case DAIKIN216: case ELECTRA_AC: case FUJITSU_AC: case GREE: @@ -510,8 +537,10 @@ bool hasACState(const decode_type_t protocol) { case MWM: case PANASONIC_AC: case SAMSUNG_AC: + case SHARP_AC: case TCL112AC: case TOSHIBA_AC: + case TROTEC: case WHIRLPOOL_AC: return true; default: @@ -525,7 +554,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; @@ -538,12 +567,14 @@ 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 resultToSourceCode(const decode_results * const 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 += F("uint16_t "); // variable type output += F("rawData["); // array name @@ -620,14 +651,16 @@ 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 resultToTimingInfo(const decode_results * const results) { std::string output = ""; std::string value = ""; #endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2048); // 2KB should cover most cases. output += F("Raw Timing["); output += uint64ToString(results->rawlen - 1, 10); output += F("]:\n"); @@ -652,12 +685,14 @@ std::string resultToTimingInfo(const decode_results *results) { // 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 resultToHexidecimal(const decode_results * const 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++) { @@ -674,12 +709,14 @@ 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 resultToHumanReadableBasic(const decode_results *const 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 += F("Encoding : "); output += typeToString(results->decode_type, results->repeat); @@ -694,16 +731,43 @@ std::string resultToHumanReadableBasic(const decode_results *results) { 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; } @@ -716,8 +780,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); @@ -760,3 +824,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 0d0b677b57..f666080cee 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -20,29 +20,34 @@ 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); +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); #else // ARDUINO 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); +std::string resultToSourceCode(const decode_results * const results); +std::string resultToTimingInfo(const decode_results * const results); +std::string resultToHumanReadableBasic(const decode_results * const results); +std::string resultToHexidecimal(const decode_results * const result); std::string htmlEscape(const std::string unescaped); #endif // ARDUINO 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_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp index d6711acd38..d673b15dc4 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266/src/ir_Argo.cpp @@ -2,10 +2,16 @@ 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 +#else +#include +#endif #include "IRremoteESP8266.h" #include "IRutils.h" @@ -16,6 +22,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 +32,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 +43,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); +} + +bool IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) { + return ((state[length - 2] >> 2) + (state[length - 1] << 6)) == + IRArgoAC::calcChecksum(state, length); +} - sum = sum % 256; // modulo 256 +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 +75,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,159 +89,171 @@ 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; - - // Store in attributes - set_temp = temp; +void IRArgoAC::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kArgoMinTemp, degrees); + temp = std::min(kArgoMaxTemp, 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::getMode() { - // return cooling 0, heating 1 - return ac_mode; -} - -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; +uint8_t IRArgoAC::getFlap(void) { return flap_mode; } - // Set cool mode at bit positions - argo[2] += mode << 3; +uint8_t IRArgoAC::getMode(void) { + return (argo[2] & kArgoModeMask) >> 3; } -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; +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::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) { @@ -262,3 +287,181 @@ uint8_t IRArgoAC::convertSwingV(const stdAc::swingv_t position) { 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. +#ifdef ARDUINO +String IRArgoAC::toString() { + String result = ""; +#else +std::string IRArgoAC::toString() { + std::string result = ""; +#endif // ARDUINO + 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 883c2ddfdb..fb4c1b39e9 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.h +++ b/lib/IRremoteESP8266/src/ir_Argo.h @@ -4,6 +4,11 @@ #ifndef IR_ARGO_H_ #define IR_ARGO_H_ +#ifndef UNIT_TEST +#include +#else +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" #ifdef UNIT_TEST @@ -33,19 +38,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 @@ -81,49 +109,60 @@ 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); #endif // SEND_ARGO - void begin(); - void on(); - void off(); - - void setPower(bool state); - uint8_t getPower(); - - void setTemp(uint8_t temp); - uint8_t getTemp(); - - void setFan(uint8_t fan); - uint8_t getFan(); - - void setFlap(uint8_t flap); - uint8_t getFlap(); - - void setCoolMode(uint8_t mode); - uint8_t getCoolMode(); - - void setHeatMode(uint8_t mode); - uint8_t getHeatMode(); - uint8_t getMode(); - - void setMax(bool state); - bool getMax(); - - void setNight(bool state); - bool getNight(); - - void setiFeel(bool state); - bool getiFeel(); - - void setTime(); - void setRoomTemp(uint8_t temp); - - uint8_t* getRaw(); - uint8_t convertFan(const stdAc::fanspeed_t speed); - uint8_t convertSwingV(const stdAc::swingv_t position); + 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 fan); + uint8_t getFan(void); + + void setFlap(const uint8_t flap); + uint8_t getFlap(void); + + void setMode(const uint8_t mode); + uint8_t getMode(void); + + void setMax(const bool on); + bool getMax(void); + + void setNight(const bool on); + bool getNight(void); + + void setiFeel(const bool on); + bool getiFeel(void); + + void setTime(void); + void setRoomTemp(const uint8_t degrees); + uint8_t getRoomTemp(void); + + 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); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif #ifndef UNIT_TEST private: @@ -133,20 +172,13 @@ class IRArgoAC { #endif // # of bytes per command uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h - void stateReset(); - void checksum(); + 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_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp index 2659a1d881..a1ea0d9a10 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266/src/ir_Coolix.cpp @@ -337,6 +337,54 @@ uint8_t IRCoolixAC::convertFan(const stdAc::fanspeed_t speed) { } } +// 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() { @@ -345,6 +393,7 @@ String IRCoolixAC::toString() { std::string IRCoolixAC::toString() { std::string result = ""; #endif // ARDUINO + result.reserve(100); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (getPower()) { result += F("On"); diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index d85db98d7d..c246a2ade8 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -125,6 +125,9 @@ class IRCoolixAC { void setRaw(const uint32_t new_code); 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); #ifdef ARDUINO String toString(); #else diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp index 2628e37b1a..ed637e8175 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.cpp +++ b/lib/IRremoteESP8266/src/ir_Daikin.cpp @@ -34,17 +34,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: @@ -55,132 +44,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 + 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, 38, - false, 0, 50); - // Data #2 + 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 + 8, - nbytes - 8, 38, false, 0, 50); + 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::stateReset() { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[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(); -} - -uint8_t *IRDaikinESP::getRaw() { - checksum(); // Ensure correct settings before sending. - return daikin; -} - -void IRDaikinESP::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = new_code[i]; +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(void) { + for (uint8_t i = 0; i < kDaikinStateLength; i++) remote[i] = 0x0; + + 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(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; +} + +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) @@ -189,229 +209,307 @@ 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); +bool IRDaikinESP::getComfort(void) { + return remote[kDaikinByteComfort] & kDaikinBitComfort; } -void IRDaikinESP::setBit(uint8_t byte, uint8_t bitmask) { - daikin[byte] |= bitmask; +// starttime: Number of minutes after midnight. +void IRDaikinESP::enableOnTimer(const uint16_t starttime) { + remote[kDaikinByteOnTimer] |= kDaikinBitOnTimer; + remote[kDaikinByteOnTimerMinsLow] = starttime; + // only keep 4 bits + remote[kDaikinByteOnTimerMinsHigh] &= 0xF0; + remote[kDaikinByteOnTimerMinsHigh] |= ((starttime >> 8) & 0x0F); } -void IRDaikinESP::clearBit(uint8_t byte, uint8_t bitmask) { - bitmask = ~bitmask; - daikin[byte] &= bitmask; +void IRDaikinESP::disableOnTimer(void) { + this->enableOnTimer(kDaikinUnusedTime); + remote[kDaikinByteOnTimer] &= ~kDaikinBitOnTimer; } -uint8_t IRDaikinESP::getBit(uint8_t byte, uint8_t bitmask) { - return daikin[byte] & bitmask; +uint16_t IRDaikinESP::getOnTime(void) { + return ((remote[kDaikinByteOnTimerMinsHigh] & 0x0F) << 8) + + remote[kDaikinByteOnTimerMinsLow]; } -// starttime: Number of minutes after midnight. -void IRDaikinESP::enableOnTimer(uint16_t starttime) { - setBit(kDaikinByteOnTimer, kDaikinBitOnTimer); - daikin[18] = (uint8_t)(starttime & 0x00FF); - // only keep 4 bits - daikin[19] &= 0xF0; - daikin[19] |= (uint8_t)((starttime >> 8) & 0x0F); +bool IRDaikinESP::getOnTimerEnabled(void) { + return remote[kDaikinByteOnTimer] & kDaikinBitOnTimer; } -void IRDaikinESP::disableOnTimer() { - enableOnTimer(kDaikinUnusedTime); - clearBit(kDaikinByteOnTimer, kDaikinBitOnTimer); +// endtime: Number of minutes after midnight. +void IRDaikinESP::enableOffTimer(const uint16_t endtime) { + remote[kDaikinByteOffTimer] |= kDaikinBitOffTimer; + remote[kDaikinByteOffTimerMinsHigh] = endtime >> 4; + remote[kDaikinByteOffTimerMinsLow] &= 0x0F; + remote[kDaikinByteOffTimerMinsLow] |= ((endtime & 0x0F) << 4); } -uint16_t IRDaikinESP::getOnTime() { - uint16_t ret; - ret = daikin[19] & 0x0F; - ret = ret << 8; - ret += daikin[18]; - return ret; +void IRDaikinESP::disableOffTimer(void) { + this->enableOffTimer(kDaikinUnusedTime); + remote[kDaikinByteOffTimer] &= ~kDaikinBitOffTimer; } -bool IRDaikinESP::getOnTimerEnabled() { - return getBit(kDaikinByteOnTimer, kDaikinBitOnTimer); +uint16_t IRDaikinESP::getOffTime(void) { + return (remote[kDaikinByteOffTimerMinsHigh] << 4) + + ((remote[kDaikinByteOffTimerMinsLow] & 0xF0) >> 4); } -// 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); +bool IRDaikinESP::getOffTimerEnabled(void) { + return remote[kDaikinByteOffTimer] & kDaikinBitOffTimer; } -void IRDaikinESP::disableOffTimer() { - enableOffTimer(kDaikinUnusedTime); - clearBit(kDaikinByteOffTimer, kDaikinBitOffTimer); +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::getOffTime() { - uint16_t ret, tmp; - ret = daikin[20]; - ret <<= 4; - tmp = daikin[19] & 0xF0; - tmp >>= 4; - ret += tmp; - return ret; +uint16_t IRDaikinESP::getCurrentTime(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x07) << 8) + + remote[kDaikinByteClockMinsLow]; } -bool IRDaikinESP::getOffTimerEnabled() { - return getBit(kDaikinByteOffTimer, kDaikinBitOffTimer); +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; } -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); +uint8_t IRDaikinESP::getCurrentDay(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x38) >> 3); } -uint16_t IRDaikinESP::getCurrentTime() { - uint16_t ret; - ret = daikin[6] & 0x0F; - ret <<= 8; - ret += daikin[5]; - return ret; +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; } #ifdef ARDUINO -String IRDaikinESP::renderTime(uint16_t timemins) { +String IRDaikinESP::renderTime(const uint16_t timemins) { String ret; #else // ARDUINO -std::string IRDaikinESP::renderTime(uint16_t timemins) { +std::string IRDaikinESP::renderTime(const uint16_t timemins) { std::string ret; #endif // ARDUINO - uint16_t hours, mins; - hours = timemins / 60; - ret = uint64ToString(hours) + ':'; - mins = timemins - (hours * 60); + ret = uint64ToString(timemins / 60) + ':'; + uint8_t mins = timemins % 60; if (mins < 10) ret += '0'; ret += uint64ToString(mins); return ret; @@ -419,20 +517,18 @@ std::string IRDaikinESP::renderTime(uint16_t timemins) { // 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 IRDaikinESP::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(230); // Reserve some heap for the string to reduce fragging. result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); + result += this->getPower() ? F("On") : F("Off"); result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kDaikinAuto: result += F(" (AUTO)"); break; @@ -452,10 +548,10 @@ std::string IRDaikinESP::toString() { result += F(" (UNKNOWN)"); } result += F(", Temp: "); - result += uint64ToString(getTemp()); + result += uint64ToString(this->getTemp()); result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { + result += uint64ToString(this->getFan()); + switch (this->getFan()) { case kDaikinFanAuto: result += F(" (AUTO)"); break; @@ -470,281 +566,123 @@ std::string IRDaikinESP::toString() { break; } result += F(", Powerful: "); - if (getPowerful()) - result += F("On"); - else - result += F("Off"); + result += this->getPowerful() ? F("On") : F("Off"); result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); + result += this->getQuiet() ? F("On") : F("Off"); result += F(", Sensor: "); - if (getSensor()) - result += F("On"); - else - result += F("Off"); - result += F(", Eye: "); - if (getEye()) - result += F("On"); - else - result += F("Off"); + result += this->getSensor() ? F("On") : F("Off"); result += F(", Mold: "); - if (getMold()) - result += F("On"); - else - result += F("Off"); + result += this->getMold() ? F("On") : F("Off"); + result += F(", Comfort: "); + result += this->getComfort() ? F("On") : F("Off"); result += F(", Swing (Horizontal): "); - if (getSwingHorizontal()) - result += F("On"); - else - result += F("Off"); + result += this->getSwingHorizontal() ? F("On") : F("Off"); result += F(", Swing (Vertical): "); - if (getSwingVertical()) - result += F("On"); - else - result += F("Off"); + result += this->getSwingVertical() ? F("On") : F("Off"); result += F(", Current Time: "); - result += renderTime(getCurrentTime()); + 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 (getOnTimerEnabled()) - result += renderTime(getOnTime()); + if (this->getOnTimerEnabled()) + result += this->renderTime(this->getOnTime()); else result += F("Off"); result += F(", Off Time: "); - if (getOffTimerEnabled()) - result += renderTime(getOffTime()); + if (this->getOffTimerEnabled()) + result += this->renderTime(this->getOffTime()); else 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); -} - -// 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(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; - } -} - #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; - - // Header (#1) - for (uint8_t i = 0; i < 10; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - } - 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; + match_result_t data_result; + uint16_t dataBitsSoFar = 0; + uint16_t i = 0; - // Data (#2) - if (!readbits(results, &offset, daikin_code, 8 * 8)) 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. - // Header (#3) - if (!checkheader(results, &offset)) return false; + // 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; - // Data (#3), read up everything else - if (!readbits(results, &offset, daikin_code, kDaikinBits - (8 * 8))) - 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; + } + } // Footer if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; @@ -754,24 +692,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 @@ -782,7 +714,7 @@ 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 @@ -1220,36 +1152,12 @@ 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) { - 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; - } + 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) { - 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; - } + return IRDaikinESP::convertFan(speed); } // Convert a standard A/C vertical swing into its native version. @@ -1266,6 +1174,53 @@ uint8_t IRDaikin2::convertSwingV(const stdAc::swingv_t position) { } } +// 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() { @@ -1274,6 +1229,7 @@ String IRDaikin2::toString() { std::string IRDaikin2::toString() { std::string result = ""; #endif // ARDUINO + result.reserve(310); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (getPower()) result += F("On"); @@ -1517,3 +1473,421 @@ 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. +#ifdef ARDUINO +String IRDaikin216::toString() { + String result = ""; +#else // ARDUINO +std::string IRDaikin216::toString() { + std::string result = ""; +#endif // ARDUINO + 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 diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 7eddd849d2..ab26d59c82 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -16,9 +16,6 @@ #include "IRsend_test.h" #endif -// 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 @@ -27,11 +24,15 @@ /* 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 @@ -44,8 +45,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 @@ -60,23 +61,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 @@ -91,24 +92,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; @@ -165,6 +188,31 @@ 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; + // Legacy defines. #define DAIKIN_COOL kDaikinCool @@ -186,60 +234,63 @@ class IRDaikinESP { #if SEND_DAIKIN void send(const uint16_t repeat = kDaikinDefaultRepeat); #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); - uint8_t convertMode(const stdAc::opmode_t mode); - uint8_t convertFan(const stdAc::fanspeed_t speed); + 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); #ifdef ARDUINO - String toString(); - static String renderTime(uint16_t timemins); + String toString(void); + static String renderTime(const uint16_t timemins); #else - std::string toString(); - static std::string renderTime(uint16_t timemins); + std::string toString(void); + static std::string renderTime(const uint16_t timemins); #endif #ifndef UNIT_TEST @@ -249,12 +300,9 @@ class IRDaikinESP { IRsendTest _irsend; #endif // # 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); + uint8_t remote[kDaikinStateLength]; + void stateReset(void); + void checksum(void); }; // Class to emulate a Daikin ARC477A1 remote. @@ -326,9 +374,12 @@ class IRDaikin2 { void setCommand(uint32_t value); static bool validChecksum(uint8_t state[], const uint16_t length = kDaikin2StateLength); - uint8_t convertMode(const stdAc::opmode_t mode); - uint8_t convertFan(const stdAc::fanspeed_t speed); + 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); #ifdef ARDUINO String toString(); static String renderTime(uint16_t timemins); @@ -351,4 +402,58 @@ class IRDaikin2 { 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); +#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); +#ifdef ARDUINO + String toString(void); + static String renderTime(const uint16_t timemins); +#else + std::string toString(void); + static std::string renderTime(const uint16_t timemins); +#endif +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin216StateLength]; + 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 6798e022e3..c4d796ad47 100644 --- a/lib/IRremoteESP8266/src/ir_Denon.cpp +++ b/lib/IRremoteESP8266/src/ir_Denon.cpp @@ -43,7 +43,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 +70,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 +82,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_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp index de1b97e87f..4ad05c8b0d 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,7 +40,7 @@ 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) { @@ -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,101 +260,171 @@ 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(void) { return _cmd != kFujitsuAcCmdTurnOff; } + +void IRFujitsuAC::setOutsideQuiet(const bool on) { + _outsideQuiet = on; + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} -bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; } +// 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? @@ -353,22 +463,91 @@ uint8_t IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { } } +// 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 IRFujitsuAC::toString(void) { std::string result = ""; #endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); + 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(getMode()); - switch (getMode()) { + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kFujitsuAcModeAuto: result += F(" (AUTO)"); break; @@ -388,9 +567,9 @@ std::string IRFujitsuAC::toString() { result += F(" (UNKNOWN)"); } result += F(", Temp: "); - result += uint64ToString(getTemp()); + result += uint64ToString(this->getTemp()); result += F("C, Fan: "); - result += uint64ToString(getFanSpeed()); + result += uint64ToString(this->getFanSpeed()); switch (getFanSpeed()) { case kFujitsuAcFanAuto: result += F(" (AUTO)"); @@ -408,34 +587,57 @@ std::string IRFujitsuAC::toString() { result += F(" (QUIET)"); break; } - result += F(", Swing: "); - switch (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"); + switch (model) { + // These models have no internal swing state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + 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 += F(", Command: "); - switch (getCmd()) { + switch (this->getCmd()) { case kFujitsuAcCmdStepHoriz: result += F("Step vane horizontally"); break; case kFujitsuAcCmdStepVert: 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 += 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 78a4f89517..6cb493e7cd 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -1,5 +1,5 @@ // Copyright 2017 Jonny Graham -// Copyright 2018 David Conran +// Copyright 2018-2019 David Conran #ifndef IR_FUJITSU_H_ #define IR_FUJITSU_H_ @@ -26,11 +26,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; @@ -70,44 +74,57 @@ 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); #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(); + 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -125,7 +142,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_Goodweather.cpp b/lib/IRremoteESP8266/src/ir_Goodweather.cpp new file mode 100644 index 0000000000..015adeb87c --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Goodweather.cpp @@ -0,0 +1,499 @@ +// 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. +#ifdef ARDUINO +String IRGoodweatherAc::toString() { + String result = ""; +#else +std::string IRGoodweatherAc::toString() { + std::string result = ""; +#endif // ARDUINO + 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 0000000000..03a70e3a2d --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Goodweather.h @@ -0,0 +1,140 @@ +// Goodweather A/C +// +// Copyright 2019 ribeirodanielf +// Copyright 2019 David Conran + +#ifndef IR_GOODWEATHER_H_ +#define IR_GOODWEATHER_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#else +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Supports: +// ZH/JT-03 remote controller +// 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); +#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); +#ifdef ARDUINO + String toString(); +#else + std::string toString(); +#endif +#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 949342dbd2..bb07a17fd4 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266/src/ir_Gree.cpp @@ -27,7 +27,7 @@ // 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; @@ -59,7 +59,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 @@ -112,7 +112,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 +122,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 +135,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 +167,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 +198,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 +212,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 +237,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,11 +297,11 @@ 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; } @@ -356,14 +356,76 @@ uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) { } } +// 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.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. #ifdef ARDUINO -String IRGreeAC::toString() { +String IRGreeAC::toString(void) { String result = ""; #else -std::string IRGreeAC::toString() { +std::string IRGreeAC::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(150); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (getPower()) result += F("On"); diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index c3c5916dc4..e2e94b7e21 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -87,43 +87,47 @@ class IRGreeAC { public: explicit IRGreeAC(uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_GREE void send(const uint16_t repeat = kGreeDefaultRepeat); #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(); + 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); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + 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(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -135,7 +139,7 @@ class IRGreeAC { // The state of the IR remote in IR code form. uint8_t remote_state[kGreeStateLength]; void checksum(const uint16_t length = kGreeStateLength); - void fixup(); + void fixup(void); }; #endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp index f76bb3447a..84cd685877 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -47,8 +47,8 @@ const uint32_t kHaierAcMinGap = 150000; // Completely made up value. // // 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 +74,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 +92,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,13 +101,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] = 0x20; remote_state[4] = 0x0C; remote_state[5] = 0xC0; - remote_state[6] = 0x20; setTemp(kHaierAcDefTemp); setFan(kHaierAcFanAuto); @@ -115,18 +114,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: @@ -144,9 +143,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: @@ -167,7 +166,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; @@ -185,11 +184,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; @@ -209,45 +210,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; @@ -273,7 +276,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; } @@ -282,7 +285,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. @@ -364,14 +369,72 @@ uint8_t IRHaierAC::convertSwingV(const stdAc::swingv_t position) { } } +// 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 IRHaierAC::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(150); // Reserve some heap for the string to reduce fragging. uint8_t cmd = getCommand(); result += F("Command: "); result += uint64ToString(cmd); @@ -497,7 +560,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) { @@ -506,7 +569,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); } @@ -516,7 +579,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; @@ -530,12 +593,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]; } @@ -557,7 +620,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; @@ -576,10 +641,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) @@ -596,43 +661,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) { @@ -645,7 +714,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) { @@ -659,7 +728,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; @@ -739,14 +808,73 @@ uint8_t IRHaierACYRW02::convertSwingV(const stdAc::swingv_t position) { } } +// 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 IRHaierACYRW02::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(130); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (getPower()) result += F("On"); diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index 8f7b351965..bf02906bbe 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -56,6 +56,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; @@ -69,6 +70,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 @@ -186,55 +190,58 @@ 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); #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); 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); #ifdef ARDUINO - String toString(); + String toString(void); static String timeToString(const uint16_t nr_mins); #else - std::string toString(); + std::string toString(void); static std::string timeToString(const uint16_t nr_mins); #endif #ifndef UNIT_TEST @@ -245,8 +252,8 @@ class IRHaierAC { 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); }; @@ -258,47 +265,51 @@ 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); 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -308,8 +319,8 @@ class IRHaierACYRW02 { IRsendTest _irsend; #endif uint8_t remote_state[kHaierACYRW02StateLength]; - void stateReset(); - void checksum(); + 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 b88189f4a0..a8a9dee251 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266/src/ir_Hitachi.cpp @@ -44,8 +44,8 @@ const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. // // 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 +71,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,8 +98,8 @@ 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); @@ -110,9 +110,9 @@ void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, // 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 +130,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 +148,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 +165,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 +174,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 +200,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 +222,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 +241,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 +250,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) @@ -291,14 +293,64 @@ uint8_t IRHitachiAc::convertFan(const stdAc::fanspeed_t speed) { } } +// 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 IRHitachiAc::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(110); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (getPower()) result += F("On"); @@ -375,8 +427,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 532717447d..68b5d162aa 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -34,28 +34,28 @@ 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); #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[], @@ -64,10 +64,13 @@ class IRHitachiAc { const uint16_t length = kHitachiAcStateLength); 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST diff --git a/lib/IRremoteESP8266/src/ir_Inax.cpp b/lib/IRremoteESP8266/src/ir_Inax.cpp new file mode 100644 index 0000000000..32a79eb86e --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Inax.cpp @@ -0,0 +1,98 @@ +// Copyright 2019 David Conran (crankyoldgit) + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Support for an IR controlled Robot Toilet +// +// Brand: Lixil +// Model: Inax +// Type: DT-BA283 + +// 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_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp index c69f4cb8ad..bd408b7cbb 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp @@ -124,36 +124,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]; } @@ -196,46 +198,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) | @@ -244,108 +246,117 @@ 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. @@ -364,19 +375,60 @@ uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) { } } +// 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 IRKelvinatorAC::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(160); // Reserve some heap for the string to reduce fragging. result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); + result += getPower() ? F("On") : F("Off"); result += F(", Mode: "); result += uint64ToString(getMode()); switch (getMode()) { @@ -411,40 +463,19 @@ std::string IRKelvinatorAC::toString() { break; } result += F(", Turbo: "); - if (getTurbo()) - result += F("On"); - else - result += F("Off"); + result += getTurbo() ? F("On") : F("Off"); result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); + result += getQuiet() ? F("On") : F("Off"); result += F(", XFan: "); - if (getXFan()) - result += F("On"); - else - result += F("Off"); + result += getXFan() ? F("On") : F("Off"); result += F(", IonFilter: "); - if (getIonFilter()) - result += F("On"); - else - result += F("Off"); + result += getIonFilter() ? F("On") : F("Off"); result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); + result += getLight() ? F("On") : F("Off"); result += F(", Swing (Horizontal): "); - if (getSwingHorizontal()) - result += F("On"); - else - result += F("Off"); + result += getSwingHorizontal() ? F("On") : F("Off"); result += F(", Swing (Vertical): "); - if (getSwingVertical()) - result += F("On"); - else - result += F("Off"); + result += getSwingVertical() ? F("On") : F("Off"); return result; } @@ -458,7 +489,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 < @@ -560,7 +591,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 ce830c70af..e18117756b 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -131,46 +131,49 @@ class IRKelvinatorAC { public: explicit IRKelvinatorAC(uint16_t pin); - void stateReset(); + void stateReset(void); #if SEND_KELVINATOR void send(const uint16_t repeat = kKelvinatorDefaultRepeat); #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); 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -182,7 +185,7 @@ class IRKelvinatorAC { // The state of the IR remote in IR code form. uint8_t remote_state[kKelvinatorStateLength]; void checksum(const uint16_t length = kKelvinatorStateLength); - void fixup(); + void fixup(void); }; #endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp index f9d922fc74..36c85ff151 100644 --- a/lib/IRremoteESP8266/src/ir_LG.cpp +++ b/lib/IRremoteESP8266/src/ir_LG.cpp @@ -85,11 +85,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 +99,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 +107,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_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp index 8d5d9494fd..20f6dd516f 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266/src/ir_Midea.cpp @@ -91,52 +91,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 +149,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 +167,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 +190,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,7 +251,7 @@ 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); @@ -290,14 +290,61 @@ uint8_t IRMideaAC::convertFan(const stdAc::fanspeed_t speed) { } } +// 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 IRMideaAC::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(70); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (getPower()) result += F("On"); diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index ab14eb252e..9017650038 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -66,34 +66,37 @@ 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); #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(); + 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -103,7 +106,7 @@ class IRMideaAC { IRsendTest _irsend; #endif uint64_t remote_state; - void checksum(); + void checksum(void); static uint8_t calcChecksum(const uint64_t state); }; diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp index ca9bef5d98..c22bf92d90 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp @@ -420,10 +420,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 +447,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 +488,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 +542,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 +568,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, @@ -661,11 +670,70 @@ uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) { } } +// 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; +} + #ifdef ARDUINO -String IRMitsubishiAC::timeToString(uint64_t time) { +String IRMitsubishiAC::timeToString(const uint64_t time) { String result = ""; #else -std::string IRMitsubishiAC::timeToString(uint64_t time) { +std::string IRMitsubishiAC::timeToString(const uint64_t time) { std::string result = ""; #endif // ARDUINO if (time / 6 < 10) result += '0'; @@ -678,18 +746,19 @@ std::string IRMitsubishiAC::timeToString(uint64_t time) { // 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 IRMitsubishiAC::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(110); // Reserve some heap for the string to reduce fragging. result += F("Power: "); - if (getPower()) + if (this->getPower()) result += F("On"); else result += F("Off"); - switch (getMode()) { + switch (this->getMode()) { case MITSUBISHI_AC_AUTO: result += F(" (AUTO)"); break; @@ -706,9 +775,9 @@ std::string IRMitsubishiAC::toString() { result += F(" (UNKNOWN)"); } result += F(", Temp: "); - result += uint64ToString(getTemp()); + result += uint64ToString(this->getTemp()); result += F("C, FAN: "); - switch (getFan()) { + switch (this->getFan()) { case MITSUBISHI_AC_FAN_AUTO: result += F("AUTO"); break; @@ -719,10 +788,10 @@ std::string IRMitsubishiAC::toString() { result += F("SILENT"); break; default: - result += uint64ToString(getFan()); + result += uint64ToString(this->getFan()); } result += F(", VANE: "); - switch (getVane()) { + switch (this->getVane()) { case MITSUBISHI_AC_VANE_AUTO: result += F("AUTO"); break; @@ -730,16 +799,16 @@ std::string IRMitsubishiAC::toString() { result += F("AUTO MOVE"); break; default: - result += uint64ToString(getVane()); + result += uint64ToString(this->getVane()); } result += F(", Time: "); - result += timeToString(getClock()); + result += this->timeToString(this->getClock()); result += F(", On timer: "); - result += timeToString(getStartClock()); + result += this->timeToString(this->getStartClock()); result += F(", Off timer: "); - result += timeToString(getStopClock()); + result += this->timeToString(this->getStopClock()); result += F(", Timer: "); - switch (getTimer()) { + switch (this->getTimer()) { case kMitsubishiAcNoTimer: result += '-'; break; @@ -754,7 +823,7 @@ std::string IRMitsubishiAC::toString() { break; default: result += F("? ("); - result += getTimer(); + 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 c8dca5dbcd..a2f440be89 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -61,44 +61,48 @@ 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); #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); + 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -108,12 +112,12 @@ class IRMitsubishiAC { IRsendTest _irsend; #endif #ifdef ARDUINO - String timeToString(uint64_t time); + String timeToString(const uint64_t time); #else - std::string timeToString(uint64_t time); + std::string timeToString(const uint64_t time); #endif uint8_t remote_state[kMitsubishiACStateLength]; - void checksum(); + void checksum(void); }; #endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp index 9048124d4b..4a1b8686f8 100644 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp @@ -76,7 +76,7 @@ void IRsend::sendMitsubishiHeavy152(const unsigned char data[], IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac( const uint16_t pin) : _irsend(pin) { stateReset(); } -void IRMitsubishiHeavy152Ac::begin() { _irsend.begin(); } +void IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHIHEAVY void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { @@ -303,7 +303,6 @@ bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state, return true; } - // Convert a standard A/C mode into its native mode. uint8_t IRMitsubishiHeavy152Ac::convertMode(const stdAc::opmode_t mode) { switch (mode) { @@ -378,6 +377,80 @@ uint8_t IRMitsubishiHeavy152Ac::convertSwingH(const stdAc::swingh_t position) { } } +// 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. #ifdef ARDUINO String IRMitsubishiHeavy152Ac::toString(void) { @@ -386,6 +459,7 @@ String IRMitsubishiHeavy152Ac::toString(void) { std::string IRMitsubishiHeavy152Ac::toString(void) { std::string result = ""; #endif // ARDUINO + 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: "); @@ -520,7 +594,7 @@ std::string IRMitsubishiHeavy152Ac::toString(void) { IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac( const uint16_t pin) : _irsend(pin) { stateReset(); } -void IRMitsubishiHeavy88Ac::begin() { _irsend.begin(); } +void IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHIHEAVY void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { @@ -737,7 +811,6 @@ 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) { @@ -796,6 +869,69 @@ uint8_t IRMitsubishiHeavy88Ac::convertSwingH(const stdAc::swingh_t position) { } } +// 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. #ifdef ARDUINO String IRMitsubishiHeavy88Ac::toString(void) { @@ -804,6 +940,7 @@ String IRMitsubishiHeavy88Ac::toString(void) { std::string IRMitsubishiHeavy88Ac::toString(void) { std::string result = ""; #endif // ARDUINO + 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: "); diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h index bcd85c6e01..a6e784bb88 100644 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.h @@ -176,6 +176,11 @@ class IRMitsubishiHeavy152Ac { 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); #ifdef ARDUINO String toString(void); #else // ARDUINO @@ -190,7 +195,7 @@ class IRMitsubishiHeavy152Ac { #endif // UNIT_TEST // The state of the IR remote in IR code form. uint8_t remote_state[kMitsubishiHeavy152StateLength]; - void checksum(); + void checksum(void); }; class IRMitsubishiHeavy88Ac { @@ -245,6 +250,10 @@ class IRMitsubishiHeavy88Ac { 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); #ifdef ARDUINO String toString(void); #else // ARDUINO @@ -259,6 +268,6 @@ class IRMitsubishiHeavy88Ac { #endif // UNIT_TEST // The state of the IR remote in IR code form. uint8_t remote_state[kMitsubishiHeavy152StateLength]; - void checksum(); + void checksum(void); }; #endif // IR_MITSUBISHIHEAVY_H_ diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp index 47aa51c96d..cdd2a2bdef 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266/src/ir_Panasonic.cpp @@ -77,7 +77,8 @@ const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a 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 +97,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 +118,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 +144,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) @@ -217,7 +220,8 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, // 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 +240,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 +270,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 @@ -302,7 +308,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; @@ -321,7 +327,7 @@ void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { } } -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)) @@ -335,8 +341,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; } @@ -357,32 +363,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: @@ -391,16 +397,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. @@ -414,7 +420,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; @@ -426,7 +434,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) { @@ -442,7 +450,7 @@ 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; @@ -462,12 +470,12 @@ 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() { - switch (getModel()) { +bool IRPanasonicAc::getQuiet(void) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: return remote_state[21] & kPanasonicAcQuietCkp; @@ -476,9 +484,9 @@ bool IRPanasonicAc::getQuiet() { } } -void IRPanasonicAc::setQuiet(const bool state) { +void IRPanasonicAc::setQuiet(const bool on) { uint8_t quiet; - switch (getModel()) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: quiet = kPanasonicAcQuietCkp; @@ -487,16 +495,16 @@ void IRPanasonicAc::setQuiet(const bool state) { 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() { - switch (getModel()) { +bool IRPanasonicAc::getPowerful(void) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: return remote_state[21] & kPanasonicAcPowerfulCkp; @@ -505,9 +513,9 @@ bool IRPanasonicAc::getPowerful() { } } -void IRPanasonicAc::setPowerful(const bool state) { +void IRPanasonicAc::setPowerful(const bool on) { uint8_t powerful; - switch (getModel()) { + switch (this->getModel()) { case kPanasonicRkr: case kPanasonicCkp: powerful = kPanasonicAcPowerfulCkp; @@ -516,8 +524,8 @@ void IRPanasonicAc::setPowerful(const bool state) { 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; @@ -528,7 +536,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; @@ -543,7 +551,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; @@ -567,13 +575,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; @@ -599,9 +607,9 @@ 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; } @@ -685,14 +693,84 @@ uint8_t IRPanasonicAc::convertSwingH(const stdAc::swingh_t position) { } } +// 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 IRPanasonicAc::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(180); // Reserve some heap for the string to reduce fragging. result += F("Model: "); result += uint64ToString(getModel()); switch (getModel()) { @@ -856,8 +934,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 1a7b4e1144..2835bc1646 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266/src/ir_Panasonic.h @@ -81,60 +81,65 @@ enum panasonic_ac_remote_model_t { 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); #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(); + 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); #ifdef ARDUINO - String toString(); + String toString(void); static String timeToString(const uint16_t mins_since_midnight); #else - std::string toString(); + std::string toString(void); static std::string timeToString(const uint16_t mins_since_midnight); #endif #ifndef UNIT_TEST diff --git a/lib/IRremoteESP8266/src/ir_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp index 7e54d17df6..83ad27b4f5 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266/src/ir_Samsung.cpp @@ -68,7 +68,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 +86,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 +114,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) @@ -326,9 +327,11 @@ void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, } #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; @@ -341,9 +344,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) { @@ -365,25 +369,33 @@ 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); } @@ -391,7 +403,7 @@ void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { // 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, @@ -430,8 +442,8 @@ void IRSamsungAc::sendOff(const uint16_t repeat) { } #endif // SEND_SAMSUNG_AC -uint8_t *IRSamsungAc::getRaw() { - checksum(); +uint8_t *IRSamsungAc::getRaw(void) { + this->checksum(); return remote_state; } @@ -446,24 +458,26 @@ void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { } } -void IRSamsungAc::on() { +void IRSamsungAc::on(void) { remote_state[1] &= ~kSamsungAcPowerMask1; remote_state[6] |= kSamsungAcPowerMask2; + _sendpower = true; // Flag that we need to send the special power message(s). } -void IRSamsungAc::off() { +void IRSamsungAc::off(void) { remote_state[1] |= kSamsungAcPowerMask1; remote_state[6] &= ~kSamsungAcPowerMask2; + _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() { +bool IRSamsungAc::getPower(void) { return ((remote_state[6] & kSamsungAcPowerMask2) != 0) && ((remote_state[1] & kSamsungAcPowerMask1) == 0); } @@ -477,7 +491,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; } @@ -489,14 +503,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; } @@ -507,10 +522,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; @@ -518,42 +533,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 { @@ -563,15 +580,16 @@ void IRSamsungAc::setClean(const bool state) { } // Very unsure this is correct. -bool IRSamsungAc::getQuiet() { +bool IRSamsungAc::getQuiet(void) { return remote_state[11] & kSamsungAcQuietMask11; } // Very unsure this is correct. -void IRSamsungAc::setQuiet(const bool state) { - if (state) { +void IRSamsungAc::setQuiet(const bool on) { + if (on) { remote_state[11] |= kSamsungAcQuietMask11; - setFan(kSamsungAcFanAuto); // Quiet mode seems to set fan speed to auto. + // Quiet mode seems to set fan speed to auto. + this->setFan(kSamsungAcFanAuto); } else { remote_state[11] &= ~kSamsungAcQuietMask11; } @@ -610,14 +628,63 @@ uint8_t IRSamsungAc::convertFan(const stdAc::fanspeed_t speed) { } } +// 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->getFan() == kSamsungAcFanTurbo; + 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 IRSamsungAc::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(100); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (getPower()) result += F("On"); @@ -706,8 +773,8 @@ std::string IRSamsungAc::toString() { // // 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 9df427c6ff..648b886619 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -61,9 +61,9 @@ 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); @@ -72,26 +72,26 @@ class IRSamsungAc { void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat); void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat); #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); + uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kSamsungAcStateLength); static bool validChecksum(const uint8_t state[], @@ -100,10 +100,13 @@ class IRSamsungAc { const uint16_t length = kSamsungAcStateLength); 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -114,6 +117,7 @@ class IRSamsungAc { #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); }; diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp index ae1b59c74d..fac5ac30ff 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.cpp +++ b/lib/IRremoteESP8266/src/ir_Sharp.cpp @@ -1,7 +1,11 @@ // Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran +// Copyright 2017, 2019 David Conran +#include "ir_Sharp.h" #include +#ifndef ARDUINO +#include +#endif #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" @@ -59,7 +63,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 +74,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 +108,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 +150,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 +178,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 +271,371 @@ 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. +#ifdef ARDUINO +String IRSharpAc::toString(void) { + String result = ""; +#else // ARDUINO +std::string IRSharpAc::toString(void) { + std::string result = ""; +#endif // ARDUINO + 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 0000000000..d9a6c8bc12 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Sharp.h @@ -0,0 +1,99 @@ +// Copyright 2019 crankyoldgit +#ifndef IR_SHARP_H_ +#define IR_SHARP_H_ + +#ifndef UNIT_TEST +#include +#else +#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); +#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); +#ifdef ARDUINO + String toString(void); + static String renderTime(const uint16_t timemins); +#else + std::string toString(void); + static std::string renderTime(const uint16_t timemins); +#endif +#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_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp index 79fb23cf1a..f3ce57efb8 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,7 +112,7 @@ 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; } @@ -151,7 +151,7 @@ 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; @@ -174,7 +174,7 @@ void IRTcl112Ac::setFan(const uint8_t speed) { } // Return the currect fan speed. -uint8_t IRTcl112Ac::getFan() { +uint8_t IRTcl112Ac::getFan(void) { return remote_state[8] & kTcl112AcFanMask; } @@ -291,14 +291,63 @@ uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { } } +// 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. #ifdef ARDUINO -String IRTcl112Ac::toString() { +String IRTcl112Ac::toString(void) { String result = ""; #else -std::string IRTcl112Ac::toString() { +std::string IRTcl112Ac::toString(void) { std::string result = ""; #endif // ARDUINO + 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: "); @@ -371,8 +420,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; diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index a1595451d1..7f1d153e5d 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -48,7 +48,7 @@ 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); @@ -85,10 +85,13 @@ class IRTcl112Ac { 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -98,7 +101,7 @@ class IRTcl112Ac { IRsendTest _irsend; #endif uint8_t remote_state[kTcl112AcStateLength]; - void stateReset(); + void stateReset(void); void checksum(const uint16_t length = kTcl112AcStateLength); }; diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp index 779bf8f8ff..e59b954916 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) { @@ -168,6 +169,53 @@ uint8_t IRTecoAc::convertFan(const stdAc::fanspeed_t speed) { } } +// 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) { @@ -176,6 +224,7 @@ String IRTecoAc::toString(void) { std::string IRTecoAc::toString(void) { std::string result = ""; #endif // ARDUINO + 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: "); @@ -237,7 +286,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 65a0050ae2..f92aee2083 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.h +++ b/lib/IRremoteESP8266/src/ir_Teco.h @@ -125,6 +125,9 @@ class IRTecoAc { 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); #ifdef ARDUINO String toString(void); #else diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.cpp b/lib/IRremoteESP8266/src/ir_Toshiba.cpp index a82a2fb24d..844b1cd4d9 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266/src/ir_Toshiba.cpp @@ -46,8 +46,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 +64,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 +83,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 +135,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 +197,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 +208,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,25 +216,23 @@ 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); } } @@ -266,22 +269,69 @@ uint8_t IRToshibaAC::convertFan(const stdAc::fanspeed_t speed) { } } +// 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 IRToshibaAC::toString(void) { std::string result = ""; #endif // ARDUINO result += F("Power: "); - if (getPower()) + if (this->getPower()) result += F("On"); else result += F("Off"); result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kToshibaAcAuto: result += F(" (AUTO)"); break; @@ -298,10 +348,10 @@ std::string IRToshibaAC::toString() { result += F(" (UNKNOWN)"); } result += F(", Temp: "); - result += uint64ToString(getTemp()); + result += uint64ToString(this->getTemp()); result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { + result += uint64ToString(this->getFan()); + switch (this->getFan()) { case kToshibaAcFanAuto: result += F(" (AUTO)"); break; @@ -326,8 +376,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 03b461add8..3eb89487b7 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -47,33 +47,36 @@ 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); #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); 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp index b5c15e7fdb..bc5b9db28d 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.cpp +++ b/lib/IRremoteESP8266/src/ir_Trotec.cpp @@ -1,75 +1,93 @@ // Copyright 2017 stufisher +// Copyright 2019 crankyoldgit #include "ir_Trotec.h" #include +#ifndef UNIT_TEST +#include +#else +#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(); + this->checksum(); _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); } #endif // SEND_TROTEC -void IRTrotecESP::checksum() { - uint8_t sum = 0; +uint8_t IRTrotecESP::calcChecksum(const uint8_t state[], + const uint16_t length) { + return sumBytes(state + 2, length - 3); +} - for (uint8_t i = 2; i < 8; i++) sum += remote_state[i]; - remote_state[8] = sum & 0xFF; +bool IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); } -void IRTrotecESP::stateReset() { +void IRTrotecESP::checksum(void) { + remote_state[kTrotecStateLength - 1] = sumBytes(remote_state + 2, + kTrotecStateLength - 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; - setPower(false); - setTemp(kTrotecDefTemp); - setSpeed(kTrotecFanMed); - setMode(kTrotecAuto); + this->setPower(false); + this->setTemp(kTrotecDefTemp); + this->setSpeed(kTrotecFanMed); + this->setMode(kTrotecAuto); } -uint8_t* IRTrotecESP::getRaw() { - checksum(); +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; @@ -77,14 +95,16 @@ void IRTrotecESP::setPower(const bool on) { remote_state[2] &= ~kTrotecPowerBit; } -bool IRTrotecESP::getPower() { return remote_state[2] & kTrotecPowerBit; } +bool IRTrotecESP::getPower(void) { return remote_state[2] & kTrotecPowerBit; } 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 (remote_state[2] & 0b00110000) >> 4; } +uint8_t IRTrotecESP::getSpeed(void) { + return (remote_state[2] & 0b00110000) >> 4; +} void IRTrotecESP::setMode(const uint8_t mode) { switch (mode) { @@ -99,7 +119,7 @@ void IRTrotecESP::setMode(const uint8_t mode) { } } -uint8_t IRTrotecESP::getMode() { return remote_state[2] & 0b00000011; } +uint8_t IRTrotecESP::getMode(void) { return remote_state[2] & 0b00000011; } void IRTrotecESP::setTemp(const uint8_t celsius) { uint8_t temp = std::max(celsius, kTrotecMinTemp); @@ -107,12 +127,12 @@ void IRTrotecESP::setTemp(const uint8_t celsius) { remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); } -uint8_t IRTrotecESP::getTemp() { +uint8_t IRTrotecESP::getTemp(void) { return (remote_state[3] & 0b01111111) + kTrotecMinTemp; } -void IRTrotecESP::setSleep(bool sleep) { - if (sleep) +void IRTrotecESP::setSleep(const bool on) { + if (on) remote_state[3] |= kTrotecSleepBit; else remote_state[3] &= ~kTrotecSleepBit; @@ -128,7 +148,7 @@ void IRTrotecESP::setTimer(const uint8_t timer) { remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; } -uint8_t IRTrotecESP::getTimer() { return remote_state[6]; } +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) { @@ -160,3 +180,161 @@ uint8_t IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { 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. +#ifdef ARDUINO +String IRTrotecESP::toString(void) { + String result = ""; +#else +std::string IRTrotecESP::toString(void) { + std::string result = ""; +#endif // ARDUINO + 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 dfbc26c074..c2097f8d09 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.h +++ b/lib/IRremoteESP8266/src/ir_Trotec.h @@ -1,8 +1,14 @@ // Copyright 2017 stufisher +// Copyright 2019 crankyoldgit #ifndef IR_TROTEC_H_ #define IR_TROTEC_H_ +#ifndef UNIT_TEST +#include +#else +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" #ifdef UNIT_TEST @@ -60,30 +66,40 @@ class IRTrotecESP { #if SEND_TROTEC void send(const uint16_t repeat = kTrotecDefaultRepeat); #endif // SEND_TROTEC - void begin(); + void begin(void); void setPower(const bool state); - bool getPower(); + bool getPower(void); void setTemp(const uint8_t celsius); - uint8_t getTemp(); + uint8_t getTemp(void); void setSpeed(const uint8_t fan); - uint8_t getSpeed(); + uint8_t getSpeed(void); - uint8_t getMode(); + 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(); + 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); +#ifdef ARDUINO + String toString(void); +#else + std::string toString(void); +#endif #ifndef UNIT_TEST private: @@ -92,8 +108,10 @@ class IRTrotecESP { IRsendTest _irsend; #endif uint8_t remote_state[kTrotecStateLength]; - void stateReset(); - void checksum(); + 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 1fbb822cf6..7ce37ed4a1 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.cpp +++ b/lib/IRremoteESP8266/src/ir_Vestel.cpp @@ -53,10 +53,12 @@ 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 = kVestelAcStateDefault; remote_time_state = kVestelAcTimeStateDefault; @@ -64,14 +66,14 @@ void IRVestelAc::stateReset() { } // 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,14 +84,14 @@ 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); @@ -109,15 +111,15 @@ void IRVestelAc::setRaw(const uint64_t newState) { } // 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); @@ -125,7 +127,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); } @@ -165,14 +167,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; } @@ -313,53 +315,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; } @@ -385,22 +388,23 @@ 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); } @@ -437,37 +441,86 @@ uint8_t IRVestelAc::convertFan(const stdAc::fanspeed_t speed) { } } +// 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 IRVestelAc::toString(void) { std::string result = ""; #endif // ARDUINO - if (isTimeCommand()) { + result.reserve(100); // Reserve some heap for the string to reduce fragging. + if (this->isTimeCommand()) { result += F("Time: "); result += IRHaierAC::timeToString(getTime()); result += F(", Timer: "); - result += isTimerActive() ? IRHaierAC::timeToString(getTimer()) : F("Off"); - + result += this->isTimerActive() ? IRHaierAC::timeToString(this->getTimer()) + : F("Off"); result += F(", On Timer: "); - result += (isOnTimerActive() && !isTimerActive()) - ? IRHaierAC::timeToString(getOnTimer()) + result += (this->isOnTimerActive() && !this->isTimerActive()) + ? IRHaierAC::timeToString(this->getOnTimer()) : F("Off"); result += F(", Off Timer: "); result += - isOffTimerActive() ? IRHaierAC::timeToString(getOffTimer()) : F("Off"); + this->isOffTimerActive() ? IRHaierAC::timeToString(this->getOffTimer()) + : F("Off"); return result; } // Not a time command, it's a normal command. result += F("Power: "); - result += (getPower() ? F("On") : F("Off")); + result += (this->getPower() ? F("On") : F("Off")); result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kVestelAcAuto: result += F(" (AUTO)"); break; @@ -487,10 +540,10 @@ std::string IRVestelAc::toString() { result += F(" (UNKNOWN)"); } result += F(", Temp: "); - result += uint64ToString(getTemp()); + result += uint64ToString(this->getTemp()); result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { + result += uint64ToString(this->getFan()); + switch (this->getFan()) { case kVestelAcFanAuto: result += F(" (AUTO)"); break; @@ -513,13 +566,13 @@ std::string IRVestelAc::toString() { result += F(" (UNKNOWN)"); } result += F(", Sleep: "); - result += (getSleep() ? F("On") : F("Off")); + result += this->getSleep() ? F("On") : F("Off"); result += F(", Turbo: "); - result += (getTurbo() ? F("On") : F("Off")); + result += this->getTurbo() ? F("On") : F("Off"); result += F(", Ion: "); - result += (getIon() ? F("On") : F("Off")); + result += this->getIon() ? F("On") : F("Off"); result += F(", Swing: "); - result += (getSwing() ? F("On") : F("Off")); + result += this->getSwing() ? F("On") : F("Off"); return result; } @@ -535,8 +588,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 ab04e8b35a..85e741f49b 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.h +++ b/lib/IRremoteESP8266/src/ir_Vestel.h @@ -31,7 +31,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) @@ -108,17 +108,17 @@ 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); #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); @@ -134,17 +134,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); @@ -154,12 +154,15 @@ class IRVestelAc { bool isTimerActive(void); void setTimerActive(const bool on); static uint8_t calcChecksum(const uint64_t state); - uint8_t convertMode(const stdAc::opmode_t mode); - uint8_t convertFan(const stdAc::fanspeed_t speed); + 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -171,7 +174,7 @@ class IRVestelAc { uint64_t remote_state; uint64_t remote_time_state; bool use_time_state; - void checksum(); + void checksum(void); }; #endif // IR_VESTEL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp index 048c1a1eb9..e317c07887 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp @@ -53,8 +53,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 +85,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 +130,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 +145,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 +162,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 +179,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 +189,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 +213,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 +238,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 +259,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 +271,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 +294,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); } +uint16_t IRWhirlpoolAc::getOnTimer(void) { + return this->getTime(kWhirlpoolAcOnTimerPos); +} -bool IRWhirlpoolAc::isOnTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOnTimerPos); +bool IRWhirlpoolAc::isOnTimerEnabled(void) { + return this->isTimerEnabled(kWhirlpoolAcOnTimerPos); } -void IRWhirlpoolAc::enableOnTimer(const bool state) { - enableTimer(kWhirlpoolAcOnTimerPos, state); - setCommand(kWhirlpoolAcCommandOnTimer); +void IRWhirlpoolAc::enableOnTimer(const bool on) { + this->enableTimer(kWhirlpoolAcOnTimerPos, on); + this->setCommand(kWhirlpoolAcCommandOnTimer); } void IRWhirlpoolAc::setPowerToggle(const bool on) { @@ -342,54 +348,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; } @@ -429,6 +435,53 @@ uint8_t IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { } } +// 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; +} + #ifdef ARDUINO String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { String result = ""; @@ -448,15 +501,16 @@ std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { // 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 IRWhirlpoolAc::toString(void) { std::string result = ""; #endif // ARDUINO + result.reserve(200); // Reserve some heap for the string to reduce fragging. result += F("Model: "); - result += uint64ToString(getModel()); - switch (getModel()) { + result += uint64ToString(this->getModel()); + switch (this->getModel()) { case DG11J191: result += F(" (DG11J191)"); break; @@ -467,13 +521,10 @@ std::string IRWhirlpoolAc::toString() { result += F(" (UNKNOWN)"); } result += F(", Power toggle: "); - if (getPowerToggle()) - result += F("On"); - else - result += F("Off"); + result += this->getPowerToggle() ? F("On") : F("Off"); result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { + result += uint64ToString(this->getMode()); + switch (this->getMode()) { case kWhirlpoolAcHeat: result += F(" (HEAT)"); break; @@ -493,9 +544,9 @@ std::string IRWhirlpoolAc::toString() { result += F(" (UNKNOWN)"); } result += F(", Temp: "); - result += uint64ToString(getTemp()); + result += uint64ToString(this->getTemp()); result += F("C, Fan: "); - result += uint64ToString(getFan()); + result += uint64ToString(this->getFan()); switch (getFan()) { case kWhirlpoolAcFanAuto: result += F(" (AUTO)"); @@ -514,40 +565,28 @@ std::string IRWhirlpoolAc::toString() { break; } result += F(", Swing: "); - if (getSwing()) - result += F("On"); - else - result += F("Off"); + result += this->getSwing() ? F("On") : F("Off"); result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); + result += this->getLight() ? F("On") : F("Off"); result += F(", Clock: "); - result += timeToString(getClock()); + result += this->timeToString(this->getClock()); result += F(", On Timer: "); - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); + if (this->isOnTimerEnabled()) + result += this->timeToString(this->getOnTimer()); else result += F("Off"); result += F(", Off Timer: "); - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); + if (this->isOffTimerEnabled()) + result += this->timeToString(this->getOffTimer()); else result += F("Off"); result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); + result += this->getSleep() ? F("On") : F("Off"); result += F(", Super: "); - if (getSuper()) - result += F("On"); - else - result += F("Off"); + result += this->getSuper() ? F("On") : F("Off"); result += F(", Command: "); - result += uint64ToString(getCommand()); - switch (getCommand()) { + result += uint64ToString(this->getCommand()); + switch (this->getCommand()) { case kWhirlpoolAcCommandLight: result += F(" (LIGHT)"); break; @@ -606,8 +645,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 9604d025c5..a7c8bf7eb4 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.h @@ -87,45 +87,45 @@ 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); #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[], @@ -134,10 +134,13 @@ class IRWhirlpoolAc { const uint16_t length = kWhirlpoolAcStateLength); 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); #ifdef ARDUINO - String toString(); + String toString(void); #else - std::string toString(); + std::string toString(void); #endif #ifndef UNIT_TEST @@ -156,7 +159,7 @@ 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(); + int8_t getTempOffset(void); #ifdef ARDUINO String timeToString(uint16_t minspastmidnight); #else diff --git a/lib/IRremoteESP8266/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp index 0b5e270412..ba65d2f610 100644 --- a/lib/IRremoteESP8266/test/IRac_test.cpp +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -1,8 +1,10 @@ // 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" @@ -12,6 +14,7 @@ #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" @@ -42,7 +45,7 @@ TEST(TestIRac, Argo) { false, // Turbo -1); // Sleep EXPECT_TRUE(ac.getPower()); - EXPECT_EQ(1, ac.getMode()); + EXPECT_EQ(kArgoHeat, ac.getMode()); EXPECT_EQ(21, ac.getTemp()); EXPECT_EQ(kArgoFlapAuto, ac.getFlap()); EXPECT_FALSE(ac.getMax()); // Turbo @@ -83,8 +86,10 @@ TEST(TestIRac, Daikin) { IRac irac(0); char expected[] = "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Powerful: Off, " - "Quiet: Off, Sensor: Off, Eye: Off, Mold: On, Swing (Horizontal): Off, " - "Swing (Vertical): Off, Current Time: 0:00, On Time: Off, Off Time: 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, @@ -137,12 +142,42 @@ TEST(TestIRac, Daikin2) { 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); - char expected[] = - "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (MED), " + 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(); @@ -154,14 +189,16 @@ TEST(TestIRac, Fujitsu) { stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing - false); // Quiet - ASSERT_EQ(expected, ac.toString()); + 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(expected, ac.toString()); + ASSERT_EQ(ardb1_expected, ac.toString()); ac._irsend.reset(); irac.fujitsu(&ac, @@ -172,13 +209,42 @@ TEST(TestIRac, Fujitsu) { stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing - false); // Quiet - ASSERT_EQ(expected, ac.toString()); + 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()); } @@ -216,7 +282,7 @@ TEST(TestIRac, Haier) { IRac irac(0); IRrecv capture(0); char expected[] = - "Command: 1 (On), Mode: 3 (HEAT), Temp: 24C, Fan: 2, Swing: 1 (Up), " + "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"; @@ -509,7 +575,7 @@ TEST(TestIRac, Samsung) { false, // Turbo true, // Clean true, // Beep - false); // with the Hack Off + false); // with dopower Off ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); @@ -529,7 +595,7 @@ TEST(TestIRac, Samsung) { false, // Turbo true, // Clean true, // Beep - true); // with the Hack On + 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)); @@ -544,6 +610,27 @@ TEST(TestIRac, Samsung) { 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); @@ -622,6 +709,9 @@ TEST(TestIRac, Toshiba) { 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, @@ -635,6 +725,13 @@ TEST(TestIRac, Trotec) { 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) { @@ -705,6 +802,7 @@ TEST(TestIRac, Vestel) { 13 * 60 + 45); // Clock EXPECT_EQ( + "f38000d50" "m3110s9066" "m520s1535m520s480m520s480m520s480m520s480m520s480m520s480m520s480" "m520s480m520s1535m520s480m520s480m520s480m520s480m520s480m520s480" diff --git a/lib/IRremoteESP8266/test/IRrecv_test.cpp b/lib/IRremoteESP8266/test/IRrecv_test.cpp index 85b6685f00..c6268a3f33 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 03a59fce73..ffd69cf71d 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266/test/IRsend_test.cpp @@ -20,9 +20,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 +30,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 +42,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 +61,7 @@ TEST(TestSendData, SendOverLargeData) { irsend.begin(); irsend.sendData(1, 2, 3, 4, 0xFFFFFFFFFFFFFFFF, 70, true); EXPECT_EQ( + "d50" "m3s4m3s4m3s4m3s4m3s4m3s4" "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" "m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2m1s2" @@ -72,9 +75,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 +180,7 @@ TEST(TestSendRaw, GeneralUse) { irsend.sendRaw(rawData, 67, 38); EXPECT_EQ( + "f38000d50" "m8950s4500" "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" @@ -110,6 +197,7 @@ TEST(TestSendRaw, GeneralUse) { EXPECT_EQ(32, irsend.capture.bits); EXPECT_EQ(0xC3E0E0E8, irsend.capture.value); EXPECT_EQ( + "f38000d50" "m8950s4500" "m550s1650m600s1650m550s550m600s500m600s550m550s550m600s1650m550s1650" "m600s1650m600s1650m550s1700m550s550m600s550m550s550m600s500m600s550" diff --git a/lib/IRremoteESP8266/test/IRsend_test.h b/lib/IRremoteESP8266/test/IRsend_test.h index abe29a0de3..f434094332 100644 --- a/lib/IRremoteESP8266/test/IRsend_test.h +++ b/lib/IRremoteESP8266/test/IRsend_test.h @@ -23,6 +23,8 @@ extern uint32_t _IRtimer_unittest_now; 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 4a49076497..992695ca65 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" @@ -415,3 +416,75 @@ TEST(TestUtils, htmlEscape) { "&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[7] = {10, 20, 30, 40, 50, 60, 70}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(test_data, 7, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + uint16_t * result = resultToRawArray(&irsend.capture); + ASSERT_EQ(7, getCorrectedRawLength(&irsend.capture)); + EXPECT_STATE_EQ(test_data, result, 7); + if (result != NULL) delete[] result; + // Stick in some large values. + irsend.capture.rawbuf[3] = 60000; + EXPECT_EQ( + "uint16_t rawData[9] = {10, 20, 65535, 0, 54465, 40," + " 50, 60, 70}; // UNKNOWN A5E5F35D\n", + resultToSourceCode(&irsend.capture)); + uint16_t large_test_data[9] = {10, 20, 65535, 0, 54465, 40, 50, 60, 70}; + ASSERT_EQ(9, getCorrectedRawLength(&irsend.capture)); + result = resultToRawArray(&irsend.capture); + EXPECT_STATE_EQ(large_test_data, result, 9); + if (result != NULL) delete[] result; +} diff --git a/lib/IRremoteESP8266/test/Makefile b/lib/IRremoteESP8266/test/Makefile index 9a64aaaaa7..0a82e27a95 100644 --- a/lib/IRremoteESP8266/test/Makefile +++ b/lib/IRremoteESP8266/test/Makefile @@ -38,7 +38,8 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_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_Lego_test IRac_test \ - ir_MitsubishiHeavy_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. @@ -83,7 +84,7 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.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_Lego.o ir_Argo.o \ - ir_Trotec.o ir_MitsubishiHeavy.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 \ @@ -94,10 +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 \ @@ -106,7 +109,8 @@ 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 IRac.o ir_GlobalCache.o \ $(PROTOCOLS) gtest_main.a @@ -288,7 +292,7 @@ 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) $(INCLUDES) -c ir_Sharp_test.cpp @@ -560,5 +564,35 @@ ir_Lego_test : $(COMMON_OBJ) ir_Lego_test.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 c5469d4a52..2fa63afe62 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 0000000000..7932416e0d --- /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 24bdc232ab..053b31dd48 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 537a64e974..078b70cccb 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" @@ -590,3 +597,32 @@ 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); +} diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index f5cb677c7a..3bae073c61 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()); - - 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(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()); + + 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()); - - irdaikin.setEye(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(false); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_FALSE(irdaikin.getEcono()); - - irdaikin.setEcono(true); - ASSERT_TRUE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); - - irdaikin.setEye(false); - ASSERT_FALSE(irdaikin.getEye()); - EXPECT_TRUE(irdaikin.getEcono()); + ac.setEcono(false); + ac.setWeeklyTimerEnable(false); + ASSERT_FALSE(ac.getWeeklyTimerEnable()); + EXPECT_FALSE(ac.getEcono()); + + ac.setWeeklyTimerEnable(true); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(false); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); + EXPECT_TRUE(ac.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 expectedState[kDaikinStateLength] = { + 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(expectedState); + 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, 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.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. @@ -1410,11 +1514,18 @@ 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("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 @@ -1502,3 +1613,414 @@ 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); +} diff --git a/lib/IRremoteESP8266/test/ir_Denon_test.cpp b/lib/IRremoteESP8266/test/ir_Denon_test.cpp index 911fd75281..bfb554bb59 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 0c58496ced..21286b7ac6 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 453004a082..7d6d0c915d 100644 --- a/lib/IRremoteESP8266/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Electra_test.cpp @@ -18,6 +18,7 @@ TEST(TestSendElectraAC, SendDataOnly) { irsend.sendElectraAC(data); EXPECT_EQ( + "f38000d50" "m9166s4470" "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index 23fa3e7a79..b71a6a8e1a 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()); - - fujitsu.setModel(ARDB1); - fujitsu.stepVert(); + 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()); + + 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()); - - fujitsu.setModel(ARDB1); + 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()); + + 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 b9bfce9974..bad9bbdede 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 16a556b57f..00742aedac 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 0000000000..5185eb4b1a --- /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 6c7a1f637f..58b2ba89f0 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 008cfd0204..bc4895f0a0 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, - 0xED, 0x2D, 0x74, 0xB4}; + 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()); @@ -999,13 +1002,14 @@ TEST(TestHaierAcIssues, Issue668) { // Turn on the AC. ac._irsend.reset(); char expected_on[] = - "Command: 1 (On), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "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(); @@ -1036,7 +1040,7 @@ TEST(TestHaierAcIssues, Issue668) { // Increase the temp by 1. ac._irsend.reset(); char expected_temp_plus_one[] = - "Command: 6 (Temp Up), Mode: 0 (AUTO), Temp: 26C, Fan: 0 (AUTO), " + "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: @@ -1060,7 +1064,7 @@ TEST(TestHaierAcIssues, Issue668) { // Decrease the temp by 1. ac._irsend.reset(); char expected_temp_minus_one[] = - "Command: 7 (Temp Down), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "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()); @@ -1075,3 +1079,65 @@ TEST(TestHaierAcIssues, Issue668) { 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 de0a4a2a18..0d9b2f4466 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 0000000000..b182cce32e --- /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 c899fa8c60..3cd99c3aa3 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 001f8bcf24..e0ff6cb1ca 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 30ca07edc6..2925494b94 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 041109fb8e..bad724f76d 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 index 028cb992d9..4e859b1708 100644 --- a/lib/IRremoteESP8266/test/ir_Lego_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Lego_test.cpp @@ -22,6 +22,7 @@ TEST(TestSendLegoPf, SendDataOnly) { irsend.reset(); irsend.sendLegoPf(0x1234); EXPECT_EQ( + "f38000d50" "m158s1026" "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" @@ -30,6 +31,7 @@ TEST(TestSendLegoPf, SendDataOnly) { irsend.reset(); irsend.send(LEGOPF, 0x1234, kLegoPfBits); EXPECT_EQ( + "f38000d50" "m158s1026" "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" @@ -44,6 +46,7 @@ TEST(TestSendLegoPf, SendDataWithRepeats) { irsend.reset(); irsend.sendLegoPf(0x1234, kLegoPfBits, 1); EXPECT_EQ( + "f38000d50" "m0s32000" "m158s1026" "m158s263m158s263m158s263m158s553m158s263m158s263m158s553m158s263" @@ -69,6 +72,7 @@ TEST(TestSendLegoPf, SendDataWithRepeats) { irsend.reset(); irsend.sendLegoPf(0x2345, kLegoPfBits, 2); EXPECT_EQ( + "f38000d50" "m0s16000" "m158s1026" "m158s263m158s263m158s553m158s263m158s263m158s263m158s553m158s553" @@ -94,6 +98,7 @@ TEST(TestSendLegoPf, SendDataWithRepeats) { irsend.reset(); irsend.sendLegoPf(0x3456, kLegoPfBits, 7); EXPECT_EQ( + "f38000d50" "m158s1026" "m158s263m158s263m158s553m158s553m158s263m158s553m158s263m158s263" "m158s263m158s553m158s263m158s553m158s263m158s553m158s553m158s263" diff --git a/lib/IRremoteESP8266/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266/test/ir_Lutron_test.cpp index 6c99b99046..d682967ca4 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 9ecd0eac14..2ca69ac833 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 e1c3da83de..bbc5f33663 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 5d5f5e932f..8f49360f89 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 index 340a040782..f6bc49f2f0 100644 --- a/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266/test/ir_MitsubishiHeavy_test.cpp @@ -849,3 +849,72 @@ TEST(TestDecodeMitsubishiHeavy, ZjsSyntheticExample) { "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 7b8eb2192f..09930b1140 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 6b84b0ec91..c881b7b447 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 4a4ea05bb0..fff242326e 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 a1d8a79794..27d155595c 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 b78469adda..36d61c706f 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 e52c6dd908..513f985da7 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 e8aa9bfc1a..da9fa027cb 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 028dbd8b3b..22306a59b9 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 390e86e79e..47b00df856 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" @@ -997,6 +1003,7 @@ TEST(TestSendSamsung36, SendDataOnly) { irsend.reset(); irsend.sendSamsung36(0); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1008,6 +1015,7 @@ TEST(TestSendSamsung36, SendDataOnly) { irsend.outputStr()); irsend.sendSamsung36(0x400E00FF); EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1034,6 +1042,7 @@ TEST(TestSendSamsung36, SendWithRepeats) { irsend.reset(); irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 1); // 1 repeat. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1053,6 +1062,7 @@ TEST(TestSendSamsung36, SendWithRepeats) { irsend.outputStr()); irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 2); // 2 repeats. EXPECT_EQ( + "f38000d50" "m4480s4480" "m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560" "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560" @@ -1119,3 +1129,120 @@ 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"; + // 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); +} diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index 14c1c7da08..165e29f17d 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 8481a46499..28b104df67 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 22d9ead382..f1f41d9c8c 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 c79ff6175e..35c3287b0f 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 249dcc6372..1ed5203a23 100644 --- a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp @@ -246,7 +246,6 @@ TEST(TestTcl112AcClass, Power) { ac.toString()); } - TEST(TestTcl112AcClass, Checksum) { uint8_t temp16C[kTcl112AcStateLength] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, @@ -382,3 +381,38 @@ TEST(TestTcl112AcClass, FanSpeed) { 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); +} diff --git a/lib/IRremoteESP8266/test/ir_Teco_test.cpp b/lib/IRremoteESP8266/test/ir_Teco_test.cpp index 8d9b4d910a..8dd47f02db 100644 --- a/lib/IRremoteESP8266/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Teco_test.cpp @@ -23,6 +23,7 @@ TEST(TestSendTeco, SendDataOnly) { irsend.reset(); irsend.sendTeco(0x250002BC9); EXPECT_EQ( + "f38000d50" "m9000s4440" "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" @@ -41,6 +42,7 @@ TEST(TestSendTeco, SendWithRepeats) { irsend.reset(); irsend.sendTeco(0x250002BC9, kTecoBits, 2); // two repeats. EXPECT_EQ( + "f38000d50" "m9000s4440" "m620s1650m620s580m620s580m620s1650m620s580m620s580m620s1650m620s1650" "m620s1650m620s1650m620s580m620s1650m620s580m620s1650m620s580m620s580" @@ -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 b5e1e07a98..0977043069 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 0000000000..ec1794dcfe --- /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 823b92f18e..aefaadbf55 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 f0c4ebece5..489d9d8711 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 748a4c9bf4..92ced5cf61 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 08488949c6..fe199a2a80 100644 --- a/lib/IRremoteESP8266/tools/Makefile +++ b/lib/IRremoteESP8266/tools/Makefile @@ -50,7 +50,8 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.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_Lego.o \ - ir_MitsubishiHeavy.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) @@ -104,6 +105,9 @@ ir_Samsung.o : $(USER_DIR)/ir_Samsung.cpp $(USER_DIR)/ir_Samsung.h $(COMMON_DEPS ir_Kelvinator.o : $(USER_DIR)/ir_Kelvinator.cpp $(USER_DIR)/ir_Kelvinator.h $(COMMON_DEPS) $(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 @@ -122,8 +126,8 @@ ir_MitsubishiHeavy.o : $(USER_DIR)/ir_MitsubishiHeavy.h $(USER_DIR)/ir_Mitsubish ir_Fujitsu.o : $(USER_DIR)/ir_Fujitsu.h $(USER_DIR)/ir_Fujitsu.cpp $(COMMON_DEPS) $(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 @@ -209,5 +213,14 @@ ir_Teco.o : $(USER_DIR)/ir_Teco.cpp $(GTEST_HEADERS) ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(USER_DIR)/ir_Tcl.h $(GTEST_HEADERS) $(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 From 8dea6617f972ac3026fe47049fcb2a182191c949 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Fri, 7 Jun 2019 10:27:55 +0300 Subject: [PATCH 07/15] [IR TX RX] Add newest IR protocols in ESPEasy --- src/_P016_IR.ino | 127 +++++++++++++++++++++++++++++++-------------- src/_P035_IRTX.ino | 57 ++++++++++++++++++-- 2 files changed, 139 insertions(+), 45 deletions(-) diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index a9813ebab6..8cb6184978 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -192,133 +192,180 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string) // Display any extra A/C info if we have it. // Display the human readable state of an A/C message if we can. 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) { + if (results->decode_type == DAIKIN) { IRDaikinESP ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } #endif // DECODE_DAIKIN #if DECODE_DAIKIN2 - if (results.decode_type == DAIKIN2) { + if (results->decode_type == DAIKIN2) { IRDaikin2 ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); + 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_DAIKIN2 +#endif // DECODE_DAIKIN216 #if DECODE_FUJITSU_AC - if (results.decode_type == FUJITSU_AC) { + if (results->decode_type == FUJITSU_AC) { IRFujitsuAC ac(0); - ac.setRaw(results.state, results.bits / 8); + ac.setRaw(results->state, results->bits / 8); description = ac.toString(); } #endif // DECODE_FUJITSU_AC #if DECODE_KELVINATOR - if (results.decode_type == KELVINATOR) { + if (results->decode_type == KELVINATOR) { IRKelvinatorAC ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } #endif // DECODE_KELVINATOR #if DECODE_MITSUBISHI_AC - if (results.decode_type == MITSUBISHI_AC) { + if (results->decode_type == MITSUBISHI_AC) { IRMitsubishiAC ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); 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) { + if (results->decode_type == TOSHIBA_AC) { IRToshibaAC ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); 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) { + if (results->decode_type == GREE) { IRGreeAC ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } #endif // DECODE_GREE #if DECODE_MIDEA - if (results.decode_type == MIDEA) { + if (results->decode_type == MIDEA) { IRMideaAC ac(0); - ac.setRaw(results.value); // Midea uses value instead of state. + ac.setRaw(results->value); // Midea uses value instead of state. description = ac.toString(); } #endif // DECODE_MIDEA #if DECODE_HAIER_AC - if (results.decode_type == HAIER_AC) { + if (results->decode_type == HAIER_AC) { IRHaierAC ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } #endif // DECODE_HAIER_AC #if DECODE_HAIER_AC_YRW02 - if (results.decode_type == HAIER_AC_YRW02) { + if (results->decode_type == HAIER_AC_YRW02) { IRHaierACYRW02 ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } #endif // DECODE_HAIER_AC_YRW02 #if DECODE_SAMSUNG_AC - if (results.decode_type == SAMSUNG_AC) { + if (results->decode_type == SAMSUNG_AC) { IRSamsungAc ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state, results->bits / 8); 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) { + if (results->decode_type == COOLIX) { IRCoolixAC ac(0); - ac.setRaw(results.value); // Coolix uses value instead of state. + ac.setRaw(results->value); // Coolix uses value instead of state. description = ac.toString(); } #endif // DECODE_COOLIX #if DECODE_PANASONIC_AC - if (results.decode_type == PANASONIC_AC && - results.bits > kPanasonicAcShortBits) { + if (results->decode_type == PANASONIC_AC && + results->bits > kPanasonicAcShortBits) { IRPanasonicAc ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); - } + } #endif // DECODE_PANASONIC_AC #if DECODE_HITACHI_AC - if (results.decode_type == HITACHI_AC) { + if (results->decode_type == HITACHI_AC) { IRHitachiAc ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } #endif // DECODE_HITACHI_AC #if DECODE_WHIRLPOOL_AC - if (results.decode_type == WHIRLPOOL_AC) { + if (results->decode_type == WHIRLPOOL_AC) { IRWhirlpoolAc ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } #endif // DECODE_WHIRLPOOL_AC #if DECODE_VESTEL_AC - if (results.decode_type == VESTEL_AC) { + if (results->decode_type == VESTEL_AC) { IRVestelAc ac(0); - ac.setRaw(results.value); // Like Coolix, use value instead of state. + ac.setRaw(results->value); // Like Coolix, use value instead of state. description = ac.toString(); } #endif // DECODE_VESTEL_AC #if DECODE_TECO - if (results.decode_type == TECO) { + if (results->decode_type == TECO) { IRTecoAc ac(0); - ac.setRaw(results.value); // Like Coolix, use value instead of state. + ac.setRaw(results->value); // Like Coolix, use value instead of state. description = ac.toString(); } #endif // DECODE_TECO #if DECODE_TCL112AC - if (results.decode_type == TCL112AC) { + if (results->decode_type == TCL112AC) { IRTcl112Ac ac(0); - ac.setRaw(results.state); + ac.setRaw(results->state); description = ac.toString(); } -#endif // DECODE_TCL112AC +#endif // DECODE_TCL112AC // If we got a human-readable description of the message, display it. if (description != "") addLog(LOG_LEVEL_INFO, description); diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index 42a158574a..851c2e8b97 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -378,6 +378,14 @@ bool sendIRCode(int const ir_type, 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) @@ -453,6 +461,7 @@ bool sendIRCode(int const ir_type, #endif case DAIKIN: // 16 case DAIKIN2: // 53 + case DAIKIN216: // 61 case KELVINATOR: // 18 case MITSUBISHI_AC: // 20 case GREE: // 24 @@ -467,6 +476,7 @@ bool sendIRCode(int const ir_type, 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 @@ -475,7 +485,7 @@ bool sendIRCode(int const ir_type, #if SEND_DENON case DENON: // 17 if (bits == 0) - bits = DENON_BITS; + bits = kDenonBits; irsend->sendDenon(code, bits, repeat); break; #endif @@ -510,7 +520,7 @@ bool sendIRCode(int const ir_type, #endif #if SEND_PRONTO case PRONTO: // 25 - // success = parseStringAndSendPronto(irsend, code_str, repeat); + //success = parseStringAndSendPronto(irsend, code_str, repeat); break; #endif #if SEND_NIKAI @@ -522,12 +532,12 @@ bool sendIRCode(int const ir_type, #endif #if SEND_RAW case RAW: // 30 - //success = parseStringAndSendRaw(irsend, code_str); + // success = parseStringAndSendRaw(irsend, code_str); break; #endif #if SEND_GLOBALCACHE case GLOBALCACHE: // 31 - // success = parseStringAndSendGC(irsend, code_str); + //success = parseStringAndSendGC(irsend, code_str); break; #endif #if SEND_MIDEA @@ -616,6 +626,13 @@ bool sendIRCode(int const ir_type, 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; @@ -651,11 +668,28 @@ uint8_t strOffset = 0; 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 DAIKIN2: stateSize = kDaikin2StateLength; break; + case DAIKIN216: + stateSize = kDaikin216StateLength; + break; case ELECTRA_AC: stateSize = kElectraAcStateLength; break; @@ -730,6 +764,9 @@ uint8_t strOffset = 0; // 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 @@ -800,6 +837,11 @@ uint8_t strOffset = 0; 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)); @@ -868,6 +910,11 @@ uint8_t strOffset = 0; 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)); From 4493ae5109875b05d221459acf735e4a449caff3 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Fri, 7 Jun 2019 16:57:59 +0300 Subject: [PATCH 08/15] [IR TX] Arbitrary bits re-enabled Added back the ability to have arbitrary bits to the IR send command. Reflected that also in the received signal decoding. Using 0 bits as an argument the ir send command will still use the default protocol bits ref: https://www.letscontrolit.com/forum/viewtopic.php?f=4&t=6452&p=37373#p37373 --- src/_P016_IR.ino | 104 ++++++++++++++++++++++----------------------- src/_P035_IRTX.ino | 4 +- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index 8cb6184978..3f5f6a44c3 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -178,7 +178,7 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string) // Display the basic output of what we found. if (results.decode_type != UNKNOWN) { - addLog(LOG_LEVEL_INFO, String(F("IRSEND,")) + typeToString(results.decode_type, results.repeat) + ',' + resultToHexidecimal(&results)); //Show the appropriate command to the user, so he can replay the message via P035 + addLog(LOG_LEVEL_INFO, String(F("IRSEND,")) + typeToString(results.decode_type, results.repeat) + ',' + resultToHexidecimal(&results) + ',' + uint64ToString(results.bits)); //Show the appropriate command to the user, so he can replay the message via P035 } //Check if a solution for RAW2 is found and if not give the user the option to access the timings info. if (results.decode_type == UNKNOWN && !displayRawToReadableB32Hex()){ @@ -193,176 +193,176 @@ boolean Plugin_016(byte function, struct EventStruct *event, String& string) // Display the human readable state of an A/C message if we can. String description = ""; #if DECODE_ARGO - if (results->decode_type == ARGO) { + if (results.decode_type == ARGO) { IRArgoAC ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_ARGO #if DECODE_DAIKIN - if (results->decode_type == DAIKIN) { + if (results.decode_type == DAIKIN) { IRDaikinESP ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_DAIKIN #if DECODE_DAIKIN2 - if (results->decode_type == DAIKIN2) { + if (results.decode_type == DAIKIN2) { IRDaikin2 ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_DAIKIN2 #if DECODE_DAIKIN216 - if (results->decode_type == DAIKIN216) { + if (results.decode_type == DAIKIN216) { IRDaikin216 ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_DAIKIN216 #if DECODE_FUJITSU_AC - if (results->decode_type == FUJITSU_AC) { + if (results.decode_type == FUJITSU_AC) { IRFujitsuAC ac(0); - ac.setRaw(results->state, results->bits / 8); + ac.setRaw(results.state, results.bits / 8); description = ac.toString(); } #endif // DECODE_FUJITSU_AC #if DECODE_KELVINATOR - if (results->decode_type == KELVINATOR) { + if (results.decode_type == KELVINATOR) { IRKelvinatorAC ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_KELVINATOR #if DECODE_MITSUBISHI_AC - if (results->decode_type == MITSUBISHI_AC) { + if (results.decode_type == MITSUBISHI_AC) { IRMitsubishiAC ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_MITSUBISHI_AC #if DECODE_MITSUBISHIHEAVY - if (results->decode_type == MITSUBISHI_HEAVY_88) { + if (results.decode_type == MITSUBISHI_HEAVY_88) { IRMitsubishiHeavy88Ac ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } - if (results->decode_type == MITSUBISHI_HEAVY_152) { + if (results.decode_type == MITSUBISHI_HEAVY_152) { IRMitsubishiHeavy152Ac ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_MITSUBISHIHEAVY #if DECODE_TOSHIBA_AC - if (results->decode_type == TOSHIBA_AC) { + if (results.decode_type == TOSHIBA_AC) { IRToshibaAC ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_TOSHIBA_AC #if DECODE_TROTEC - if (results->decode_type == TROTEC) { + if (results.decode_type == TROTEC) { IRTrotecESP ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_TROTEC #if DECODE_GOODWEATHER - if (results->decode_type == GOODWEATHER) { + if (results.decode_type == GOODWEATHER) { IRGoodweatherAc ac(0); - ac.setRaw(results->value); // Goodweather uses value instead of state. + ac.setRaw(results.value); // Goodweather uses value instead of state. description = ac.toString(); } #endif // DECODE_GOODWEATHER #if DECODE_GREE - if (results->decode_type == GREE) { + if (results.decode_type == GREE) { IRGreeAC ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_GREE #if DECODE_MIDEA - if (results->decode_type == MIDEA) { + if (results.decode_type == MIDEA) { IRMideaAC ac(0); - ac.setRaw(results->value); // Midea uses value instead of state. + ac.setRaw(results.value); // Midea uses value instead of state. description = ac.toString(); } #endif // DECODE_MIDEA #if DECODE_HAIER_AC - if (results->decode_type == HAIER_AC) { + if (results.decode_type == HAIER_AC) { IRHaierAC ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_HAIER_AC #if DECODE_HAIER_AC_YRW02 - if (results->decode_type == HAIER_AC_YRW02) { + if (results.decode_type == HAIER_AC_YRW02) { IRHaierACYRW02 ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_HAIER_AC_YRW02 #if DECODE_SAMSUNG_AC - if (results->decode_type == SAMSUNG_AC) { + if (results.decode_type == SAMSUNG_AC) { IRSamsungAc ac(0); - ac.setRaw(results->state, results->bits / 8); + ac.setRaw(results.state, results.bits / 8); description = ac.toString(); } #endif // DECODE_SAMSUNG_AC #if DECODE_SHARP_AC - if (results->decode_type == SHARP_AC) { + if (results.decode_type == SHARP_AC) { IRSharpAc ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_SHARP_AC #if DECODE_COOLIX - if (results->decode_type == COOLIX) { + if (results.decode_type == COOLIX) { IRCoolixAC ac(0); - ac.setRaw(results->value); // Coolix uses value instead of state. + ac.setRaw(results.value); // Coolix uses value instead of state. description = ac.toString(); } #endif // DECODE_COOLIX #if DECODE_PANASONIC_AC - if (results->decode_type == PANASONIC_AC && - results->bits > kPanasonicAcShortBits) { + if (results.decode_type == PANASONIC_AC && + results.bits > kPanasonicAcShortBits) { IRPanasonicAc ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_PANASONIC_AC #if DECODE_HITACHI_AC - if (results->decode_type == HITACHI_AC) { + if (results.decode_type == HITACHI_AC) { IRHitachiAc ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_HITACHI_AC #if DECODE_WHIRLPOOL_AC - if (results->decode_type == WHIRLPOOL_AC) { + if (results.decode_type == WHIRLPOOL_AC) { IRWhirlpoolAc ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_WHIRLPOOL_AC #if DECODE_VESTEL_AC - if (results->decode_type == VESTEL_AC) { + if (results.decode_type == VESTEL_AC) { IRVestelAc ac(0); - ac.setRaw(results->value); // Like Coolix, use value instead of state. + ac.setRaw(results.value); // Like Coolix, use value instead of state. description = ac.toString(); } #endif // DECODE_VESTEL_AC #if DECODE_TECO - if (results->decode_type == TECO) { + if (results.decode_type == TECO) { IRTecoAc ac(0); - ac.setRaw(results->value); // Like Coolix, use value instead of state. + ac.setRaw(results.value); // Like Coolix, use value instead of state. description = ac.toString(); } #endif // DECODE_TECO #if DECODE_TCL112AC - if (results->decode_type == TCL112AC) { + if (results.decode_type == TCL112AC) { IRTcl112Ac ac(0); - ac.setRaw(results->state); + ac.setRaw(results.state); description = ac.toString(); } #endif // DECODE_TCL112AC diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index 851c2e8b97..d5b3b990a4 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -264,11 +264,9 @@ boolean Plugin_035(byte function, struct EventStruct *event, String& string) if (GetArgv(string.c_str(), ircodestr, 3)) { IrCode = strtoull(ircodestr.c_str(), NULL, 16); } - - if (GetArgv(string.c_str(), TmpStr1, 4)) IrBits = str2int(TmpStr1.c_str()); //not needed any more as I can tell... Left for reverse compitability so we won't brake old forum posts etc.. IrBits = 0; //Leave it to 0 for default protocol bits + if (GetArgv(string.c_str(), TmpStr1, 4)) IrBits = str2int(TmpStr1.c_str()); // Number of bits to be sent. USE 0 for default protocol bits if (GetArgv(string.c_str(), TmpStr1, 5)) IrRepeat = str2int(TmpStr1.c_str()); // Nr. of times the message is to be repeated - sendIRCode(strToDecodeType(IrType.c_str()),IrCode,ircodestr.c_str(),IrBits,IrRepeat); } From 4b5db4c57f9c29890c866d59335430bccb24ca56 Mon Sep 17 00:00:00 2001 From: jimmys01 Date: Sat, 8 Jun 2019 15:50:44 +0300 Subject: [PATCH 09/15] Update Json to ver 6 --- .../.clang-format | 0 .../.gitattributes | 0 lib/ArduinoJson-6.x/.github/FUNDING.yml | 1 + .../.github/ISSUE_TEMPLATE.md | 0 .../.gitignore | 1 + .../.mbedignore | 0 lib/ArduinoJson-6.x/.travis.yml | 132 ++ .../ArduinoJson.h | 2 +- .../CHANGELOG.md | 1292 +++++++++++------ .../CMakeLists.txt | 10 +- .../CONTRIBUTING.md | 0 .../LICENSE.md | 20 +- .../README.md | 223 +-- .../SUPPORT.md | 0 .../appveyor.yml | 4 +- .../banner.svg | 0 .../JsonConfigFile/JsonConfigFile.ino | 67 +- .../JsonGeneratorExample.ino | 77 + .../JsonHttpClient/JsonHttpClient.ino | 38 +- .../JsonParserExample/JsonParserExample.ino | 80 + .../examples/JsonServer/JsonServer.ino | 47 +- .../examples/JsonUdpBeacon/JsonUdpBeacon.ino | 33 +- .../examples/MsgPackParser/MsgPackParser.ino | 75 + .../ProgmemExample/ProgmemExample.ino | 40 +- .../examples/StringExample/StringExample.ino | 49 +- lib/ArduinoJson-6.x/fuzzing/CMakeLists.txt | 17 + lib/ArduinoJson-6.x/fuzzing/Makefile | 22 + lib/ArduinoJson-6.x/fuzzing/fuzzer_main.cpp | 50 + .../fuzzing/json_corpus}/.gitignore | 0 lib/ArduinoJson-6.x/fuzzing/json_fuzzer.cpp | 11 + .../fuzzing/json_seed_corpus}/Comments.json | 0 .../fuzzing/json_seed_corpus}/EmptyArray.json | 0 .../json_seed_corpus}/EmptyObject.json | 0 .../json_seed_corpus}/ExcessiveNesting.json | 0 .../json_seed_corpus/IntegerOverflow.json | 1 + .../fuzzing/json_seed_corpus}/Numbers.json | 0 .../json_seed_corpus}/OpenWeatherMap.json | 0 .../fuzzing/json_seed_corpus}/Strings.json | 0 .../json_seed_corpus}/WeatherUnderground.json | 0 .../fuzzing/msgpack_corpus/.gitignore | 2 + .../fuzzing/msgpack_fuzzer.cpp | 11 + .../fuzzing/msgpack_seed_corpus/array16 | Bin 0 -> 15 bytes .../fuzzing/msgpack_seed_corpus/array32 | Bin 0 -> 15 bytes .../fuzzing/msgpack_seed_corpus/false | 1 + .../fuzzing/msgpack_seed_corpus/fixarray | 1 + .../msgpack_seed_corpus/fixint_negative | 1 + .../msgpack_seed_corpus/fixint_positive | 1 + .../fuzzing/msgpack_seed_corpus/fixmap | 1 + .../fuzzing/msgpack_seed_corpus/fixstr | 1 + .../fuzzing/msgpack_seed_corpus/float32 | 1 + .../fuzzing/msgpack_seed_corpus/float64 | 1 + .../fuzzing/msgpack_seed_corpus/int16 | 1 + .../fuzzing/msgpack_seed_corpus/int32 | 1 + .../fuzzing/msgpack_seed_corpus/int64 | 1 + .../fuzzing/msgpack_seed_corpus/int8 | 1 + .../fuzzing/msgpack_seed_corpus/map16 | Bin 0 -> 19 bytes .../fuzzing/msgpack_seed_corpus/map32 | Bin 0 -> 23 bytes .../fuzzing/msgpack_seed_corpus/nil | 1 + .../fuzzing/msgpack_seed_corpus/str16 | Bin 0 -> 8 bytes .../fuzzing/msgpack_seed_corpus/str32 | Bin 0 -> 10 bytes .../fuzzing/msgpack_seed_corpus/str8 | 1 + .../fuzzing/msgpack_seed_corpus/true | 1 + .../fuzzing/msgpack_seed_corpus/uint16 | 1 + .../fuzzing/msgpack_seed_corpus/uint32 | 1 + .../fuzzing/msgpack_seed_corpus/uint64 | 1 + .../fuzzing/msgpack_seed_corpus/uint8 | 1 + lib/ArduinoJson-6.x/keywords.txt | 36 + .../library.json | 2 +- .../library.properties | 4 +- .../scripts/build-arduino-package.sh | 0 .../scripts/build-single-header.sh | 0 .../scripts/create-build-envs.sh | 0 .../scripts/oss-fuzz/.gitignore | 0 .../scripts/oss-fuzz/Vagrantfile | 11 +- .../scripts/publish-particle-library.sh | 18 + lib/ArduinoJson-6.x/scripts/publish.sh | 61 + .../scripts/travis/arduino.sh | 10 +- lib/ArduinoJson-6.x/scripts/travis/build.sh | 14 + .../scripts/travis/coverage.sh | 4 +- lib/ArduinoJson-6.x/scripts/travis/fuzz.sh | 26 + .../scripts/travis/platformio.sh | 22 + lib/ArduinoJson-6.x/scripts/travis/test.sh | 4 + .../scripts/wandbox/JsonGeneratorExample.cpp | 60 + .../scripts/wandbox/JsonParserExample.cpp | 59 + .../scripts/wandbox/MsgPackParserExample.cpp | 68 + .../scripts/wandbox/publish.sh | 29 + .../src/ArduinoJson.h | 2 +- lib/ArduinoJson-6.x/src/ArduinoJson.hpp | 69 + .../src/ArduinoJson/Array/ArrayFunctions.hpp | 29 + .../src/ArduinoJson/Array/ArrayImpl.hpp | 22 + .../src/ArduinoJson/Array/ArrayIterator.hpp | 121 ++ .../src/ArduinoJson/Array/ArrayRef.hpp | 153 ++ .../src/ArduinoJson/Array/ArrayShortcuts.hpp | 47 + .../src/ArduinoJson/Array/ElementProxy.hpp | 165 +++ .../src/ArduinoJson/Array/Utilities.hpp | 66 + .../ArduinoJson/Collection/CollectionData.hpp | 70 + .../ArduinoJson/Collection/CollectionImpl.hpp | 163 +++ .../src/ArduinoJson/Configuration.hpp | 82 +- .../Deserialization/ArduinoStreamReader.hpp | 31 + .../Deserialization/CharPointerReader.hpp | 67 + .../Deserialization/DeserializationError.hpp | 113 ++ .../Deserialization/FlashStringReader.hpp | 48 + .../Deserialization/IteratorReader.hpp | 31 + .../Deserialization/NestingLimit.hpp | 17 + .../Deserialization/StdStreamReader.hpp | 34 + .../Deserialization/deserialize.hpp | 76 + .../Document/BasicJsonDocument.hpp | 89 ++ .../Document/DynamicJsonDocument.hpp | 25 + .../src/ArduinoJson/Document/JsonDocument.hpp | 307 ++++ .../Document/StaticJsonDocument.hpp | 51 + .../src/ArduinoJson/Json/EscapeSequence.hpp} | 10 +- .../src/ArduinoJson/Json/JsonDeserializer.hpp | 420 ++++++ .../src/ArduinoJson/Json/JsonSerializer.hpp | 124 ++ .../ArduinoJson/Json/PrettyJsonSerializer.hpp | 83 ++ .../src/ArduinoJson/Json/TextFormatter.hpp | 155 ++ .../src/ArduinoJson/Json/Utf8.hpp | 26 + .../src/ArduinoJson/Memory/Alignment.hpp | 28 + .../src/ArduinoJson/Memory/MemoryPool.hpp | 120 ++ .../src/ArduinoJson/Memory/StringBuilder.hpp | 50 + .../src/ArduinoJson/Memory/StringSlot.hpp | 18 + .../src/ArduinoJson/Misc/SerializedValue.hpp | 68 + .../src/ArduinoJson/Misc/Visitable.hpp | 21 + .../MsgPack/MsgPackDeserializer.hpp | 346 +++++ .../ArduinoJson/MsgPack/MsgPackSerializer.hpp | 186 +++ .../src/ArduinoJson/MsgPack/endianess.hpp | 41 + .../src/ArduinoJson/MsgPack/ieee754.hpp | 16 + .../src/ArduinoJson/Namespace.hpp | 28 + .../src/ArduinoJson/Numbers/Float.hpp | 16 + .../src/ArduinoJson/Numbers}/FloatParts.hpp | 10 +- .../src/ArduinoJson/Numbers}/FloatTraits.hpp | 94 +- .../src/ArduinoJson/Numbers/Integer.hpp | 20 + .../src/ArduinoJson/Numbers/convertNumber.hpp | 105 ++ .../src/ArduinoJson/Numbers/parseFloat.hpp | 18 + .../src/ArduinoJson/Numbers/parseInteger.hpp | 19 + .../src/ArduinoJson/Numbers/parseNumber.hpp | 156 ++ .../src/ArduinoJson/Object/MemberProxy.hpp | 189 +++ .../ArduinoJson/Object/ObjectFunctions.hpp | 51 + .../src/ArduinoJson/Object/ObjectImpl.hpp | 52 + .../src/ArduinoJson/Object/ObjectIterator.hpp | 123 ++ .../src/ArduinoJson/Object/ObjectRef.hpp | 238 +++ .../ArduinoJson/Object/ObjectShortcuts.hpp | 74 + .../src/ArduinoJson/Object/Pair.hpp | 55 + .../ArduinoJson/Operators/VariantCasts.hpp | 24 + .../Operators/VariantComparisons.hpp | 244 ++++ .../Operators/VariantOperators.hpp | 19 + .../src/ArduinoJson/Operators/VariantOr.hpp | 37 + .../Operators/VariantShortcuts.hpp | 23 + .../src/ArduinoJson/Polyfills/alias_cast.hpp | 28 + .../src/ArduinoJson/Polyfills/assert.hpp | 12 + .../src/ArduinoJson/Polyfills/attributes.hpp | 18 +- .../src/ArduinoJson/Polyfills/ctype.hpp | 11 +- .../ArduinoJson/Polyfills/gsl/not_null.hpp | 33 + .../src/ArduinoJson/Polyfills/limits.hpp | 45 + .../src/ArduinoJson/Polyfills/math.hpp | 25 + .../src/ArduinoJson/Polyfills/mpl/max.hpp | 24 + .../src/ArduinoJson/Polyfills/safe_strcmp.hpp | 22 + .../src/ArduinoJson/Polyfills/type_traits.hpp | 20 + .../Polyfills/type_traits/conditional.hpp | 18 + .../Polyfills/type_traits/enable_if.hpp} | 12 +- .../type_traits/integral_constant.hpp | 17 + .../Polyfills/type_traits/is_array.hpp | 19 + .../Polyfills/type_traits/is_base_of.hpp} | 15 +- .../Polyfills/type_traits/is_const.hpp | 17 + .../type_traits/is_floating_point.hpp | 19 + .../Polyfills/type_traits/is_integral.hpp | 35 + .../Polyfills/type_traits/is_same.hpp | 17 + .../Polyfills/type_traits/is_signed.hpp | 43 + .../Polyfills/type_traits/is_unsigned.hpp | 37 + .../Polyfills/type_traits/make_unsigned.hpp | 49 + .../Polyfills/type_traits/remove_const.hpp} | 12 +- .../type_traits/remove_reference.hpp} | 12 +- .../Polyfills/type_traits/type_identity.hpp | 15 + .../src/ArduinoJson/Polyfills/utility.hpp | 14 + .../ArduinoJson/Serialization/DummyWriter.hpp | 19 + .../Serialization/DynamicStringWriter.hpp | 79 + .../Serialization/StaticStringWriter.hpp | 37 + .../Serialization/StreamWriter.hpp | 38 + .../src/ArduinoJson/Serialization/measure.hpp | 19 + .../ArduinoJson/Serialization/serialize.hpp | 59 + .../StringStorage/StringCopier.hpp | 25 + .../ArduinoJson/StringStorage/StringMover.hpp | 38 + .../StringStorage/StringStorage.hpp | 42 + .../Strings/ArduinoStringAdapter.hpp | 65 + .../Strings/ConstRamStringAdapter.hpp | 55 + .../Strings/FlashStringAdapter.hpp | 59 + .../ArduinoJson/Strings/RamStringAdapter.hpp | 47 + .../Strings/SizedFlashStringAdapter.hpp | 53 + .../Strings/SizedRamStringAdapter.hpp | 52 + .../ArduinoJson/Strings/StlStringAdapter.hpp | 59 + .../src/ArduinoJson/Strings/String.hpp | 65 + .../ArduinoJson/Strings/StringAdapters.hpp | 36 + .../src/ArduinoJson/Variant/SlotFunctions.hpp | 39 + .../src/ArduinoJson/Variant/VariantAs.hpp | 96 ++ .../src/ArduinoJson/Variant/VariantAsImpl.hpp | 41 + .../ArduinoJson/Variant/VariantContent.hpp | 51 + .../src/ArduinoJson/Variant/VariantData.hpp | 363 +++++ .../ArduinoJson/Variant/VariantFunctions.hpp | 166 +++ .../src/ArduinoJson/Variant/VariantImpl.hpp | 150 ++ .../src/ArduinoJson/Variant/VariantRef.hpp | 383 +++++ .../src/ArduinoJson/Variant/VariantSlot.hpp | 96 ++ .../src/ArduinoJson/Variant/VariantTo.hpp | 30 + .../src/ArduinoJson/compatibility.hpp | 23 + .../src/ArduinoJson/version.hpp | 10 + .../test/CMakeLists.txt | 26 +- .../test/ElementProxy/CMakeLists.txt | 14 + lib/ArduinoJson-6.x/test/ElementProxy/add.cpp | 26 + .../test/ElementProxy/clear.cpp | 28 + .../test/ElementProxy/remove.cpp | 56 + lib/ArduinoJson-6.x/test/ElementProxy/set.cpp | 26 + .../test/ElementProxy/size.cpp | 30 + .../test/IntegrationTests/CMakeLists.txt | 5 +- .../test/IntegrationTests/gbathree.cpp | 78 +- .../test/IntegrationTests/issue772.cpp | 28 + .../test/IntegrationTests/round_trip.cpp | 10 +- .../test/JsonArray/CMakeLists.txt | 18 +- lib/ArduinoJson-6.x/test/JsonArray/add.cpp | 138 ++ .../test/JsonArray/copyArray.cpp | 117 ++ .../test/JsonArray/createNested.cpp | 21 + lib/ArduinoJson-6.x/test/JsonArray/equals.cpp | 50 + lib/ArduinoJson-6.x/test/JsonArray/get.cpp | 16 + lib/ArduinoJson-6.x/test/JsonArray/isNull.cpp | 34 + .../test/JsonArray/iterator.cpp | 28 +- .../test/JsonArray/memoryUsage.cpp | 42 + .../test/JsonArray/nesting.cpp | 35 + .../test/JsonArray/remove.cpp | 6 +- lib/ArduinoJson-6.x/test/JsonArray/size.cpp | 31 + .../test/JsonArray/std_string.cpp | 31 + .../test/JsonArray/subscript.cpp | 162 +++ .../test/JsonArray/undefined.cpp | 35 + .../test/JsonDeserializer/CMakeLists.txt | 21 + .../JsonDeserializer/DeserializationError.cpp | 137 ++ .../test/JsonDeserializer/array.cpp | 402 +++++ .../test/JsonDeserializer/array_static.cpp | 89 ++ .../JsonDeserializer/incomplete_input.cpp | 27 + .../test/JsonDeserializer/input_types.cpp | 115 ++ .../test/JsonDeserializer/invalid_input.cpp | 35 + .../test/JsonDeserializer/misc.cpp | 143 ++ .../test/JsonDeserializer/nestingLimit.cpp | 101 ++ .../test/JsonDeserializer/number.cpp | 133 ++ .../test/JsonDeserializer/object.cpp | 498 +++++++ .../test/JsonDeserializer/object_static.cpp | 64 + .../test/JsonDeserializer/string.cpp | 72 + .../test/JsonDocument/BasicJsonDocument.cpp | 49 + .../test/JsonDocument/CMakeLists.txt | 20 + .../test/JsonDocument/DynamicJsonDocument.cpp | 209 +++ .../test/JsonDocument/StaticJsonDocument.cpp | 212 +++ lib/ArduinoJson-6.x/test/JsonDocument/add.cpp | 22 + .../test/JsonDocument/containsKey.cpp | 44 + .../test/JsonDocument/createNested.cpp | 66 + .../test/JsonDocument/isNull.cpp | 39 + .../test/JsonDocument/nesting.cpp | 30 + .../test/JsonDocument/remove.cpp | 52 + .../test/JsonDocument/size.cpp | 28 + .../test/JsonDocument/subscript.cpp | 45 + .../test/JsonObject/CMakeLists.txt | 15 +- .../test/JsonObject/containsKey.cpp | 39 + lib/ArduinoJson-6.x/test/JsonObject/copy.cpp | 68 + .../test/JsonObject/createNestedArray.cpp | 27 + .../test/JsonObject/createNestedObject.cpp | 25 + .../test/JsonObject/equals.cpp | 53 + .../test/JsonObject/invalid.cpp | 35 + .../test/JsonObject/isNull.cpp | 34 + .../test/JsonObject/iterator.cpp | 60 + .../test/JsonObject/memoryUsage.cpp | 43 + .../test/JsonObject/nesting.cpp | 35 + .../test/JsonObject/remove.cpp | 72 + lib/ArduinoJson-6.x/test/JsonObject/size.cpp | 39 + .../test/JsonObject/std_string.cpp | 109 ++ .../test/JsonObject/subscript.cpp | 234 +++ .../test/JsonSerializer/CMakeLists.txt | 17 + .../test/JsonSerializer/JsonArray.cpp} | 27 +- .../test/JsonSerializer/JsonArrayPretty.cpp} | 18 +- .../test/JsonSerializer/JsonObject.cpp} | 45 +- .../test/JsonSerializer/JsonObjectPretty.cpp} | 18 +- .../test/JsonSerializer/JsonVariant.cpp} | 37 +- .../test/JsonSerializer/misc.cpp | 46 + .../test/JsonSerializer/std_stream.cpp | 66 + .../test/JsonSerializer/std_string.cpp | 47 + .../test/JsonVariant/CMakeLists.txt | 28 + lib/ArduinoJson-6.x/test/JsonVariant/add.cpp | 32 + lib/ArduinoJson-6.x/test/JsonVariant/as.cpp | 208 +++ .../test/JsonVariant/clear.cpp | 28 + .../test/JsonVariant/compare.cpp | 453 ++++++ .../test/JsonVariant/containsKey.cpp | 28 + lib/ArduinoJson-6.x/test/JsonVariant/copy.cpp | 86 ++ .../test/JsonVariant/createNested.cpp | 88 ++ lib/ArduinoJson-6.x/test/JsonVariant/is.cpp | 162 +++ .../test/JsonVariant/isnull.cpp | 80 + .../test/JsonVariant/memoryUsage.cpp | 39 + lib/ArduinoJson-6.x/test/JsonVariant/misc.cpp | 50 + .../test/JsonVariant/nesting.cpp | 31 + lib/ArduinoJson-6.x/test/JsonVariant/or.cpp | 96 ++ .../test/JsonVariant/overflow.cpp | 72 + .../test/JsonVariant/remove.cpp | 42 + lib/ArduinoJson-6.x/test/JsonVariant/set.cpp | 110 ++ .../test/JsonVariant/subscript.cpp | 195 +++ .../test/JsonVariant/types.cpp} | 34 +- .../test/JsonVariant/undefined.cpp | 70 + .../test/MemberProxy/CMakeLists.txt | 16 + lib/ArduinoJson-6.x/test/MemberProxy/add.cpp | 25 + .../test/MemberProxy/clear.cpp | 27 + .../test/MemberProxy/containsKey.cpp | 27 + .../test/MemberProxy/remove.cpp | 55 + lib/ArduinoJson-6.x/test/MemberProxy/set.cpp | 25 + lib/ArduinoJson-6.x/test/MemberProxy/size.cpp | 31 + .../test/MemberProxy/subscript.cpp | 19 + .../test/MemoryPool/CMakeLists.txt | 14 + .../test/MemoryPool/StringBuilder.cpp | 41 + .../test/MemoryPool/allocString.cpp | 67 + .../test/MemoryPool/allocVariant.cpp | 50 + lib/ArduinoJson-6.x/test/MemoryPool/clear.cpp | 31 + lib/ArduinoJson-6.x/test/MemoryPool/size.cpp | 58 + lib/ArduinoJson-6.x/test/Misc/CMakeLists.txt | 38 + .../test/Misc/FloatParts.cpp | 6 +- lib/ArduinoJson-6.x/test/Misc/Issue978.cpp | 13 + .../test/Misc/StreamReader.cpp | 33 + .../test/Misc/StringAdapters.cpp | 45 + .../test/Misc/StringWriter.cpp | 55 + lib/ArduinoJson-6.x/test/Misc/TypeTraits.cpp | 65 + lib/ArduinoJson-6.x/test/Misc/conflicts.cpp | 47 + .../test/Misc/unsigned_char.cpp | 207 +++ .../test/Misc/version.cpp | 6 +- .../test/MixedConfiguration/CMakeLists.txt | 23 + .../test/MixedConfiguration/cpp11.cpp | 31 + .../MixedConfiguration/decode_unicode_0.cpp | 11 + .../MixedConfiguration/decode_unicode_1.cpp | 11 + .../MixedConfiguration/enable_infinity_0.cpp | 35 + .../MixedConfiguration/enable_infinity_1.cpp | 38 + .../test/MixedConfiguration/enable_nan_0.cpp | 25 + .../test/MixedConfiguration/enable_nan_1.cpp | 31 + .../test/MixedConfiguration/use_double_0.cpp | 17 + .../test/MixedConfiguration/use_double_1.cpp | 17 + .../MixedConfiguration/use_long_long_0.cpp | 30 + .../MixedConfiguration/use_long_long_1.cpp | 17 + .../test/MsgPackDeserializer/CMakeLists.txt | 18 + .../MsgPackDeserializer/deserializeArray.cpp | 83 ++ .../MsgPackDeserializer/deserializeObject.cpp | 130 ++ .../deserializeStaticVariant.cpp | 152 ++ .../deserializeVariant.cpp | 146 ++ .../MsgPackDeserializer/doubleToFloat.cpp | 25 + .../MsgPackDeserializer/incompleteInput.cpp | 110 ++ .../test/MsgPackDeserializer/input_types.cpp | 82 ++ .../test/MsgPackDeserializer/nestingLimit.cpp | 85 ++ .../test/MsgPackDeserializer/notSupported.cpp | 73 + .../test/MsgPackSerializer/CMakeLists.txt | 15 + .../MsgPackSerializer/destination_types.cpp | 47 + .../test/MsgPackSerializer/measure.cpp | 14 + .../test/MsgPackSerializer/misc.cpp | 46 + .../test/MsgPackSerializer/serializeArray.cpp | 58 + .../MsgPackSerializer/serializeObject.cpp | 83 ++ .../MsgPackSerializer/serializeVariant.cpp | 138 ++ .../test/Numbers/CMakeLists.txt | 12 + .../test/Numbers}/parseFloat.cpp | 29 +- .../test/Numbers}/parseInteger.cpp | 34 +- .../test/Numbers/parseNumber.cpp | 24 + .../test/TextFormatter}/CMakeLists.txt | 4 +- .../test/TextFormatter}/writeFloat.cpp | 18 +- .../test/TextFormatter}/writeString.cpp | 14 +- .../third-party/catch/CMakeLists.txt | 18 + .../third-party/catch/catch.cpp | 2 +- .../third-party/catch/catch.hpp | 2 +- lib/ArduinoJson/.travis.yml | 126 -- .../JsonGeneratorExample.ino | 81 -- .../JsonParserExample/JsonParserExample.ino | 78 - lib/ArduinoJson/fuzzing/Makefile | 19 - lib/ArduinoJson/fuzzing/fuzzer.cpp | 26 - lib/ArduinoJson/keywords.txt | 15 - lib/ArduinoJson/scripts/create-size-graph.sh | 42 - lib/ArduinoJson/scripts/travis/cmake.sh | 30 - lib/ArduinoJson/scripts/travis/fuzz.sh | 20 - lib/ArduinoJson/scripts/travis/platformio.sh | 10 - lib/ArduinoJson/src/ArduinoJson.hpp | 19 - .../ArduinoJson/Data/JsonBufferAllocated.hpp | 22 - .../src/ArduinoJson/Data/JsonFloat.hpp | 18 - .../src/ArduinoJson/Data/JsonInteger.hpp | 23 - .../src/ArduinoJson/Data/JsonVariantAs.hpp | 42 - .../ArduinoJson/Data/JsonVariantContent.hpp | 27 - .../ArduinoJson/Data/JsonVariantDefault.hpp | 23 - .../src/ArduinoJson/Data/JsonVariantType.hpp | 27 - lib/ArduinoJson/src/ArduinoJson/Data/List.hpp | 94 -- .../ArduinoJson/Data/ListConstIterator.hpp | 50 - .../src/ArduinoJson/Data/ListIterator.hpp | 60 - .../src/ArduinoJson/Data/ListNode.hpp | 24 - .../src/ArduinoJson/Data/NonCopyable.hpp | 23 - .../src/ArduinoJson/Data/ReferenceType.hpp | 24 - .../src/ArduinoJson/Data/ValueSaver.hpp | 52 - .../ArduinoJson/Deserialization/Comments.hpp | 61 - .../Deserialization/JsonParser.hpp | 102 -- .../Deserialization/JsonParserImpl.hpp | 189 --- .../Deserialization/StringWriter.hpp | 41 - .../src/ArduinoJson/DynamicJsonBuffer.hpp | 170 --- lib/ArduinoJson/src/ArduinoJson/JsonArray.hpp | 227 --- .../src/ArduinoJson/JsonArrayImpl.hpp | 26 - .../src/ArduinoJson/JsonArraySubscript.hpp | 122 -- .../src/ArduinoJson/JsonBuffer.hpp | 78 - .../src/ArduinoJson/JsonBufferBase.hpp | 127 -- .../src/ArduinoJson/JsonBufferImpl.hpp | 17 - .../src/ArduinoJson/JsonObject.hpp | 328 ----- .../src/ArduinoJson/JsonObjectImpl.hpp | 28 - .../src/ArduinoJson/JsonObjectSubscript.hpp | 110 -- lib/ArduinoJson/src/ArduinoJson/JsonPair.hpp | 16 - .../src/ArduinoJson/JsonVariant.hpp | 355 ----- .../src/ArduinoJson/JsonVariantBase.hpp | 24 - .../src/ArduinoJson/JsonVariantCasts.hpp | 59 - .../ArduinoJson/JsonVariantComparisons.hpp | 139 -- .../src/ArduinoJson/JsonVariantImpl.hpp | 126 -- .../src/ArduinoJson/JsonVariantOr.hpp | 52 - .../src/ArduinoJson/JsonVariantSubscripts.hpp | 86 -- .../src/ArduinoJson/Polyfills/isFloat.hpp | 38 - .../src/ArduinoJson/Polyfills/isInteger.hpp | 19 - .../src/ArduinoJson/Polyfills/math.hpp | 19 - .../src/ArduinoJson/Polyfills/parseFloat.hpp | 90 -- .../ArduinoJson/Polyfills/parseInteger.hpp | 41 - lib/ArduinoJson/src/ArduinoJson/RawJson.hpp | 46 - .../ArduinoJson/Serialization/DummyPrint.hpp | 22 - .../Serialization/DynamicStringBuilder.hpp | 35 - .../Serialization/IndentedPrint.hpp | 68 - .../Serialization/JsonPrintable.hpp | 117 -- .../Serialization/JsonSerializer.hpp | 32 - .../Serialization/JsonSerializerImpl.hpp | 103 -- .../ArduinoJson/Serialization/JsonWriter.hpp | 155 -- .../ArduinoJson/Serialization/Prettyfier.hpp | 133 -- .../Serialization/StaticStringBuilder.hpp | 36 - .../Serialization/StreamPrintAdapter.hpp | 39 - .../src/ArduinoJson/StaticJsonBuffer.hpp | 126 -- .../StringTraits/ArduinoStream.hpp | 61 - .../ArduinoJson/StringTraits/CharPointer.hpp | 64 - .../ArduinoJson/StringTraits/FlashString.hpp | 61 - .../ArduinoJson/StringTraits/StdStream.hpp | 60 - .../ArduinoJson/StringTraits/StdString.hpp | 77 - .../ArduinoJson/StringTraits/StringTraits.hpp | 36 - .../src/ArduinoJson/TypeTraits/IsArray.hpp | 24 - .../src/ArduinoJson/TypeTraits/IsChar.hpp | 23 - .../src/ArduinoJson/TypeTraits/IsConst.hpp | 21 - .../TypeTraits/IsFloatingPoint.hpp | 18 - .../src/ArduinoJson/TypeTraits/IsIntegral.hpp | 26 - .../src/ArduinoJson/TypeTraits/IsSame.hpp | 21 - .../TypeTraits/IsSignedIntegral.hpp | 28 - .../TypeTraits/IsUnsignedIntegral.hpp | 28 - .../src/ArduinoJson/TypeTraits/IsVariant.hpp | 17 - lib/ArduinoJson/src/ArduinoJson/version.hpp | 10 - .../test/DynamicJsonBuffer/CMakeLists.txt | 15 - .../test/DynamicJsonBuffer/alloc.cpp | 77 - .../test/DynamicJsonBuffer/createArray.cpp | 28 - .../test/DynamicJsonBuffer/createObject.cpp | 22 - .../test/DynamicJsonBuffer/no_memory.cpp | 49 - .../test/DynamicJsonBuffer/size.cpp | 27 - .../test/DynamicJsonBuffer/startString.cpp | 47 - lib/ArduinoJson/test/JsonArray/add.cpp | 110 -- lib/ArduinoJson/test/JsonArray/basics.cpp | 29 - lib/ArduinoJson/test/JsonArray/copyFrom.cpp | 63 - lib/ArduinoJson/test/JsonArray/copyTo.cpp | 52 - lib/ArduinoJson/test/JsonArray/invalid.cpp | 34 - lib/ArduinoJson/test/JsonArray/set.cpp | 98 -- lib/ArduinoJson/test/JsonArray/size.cpp | 35 - lib/ArduinoJson/test/JsonArray/subscript.cpp | 119 -- .../test/JsonBuffer/CMakeLists.txt | 14 - lib/ArduinoJson/test/JsonBuffer/nested.cpp | 63 - .../test/JsonBuffer/nestingLimit.cpp | 66 - lib/ArduinoJson/test/JsonBuffer/parse.cpp | 80 - .../test/JsonBuffer/parseArray.cpp | 318 ---- .../test/JsonBuffer/parseObject.cpp | 170 --- lib/ArduinoJson/test/JsonObject/basics.cpp | 19 - .../test/JsonObject/containsKey.cpp | 30 - lib/ArduinoJson/test/JsonObject/get.cpp | 19 - lib/ArduinoJson/test/JsonObject/invalid.cpp | 35 - lib/ArduinoJson/test/JsonObject/iterator.cpp | 51 - lib/ArduinoJson/test/JsonObject/remove.cpp | 41 - lib/ArduinoJson/test/JsonObject/set.cpp | 138 -- lib/ArduinoJson/test/JsonObject/size.cpp | 23 - lib/ArduinoJson/test/JsonObject/subscript.cpp | 163 --- .../test/JsonVariant/CMakeLists.txt | 19 - lib/ArduinoJson/test/JsonVariant/as.cpp | 232 --- lib/ArduinoJson/test/JsonVariant/compare.cpp | 265 ---- lib/ArduinoJson/test/JsonVariant/copy.cpp | 64 - lib/ArduinoJson/test/JsonVariant/is.cpp | 115 -- lib/ArduinoJson/test/JsonVariant/or.cpp | 83 -- .../test/JsonVariant/subscript.cpp | 77 - lib/ArduinoJson/test/JsonVariant/success.cpp | 42 - .../test/JsonVariant/undefined.cpp | 54 - lib/ArduinoJson/test/Misc/CMakeLists.txt | 19 - lib/ArduinoJson/test/Misc/StringBuilder.cpp | 50 - lib/ArduinoJson/test/Misc/StringTraits.cpp | 22 - lib/ArduinoJson/test/Misc/TypeTraits.cpp | 37 - lib/ArduinoJson/test/Misc/deprecated.cpp | 140 -- lib/ArduinoJson/test/Misc/std_stream.cpp | 84 -- lib/ArduinoJson/test/Misc/std_string.cpp | 243 ---- lib/ArduinoJson/test/Misc/unsigned_char.cpp | 262 ---- lib/ArduinoJson/test/Misc/vla.cpp | 331 ----- lib/ArduinoJson/test/Polyfills/CMakeLists.txt | 13 - lib/ArduinoJson/test/Polyfills/isFloat.cpp | 76 - lib/ArduinoJson/test/Polyfills/isInteger.cpp | 36 - .../test/StaticJsonBuffer/CMakeLists.txt | 16 - .../test/StaticJsonBuffer/alloc.cpp | 52 - .../test/StaticJsonBuffer/createArray.cpp | 45 - .../test/StaticJsonBuffer/createObject.cpp | 56 - .../test/StaticJsonBuffer/parseArray.cpp | 71 - .../test/StaticJsonBuffer/parseObject.cpp | 62 - .../test/StaticJsonBuffer/size.cpp | 42 - .../test/StaticJsonBuffer/startString.cpp | 49 - .../third-party/catch/CMakeLists.txt | 13 - 501 files changed, 19376 insertions(+), 10936 deletions(-) rename lib/{ArduinoJson => ArduinoJson-6.x}/.clang-format (100%) rename lib/{ArduinoJson => ArduinoJson-6.x}/.gitattributes (100%) create mode 100644 lib/ArduinoJson-6.x/.github/FUNDING.yml rename lib/{ArduinoJson => ArduinoJson-6.x}/.github/ISSUE_TEMPLATE.md (100%) rename lib/{ArduinoJson => ArduinoJson-6.x}/.gitignore (96%) rename lib/{ArduinoJson => ArduinoJson-6.x}/.mbedignore (100%) create mode 100644 lib/ArduinoJson-6.x/.travis.yml rename lib/{ArduinoJson => ArduinoJson-6.x}/ArduinoJson.h (66%) rename lib/{ArduinoJson => ArduinoJson-6.x}/CHANGELOG.md (53%) rename lib/{ArduinoJson => ArduinoJson-6.x}/CMakeLists.txt (51%) rename lib/{ArduinoJson => ArduinoJson-6.x}/CONTRIBUTING.md (100%) rename lib/{ArduinoJson => ArduinoJson-6.x}/LICENSE.md (95%) rename lib/{ArduinoJson => ArduinoJson-6.x}/README.md (83%) rename lib/{ArduinoJson => ArduinoJson-6.x}/SUPPORT.md (100%) rename lib/{ArduinoJson => ArduinoJson-6.x}/appveyor.yml (90%) rename lib/{ArduinoJson => ArduinoJson-6.x}/banner.svg (100%) rename lib/{ArduinoJson => ArduinoJson-6.x}/examples/JsonConfigFile/JsonConfigFile.ino (63%) create mode 100644 lib/ArduinoJson-6.x/examples/JsonGeneratorExample/JsonGeneratorExample.ino rename lib/{ArduinoJson => ArduinoJson-6.x}/examples/JsonHttpClient/JsonHttpClient.ino (69%) create mode 100644 lib/ArduinoJson-6.x/examples/JsonParserExample/JsonParserExample.ino rename lib/{ArduinoJson => ArduinoJson-6.x}/examples/JsonServer/JsonServer.ino (64%) rename lib/{ArduinoJson => ArduinoJson-6.x}/examples/JsonUdpBeacon/JsonUdpBeacon.ino (72%) create mode 100644 lib/ArduinoJson-6.x/examples/MsgPackParser/MsgPackParser.ino rename lib/{ArduinoJson => ArduinoJson-6.x}/examples/ProgmemExample/ProgmemExample.ino (58%) rename lib/{ArduinoJson => ArduinoJson-6.x}/examples/StringExample/StringExample.ino (59%) create mode 100644 lib/ArduinoJson-6.x/fuzzing/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/fuzzing/Makefile create mode 100644 lib/ArduinoJson-6.x/fuzzing/fuzzer_main.cpp rename lib/{ArduinoJson/fuzzing/my_corpus => ArduinoJson-6.x/fuzzing/json_corpus}/.gitignore (100%) create mode 100644 lib/ArduinoJson-6.x/fuzzing/json_fuzzer.cpp rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/Comments.json (100%) rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/EmptyArray.json (100%) rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/EmptyObject.json (100%) rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/ExcessiveNesting.json (100%) create mode 100644 lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/IntegerOverflow.json rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/Numbers.json (100%) rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/OpenWeatherMap.json (100%) rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/Strings.json (100%) rename lib/{ArduinoJson/fuzzing/seed_corpus => ArduinoJson-6.x/fuzzing/json_seed_corpus}/WeatherUnderground.json (100%) create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_corpus/.gitignore create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_fuzzer.cpp create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/array16 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/array32 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/false create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/fixarray create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/fixint_negative create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/fixint_positive create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/fixmap create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/fixstr create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/float32 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/float64 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/int16 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/int32 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/int64 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/int8 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/map16 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/map32 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/nil create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/str16 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/str32 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/str8 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/true create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/uint16 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/uint32 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/uint64 create mode 100644 lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/uint8 create mode 100644 lib/ArduinoJson-6.x/keywords.txt rename lib/{ArduinoJson => ArduinoJson-6.x}/library.json (95%) rename lib/{ArduinoJson => ArduinoJson-6.x}/library.properties (65%) rename lib/{ArduinoJson => ArduinoJson-6.x}/scripts/build-arduino-package.sh (100%) mode change 100755 => 100644 rename lib/{ArduinoJson => ArduinoJson-6.x}/scripts/build-single-header.sh (100%) rename lib/{ArduinoJson => ArduinoJson-6.x}/scripts/create-build-envs.sh (100%) mode change 100755 => 100644 rename lib/{ArduinoJson => ArduinoJson-6.x}/scripts/oss-fuzz/.gitignore (100%) rename lib/{ArduinoJson => ArduinoJson-6.x}/scripts/oss-fuzz/Vagrantfile (79%) create mode 100644 lib/ArduinoJson-6.x/scripts/publish-particle-library.sh create mode 100644 lib/ArduinoJson-6.x/scripts/publish.sh rename lib/{ArduinoJson => ArduinoJson-6.x}/scripts/travis/arduino.sh (79%) mode change 100755 => 100644 create mode 100644 lib/ArduinoJson-6.x/scripts/travis/build.sh rename lib/{ArduinoJson => ArduinoJson-6.x}/scripts/travis/coverage.sh (53%) mode change 100755 => 100644 create mode 100644 lib/ArduinoJson-6.x/scripts/travis/fuzz.sh create mode 100644 lib/ArduinoJson-6.x/scripts/travis/platformio.sh create mode 100644 lib/ArduinoJson-6.x/scripts/travis/test.sh create mode 100644 lib/ArduinoJson-6.x/scripts/wandbox/JsonGeneratorExample.cpp create mode 100644 lib/ArduinoJson-6.x/scripts/wandbox/JsonParserExample.cpp create mode 100644 lib/ArduinoJson-6.x/scripts/wandbox/MsgPackParserExample.cpp create mode 100644 lib/ArduinoJson-6.x/scripts/wandbox/publish.sh rename lib/{ArduinoJson => ArduinoJson-6.x}/src/ArduinoJson.h (86%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayFunctions.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayImpl.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayIterator.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayRef.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayShortcuts.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Array/ElementProxy.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Array/Utilities.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionData.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionImpl.hpp rename lib/{ArduinoJson => ArduinoJson-6.x}/src/ArduinoJson/Configuration.hpp (65%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/CharPointerReader.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/DeserializationError.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/FlashStringReader.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/IteratorReader.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/NestingLimit.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/StdStreamReader.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/deserialize.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Document/BasicJsonDocument.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Document/DynamicJsonDocument.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Document/JsonDocument.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Document/StaticJsonDocument.hpp rename lib/{ArduinoJson/src/ArduinoJson/Data/Encoding.hpp => ArduinoJson-6.x/src/ArduinoJson/Json/EscapeSequence.hpp} (82%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Json/JsonDeserializer.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Json/JsonSerializer.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Json/PrettyJsonSerializer.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Json/TextFormatter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Json/Utf8.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Memory/Alignment.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Memory/MemoryPool.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Memory/StringBuilder.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Memory/StringSlot.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Misc/SerializedValue.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Misc/Visitable.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/MsgPack/endianess.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/MsgPack/ieee754.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Namespace.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Numbers/Float.hpp rename lib/{ArduinoJson/src/ArduinoJson/Serialization => ArduinoJson-6.x/src/ArduinoJson/Numbers}/FloatParts.hpp (93%) rename lib/{ArduinoJson/src/ArduinoJson/TypeTraits => ArduinoJson-6.x/src/ArduinoJson/Numbers}/FloatTraits.hpp (56%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Numbers/Integer.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Numbers/convertNumber.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Numbers/parseFloat.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Numbers/parseInteger.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Numbers/parseNumber.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Object/MemberProxy.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Object/ObjectFunctions.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Object/ObjectImpl.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Object/ObjectIterator.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Object/ObjectRef.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Object/ObjectShortcuts.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Object/Pair.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Operators/VariantCasts.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Operators/VariantComparisons.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Operators/VariantOperators.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Operators/VariantOr.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Operators/VariantShortcuts.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/alias_cast.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/assert.hpp rename lib/{ArduinoJson => ArduinoJson-6.x}/src/ArduinoJson/Polyfills/attributes.hpp (64%) rename lib/{ArduinoJson => ArduinoJson-6.x}/src/ArduinoJson/Polyfills/ctype.hpp (62%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/gsl/not_null.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/limits.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/math.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/mpl/max.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/safe_strcmp.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/conditional.hpp rename lib/{ArduinoJson/src/ArduinoJson/TypeTraits/EnableIf.hpp => ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/enable_if.hpp} (57%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/integral_constant.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_array.hpp rename lib/{ArduinoJson/src/ArduinoJson/TypeTraits/IsBaseOf.hpp => ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_base_of.hpp} (63%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_const.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_same.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp rename lib/{ArduinoJson/src/ArduinoJson/TypeTraits/RemoveConst.hpp => ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/remove_const.hpp} (57%) rename lib/{ArduinoJson/src/ArduinoJson/TypeTraits/RemoveReference.hpp => ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/remove_reference.hpp} (57%) create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Polyfills/utility.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Serialization/DummyWriter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Serialization/DynamicStringWriter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Serialization/StaticStringWriter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Serialization/StreamWriter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Serialization/measure.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Serialization/serialize.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/StringStorage/StringCopier.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/StringStorage/StringMover.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/StringStorage/StringStorage.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/ArduinoStringAdapter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/ConstRamStringAdapter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/FlashStringAdapter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/RamStringAdapter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/SizedFlashStringAdapter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/SizedRamStringAdapter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/StlStringAdapter.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/String.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Strings/StringAdapters.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/SlotFunctions.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantAs.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantAsImpl.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantContent.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantData.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantFunctions.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantImpl.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantRef.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantSlot.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/Variant/VariantTo.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/compatibility.hpp create mode 100644 lib/ArduinoJson-6.x/src/ArduinoJson/version.hpp rename lib/{ArduinoJson => ArduinoJson-6.x}/test/CMakeLists.txt (67%) create mode 100644 lib/ArduinoJson-6.x/test/ElementProxy/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/ElementProxy/add.cpp create mode 100644 lib/ArduinoJson-6.x/test/ElementProxy/clear.cpp create mode 100644 lib/ArduinoJson-6.x/test/ElementProxy/remove.cpp create mode 100644 lib/ArduinoJson-6.x/test/ElementProxy/set.cpp create mode 100644 lib/ArduinoJson-6.x/test/ElementProxy/size.cpp rename lib/{ArduinoJson => ArduinoJson-6.x}/test/IntegrationTests/CMakeLists.txt (78%) rename lib/{ArduinoJson => ArduinoJson-6.x}/test/IntegrationTests/gbathree.cpp (67%) create mode 100644 lib/ArduinoJson-6.x/test/IntegrationTests/issue772.cpp rename lib/{ArduinoJson => ArduinoJson-6.x}/test/IntegrationTests/round_trip.cpp (95%) rename lib/{ArduinoJson => ArduinoJson-6.x}/test/JsonArray/CMakeLists.txt (57%) create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/add.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/copyArray.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/createNested.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/equals.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/get.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/isNull.cpp rename lib/{ArduinoJson => ArduinoJson-6.x}/test/JsonArray/iterator.cpp (50%) create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/memoryUsage.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/nesting.cpp rename lib/{ArduinoJson => ArduinoJson-6.x}/test/JsonArray/remove.cpp (91%) create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/size.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/std_string.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/subscript.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonArray/undefined.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/DeserializationError.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/array.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/array_static.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/incomplete_input.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/input_types.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/invalid_input.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/misc.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/nestingLimit.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/number.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/object.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/object_static.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDeserializer/string.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/BasicJsonDocument.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/DynamicJsonDocument.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/StaticJsonDocument.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/add.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/containsKey.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/createNested.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/isNull.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/nesting.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/remove.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/size.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonDocument/subscript.cpp rename lib/{ArduinoJson => ArduinoJson-6.x}/test/JsonObject/CMakeLists.txt (59%) create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/containsKey.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/copy.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/createNestedArray.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/createNestedObject.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/equals.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/invalid.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/isNull.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/iterator.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/memoryUsage.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/nesting.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/remove.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/size.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/std_string.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonObject/subscript.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonSerializer/CMakeLists.txt rename lib/{ArduinoJson/test/JsonArray/printTo.cpp => ArduinoJson-6.x/test/JsonSerializer/JsonArray.cpp} (76%) rename lib/{ArduinoJson/test/JsonArray/prettyPrintTo.cpp => ArduinoJson-6.x/test/JsonSerializer/JsonArrayPretty.cpp} (71%) rename lib/{ArduinoJson/test/JsonObject/printTo.cpp => ArduinoJson-6.x/test/JsonSerializer/JsonObject.cpp} (67%) rename lib/{ArduinoJson/test/JsonObject/prettyPrintTo.cpp => ArduinoJson-6.x/test/JsonSerializer/JsonObjectPretty.cpp} (72%) rename lib/{ArduinoJson/test/JsonVariant/printTo.cpp => ArduinoJson-6.x/test/JsonSerializer/JsonVariant.cpp} (55%) create mode 100644 lib/ArduinoJson-6.x/test/JsonSerializer/misc.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonSerializer/std_stream.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonSerializer/std_string.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/add.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/as.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/clear.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/compare.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/containsKey.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/copy.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/createNested.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/is.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/isnull.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/memoryUsage.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/misc.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/nesting.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/or.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/overflow.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/remove.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/set.cpp create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/subscript.cpp rename lib/{ArduinoJson/test/JsonVariant/set_get.cpp => ArduinoJson-6.x/test/JsonVariant/types.cpp} (76%) create mode 100644 lib/ArduinoJson-6.x/test/JsonVariant/undefined.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/add.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/clear.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/containsKey.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/remove.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/set.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/size.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemberProxy/subscript.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemoryPool/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/MemoryPool/StringBuilder.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemoryPool/allocString.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemoryPool/allocVariant.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemoryPool/clear.cpp create mode 100644 lib/ArduinoJson-6.x/test/MemoryPool/size.cpp create mode 100644 lib/ArduinoJson-6.x/test/Misc/CMakeLists.txt rename lib/{ArduinoJson => ArduinoJson-6.x}/test/Misc/FloatParts.cpp (89%) create mode 100644 lib/ArduinoJson-6.x/test/Misc/Issue978.cpp create mode 100644 lib/ArduinoJson-6.x/test/Misc/StreamReader.cpp create mode 100644 lib/ArduinoJson-6.x/test/Misc/StringAdapters.cpp create mode 100644 lib/ArduinoJson-6.x/test/Misc/StringWriter.cpp create mode 100644 lib/ArduinoJson-6.x/test/Misc/TypeTraits.cpp create mode 100644 lib/ArduinoJson-6.x/test/Misc/conflicts.cpp create mode 100644 lib/ArduinoJson-6.x/test/Misc/unsigned_char.cpp rename lib/{ArduinoJson => ArduinoJson-6.x}/test/Misc/version.cpp (69%) create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/cpp11.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/decode_unicode_0.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/decode_unicode_1.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/enable_infinity_0.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/enable_infinity_1.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/enable_nan_0.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/enable_nan_1.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/use_double_0.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/use_double_1.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/use_long_long_0.cpp create mode 100644 lib/ArduinoJson-6.x/test/MixedConfiguration/use_long_long_1.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/deserializeArray.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/deserializeObject.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/deserializeStaticVariant.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/deserializeVariant.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/doubleToFloat.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/incompleteInput.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/input_types.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/nestingLimit.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackDeserializer/notSupported.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackSerializer/CMakeLists.txt create mode 100644 lib/ArduinoJson-6.x/test/MsgPackSerializer/destination_types.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackSerializer/measure.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackSerializer/misc.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackSerializer/serializeArray.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackSerializer/serializeObject.cpp create mode 100644 lib/ArduinoJson-6.x/test/MsgPackSerializer/serializeVariant.cpp create mode 100644 lib/ArduinoJson-6.x/test/Numbers/CMakeLists.txt rename lib/{ArduinoJson/test/Polyfills => ArduinoJson-6.x/test/Numbers}/parseFloat.cpp (92%) rename lib/{ArduinoJson/test/Polyfills => ArduinoJson-6.x/test/Numbers}/parseInteger.cpp (63%) create mode 100644 lib/ArduinoJson-6.x/test/Numbers/parseNumber.cpp rename lib/{ArduinoJson/test/JsonWriter => ArduinoJson-6.x/test/TextFormatter}/CMakeLists.txt (67%) rename lib/{ArduinoJson/test/JsonWriter => ArduinoJson-6.x/test/TextFormatter}/writeFloat.cpp (84%) rename lib/{ArduinoJson/test/JsonWriter => ArduinoJson-6.x/test/TextFormatter}/writeString.cpp (72%) create mode 100644 lib/ArduinoJson-6.x/third-party/catch/CMakeLists.txt rename lib/{ArduinoJson => ArduinoJson-6.x}/third-party/catch/catch.cpp (71%) rename lib/{ArduinoJson => ArduinoJson-6.x}/third-party/catch/catch.hpp (99%) delete mode 100644 lib/ArduinoJson/.travis.yml delete mode 100644 lib/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino delete mode 100644 lib/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino delete mode 100644 lib/ArduinoJson/fuzzing/Makefile delete mode 100644 lib/ArduinoJson/fuzzing/fuzzer.cpp delete mode 100644 lib/ArduinoJson/keywords.txt delete mode 100755 lib/ArduinoJson/scripts/create-size-graph.sh delete mode 100755 lib/ArduinoJson/scripts/travis/cmake.sh delete mode 100644 lib/ArduinoJson/scripts/travis/fuzz.sh delete mode 100755 lib/ArduinoJson/scripts/travis/platformio.sh delete mode 100644 lib/ArduinoJson/src/ArduinoJson.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/JsonBufferAllocated.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/JsonFloat.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/JsonInteger.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/JsonVariantAs.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/JsonVariantContent.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/JsonVariantDefault.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/JsonVariantType.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/List.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/ListConstIterator.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/ListIterator.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/ListNode.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/NonCopyable.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/ReferenceType.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Data/ValueSaver.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Deserialization/Comments.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Deserialization/JsonParser.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Deserialization/JsonParserImpl.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Deserialization/StringWriter.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/DynamicJsonBuffer.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonArray.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonArrayImpl.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonArraySubscript.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonBuffer.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonBufferBase.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonBufferImpl.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonObject.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonObjectImpl.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonObjectSubscript.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonPair.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonVariant.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonVariantBase.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonVariantCasts.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonVariantComparisons.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonVariantImpl.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonVariantOr.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/JsonVariantSubscripts.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Polyfills/isFloat.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Polyfills/isInteger.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Polyfills/parseFloat.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Polyfills/parseInteger.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/RawJson.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/DummyPrint.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/IndentedPrint.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/JsonPrintable.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializer.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/JsonWriter.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/Prettyfier.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/StaticStringBuilder.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/StaticJsonBuffer.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/StringTraits/ArduinoStream.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/StringTraits/CharPointer.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/StringTraits/FlashString.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/StringTraits/StdStream.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/StringTraits/StdString.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/StringTraits/StringTraits.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsArray.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsChar.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsConst.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsIntegral.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsSame.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/TypeTraits/IsVariant.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/version.hpp delete mode 100644 lib/ArduinoJson/test/DynamicJsonBuffer/CMakeLists.txt delete mode 100644 lib/ArduinoJson/test/DynamicJsonBuffer/alloc.cpp delete mode 100644 lib/ArduinoJson/test/DynamicJsonBuffer/createArray.cpp delete mode 100644 lib/ArduinoJson/test/DynamicJsonBuffer/createObject.cpp delete mode 100644 lib/ArduinoJson/test/DynamicJsonBuffer/no_memory.cpp delete mode 100644 lib/ArduinoJson/test/DynamicJsonBuffer/size.cpp delete mode 100644 lib/ArduinoJson/test/DynamicJsonBuffer/startString.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/add.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/basics.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/copyFrom.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/copyTo.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/invalid.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/set.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/size.cpp delete mode 100644 lib/ArduinoJson/test/JsonArray/subscript.cpp delete mode 100644 lib/ArduinoJson/test/JsonBuffer/CMakeLists.txt delete mode 100644 lib/ArduinoJson/test/JsonBuffer/nested.cpp delete mode 100644 lib/ArduinoJson/test/JsonBuffer/nestingLimit.cpp delete mode 100644 lib/ArduinoJson/test/JsonBuffer/parse.cpp delete mode 100644 lib/ArduinoJson/test/JsonBuffer/parseArray.cpp delete mode 100644 lib/ArduinoJson/test/JsonBuffer/parseObject.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/basics.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/containsKey.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/get.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/invalid.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/iterator.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/remove.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/set.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/size.cpp delete mode 100644 lib/ArduinoJson/test/JsonObject/subscript.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/CMakeLists.txt delete mode 100644 lib/ArduinoJson/test/JsonVariant/as.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/compare.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/copy.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/is.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/or.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/subscript.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/success.cpp delete mode 100644 lib/ArduinoJson/test/JsonVariant/undefined.cpp delete mode 100644 lib/ArduinoJson/test/Misc/CMakeLists.txt delete mode 100644 lib/ArduinoJson/test/Misc/StringBuilder.cpp delete mode 100644 lib/ArduinoJson/test/Misc/StringTraits.cpp delete mode 100644 lib/ArduinoJson/test/Misc/TypeTraits.cpp delete mode 100644 lib/ArduinoJson/test/Misc/deprecated.cpp delete mode 100644 lib/ArduinoJson/test/Misc/std_stream.cpp delete mode 100644 lib/ArduinoJson/test/Misc/std_string.cpp delete mode 100644 lib/ArduinoJson/test/Misc/unsigned_char.cpp delete mode 100644 lib/ArduinoJson/test/Misc/vla.cpp delete mode 100644 lib/ArduinoJson/test/Polyfills/CMakeLists.txt delete mode 100644 lib/ArduinoJson/test/Polyfills/isFloat.cpp delete mode 100644 lib/ArduinoJson/test/Polyfills/isInteger.cpp delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/CMakeLists.txt delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/alloc.cpp delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/createArray.cpp delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/createObject.cpp delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/parseArray.cpp delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/parseObject.cpp delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/size.cpp delete mode 100644 lib/ArduinoJson/test/StaticJsonBuffer/startString.cpp delete mode 100644 lib/ArduinoJson/third-party/catch/CMakeLists.txt diff --git a/lib/ArduinoJson/.clang-format b/lib/ArduinoJson-6.x/.clang-format similarity index 100% rename from lib/ArduinoJson/.clang-format rename to lib/ArduinoJson-6.x/.clang-format diff --git a/lib/ArduinoJson/.gitattributes b/lib/ArduinoJson-6.x/.gitattributes similarity index 100% rename from lib/ArduinoJson/.gitattributes rename to lib/ArduinoJson-6.x/.gitattributes diff --git a/lib/ArduinoJson-6.x/.github/FUNDING.yml b/lib/ArduinoJson-6.x/.github/FUNDING.yml new file mode 100644 index 0000000000..de9e0ff1f2 --- /dev/null +++ b/lib/ArduinoJson-6.x/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://arduinojson.org/book/ diff --git a/lib/ArduinoJson/.github/ISSUE_TEMPLATE.md b/lib/ArduinoJson-6.x/.github/ISSUE_TEMPLATE.md similarity index 100% rename from lib/ArduinoJson/.github/ISSUE_TEMPLATE.md rename to lib/ArduinoJson-6.x/.github/ISSUE_TEMPLATE.md diff --git a/lib/ArduinoJson/.gitignore b/lib/ArduinoJson-6.x/.gitignore similarity index 96% rename from lib/ArduinoJson/.gitignore rename to lib/ArduinoJson-6.x/.gitignore index 567dc13bef..1071ea6b14 100644 --- a/lib/ArduinoJson/.gitignore +++ b/lib/ArduinoJson-6.x/.gitignore @@ -9,3 +9,4 @@ /fuzzing/*_fuzzer /fuzzing/*_fuzzer.options /fuzzing/*_fuzzer_seed_corpus.zip +.vs/ diff --git a/lib/ArduinoJson/.mbedignore b/lib/ArduinoJson-6.x/.mbedignore similarity index 100% rename from lib/ArduinoJson/.mbedignore rename to lib/ArduinoJson-6.x/.mbedignore diff --git a/lib/ArduinoJson-6.x/.travis.yml b/lib/ArduinoJson-6.x/.travis.yml new file mode 100644 index 0000000000..db996d7019 --- /dev/null +++ b/lib/ArduinoJson-6.x/.travis.yml @@ -0,0 +1,132 @@ +sudo: false +language: cpp +matrix: + include: + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.4'] + env: SCRIPT=test _CC=gcc-4.4 _CXX=g++-4.4 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.6'] + env: SCRIPT=test _CC=gcc-4.6 _CXX=g++-4.6 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.7'] + env: SCRIPT=test _CC=gcc-4.7 _CXX=g++-4.7 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.8'] + env: SCRIPT=test _CC=gcc-4.8 _CXX=g++-4.8 SANITIZE=address + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-4.9'] + env: SCRIPT=test _CC=gcc-4.9 _CXX=g++-4.9 SANITIZE=leak + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-5'] + env: SCRIPT=test _CC=gcc-5 _CXX=g++-5 # SANITIZE=undefined + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-6'] + env: SCRIPT=test _CC=gcc-6 _CXX=g++-6 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-7'] + env: SCRIPT=test _CC=gcc-7 _CXX=g++-7 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-8'] + env: SCRIPT=test _CC=gcc-8 _CXX=g++-8 + - addons: + apt: + packages: ['g++-arm-linux-gnueabihf'] + env: SCRIPT=build _CC=arm-linux-gnueabihf-gcc _CXX=arm-linux-gnueabihf-g++ + - env: SCRIPT=test _CC=clang _CXX=clang++ + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5'] + packages: ['clang-3.5'] + env: SCRIPT=test _CC=clang-3.5 _CXX=clang++-3.5 SANITIZE=address + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.6'] + packages: ['clang-3.6'] + env: SCRIPT=test _CC=clang-3.6 _CXX=clang++-3.6 SANITIZE=leak + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.7'] + packages: ['clang-3.7'] + env: SCRIPT=test _CC=clang-3.7 _CXX=clang++-3.7 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.8'] + packages: ['clang-3.8'] + env: SCRIPT=test _CC=clang-3.8 _CXX=clang++-3.8 SANITIZE=undefined + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-3.9'] + packages: ['clang-3.9'] + env: SCRIPT=test _CC=clang-3.9 _CXX=clang++-3.9 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-4.0'] + packages: ['clang-4.0'] + env: SCRIPT=test _CC=clang-4.0 _CXX=clang++-4.0 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-5.0'] + packages: ['clang-5.0'] + env: SCRIPT=test _CC=clang-5.0 _CXX=clang++-5.0 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-6.0'] + packages: ['clang-6.0'] + env: SCRIPT=test _CC=clang-6.0 _CXX=clang++-6.0 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-7'] + packages: ['clang-7'] + env: SCRIPT=test _CC=clang-7 _CXX=clang++-7 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-8'] + packages: ['clang-8'] + env: SCRIPT=test _CC=clang-8 _CXX=clang++-8 + - env: SCRIPT=coverage + - os: osx + osx_image: xcode7.3 + env: SCRIPT=test + - os: osx + osx_image: xcode8.3 + env: SCRIPT=test + - os: osx + osx_image: xcode9.4 + env: SCRIPT=test + - os: osx + osx_image: xcode10 + env: SCRIPT=test SANITIZE=address + - env: SCRIPT=arduino VERSION=1.6.7 BOARD=arduino:avr:uno + - env: SCRIPT=arduino VERSION=1.8.2 BOARD=arduino:samd:mkr1000 + - env: SCRIPT=platformio BOARD=uno + - env: SCRIPT=platformio BOARD=esp01 + - addons: + apt: + sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-6.0'] + packages: ['clang-6.0','llvm-6.0'] + env: SCRIPT=fuzz CLANG=6.0 +cache: + directories: + - "~/.platformio" + - "fuzzing/json_corpus" + - "fuzzing/msgpack_corpus" +script: scripts/travis/$SCRIPT.sh diff --git a/lib/ArduinoJson/ArduinoJson.h b/lib/ArduinoJson-6.x/ArduinoJson.h similarity index 66% rename from lib/ArduinoJson/ArduinoJson.h rename to lib/ArduinoJson-6.x/ArduinoJson.h index 9f78b9f185..d69f479109 100644 --- a/lib/ArduinoJson/ArduinoJson.h +++ b/lib/ArduinoJson-6.x/ArduinoJson.h @@ -1,5 +1,5 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License #include "src/ArduinoJson.h" diff --git a/lib/ArduinoJson/CHANGELOG.md b/lib/ArduinoJson-6.x/CHANGELOG.md similarity index 53% rename from lib/ArduinoJson/CHANGELOG.md rename to lib/ArduinoJson-6.x/CHANGELOG.md index 1e51203829..df45b6fdb3 100644 --- a/lib/ArduinoJson/CHANGELOG.md +++ b/lib/ArduinoJson-6.x/CHANGELOG.md @@ -1,471 +1,821 @@ -ArduinoJson: change log -======================= - -v5.13.2 -------- - -* Fixed `JsonBuffer::parse()` not respecting nesting limit correctly (issue #693) -* Fixed inconsistencies in nesting level counting (PR #695 from Zhenyu Wu) -* Fixed null values that could be pass to `strcmp()` (PR #745 from Mike Karlesky) -* Added macros `ARDUINOJSON_VERSION`, `ARDUINOJSON_VERSION_MAJOR`... - -v5.13.1 -------- - -* Fixed `JsonVariant::operator|(int)` that returned the default value if the variant contained a double (issue #675) -* Allowed non-quoted key to contain underscores (issue #665) - -v5.13.0 -------- - -* Changed the rules of string duplication (issue #658) -* `RawJson()` accepts any kind of string and obeys to the same rules for duplication -* Changed the return type of `strdup()` to `const char*` to prevent double duplication -* Marked `strdup()` as deprecated - -> ### New rules for string duplication -> -> | type | duplication | -> |:---------------------------|:------------| -> | const char* | no | -> | char* | ~~no~~ yes | -> | String | yes | -> | std::string | yes | -> | const __FlashStringHelper* | yes | -> -> These new rules make `JsonBuffer::strdup()` useless. - -v5.12.0 -------- - -* Added `JsonVariant::operator|` to return a default value (see below) -* Added a clear error message when compiled as C instead of C++ (issue #629) -* Added detection of MPLAB XC compiler (issue #629) -* Added detection of Keil ARM Compiler (issue #629) -* Added an example that shows how to save and load a configuration file -* Reworked all other examples - -> ### How to use the new feature? -> -> If you have a block like this: -> -> ```c++ -> const char* ssid = root["ssid"]; -> if (!ssid) -> ssid = "default ssid"; -> ``` -> -> You can simplify like that: -> -> ```c++ -> const char* ssid = root["ssid"] | "default ssid"; -> ``` - -v5.11.2 -------- - -* Fixed `DynamicJsonBuffer::clear()` not resetting allocation size (issue #561) -* Fixed incorrect rounding for float values (issue #588) - -v5.11.1 -------- - -* Removed dependency on `PGM_P` as Particle 0.6.2 doesn't define it (issue #546) -* Fixed warning "dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]" -* Fixed warning "floating constant exceeds range of 'float' [-Woverflow]" (issue #544) -* Fixed warning "this statement may fall through" [-Wimplicit-fallthrough=] (issue #539) -* Removed `ARDUINOJSON_DOUBLE_IS_64BITS` as it became useless. -* Fixed too many decimals places in float serialization (issue #543) - -v5.11.0 -------- - -* Made `JsonBuffer` non-copyable (PR #524 by @luisrayas3) -* Added `StaticJsonBuffer::clear()` -* Added `DynamicJsonBuffer::clear()` - -v5.10.1 -------- - -* Fixed IntelliSense errors in Visual Micro (issue #483) -* Fixed compilation in IAR Embedded Workbench (issue #515) -* Fixed reading "true" as a float (issue #516) -* Added `ARDUINOJSON_DOUBLE_IS_64BITS` -* Added `ARDUINOJSON_EMBEDDED_MODE` - -v5.10.0 -------- - -* Removed configurable number of decimal places (issues #288, #427 and #506) -* Changed exponentiation thresholds to `1e7` and `1e-5` (issues #288, #427 and #506) -* `JsonVariant::is()` now returns `true` for integers -* Fixed error `IsBaseOf is not a member of ArduinoJson::TypeTraits` (issue #495) -* Fixed error `forming reference to reference` (issue #495) - -> ### BREAKING CHANGES :warning: -> -> | Old syntax | New syntax | -> |:--------------------------------|:--------------------| -> | `double_with_n_digits(3.14, 2)` | `3.14` | -> | `float_with_n_digits(3.14, 2)` | `3.14f` | -> | `obj.set("key", 3.14, 2)` | `obj["key"] = 3.14` | -> | `arr.add(3.14, 2)` | `arr.add(3.14)` | -> -> | Input | Old output | New output | -> |:----------|:-----------|:-----------| -> | `3.14159` | `3.14` | `3.14159` | -> | `42.0` | `42.00` | `42` | -> | `0.0` | `0.00` | `0` | -> -> | Expression | Old result | New result | -> |:-------------------------------|:-----------|:-----------| -> | `JsonVariant(42).is()` | `true` | `true` | -> | `JsonVariant(42).is()` | `false` | `true` | -> | `JsonVariant(42).is()` | `false` | `true` | - -v5.9.0 ------- - -* Added `JsonArray::remove(iterator)` (issue #479) -* Added `JsonObject::remove(iterator)` -* Renamed `JsonArray::removeAt(size_t)` into `remove(size_t)` -* Renamed folder `include/` to `src/` -* Fixed warnings `floating constant exceeds range of float`and `floating constant truncated to zero` (issue #483) -* Removed `Print` class and converted `printTo()` to a template method (issue #276) -* Removed example `IndentedPrintExample.ino` -* Now compatible with Particle 0.6.1, thanks to Jacob Nite (issue #294 and PR #461 by @foodbag) - -v5.8.4 ------- - -* Added custom implementation of `strtod()` (issue #453) -* Added custom implementation of `strtol()` (issue #465) -* `char` is now treated as an integral type (issue #337, #370) - -v5.8.3 ------- - -* Fixed an access violation in `DynamicJsonBuffer` when memory allocation fails (issue #433) -* Added operators `==` and `!=` for two `JsonVariant`s (issue #436) -* Fixed `JsonVariant::operator[const FlashStringHelper*]` (issue #441) - -v5.8.2 ------- - -* Fixed parsing of comments (issue #421) -* Fixed ignored `Stream` timeout (issue #422) -* Made sure we don't read more that necessary (issue #422) -* Fixed error when the key of a `JsonObject` is a `char[]` (issue #423) -* Reduced code size when using `const` references -* Fixed error with string of type `unsigned char*` (issue #428) -* Added `deprecated` attribute on `asArray()`, `asObject()` and `asString()` (issue #420) - -v5.8.1 ------- - -* Fixed error when assigning a `volatile int` to a `JsonVariant` (issue #415) -* Fixed errors with Variable Length Arrays (issue #416) -* Fixed error when both `ARDUINOJSON_ENABLE_STD_STREAM` and `ARDUINOJSON_ENABLE_ARDUINO_STREAM` are set to `1` -* Fixed error "Stream does not name a type" (issue #412) - -v5.8.0 ------- - -* Added operator `==` to compare `JsonVariant` and strings (issue #402) -* Added support for `Stream` (issue #300) -* Reduced memory consumption by not duplicating spaces and comments - -> ### BREAKING CHANGES :warning: -> -> `JsonBuffer::parseObject()` and `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`. -> -> This means that if you have code like: -> -> ```c++ -> void myFunction(JsonBuffer& jsonBuffer); -> ``` -> -> you need to replace it with one of the following: -> -> ```c++ -> void myFunction(DynamicJsonBuffer& jsonBuffer); -> void myFunction(StaticJsonBufferBase& jsonBuffer); -> template void myFunction(TJsonBuffer& jsonBuffer); -> ``` - -v5.7.3 ------- - -* Added an `printTo(char[N])` and `prettyPrintTo(char[N])` (issue #292) -* Added ability to set a nested value like this: `root["A"]["B"] = "C"` (issue #352) -* Renamed `*.ipp` to `*Impl.hpp` because they were ignored by Arduino IDE (issue #396) - -v5.7.2 ------- - -* Made PROGMEM available on more platforms (issue #381) -* Fixed PROGMEM causing an exception on ESP8266 (issue #383) - -v5.7.1 ------- - -* Added support for PROGMEM (issue #76) -* Fixed compilation error when index is not an `int` (issue #381) - -v5.7.0 ------- - -* Templatized all functions using `String` or `std::string` -* Removed `ArduinoJson::String` -* Removed `JsonVariant::defaultValue()` -* Removed non-template `JsonObject::get()` and `JsonArray.get()` -* Fixed support for `StringSumHelper` (issue #184) -* Replaced `ARDUINOJSON_USE_ARDUINO_STRING` by `ARDUINOJSON_ENABLE_STD_STRING` and `ARDUINOJSON_ENABLE_ARDUINO_STRING` (issue #378) -* Added example `StringExample.ino` to show where `String` can be used -* Increased default nesting limit to 50 when compiled for a computer (issue #349) - -> ### BREAKING CHANGES :warning: -> -> The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return. -> -> Old code: -> -> ```c++ -> #define ARDUINOJSON_USE_ARDUINO_STRING 0 -> JsonVariant value1 = myObject.get("myKey"); -> JsonVariant value2 = myArray.get(0); -> ``` -> -> New code: -> -> ```c++ -> #define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 -> #define ARDUINOJSON_ENABLE_STD_STRING 1 -> JsonVariant value1 = myObject.get("myKey"); -> JsonVariant value2 = myArray.get(0); -> ``` - -v5.6.7 ------- - -* Fixed `array[idx].as()` and `object[key].as()` -* Fixed return value of `JsonObject::set()` (issue #350) -* Fixed undefined behavior in `Prettyfier` and `Print` (issue #354) -* Fixed parser that incorrectly rejected floats containing a `+` (issue #349) - -v5.6.6 ------- - -* Fixed `-Wparentheses` warning introduced in v5.6.5 (PR #335 by @nuket) -* Added `.mbedignore` for ARM mbdeb (PR #334 by @nuket) -* Fixed `JsonVariant::success()` which didn't propagate `JsonArray::success()` nor `JsonObject::success()` (issue #342). - -v5.6.5 ------- - -* `as()` now returns `true` when input is `null` (issue #330) - -v5.6.4 ------- - -* Fixed error in float serialization (issue #324) - -v5.6.3 ------- - -* Improved speed of float serialization (about twice faster) -* Added `as()` as a synonym for `as()`... (issue #291) -* Fixed `call of overloaded isinf(double&) is ambiguous` (issue #284) - -v5.6.2 ------- - -* Fixed build when another lib does `#undef isnan` (issue #284) - -v5.6.1 ------- - -* Added missing `#pragma once` (issue #310) - -v5.6.0 ------- - -* ArduinoJson is now a header-only library (issue #199) - -v5.5.1 ------- - -* Fixed compilation error with Intel Galileo (issue #299) - -v5.5.0 ------- - -* Added `JsonVariant::success()` (issue #279) -* Renamed `JsonVariant::invalid()` to `JsonVariant::defaultValue()` - -v5.4.0 ------- - -* Changed `::String` to `ArduinoJson::String` (issue #275) -* Changed `::Print` to `ArduinoJson::Print` too - -v5.3.0 ------- - -* Added custom implementation of `ftoa` (issues #266, #267, #269 and #270) -* Added `JsonVariant JsonBuffer::parse()` (issue #265) -* Fixed `unsigned long` printed as `signed long` (issue #170) - -v5.2.0 ------- - -* Added `JsonVariant::as()` as a synonym for `JsonVariant::as()` (issue #257) -* Added example `JsonHttpClient` (issue #256) -* Added `JsonArray::copyTo()` and `JsonArray::copyFrom()` (issue #254) -* Added `RawJson()` to insert pregenerated JSON portions (issue #259) - -v5.1.1 ------- - -* Removed `String` duplication when one replaces a value in a `JsonObject` (PR #232 by @ulion) - -v5.1.0 ------- - -* Added support of `long long` (issue #171) -* Moved all build settings to `ArduinoJson/Configuration.hpp` - -> ### BREAKING CHANGE :warning: -> -> If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`. - -v5.0.8 ------- - -* Made the library compatible with [PlatformIO](http://platformio.org/) (issue #181) -* Fixed `JsonVariant::is()` that was incorrectly returning false (issue #214) - -v5.0.7 ------- - -* Made library easier to use from a CMake project: simply `add_subdirectory(ArduinoJson/src)` -* Changed `String` to be a `typedef` of `std::string` (issues #142 and #161) - -> ### BREAKING CHANGES :warning: -> -> - `JsonVariant(true).as()` now returns `"true"` instead of `"1"` -> - `JsonVariant(false).as()` now returns `"false"` instead of `"0"` - -v5.0.6 ------- - -* Added parameter to `DynamicJsonBuffer` constructor to set initial size (issue #152) -* Fixed warning about library category in Arduino 1.6.6 (issue #147) -* Examples: Added a loop to wait for serial port to be ready (issue #156) - -v5.0.5 ------- - -* Added overload `JsonObjectSuscript::set(value, decimals)` (issue #143) -* Use `float` instead of `double` to reduce the size of `JsonVariant` (issue #134) - -v5.0.4 ------- - -* Fixed ambiguous overload with `JsonArraySubscript` and `JsonObjectSubscript` (issue #122) - -v5.0.3 ------- - -* Fixed `printTo(String)` which wrote numbers instead of strings (issue #120) -* Fixed return type of `JsonArray::is()` and some others (issue #121) - -v5.0.2 ------- - -* Fixed segmentation fault in `parseObject(String)` and `parseArray(String)`, when the - `StaticJsonBuffer` is too small to hold a copy of the string -* Fixed Clang warning "register specifier is deprecated" (issue #102) -* Fixed GCC warning "declaration shadows a member" (issue #103) -* Fixed memory alignment, which made ESP8266 crash (issue #104) -* Fixed compilation on Visual Studio 2010 and 2012 (issue #107) - -v5.0.1 ------- - -* Fixed compilation with Arduino 1.0.6 (issue #99) - -v5.0.0 ------- - -* Added support of `String` class (issues #55, #56, #70, #77) -* Added `JsonBuffer::strdup()` to make a copy of a string (issues #10, #57) -* Implicitly call `strdup()` for `String` but not for `char*` (issues #84, #87) -* Added support of non standard JSON input (issue #44) -* Added support of comments in JSON input (issue #88) -* Added implicit cast between numerical types (issues #64, #69, #93) -* Added ability to read number values as string (issue #90) -* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66) -* Switched to new the library layout (requires Arduino 1.0.6 or above) - -> ### BREAKING CHANGES :warning: -> -> - `JsonObject::add()` was renamed to `set()` -> - `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` -> - Number of digits of floating point value are now set with `double_with_n_digits()` - -**Personal note about the `String` class**: -Support of the `String` class has been added to the library because many people use it in their programs. -However, you should not see this as an invitation to use the `String` class. -The `String` class is **bad** because it uses dynamic memory allocation. -Compared to static allocation, it compiles to a bigger, slower program, and is less predictable. -You certainly don't want that in an embedded environment! - -v4.6 ----- - -* Fixed segmentation fault in `DynamicJsonBuffer` when memory allocation fails (issue #92) - -v4.5 ----- - -* Fixed buffer overflow when input contains a backslash followed by a terminator (issue #81) - -**Upgrading is recommended** since previous versions contain a potential security risk. - -Special thanks to [Giancarlo Canales Barreto](https://github.com/gcanalesb) for finding this nasty bug. - -v4.4 ----- - -* Added `JsonArray::measureLength()` and `JsonObject::measureLength()` (issue #75) - -v4.3 ----- - -* Added `JsonArray::removeAt()` to remove an element of an array (issue #58) -* Fixed stack-overflow in `DynamicJsonBuffer` when parsing huge JSON files (issue #65) -* Fixed wrong return value of `parseArray()` and `parseObject()` when allocation fails (issue #68) - -v4.2 ----- - -* Switched back to old library layout (issues #39, #43 and #45) -* Removed global new operator overload (issue #40, #45 and #46) -* Added an example with EthernetServer - -v4.1 ----- - -* Added DynamicJsonBuffer (issue #19) - -v4.0 ----- - -* Unified parser and generator API (issue #23) -* Updated library layout, now requires Arduino 1.0.6 or newer - -> ### BREAKING CHANGES :warning: -> -> API changed significantly since v3, see [Migrating code to the new API](https://arduinojson.org/doc/migration/). - +ArduinoJson: change log +======================= + +v6.11.0 (2019-05-26) +------- + +* Fixed `deserializeJson()` silently accepting a `Stream*` (issue #978) +* Fixed invalid result from `operator|` (issue #981) +* Made `deserializeJson()` more picky about trailing characters (issue #980) +* Added `ARDUINOJSON_ENABLE_NAN` (default=0) to enable NaN in JSON (issue #973) +* Added `ARDUINOJSON_ENABLE_INFINITY` (default=0) to enable Infinity in JSON +* Removed implicit conversion in comparison operators (issue #998) +* Added lexicographical comparison for `JsonVariant` +* Added support for `nullptr` (issue #998) + +> ### BREAKING CHANGES +> +> #### NaN and Infinity +> +> The JSON specification allows neither NaN not Infinity, but previous +> versions of ArduinoJson supported it. Now, ArduinoJson behaves like most +> other libraries: a NaN or and Infinity in the `JsonDocument`, becomes +> a `null` in the output JSON. Also, `deserializeJson()` returns +> `InvalidInput` if the JSON document contains NaN or Infinity. +> +> This version still supports NaN and Infinity in JSON documents, but +> it's disabled by default to be compatible with other JSON parsers. +> If you need the old behavior back, define `ARDUINOJSON_ENABLE_NAN` and +> `ARDUINOJSON_ENABLE_INFINITY` to `1`;: +> +> ```c++ +> #define ARDUINOJSON_ENABLE_NAN 1 +> #define ARDUINOJSON_ENABLE_INFINITY 1 +> #include +> ``` +> +> #### The "or" operator +> +> This version slightly changes the behavior of the | operator when the +> variant contains a float and the user requests an integer. +> +> Older versions returned the floating point value truncated. +> Now, it returns the default value. +> +> ```c++ +> // suppose variant contains 1.2 +> int value = variant | 3; +> +> // old behavior: +> value == 1 +> +> // new behavior +> value == 3 +> ``` +> +> If you need the old behavior, you must add `if (variant.is())`. + +v6.10.1 (2019-04-23) +------- + +* Fixed error "attributes are not allowed on a function-definition" +* Fixed `deserializeJson()` not being picky enough (issue #969) +* Fixed error "no matching function for call to write(uint8_t)" (issue #972) + +v6.10.0 (2019-03-22) +------- + +* Fixed an integer overflow in the JSON deserializer +* Added overflow handling in `JsonVariant::as()` and `JsonVariant::is()`. + - `as()` returns `0` if the integer `T` overflows + - `is()` returns `false` if the integer `T` overflows +* Added `BasicJsonDocument` to support custom allocator (issue #876) +* Added `JsonDocument::containsKey()` (issue #938) +* Added `JsonVariant::containsKey()` + +v6.9.1 (2019-03-01) +------ + +* Fixed warning "unused variable" with GCC 4.4 (issue #912) +* Fixed warning "cast increases required alignment" (issue #914) +* Fixed warning "conversion may alter value" (issue #914) +* Fixed naming conflict with "CAPACITY" (issue #839) +* Muted warning "will change in GCC 7.1" (issue #914) +* Added a clear error message for `StaticJsonBuffer` and `DynamicJsonBuffer` +* Marked ArduinoJson.h as a "system header" + +v6.9.0 (2019-02-26) +------ + +* Decode escaped Unicode characters like \u00DE (issue #304, PR #791) + Many thanks to Daniel Schulte (aka @trilader) who implemented this feature. +* Added option ARDUINOJSON_DECODE_UNICODE to enable it +* Converted `JsonArray::copyFrom()/copyTo()` to free functions `copyArray()` +* Renamed `JsonArray::copyFrom()` and `JsonObject::copyFrom()` to `set()` +* Renamed `JsonArray::get()` to `getElement()` +* Renamed `JsonArray::add()` (without arg) to `addElement()` +* Renamed `JsonObject::get()` to `getMember()` +* Renamed `JsonObject::getOrCreate()` to `getOrAddMember()` +* Fixed `JsonVariant::isNull()` not returning `true` after `set((char*)0)` +* Fixed segfault after `variant.set(serialized((char*)0))` +* Detect `IncompleteInput` in `false`, `true`, and `null` +* Added `JsonDocument::size()` +* Added `JsonDocument::remove()` +* Added `JsonVariant::clear()` +* Added `JsonVariant::remove()` + +v6.8.0-beta (2019-01-30) +----------- + +* Import functions in the ArduinoJson namespace to get clearer errors +* Improved syntax highlighting in Arduino IDE +* Removed default capacity of `DynamicJsonDocument` +* `JsonArray::copyFrom()` accepts `JsonArrayConst` +* `JsonVariant::set()` accepts `JsonArrayConst` and `JsonObjectConst` +* `JsonDocument` was missing in the ArduinoJson namespace +* Added `memoryUsage()` to `JsonArray`, `JsonObject`, and `JsonVariant` +* Added `nesting()` to `JsonArray`, `JsonDocument`, `JsonObject`, and `JsonVariant` +* Replaced `JsonDocument::nestingLimit` with an additional parameter + to `deserializeJson()` and `deserializeMsgPack()` +* Fixed uninitialized variant in `JsonDocument` +* Fixed `StaticJsonDocument` copy constructor and copy assignment +* The copy constructor of `DynamicJsonDocument` chooses the capacity according to the memory usage of the source, not from the capacity of the source. +* Added the ability to create/assign a `StaticJsonDocument`/`DynamicJsonDocument` from a `JsonArray`/`JsonObject`/`JsonVariant` +* Added `JsonDocument::isNull()` +* Added `JsonDocument::operator[]` +* Added `ARDUINOJSON_TAB` to configure the indentation character +* Reduced the size of the pretty JSON serializer +* Added `add()`, `createNestedArray()` and `createNestedObject()` to `JsonVariant` +* `JsonVariant` automatically promotes to `JsonObject` or `JsonArray` on write. + Calling `JsonVariant::to()` is not required anymore. +* `JsonDocument` now support the same operations as `JsonVariant`. + Calling `JsonDocument::as()` is not required anymore. +* Fixed example `JsonHttpClient.ino` +* User can now use a `JsonString` as a key or a value + +> ### BREAKING CHANGES +> +> #### `DynamicJsonDocument`'s constructor +> +> The parameter to the constructor of `DynamicJsonDocument` is now mandatory +> +> Old code: +> +> ```c++ +> DynamicJsonDocument doc; +> ``` +> +> New code: +> +> ```c++ +> DynamicJsonDocument doc(1024); +> ``` +> +> #### Nesting limit +> +> `JsonDocument::nestingLimit` was replaced with a new parameter to `deserializeJson()` and `deserializeMsgPack()`. +> +> Old code: +> +> ```c++ +> doc.nestingLimit = 15; +> deserializeJson(doc, input); +> ``` +> +> New code: +> +> ```c++ +> deserializeJson(doc, input, DeserializationOption::NestingLimit(15)); +> ``` + +v6.7.0-beta (2018-12-07) +----------- + +* Removed the automatic expansion of `DynamicJsonDocument`, it now has a fixed capacity. +* Restored the monotonic allocator because the code was getting too big +* Reduced the memory usage +* Reduced the code size +* Renamed `JsonKey` to `JsonString` +* Removed spurious files in the Particle library + +v6.6.0-beta (2018-11-13) +----------- + +* Removed `JsonArray::is(i)` and `JsonArray::set(i,v)` +* Removed `JsonObject::is(k)` and `JsonObject::set(k,v)` +* Replaced `T JsonArray::get(i)` with `JsonVariant JsonArray::get(i)` +* Replaced `T JsonObject::get(k)` with `JsonVariant JsonObject::get(k)` +* Added `JSON_STRING_SIZE()` +* ~~Replacing or removing a value now releases the memory~~ +* Added `DeserializationError::code()` to be used in switch statements (issue #846) + +v6.5.0-beta (2018-10-13) +----------- + +* Added implicit conversion from `JsonArray` and `JsonObject` to `JsonVariant` +* Allow mixed configuration in compilation units (issue #809) +* Fixed object keys not being duplicated +* `JsonPair::key()` now returns a `JsonKey` +* Increased the default capacity of `DynamicJsonDocument` +* Fixed `JsonVariant::is()` (closes #763) +* Added `JsonArrayConst`, `JsonObjectConst`, and `JsonVariantConst` +* Added copy-constructor and copy-assignment-operator for `JsonDocument` (issue #827) + +v6.4.0-beta (2018-09-11) +----------- + +* Copy `JsonArray` and `JsonObject`, instead of storing pointers (issue #780) +* Added `JsonVariant::to()` and `JsonVariant::to()` + +v6.3.0-beta (2018-08-31) +----------- + +* Implemented reference semantics for `JsonVariant` +* Replaced `JsonPair`'s `key` and `value` with `key()` and `value()` +* Fixed `serializeJson(obj[key], dst)` (issue #794) + +> ### BREAKING CHANGES +> +> #### JsonVariant +> +> `JsonVariant` now has a semantic similar to `JsonObject` and `JsonArray`. +> It's a reference to a value stored in the `JsonDocument`. +> As a consequence, a `JsonVariant` cannot be used as a standalone variable anymore. +> +> Old code: +> +> ```c++ +> JsonVariant myValue = 42; +> ``` +> +> New code: +> +> ```c++ +> DynamicJsonDocument doc; +> JsonVariant myValue = doc.to(); +> myValue.set(42); +> ``` +> +> #### JsonPair +> +> Old code: +> +> ```c++ +> for(JsonPair p : myObject) { +> Serial.println(p.key); +> Serial.println(p.value.as()); +> } +> ``` +> +> New code: +> +> ```c++ +> for(JsonPair p : myObject) { +> Serial.println(p.key()); +> Serial.println(p.value().as()); +> } +> ``` +> +> CAUTION: the key is now read only! + +v6.2.3-beta (2018-07-19) +----------- + +* Fixed exception when using Flash strings as object keys (issue #784) + +v6.2.2-beta (2018-07-18) +----------- + +* Fixed `invalid application of 'sizeof' to incomplete type '__FlashStringHelper'` (issue #783) +* Fixed `char[]` not duplicated when passed to `JsonVariant::operator[]` + +v6.2.1-beta (2018-07-17) +----------- + +* Fixed `JsonObject` not inserting keys of type `String` (issue #782) + +v6.2.0-beta (2018-07-12) +----------- + +* Disabled lazy number deserialization (issue #772) +* Fixed `JsonVariant::is()` that returned true for empty strings +* Improved float serialization when `-fsingle-precision-constant` is used +* Renamed function `RawJson()` to `serialized()` +* `serializeMsgPack()` now supports values marked with `serialized()` + +> ### BREAKING CHANGES +> +> #### Non quoted strings +> +> Non quoted strings are now forbidden in values, but they are still allowed in keys. +> For example, `{key:"value"}` is accepted, but `{key:value}` is not. +> +> #### Preformatted values +> +> Old code: +> +> ```c++ +> object["values"] = RawJson("[1,2,3,4]"); +> ``` +> +> New code: +> +> ```c++ +> object["values"] = serialized("[1,2,3,4]"); +> ``` + +v6.1.0-beta (2018-07-02) +----------- + +* Return `JsonArray` and `JsonObject` by value instead of reference (issue #309) +* Replaced `success()` with `isNull()` + +> ### BREAKING CHANGES +> +> Old code: +> +> ```c++ +> JsonObject& obj = doc.to(); +> JsonArray& arr = obj.createNestedArray("key"); +> if (!arr.success()) { +> Serial.println("Not enough memory"); +> return; +> } +> ``` +> +> New code: +> +> ```c++ +> JsonObject obj = doc.to(); +> JsonArray arr = obj.createNestedArray("key"); +> if (arr.isNull()) { +> Serial.println("Not enough memory"); +> return; +> } +> ``` + +v6.0.1-beta (2018-06-11) +----------- + +* Fixed conflicts with `isnan()` and `isinf()` macros (issue #752) + +v6.0.0-beta (2018-06-07) +----------- + +* Added `DynamicJsonDocument` and `StaticJsonDocument` +* Added `deserializeJson()` +* Added `serializeJson()` and `serializeJsonPretty()` +* Added `measureJson()` and `measureJsonPretty()` +* Added `serializeMsgPack()`, `deserializeMsgPack()` and `measureMsgPack()` (issue #358) +* Added example `MsgPackParser.ino` (issue #358) +* Added support for non zero-terminated strings (issue #704) +* Removed `JsonBuffer::parseArray()`, `parseObject()` and `parse()` +* Removed `JsonBuffer::createArray()` and `createObject()` +* Removed `printTo()` and `prettyPrintTo()` +* Removed `measureLength()` and `measurePrettyLength()` +* Removed all deprecated features + +> ### BREAKING CHANGES +> +> #### Deserialization +> +> Old code: +> +> ```c++ +> DynamicJsonBuffer jb; +> JsonObject& obj = jb.parseObject(json); +> if (obj.success()) { +> +> } +> ``` +> +> New code: +> +> ```c++ +> DynamicJsonDocument doc; +> DeserializationError error = deserializeJson(doc, json); +> if (error) { +> +> } +> JsonObject& obj = doc.as(); +> ``` +> +> #### Serialization +> +> Old code: +> +> ```c++ +> DynamicJsonBuffer jb; +> JsonObject& obj = jb.createObject(); +> obj["key"] = "value"; +> obj.printTo(Serial); +> ``` +> +> New code: +> +> ```c++ +> DynamicJsonDocument obj; +> JsonObject& obj = doc.to(); +> obj["key"] = "value"; +> serializeJson(doc, Serial); +> ``` + +v5.13.2 +------- + +* Fixed `JsonBuffer::parse()` not respecting nesting limit correctly (issue #693) +* Fixed inconsistencies in nesting level counting (PR #695 from Zhenyu Wu) +* Fixed null values that could be pass to `strcmp()` (PR #745 from Mike Karlesky) +* Added macros `ARDUINOJSON_VERSION`, `ARDUINOJSON_VERSION_MAJOR`... + +v5.13.1 +------- + +* Fixed `JsonVariant::operator|(int)` that returned the default value if the variant contained a double (issue #675) +* Allowed non-quoted key to contain underscores (issue #665) + +v5.13.0 +------- + +* Changed the rules of string duplication (issue #658) +* `RawJson()` accepts any kind of string and obeys to the same rules for duplication +* Changed the return type of `strdup()` to `const char*` to prevent double duplication +* Marked `strdup()` as deprecated + +> ### New rules for string duplication +> +> | type | duplication | +> |:---------------------------|:------------| +> | const char* | no | +> | char* | ~~no~~ yes | +> | String | yes | +> | std::string | yes | +> | const __FlashStringHelper* | yes | +> +> These new rules make `JsonBuffer::strdup()` useless. + +v5.12.0 +------- + +* Added `JsonVariant::operator|` to return a default value (see below) +* Added a clear error message when compiled as C instead of C++ (issue #629) +* Added detection of MPLAB XC compiler (issue #629) +* Added detection of Keil ARM Compiler (issue #629) +* Added an example that shows how to save and load a configuration file +* Reworked all other examples + +> ### How to use the new feature? +> +> If you have a block like this: +> +> ```c++ +> const char* ssid = root["ssid"]; +> if (!ssid) +> ssid = "default ssid"; +> ``` +> +> You can simplify like that: +> +> ```c++ +> const char* ssid = root["ssid"] | "default ssid"; +> ``` + +v5.11.2 +------- + +* Fixed `DynamicJsonBuffer::clear()` not resetting allocation size (issue #561) +* Fixed incorrect rounding for float values (issue #588) + +v5.11.1 +------- + +* Removed dependency on `PGM_P` as Particle 0.6.2 doesn't define it (issue #546) +* Fixed warning "dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]" +* Fixed warning "floating constant exceeds range of 'float' [-Woverflow]" (issue #544) +* Fixed warning "this statement may fall through" [-Wimplicit-fallthrough=] (issue #539) +* Removed `ARDUINOJSON_DOUBLE_IS_64BITS` as it became useless. +* Fixed too many decimals places in float serialization (issue #543) + +v5.11.0 +------- + +* Made `JsonBuffer` non-copyable (PR #524 by @luisrayas3) +* Added `StaticJsonBuffer::clear()` +* Added `DynamicJsonBuffer::clear()` + +v5.10.1 +------- + +* Fixed IntelliSense errors in Visual Micro (issue #483) +* Fixed compilation in IAR Embedded Workbench (issue #515) +* Fixed reading "true" as a float (issue #516) +* Added `ARDUINOJSON_DOUBLE_IS_64BITS` +* Added `ARDUINOJSON_EMBEDDED_MODE` + +v5.10.0 +------- + +* Removed configurable number of decimal places (issues #288, #427 and #506) +* Changed exponentiation thresholds to `1e7` and `1e-5` (issues #288, #427 and #506) +* `JsonVariant::is()` now returns `true` for integers +* Fixed error `IsBaseOf is not a member of ArduinoJson::TypeTraits` (issue #495) +* Fixed error `forming reference to reference` (issue #495) + +> ### BREAKING CHANGES :warning: +> +> | Old syntax | New syntax | +> |:--------------------------------|:--------------------| +> | `double_with_n_digits(3.14, 2)` | `3.14` | +> | `float_with_n_digits(3.14, 2)` | `3.14f` | +> | `obj.set("key", 3.14, 2)` | `obj["key"] = 3.14` | +> | `arr.add(3.14, 2)` | `arr.add(3.14)` | +> +> | Input | Old output | New output | +> |:----------|:-----------|:-----------| +> | `3.14159` | `3.14` | `3.14159` | +> | `42.0` | `42.00` | `42` | +> | `0.0` | `0.00` | `0` | +> +> | Expression | Old result | New result | +> |:-------------------------------|:-----------|:-----------| +> | `JsonVariant(42).is()` | `true` | `true` | +> | `JsonVariant(42).is()` | `false` | `true` | +> | `JsonVariant(42).is()` | `false` | `true` | + +v5.9.0 +------ + +* Added `JsonArray::remove(iterator)` (issue #479) +* Added `JsonObject::remove(iterator)` +* Renamed `JsonArray::removeAt(size_t)` into `remove(size_t)` +* Renamed folder `include/` to `src/` +* Fixed warnings `floating constant exceeds range of float`and `floating constant truncated to zero` (issue #483) +* Removed `Print` class and converted `printTo()` to a template method (issue #276) +* Removed example `IndentedPrintExample.ino` +* Now compatible with Particle 0.6.1, thanks to Jacob Nite (issue #294 and PR #461 by @foodbag) + +v5.8.4 +------ + +* Added custom implementation of `strtod()` (issue #453) +* Added custom implementation of `strtol()` (issue #465) +* `char` is now treated as an integral type (issue #337, #370) + +v5.8.3 +------ + +* Fixed an access violation in `DynamicJsonBuffer` when memory allocation fails (issue #433) +* Added operators `==` and `!=` for two `JsonVariant`s (issue #436) +* Fixed `JsonVariant::operator[const FlashStringHelper*]` (issue #441) + +v5.8.2 +------ + +* Fixed parsing of comments (issue #421) +* Fixed ignored `Stream` timeout (issue #422) +* Made sure we don't read more that necessary (issue #422) +* Fixed error when the key of a `JsonObject` is a `char[]` (issue #423) +* Reduced code size when using `const` references +* Fixed error with string of type `unsigned char*` (issue #428) +* Added `deprecated` attribute on `asArray()`, `asObject()` and `asString()` (issue #420) + +v5.8.1 +------ + +* Fixed error when assigning a `volatile int` to a `JsonVariant` (issue #415) +* Fixed errors with Variable Length Arrays (issue #416) +* Fixed error when both `ARDUINOJSON_ENABLE_STD_STREAM` and `ARDUINOJSON_ENABLE_ARDUINO_STREAM` are set to `1` +* Fixed error "Stream does not name a type" (issue #412) + +v5.8.0 +------ + +* Added operator `==` to compare `JsonVariant` and strings (issue #402) +* Added support for `Stream` (issue #300) +* Reduced memory consumption by not duplicating spaces and comments + +> ### BREAKING CHANGES :warning: +> +> `JsonBuffer::parseObject()` and `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`. +> +> This means that if you have code like: +> +> ```c++ +> void myFunction(JsonBuffer& jsonBuffer); +> ``` +> +> you need to replace it with one of the following: +> +> ```c++ +> void myFunction(DynamicJsonBuffer& jsonBuffer); +> void myFunction(StaticJsonBufferBase& jsonBuffer); +> template void myFunction(TJsonBuffer& jsonBuffer); +> ``` + +v5.7.3 +------ + +* Added an `printTo(char[N])` and `prettyPrintTo(char[N])` (issue #292) +* Added ability to set a nested value like this: `root["A"]["B"] = "C"` (issue #352) +* Renamed `*.ipp` to `*Impl.hpp` because they were ignored by Arduino IDE (issue #396) + +v5.7.2 +------ + +* Made PROGMEM available on more platforms (issue #381) +* Fixed PROGMEM causing an exception on ESP8266 (issue #383) + +v5.7.1 +------ + +* Added support for PROGMEM (issue #76) +* Fixed compilation error when index is not an `int` (issue #381) + +v5.7.0 +------ + +* Templatized all functions using `String` or `std::string` +* Removed `ArduinoJson::String` +* Removed `JsonVariant::defaultValue()` +* Removed non-template `JsonObject::get()` and `JsonArray.get()` +* Fixed support for `StringSumHelper` (issue #184) +* Replaced `ARDUINOJSON_USE_ARDUINO_STRING` by `ARDUINOJSON_ENABLE_STD_STRING` and `ARDUINOJSON_ENABLE_ARDUINO_STRING` (issue #378) +* Added example `StringExample.ino` to show where `String` can be used +* Increased default nesting limit to 50 when compiled for a computer (issue #349) + +> ### BREAKING CHANGES :warning: +> +> The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return. +> +> Old code: +> +> ```c++ +> #define ARDUINOJSON_USE_ARDUINO_STRING 0 +> JsonVariant value1 = myObject.get("myKey"); +> JsonVariant value2 = myArray.get(0); +> ``` +> +> New code: +> +> ```c++ +> #define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +> #define ARDUINOJSON_ENABLE_STD_STRING 1 +> JsonVariant value1 = myObject.get("myKey"); +> JsonVariant value2 = myArray.get(0); +> ``` + +v5.6.7 +------ + +* Fixed `array[idx].as()` and `object[key].as()` +* Fixed return value of `JsonObject::set()` (issue #350) +* Fixed undefined behavior in `Prettyfier` and `Print` (issue #354) +* Fixed parser that incorrectly rejected floats containing a `+` (issue #349) + +v5.6.6 +------ + +* Fixed `-Wparentheses` warning introduced in v5.6.5 (PR #335 by @nuket) +* Added `.mbedignore` for ARM mbdeb (PR #334 by @nuket) +* Fixed `JsonVariant::success()` which didn't propagate `JsonArray::success()` nor `JsonObject::success()` (issue #342). + +v5.6.5 +------ + +* `as()` now returns `true` when input is `null` (issue #330) + +v5.6.4 +------ + +* Fixed error in float serialization (issue #324) + +v5.6.3 +------ + +* Improved speed of float serialization (about twice faster) +* Added `as()` as a synonym for `as()`... (issue #291) +* Fixed `call of overloaded isinf(double&) is ambiguous` (issue #284) + +v5.6.2 +------ + +* Fixed build when another lib does `#undef isnan` (issue #284) + +v5.6.1 +------ + +* Added missing `#pragma once` (issue #310) + +v5.6.0 +------ + +* ArduinoJson is now a header-only library (issue #199) + +v5.5.1 +------ + +* Fixed compilation error with Intel Galileo (issue #299) + +v5.5.0 +------ + +* Added `JsonVariant::success()` (issue #279) +* Renamed `JsonVariant::invalid()` to `JsonVariant::defaultValue()` + +v5.4.0 +------ + +* Changed `::String` to `ArduinoJson::String` (issue #275) +* Changed `::Print` to `ArduinoJson::Print` too + +v5.3.0 +------ + +* Added custom implementation of `ftoa` (issues #266, #267, #269 and #270) +* Added `JsonVariant JsonBuffer::parse()` (issue #265) +* Fixed `unsigned long` printed as `signed long` (issue #170) + +v5.2.0 +------ + +* Added `JsonVariant::as()` as a synonym for `JsonVariant::as()` (issue #257) +* Added example `JsonHttpClient` (issue #256) +* Added `JsonArray::copyTo()` and `JsonArray::copyFrom()` (issue #254) +* Added `RawJson()` to insert pregenerated JSON portions (issue #259) + +v5.1.1 +------ + +* Removed `String` duplication when one replaces a value in a `JsonObject` (PR #232 by @ulion) + +v5.1.0 +------ + +* Added support of `long long` (issue #171) +* Moved all build settings to `ArduinoJson/Configuration.hpp` + +> ### BREAKING CHANGE :warning: +> +> If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`. + +v5.0.8 +------ + +* Made the library compatible with [PlatformIO](http://platformio.org/) (issue #181) +* Fixed `JsonVariant::is()` that was incorrectly returning false (issue #214) + +v5.0.7 +------ + +* Made library easier to use from a CMake project: simply `add_subdirectory(ArduinoJson/src)` +* Changed `String` to be a `typedef` of `std::string` (issues #142 and #161) + +> ### BREAKING CHANGES :warning: +> +> - `JsonVariant(true).as()` now returns `"true"` instead of `"1"` +> - `JsonVariant(false).as()` now returns `"false"` instead of `"0"` + +v5.0.6 +------ + +* Added parameter to `DynamicJsonBuffer` constructor to set initial size (issue #152) +* Fixed warning about library category in Arduino 1.6.6 (issue #147) +* Examples: Added a loop to wait for serial port to be ready (issue #156) + +v5.0.5 +------ + +* Added overload `JsonObjectSuscript::set(value, decimals)` (issue #143) +* Use `float` instead of `double` to reduce the size of `JsonVariant` (issue #134) + +v5.0.4 +------ + +* Fixed ambiguous overload with `JsonArraySubscript` and `JsonObjectSubscript` (issue #122) + +v5.0.3 +------ + +* Fixed `printTo(String)` which wrote numbers instead of strings (issue #120) +* Fixed return type of `JsonArray::is()` and some others (issue #121) + +v5.0.2 +------ + +* Fixed segmentation fault in `parseObject(String)` and `parseArray(String)`, when the + `StaticJsonBuffer` is too small to hold a copy of the string +* Fixed Clang warning "register specifier is deprecated" (issue #102) +* Fixed GCC warning "declaration shadows a member" (issue #103) +* Fixed memory alignment, which made ESP8266 crash (issue #104) +* Fixed compilation on Visual Studio 2010 and 2012 (issue #107) + +v5.0.1 +------ + +* Fixed compilation with Arduino 1.0.6 (issue #99) + +v5.0.0 +------ + +* Added support of `String` class (issues #55, #56, #70, #77) +* Added `JsonBuffer::strdup()` to make a copy of a string (issues #10, #57) +* Implicitly call `strdup()` for `String` but not for `char*` (issues #84, #87) +* Added support of non standard JSON input (issue #44) +* Added support of comments in JSON input (issue #88) +* Added implicit cast between numerical types (issues #64, #69, #93) +* Added ability to read number values as string (issue #90) +* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66) +* Switched to new the library layout (requires Arduino 1.0.6 or above) + +> ### BREAKING CHANGES :warning: +> +> - `JsonObject::add()` was renamed to `set()` +> - `JsonArray::at()` and `JsonObject::at()` were renamed to `get()` +> - Number of digits of floating point value are now set with `double_with_n_digits()` + +**Personal note about the `String` class**: +Support of the `String` class has been added to the library because many people use it in their programs. +However, you should not see this as an invitation to use the `String` class. +The `String` class is **bad** because it uses dynamic memory allocation. +Compared to static allocation, it compiles to a bigger, slower program, and is less predictable. +You certainly don't want that in an embedded environment! diff --git a/lib/ArduinoJson/CMakeLists.txt b/lib/ArduinoJson-6.x/CMakeLists.txt similarity index 51% rename from lib/ArduinoJson/CMakeLists.txt rename to lib/ArduinoJson-6.x/CMakeLists.txt index 66c565b5b4..1ef4459f9d 100644 --- a/lib/ArduinoJson/CMakeLists.txt +++ b/lib/ArduinoJson-6.x/CMakeLists.txt @@ -1,5 +1,5 @@ # ArduinoJson - arduinojson.org -# Copyright Benoit Blanchon 2014-2018 +# Copyright Benoit Blanchon 2014-2019 # MIT License cmake_minimum_required(VERSION 3.0) @@ -7,10 +7,16 @@ project(ArduinoJson) enable_testing() +add_definitions(-DARDUINOJSON_DEBUG) +if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") + add_compile_options(-g -O0) +endif() + if(${COVERAGE}) - set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage") endif() include_directories(${CMAKE_CURRENT_LIST_DIR}/src) add_subdirectory(third-party/catch) add_subdirectory(test) +add_subdirectory(fuzzing) diff --git a/lib/ArduinoJson/CONTRIBUTING.md b/lib/ArduinoJson-6.x/CONTRIBUTING.md similarity index 100% rename from lib/ArduinoJson/CONTRIBUTING.md rename to lib/ArduinoJson-6.x/CONTRIBUTING.md diff --git a/lib/ArduinoJson/LICENSE.md b/lib/ArduinoJson-6.x/LICENSE.md similarity index 95% rename from lib/ArduinoJson/LICENSE.md rename to lib/ArduinoJson-6.x/LICENSE.md index 247c508430..3805c383ec 100644 --- a/lib/ArduinoJson/LICENSE.md +++ b/lib/ArduinoJson-6.x/LICENSE.md @@ -1,10 +1,10 @@ -The MIT License (MIT) ---------------------- - -Copyright © 2014-2018 Benoit BLANCHON - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Softwareâ€), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS ISâ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +The MIT License (MIT) +--------------------- + +Copyright © 2014-2019 Benoit BLANCHON + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Softwareâ€), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS ISâ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/ArduinoJson/README.md b/lib/ArduinoJson-6.x/README.md similarity index 83% rename from lib/ArduinoJson/README.md rename to lib/ArduinoJson-6.x/README.md index f9e08ff89c..162e85a13b 100644 --- a/lib/ArduinoJson/README.md +++ b/lib/ArduinoJson-6.x/README.md @@ -1,110 +1,113 @@ -![ArduinoJson](banner.svg) - ---- - -[![Build status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/master?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/master) [![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=master)](https://travis-ci.org/bblanchon/ArduinoJson) [![Coverage Status](https://img.shields.io/coveralls/bblanchon/ArduinoJson.svg)](https://coveralls.io/r/bblanchon/ArduinoJson?branch=master) [![Star this project](http://githubbadges.com/star.svg?user=bblanchon&repo=ArduinoJson&style=flat&color=fff&background=007ec6)](https://github.com/bblanchon/ArduinoJson) - -ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). - -## Features - -* JSON decoding (comments are supported) -* JSON encoding (with optional indentation) -* Elegant API, easy to use -* Fixed memory allocation (zero malloc) -* No data duplication (zero copy) -* Portable (written in C++98, can be used in any C++ project) -* Self-contained (no external dependency) -* Small footprint -* Input and output streams -* [100% code coverage](https://coveralls.io/github/bblanchon/ArduinoJson) -* [Header-only library](https://en.wikipedia.org/wiki/Header-only) -* [MIT License](https://en.wikipedia.org/wiki/MIT_License) -* [Comprehensive documentation](https://arduinojson.org?utm_source=github&utm_medium=readme) - -## Compatibility - -ArduinoJson works on the following hardware: - -* Arduino boards: [Uno](https://www.arduino.cc/en/Main/ArduinoBoardUno), [Due](https://www.arduino.cc/en/Main/ArduinoBoardDue), [Mini](https://www.arduino.cc/en/Main/ArduinoBoardMini), [Micro](https://www.arduino.cc/en/Main/ArduinoBoardMicro), [Yun](https://www.arduino.cc/en/Main/ArduinoBoardYun)... -* Espressif chips: [ESP8266](https://en.wikipedia.org/wiki/ESP8266), [ESP32](https://en.wikipedia.org/wiki/ESP32) -* WeMos boards: [D1](https://wiki.wemos.cc/products:d1:d1), [D1 mini](https://wiki.wemos.cc/products:d1:d1_mini), ... -* RedBearLab boards: [BLE Nano](http://redbearlab.com/blenano/), [BLE Mini](http://redbearlab.com/blemini/), [WiFi Micro](https://redbear.cc/product/wifi/wifi-micro.html), [LOLIN32](https://wiki.wemos.cc/products:lolin32:lolin32)... -* [Teensy](https://www.pjrc.com/teensy/) boards -* Intel boards: Edison, Galileo... -* Particle boards: [Photon](https://www.particle.io/products/hardware/photon-wifi-dev-kit), [Electron](https://www.particle.io/products/hardware/electron-cellular-dev-kit)... -* Texas Instruments boards: [MSP430](http://www.ti.com/microcontrollers/msp430-ultra-low-power-mcus/overview/overview.html)... - -ArduinoJson compiles with zero warning on the following compilers, IDEs, and platforms: - -* [Arduino IDE](https://www.arduino.cc/en/Main/Software) -* [PlatformIO](http://platformio.org/) -* [Energia](http://energia.nu/) -* [Visual Micro](http://www.visualmicro.com/) -* [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/) -* [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/) -* [Atollic TrueSTUDIO](https://atollic.com/truestudio/) -* [Keil uVision](http://www.keil.com/) -* [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide) -* [GCC](https://gcc.gnu.org/) -* [Clang](https://clang.llvm.org/) -* [Visual Studio](https://www.visualstudio.com/) - -## Quickstart - -### Deserialization - -Here is a program that parses a JSON document with ArduinoJson. - -```c++ -char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - -StaticJsonBuffer<200> jsonBuffer; - -JsonObject& root = jsonBuffer.parseObject(json); - -const char* sensor = root["sensor"]; -long time = root["time"]; -double latitude = root["data"][0]; -double longitude = root["data"][1]; -``` - -See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/?utm_source=github&utm_medium=readme) - -### Serialization - -Here is a program that generates a JSON document with ArduinoJson: - -```c++ -StaticJsonBuffer<200> jsonBuffer; - -JsonObject& root = jsonBuffer.createObject(); -root["sensor"] = "gps"; -root["time"] = 1351824120; - -JsonArray& data = root.createNestedArray("data"); -data.add(48.756080); -data.add(2.302038); - -root.printTo(Serial); -// This prints: -// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} -``` - -See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme) - -## Documentation - -The documentation is available on [arduinojson.org](https://arduinojson.org/?utm_source=github&utm_medium=readme), here are some shortcuts: - -* The [Examples](https://arduinojson.org/example/?utm_source=github&utm_medium=readme) show how to use the library in various situations. -* The [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=readme) contains the description of each class and function. -* The [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=readme) has the answer to virtually every question. -* The [ArduinoJson Assistant](https://arduinojson.org/assistant/?utm_source=github&utm_medium=readme) writes programs for you! - ---- - -Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)! - -What? You don't like it but you *love* it? -We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time! \ No newline at end of file +![ArduinoJson](banner.svg) + +--- + +[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.11.0)](https://www.ardu-badge.com/ArduinoJson/6.11.0) +[![Build Status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x) +[![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=6.x)](https://travis-ci.org/bblanchon/ArduinoJson) +[![Coverage Status](https://coveralls.io/repos/github/bblanchon/ArduinoJson/badge.svg?branch=6.x)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x) +[![Star this project](http://githubbadges.com/star.svg?user=bblanchon&repo=ArduinoJson&style=flat&color=fff&background=007ec6)](https://github.com/bblanchon/ArduinoJson) + +ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). + +## Features + +* JSON decoding (comments are supported) +* JSON encoding (with optional indentation) +* MessagePack +* Elegant API, easy to use +* Fixed memory allocation (zero malloc) +* No data duplication (zero copy) +* Portable (written in C++98, can be used in any C++ project) +* Self-contained (no external dependency) +* Small footprint +* Input and output streams +* [100% code coverage](https://coveralls.io/github/bblanchon/ArduinoJson) +* [Header-only library](https://en.wikipedia.org/wiki/Header-only) +* [MIT License](https://en.wikipedia.org/wiki/MIT_License) +* [Comprehensive documentation](https://arduinojson.org?utm_source=github&utm_medium=readme) + +## Compatibility + +ArduinoJson works on the following hardware: + +* Arduino boards: [Uno](https://www.arduino.cc/en/Main/ArduinoBoardUno), [Due](https://www.arduino.cc/en/Main/ArduinoBoardDue), [Mini](https://www.arduino.cc/en/Main/ArduinoBoardMini), [Micro](https://www.arduino.cc/en/Main/ArduinoBoardMicro), [Yun](https://www.arduino.cc/en/Main/ArduinoBoardYun)... +* Espressif chips: [ESP8266](https://en.wikipedia.org/wiki/ESP8266), [ESP32](https://en.wikipedia.org/wiki/ESP32) +* WeMos boards: [D1](https://wiki.wemos.cc/products:d1:d1), [D1 mini](https://wiki.wemos.cc/products:d1:d1_mini), ... +* RedBearLab boards: [BLE Nano](http://redbearlab.com/blenano/), [BLE Mini](http://redbearlab.com/blemini/), [WiFi Micro](https://redbear.cc/product/wifi/wifi-micro.html), [LOLIN32](https://wiki.wemos.cc/products:lolin32:lolin32)... +* [Teensy](https://www.pjrc.com/teensy/) boards +* Intel boards: Edison, Galileo... +* Particle boards: [Photon](https://www.particle.io/products/hardware/photon-wifi-dev-kit), [Electron](https://www.particle.io/products/hardware/electron-cellular-dev-kit)... +* Texas Instruments boards: [MSP430](http://www.ti.com/microcontrollers/msp430-ultra-low-power-mcus/overview/overview.html)... + +ArduinoJson compiles with zero warning on the following compilers, IDEs, and platforms: + +* [Arduino IDE](https://www.arduino.cc/en/Main/Software) +* [PlatformIO](http://platformio.org/) +* [Energia](http://energia.nu/) +* [Visual Micro](http://www.visualmicro.com/) +* [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/) +* [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/) +* [Atollic TrueSTUDIO](https://atollic.com/truestudio/) +* [Keil uVision](http://www.keil.com/) +* [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide) +* [GCC](https://gcc.gnu.org/) +* [Clang](https://clang.llvm.org/) +* [Visual Studio](https://www.visualstudio.com/) + +## Quickstart + +### Deserialization + +Here is a program that parses a JSON document with ArduinoJson. + +```c++ +char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; + +DynamicJsonDocument doc(1024); +deserializeJson(doc, json); + +const char* sensor = doc["sensor"]; +long time = doc["time"]; +double latitude = doc["data"][0]; +double longitude = doc["data"][1]; +``` + +See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/?utm_source=github&utm_medium=readme) + +### Serialization + +Here is a program that generates a JSON document with ArduinoJson: + +```c++ +DynamicJsonDocument doc(1024); + +doc["sensor"] = "gps"; +doc["time"] = 1351824120; + +JsonArray data = doc.createNestedArray("data"); +data.add(48.756080); +data.add(2.302038); + +serializeJson(doc, Serial); +// This prints: +// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} +``` + +See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme) + +## Documentation + +The documentation is available on [arduinojson.org](https://arduinojson.org/?utm_source=github&utm_medium=readme), here are some shortcuts: + +* The [Examples](https://arduinojson.org/example/?utm_source=github&utm_medium=readme) show how to use the library in various situations. +* The [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=readme) contains the description of each class and function. +* The [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=readme) has the answer to virtually every question. +* The [ArduinoJson Assistant](https://arduinojson.org/assistant/?utm_source=github&utm_medium=readme) writes programs for you! + +--- + +Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)! + +What? You don't like it but you *love* it? +We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time! diff --git a/lib/ArduinoJson/SUPPORT.md b/lib/ArduinoJson-6.x/SUPPORT.md similarity index 100% rename from lib/ArduinoJson/SUPPORT.md rename to lib/ArduinoJson-6.x/SUPPORT.md diff --git a/lib/ArduinoJson/appveyor.yml b/lib/ArduinoJson-6.x/appveyor.yml similarity index 90% rename from lib/ArduinoJson/appveyor.yml rename to lib/ArduinoJson-6.x/appveyor.yml index c864cfdab3..2a6fecba7c 100644 --- a/lib/ArduinoJson/appveyor.yml +++ b/lib/ArduinoJson-6.x/appveyor.yml @@ -1,4 +1,4 @@ -version: 5.13.2.{build} +version: 6.11.0.{build} environment: matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 @@ -17,4 +17,4 @@ before_build: build_script: - cmake --build . --config %CONFIGURATION% test_script: -- ctest --output-on-failure . +- ctest -C %CONFIGURATION% --output-on-failure . diff --git a/lib/ArduinoJson/banner.svg b/lib/ArduinoJson-6.x/banner.svg similarity index 100% rename from lib/ArduinoJson/banner.svg rename to lib/ArduinoJson-6.x/banner.svg diff --git a/lib/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino b/lib/ArduinoJson-6.x/examples/JsonConfigFile/JsonConfigFile.ino similarity index 63% rename from lib/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino rename to lib/ArduinoJson-6.x/examples/JsonConfigFile/JsonConfigFile.ino index ce6dca3e69..675da498b1 100644 --- a/lib/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino +++ b/lib/ArduinoJson-6.x/examples/JsonConfigFile/JsonConfigFile.ino @@ -1,5 +1,5 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License // // This example shows how to store your project configuration in a file. @@ -10,12 +10,19 @@ // "hostname": "examples.com", // "port": 2731 // } +// +// https://arduinojson.org/v6/example/config/ #include #include #include -// Configuration that we'll store on disk +// Our configuration structure. +// +// Never use a JsonDocument to store the configuration! +// A JsonDocument is *not* a permanent storage; it's only a temporary storage +// used during the serialization phase. See: +// https://arduinojson.org/v6/faq/why-must-i-create-a-separate-config-object/ struct Config { char hostname[64]; int port; @@ -29,24 +36,23 @@ void loadConfiguration(const char *filename, Config &config) { // Open file for reading File file = SD.open(filename); - // Allocate the memory pool on the stack. - // Don't forget to change the capacity to match your JSON document. - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<512> jsonBuffer; - - // Parse the root object - JsonObject &root = jsonBuffer.parseObject(file); + // Allocate a temporary JsonDocument + // Don't forget to change the capacity to match your requirements. + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<512> doc; - if (!root.success()) + // Deserialize the JSON document + DeserializationError error = deserializeJson(doc, file); + if (error) Serial.println(F("Failed to read file, using default configuration")); - // Copy values from the JsonObject to the Config - config.port = root["port"] | 2731; - strlcpy(config.hostname, // <- destination - root["hostname"] | "example.com", // <- source - sizeof(config.hostname)); // <- destination's capacity + // Copy values from the JsonDocument to the Config + config.port = doc["port"] | 2731; + strlcpy(config.hostname, // <- destination + doc["hostname"] | "example.com", // <- source + sizeof(config.hostname)); // <- destination's capacity - // Close the file (File's destructor doesn't close the file) + // Close the file (Curiously, File's destructor doesn't close the file) file.close(); } @@ -62,24 +68,21 @@ void saveConfiguration(const char *filename, const Config &config) { return; } - // Allocate the memory pool on the stack - // Don't forget to change the capacity to match your JSON document. - // Use https://arduinojson.org/assistant/ to compute the capacity. - StaticJsonBuffer<256> jsonBuffer; - - // Parse the root object - JsonObject &root = jsonBuffer.createObject(); + // Allocate a temporary JsonDocument + // Don't forget to change the capacity to match your requirements. + // Use arduinojson.org/assistant to compute the capacity. + StaticJsonDocument<256> doc; - // Set the values - root["hostname"] = config.hostname; - root["port"] = config.port; + // Set the values in the document + doc["hostname"] = config.hostname; + doc["port"] = config.port; // Serialize JSON to file - if (root.printTo(file) == 0) { + if (serializeJson(doc, file) == 0) { Serial.println(F("Failed to write to file")); } - // Close the file (File's destructor doesn't close the file) + // Close the file file.close(); } @@ -98,7 +101,7 @@ void printFile(const char *filename) { } Serial.println(); - // Close the file (File's destructor doesn't close the file) + // Close the file file.close(); } @@ -133,12 +136,12 @@ void loop() { // See also // -------- // -// The website arduinojson.org contains the documentation for all the functions +// https://arduinojson.org/ contains the documentation for all the functions // used above. It also includes an FAQ that will help you solve any // serialization or deserialization problem. -// Please check it out at: https://arduinojson.org/ // // The book "Mastering ArduinoJson" contains a case study of a project that has // a complex configuration with nested members. // Contrary to this example, the project in the book uses the SPIFFS filesystem. -// Please check it out at: https://arduinojson.org/book/ \ No newline at end of file +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson-6.x/examples/JsonGeneratorExample/JsonGeneratorExample.ino b/lib/ArduinoJson-6.x/examples/JsonGeneratorExample/JsonGeneratorExample.ino new file mode 100644 index 0000000000..d43de25cbb --- /dev/null +++ b/lib/ArduinoJson-6.x/examples/JsonGeneratorExample/JsonGeneratorExample.ino @@ -0,0 +1,77 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License +// +// This example shows how to generate a JSON document with ArduinoJson. +// +// https://arduinojson.org/v6/example/generator/ + +#include + +void setup() { + // Initialize Serial port + Serial.begin(9600); + while (!Serial) continue; + + // Allocate the JSON document + // + // Inside the brackets, 200 is the RAM allocated to this document. + // Don't forget to change this value to match your requirement. + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<200> doc; + + // StaticJsonObject allocates memory on the stack, it can be + // replaced by DynamicJsonDocument which allocates in the heap. + // + // DynamicJsonDocument doc(200); + + // Add values in the document + // + doc["sensor"] = "gps"; + doc["time"] = 1351824120; + + // Add an array. + // + JsonArray data = doc.createNestedArray("data"); + data.add(48.756080); + data.add(2.302038); + + // Generate the minified JSON and send it to the Serial port. + // + serializeJson(doc, Serial); + // The above line prints: + // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} + + // Start a new line + Serial.println(); + + // Generate the prettified JSON and send it to the Serial port. + // + serializeJsonPretty(doc, Serial); + // The above line prints: + // { + // "sensor": "gps", + // "time": 1351824120, + // "data": [ + // 48.756080, + // 2.302038 + // ] + // } +} + +void loop() { + // not used in this example +} + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// serialization problem. +// +// The book "Mastering ArduinoJson" contains a tutorial on serialization. +// It begins with a simple example, like the one above, and then adds more +// features like serializing directly to a file or an HTTP request. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino b/lib/ArduinoJson-6.x/examples/JsonHttpClient/JsonHttpClient.ino similarity index 69% rename from lib/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino rename to lib/ArduinoJson-6.x/examples/JsonHttpClient/JsonHttpClient.ino index 6e5c05efc0..57301ca41c 100644 --- a/lib/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino +++ b/lib/ArduinoJson-6.x/examples/JsonHttpClient/JsonHttpClient.ino @@ -1,5 +1,5 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License // // This example shows how to parse a JSON document in an HTTP response. @@ -15,6 +15,8 @@ // 2.302038 // ] // } +// +// https://arduinojson.org/v6/example/http-client/ #include #include @@ -57,7 +59,8 @@ void setup() { // Check HTTP status char status[32] = {0}; client.readBytesUntil('\r', status, sizeof(status)); - if (strcmp(status, "HTTP/1.1 200 OK") != 0) { + // It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK" + if (strcmp(status + 9, "200 OK") != 0) { Serial.print(F("Unexpected response: ")); Serial.println(status); return; @@ -70,24 +73,25 @@ void setup() { return; } - // Allocate JsonBuffer - // Use arduinojson.org/assistant to compute the capacity. + // Allocate the JSON document + // Use arduinojson.org/v6/assistant to compute the capacity. const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60; - DynamicJsonBuffer jsonBuffer(capacity); + DynamicJsonDocument doc(capacity); // Parse JSON object - JsonObject& root = jsonBuffer.parseObject(client); - if (!root.success()) { - Serial.println(F("Parsing failed!")); + DeserializationError error = deserializeJson(doc, client); + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.c_str()); return; } // Extract values Serial.println(F("Response:")); - Serial.println(root["sensor"].as()); - Serial.println(root["time"].as()); - Serial.println(root["data"][0].as()); - Serial.println(root["data"][1].as()); + Serial.println(doc["sensor"].as()); + Serial.println(doc["time"].as()); + Serial.println(doc["data"][0].as(), 6); + Serial.println(doc["data"][1].as(), 6); // Disconnect client.stop(); @@ -100,13 +104,13 @@ void loop() { // See also // -------- // -// The website arduinojson.org contains the documentation for all the functions +// https://arduinojson.org/ contains the documentation for all the functions // used above. It also includes an FAQ that will help you solve any // serialization problem. -// Please check it out at: https://arduinojson.org/ // // The book "Mastering ArduinoJson" contains a tutorial on deserialization -// showing how to parse the response from Yahoo Weather. In the last chapter, +// showing how to parse the response from GitHub's API. In the last chapter, // it shows how to parse the huge documents from OpenWeatherMap -// and Weather Underground. -// Please check it out at: https://arduinojson.org/book/ \ No newline at end of file +// and Reddit. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson-6.x/examples/JsonParserExample/JsonParserExample.ino b/lib/ArduinoJson-6.x/examples/JsonParserExample/JsonParserExample.ino new file mode 100644 index 0000000000..11ab1fdf93 --- /dev/null +++ b/lib/ArduinoJson-6.x/examples/JsonParserExample/JsonParserExample.ino @@ -0,0 +1,80 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License +// +// This example shows how to deserialize a JSON document with ArduinoJson. +// +// https://arduinojson.org/v6/example/parser/ + +#include + +void setup() { + // Initialize serial port + Serial.begin(9600); + while (!Serial) continue; + + // Allocate the JSON document + // + // Inside the brackets, 200 is the capacity of the memory pool in bytes. + // Don't forget to change this value to match your JSON document. + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<200> doc; + + // StaticJsonDocument allocates memory on the stack, it can be + // replaced by DynamicJsonDocument which allocates in the heap. + // + // DynamicJsonDocument doc(200); + + // JSON input string. + // + // Using a char[], as shown here, enables the "zero-copy" mode. This mode uses + // the minimal amount of memory because the JsonDocument stores pointers to + // the input buffer. + // If you use another type of input, ArduinoJson must copy the strings from + // the input to the JsonDocument, so you need to increase the capacity of the + // JsonDocument. + char json[] = + "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; + + // Deserialize the JSON document + DeserializationError error = deserializeJson(doc, json); + + // Test if parsing succeeds. + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.c_str()); + return; + } + + // Fetch values. + // + // Most of the time, you can rely on the implicit casts. + // In other case, you can do doc["time"].as(); + const char* sensor = doc["sensor"]; + long time = doc["time"]; + double latitude = doc["data"][0]; + double longitude = doc["data"][1]; + + // Print values. + Serial.println(sensor); + Serial.println(time); + Serial.println(latitude, 6); + Serial.println(longitude, 6); +} + +void loop() { + // not used in this example +} + +// See also +// -------- +// +// https://arduinojson.org/ contains the documentation for all the functions +// used above. It also includes an FAQ that will help you solve any +// deserialization problem. +// +// The book "Mastering ArduinoJson" contains a tutorial on deserialization. +// It begins with a simple example, like the one above, and then adds more +// features like deserializing directly from a file or an HTTP request. +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson/examples/JsonServer/JsonServer.ino b/lib/ArduinoJson-6.x/examples/JsonServer/JsonServer.ino similarity index 64% rename from lib/ArduinoJson/examples/JsonServer/JsonServer.ino rename to lib/ArduinoJson-6.x/examples/JsonServer/JsonServer.ino index 229e289ac4..9d2cc58a4e 100644 --- a/lib/ArduinoJson/examples/JsonServer/JsonServer.ino +++ b/lib/ArduinoJson-6.x/examples/JsonServer/JsonServer.ino @@ -1,17 +1,19 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License // -// This example shows how to implement an HTTP server that sends JSON document -// in the responses. +// This example shows how to implement an HTTP server that sends a JSON document +// in the response. // It uses the Ethernet library but can be easily adapted for Wifi. // -// It sends the value of the analog and digital pins. -// The JSON document looks like the following: +// The JSON document contains the values of the analog and digital pins. +// It looks like that: // { -// "analog": [ 0, 1, 2, 3, 4, 5 ], -// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +// "analog": [0, 76, 123, 158, 192, 205], +// "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0] // } +// +// https://arduinojson.org/v6/example/http-server/ #include #include @@ -51,15 +53,12 @@ void loop() { // Read the request (we ignore the content in this example) while (client.available()) client.read(); - // Allocate JsonBuffer - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<500> jsonBuffer; - - // Create the root object - JsonObject& root = jsonBuffer.createObject(); + // Allocate a temporary JsonDocument + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<500> doc; // Create the "analog" array - JsonArray& analogValues = root.createNestedArray("analog"); + JsonArray analogValues = doc.createNestedArray("analog"); for (int pin = 0; pin < 6; pin++) { // Read the analog input int value = analogRead(pin); @@ -69,7 +68,7 @@ void loop() { } // Create the "digital" array - JsonArray& digitalValues = root.createNestedArray("digital"); + JsonArray digitalValues = doc.createNestedArray("digital"); for (int pin = 0; pin < 14; pin++) { // Read the digital input int value = digitalRead(pin); @@ -79,17 +78,19 @@ void loop() { } Serial.print(F("Sending: ")); - root.printTo(Serial); + serializeJson(doc, Serial); Serial.println(); // Write response headers - client.println("HTTP/1.0 200 OK"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); + client.println(F("HTTP/1.0 200 OK")); + client.println(F("Content-Type: application/json")); + client.println(F("Connection: close")); + client.print(F("Content-Length: ")); + client.println(measureJsonPretty(doc)); client.println(); // Write JSON document - root.prettyPrintTo(client); + serializeJsonPretty(doc, client); // Disconnect client.stop(); @@ -98,12 +99,12 @@ void loop() { // See also // -------- // -// The website arduinojson.org contains the documentation for all the functions +// https://arduinojson.org/ contains the documentation for all the functions // used above. It also includes an FAQ that will help you solve any // serialization problem. -// Please check it out at: https://arduinojson.org/ // // The book "Mastering ArduinoJson" contains a tutorial on serialization. // It begins with a simple example, then adds more features like serializing // directly to a file or an HTTP client. -// Please check it out at: https://arduinojson.org/book/ \ No newline at end of file +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino b/lib/ArduinoJson-6.x/examples/JsonUdpBeacon/JsonUdpBeacon.ino similarity index 72% rename from lib/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino rename to lib/ArduinoJson-6.x/examples/JsonUdpBeacon/JsonUdpBeacon.ino index eb9f19a663..0514fe4b77 100644 --- a/lib/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino +++ b/lib/ArduinoJson-6.x/examples/JsonUdpBeacon/JsonUdpBeacon.ino @@ -1,14 +1,14 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License // // This example shows how to send a JSON document to a UDP socket. // At regular interval, it sends a UDP packet that contains the status of // analog and digital pins. -// The JSON document looks like the following: +// It looks like that: // { -// "analog": [ 0, 1, 2, 3, 4, 5 ], -// "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ] +// "analog": [0, 76, 123, 158, 192, 205], +// "digital": [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0] // } // // If you want to test this program, you need to be able to receive the UDP @@ -16,6 +16,8 @@ // For example, you can run netcat on your computer // $ ncat -ulp 8888 // See https://nmap.org/ncat/ +// +// https://arduinojson.org/v6/example/udp-beacon/ #include #include @@ -43,15 +45,12 @@ void setup() { } void loop() { - // Allocate JsonBuffer - // Use arduinojson.org/assistant to compute the capacity. - StaticJsonBuffer<500> jsonBuffer; - - // Create the root object - JsonObject& root = jsonBuffer.createObject(); + // Allocate a temporary JsonDocument + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<500> doc; // Create the "analog" array - JsonArray& analogValues = root.createNestedArray("analog"); + JsonArray analogValues = doc.createNestedArray("analog"); for (int pin = 0; pin < 6; pin++) { // Read the analog input int value = analogRead(pin); @@ -61,7 +60,7 @@ void loop() { } // Create the "digital" array - JsonArray& digitalValues = root.createNestedArray("digital"); + JsonArray digitalValues = doc.createNestedArray("digital"); for (int pin = 0; pin < 14; pin++) { // Read the digital input int value = digitalRead(pin); @@ -75,11 +74,11 @@ void loop() { Serial.print(remoteIp); Serial.print(F(" on port ")); Serial.println(remotePort); - root.printTo(Serial); + serializeJson(doc, Serial); // Send UDP packet udp.beginPacket(remoteIp, remotePort); - root.printTo(udp); + serializeJson(doc, udp); udp.println(); udp.endPacket(); @@ -90,12 +89,12 @@ void loop() { // See also // -------- // -// The website arduinojson.org contains the documentation for all the functions +// https://arduinojson.org/ contains the documentation for all the functions // used above. It also includes an FAQ that will help you solve any // serialization problem. -// Please check it out at: https://arduinojson.org/ // // The book "Mastering ArduinoJson" contains a tutorial on serialization. // It begins with a simple example, then adds more features like serializing // directly to a file or any stream. -// Please check it out at: https://arduinojson.org/book/ \ No newline at end of file +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson-6.x/examples/MsgPackParser/MsgPackParser.ino b/lib/ArduinoJson-6.x/examples/MsgPackParser/MsgPackParser.ino new file mode 100644 index 0000000000..d149e39770 --- /dev/null +++ b/lib/ArduinoJson-6.x/examples/MsgPackParser/MsgPackParser.ino @@ -0,0 +1,75 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License +// +// This example shows how to deserialize a MessagePack document with +// ArduinoJson. +// +// https://arduinojson.org/v6/example/msgpack-parser/ + +#include + +void setup() { + // Initialize serial port + Serial.begin(9600); + while (!Serial) continue; + + // Allocate the JSON document + // + // Inside the brackets, 200 is the capacity of the memory pool in bytes. + // Don't forget to change this value to match your JSON document. + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<200> doc; + + // StaticJsonObject allocates memory on the stack, it can be + // replaced by DynamicJsonObject which allocates in the heap. + // + // DynamicJsonObject doc(200); + + // MessagePack input string. + // + // Using a char[], as shown here, enables the "zero-copy" mode. This mode uses + // the minimal amount of memory because the JsonDocument stores pointers to + // the input buffer. + // If you use another type of input, ArduinoJson must copy the strings from + // the input to the JsonDocument, so you need to increase the capacity of the + // JsonDocument. + uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115, + 164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100, + 97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148, + 112, 203, 64, 2, 106, 146, 230, 33, 49, 169}; + // This MessagePack document contains: + // { + // "sensor": "gps", + // "time": 1351824120, + // "data": [48.75608, 2.302038] + // } + + DeserializationError error = deserializeMsgPack(doc, input); + + // Test if parsing succeeded. + if (error) { + Serial.print("deserializeMsgPack() failed: "); + Serial.println(error.c_str()); + return; + } + + // Fetch values. + // + // Most of the time, you can rely on the implicit casts. + // In other case, you can do doc["time"].as(); + const char* sensor = doc["sensor"]; + long time = doc["time"]; + double latitude = doc["data"][0]; + double longitude = doc["data"][1]; + + // Print values. + Serial.println(sensor); + Serial.println(time); + Serial.println(latitude, 6); + Serial.println(longitude, 6); +} + +void loop() { + // not used in this example +} diff --git a/lib/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino b/lib/ArduinoJson-6.x/examples/ProgmemExample/ProgmemExample.ino similarity index 58% rename from lib/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino rename to lib/ArduinoJson-6.x/examples/ProgmemExample/ProgmemExample.ino index 15be8ed1cf..310a820cc6 100644 --- a/lib/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino +++ b/lib/ArduinoJson-6.x/examples/ProgmemExample/ProgmemExample.ino @@ -1,47 +1,49 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License // // This example shows the different ways you can use Flash strings with // ArduinoJson. // // Use Flash strings sparingly, because ArduinoJson duplicates them in the -// JsonBuffer. Prefer plain old char*, as they are more efficient in term of +// JsonDocument. Prefer plain old char*, as they are more efficient in term of // code size, speed, and memory usage. +// +// https://arduinojson.org/v6/example/progmem/ #include void setup() { #ifdef PROGMEM // <- check that Flash strings are supported - DynamicJsonBuffer jsonBuffer; + DynamicJsonDocument doc(1024); // You can use a Flash String as your JSON input. - // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. - JsonObject& root = - jsonBuffer.parseObject(F("{\"sensor\":\"gps\",\"time\":1351824120," - "\"data\":[48.756080,2.302038]}")); + // WARNING: the strings in the input will be duplicated in the JsonDocument. + deserializeJson(doc, F("{\"sensor\":\"gps\",\"time\":1351824120," + "\"data\":[48.756080,2.302038]}")); + JsonObject obj = doc.as(); // You can use a Flash String to get an element of a JsonObject // No duplication is done. - long time = root[F("time")]; + long time = obj[F("time")]; // You can use a Flash String to set an element of a JsonObject // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. - root[F("time")] = time; + // JsonDocument. + obj[F("time")] = time; // You can set a Flash String to a JsonObject or JsonArray: // WARNING: the content of the Flash String will be duplicated in the - // JsonBuffer. - root["sensor"] = F("gps"); + // JsonDocument. + obj["sensor"] = F("gps"); - // It works with RawJson too: - root["sensor"] = RawJson(F("\"gps\"")); + // It works with serialized() too: + obj["sensor"] = serialized(F("\"gps\"")); + obj["sensor"] = serialized(F("\xA3gps"), 3); // You can compare the content of a JsonVariant to a Flash String - if (root["sensor"] == F("gps")) { + if (obj["sensor"] == F("gps")) { // ... } @@ -59,12 +61,12 @@ void loop() { // See also // -------- // -// The website arduinojson.org contains the documentation for all the functions +// https://arduinojson.org/ contains the documentation for all the functions // used above. It also includes an FAQ that will help you solve any memory // problem. -// Please check it out at: https://arduinojson.org/ // // The book "Mastering ArduinoJson" contains a quick C++ course that explains // how your microcontroller stores strings in memory. It also tells why you // should not abuse Flash strings with ArduinoJson. -// Please check it out at: https://arduinojson.org/book/ \ No newline at end of file +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson/examples/StringExample/StringExample.ino b/lib/ArduinoJson-6.x/examples/StringExample/StringExample.ino similarity index 59% rename from lib/ArduinoJson/examples/StringExample/StringExample.ino rename to lib/ArduinoJson-6.x/examples/StringExample/StringExample.ino index d5994ffb2b..040f8e4633 100644 --- a/lib/ArduinoJson/examples/StringExample/StringExample.ino +++ b/lib/ArduinoJson-6.x/examples/StringExample/StringExample.ino @@ -1,60 +1,63 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License // // This example shows the different ways you can use String with ArduinoJson. // // Use String objects sparingly, because ArduinoJson duplicates them in the -// JsonBuffer. Prefer plain old char[], as they are more efficient in term of +// JsonDocument. Prefer plain old char[], as they are more efficient in term of // code size, speed, and memory usage. +// +// https://arduinojson.org/v6/example/string/ #include void setup() { - DynamicJsonBuffer jsonBuffer; + DynamicJsonDocument doc(1024); // You can use a String as your JSON input. - // WARNING: the content of the String will be duplicated in the JsonBuffer. + // WARNING: the string in the input will be duplicated in the JsonDocument. String input = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; - JsonObject& root = jsonBuffer.parseObject(input); + deserializeJson(doc, input); + JsonObject obj = doc.as(); // You can use a String to get an element of a JsonObject // No duplication is done. - long time = root[String("time")]; + long time = obj[String("time")]; // You can use a String to set an element of a JsonObject - // WARNING: the content of the String will be duplicated in the JsonBuffer. - root[String("time")] = time; + // WARNING: the content of the String will be duplicated in the JsonDocument. + obj[String("time")] = time; // You can get a String from a JsonObject or JsonArray: - // No duplication is done, at least not in the JsonBuffer. - String sensor = root["sensor"]; + // No duplication is done, at least not in the JsonDocument. + String sensor = obj["sensor"]; // Unfortunately, the following doesn't work (issue #118): - // sensor = root["sensor"]; // <- error "ambiguous overload for 'operator='" + // sensor = obj["sensor"]; // <- error "ambiguous overload for 'operator='" // As a workaround, you need to replace by: - sensor = root["sensor"].as(); + sensor = obj["sensor"].as(); // You can set a String to a JsonObject or JsonArray: - // WARNING: the content of the String will be duplicated in the JsonBuffer. - root["sensor"] = sensor; + // WARNING: the content of the String will be duplicated in the JsonDocument. + obj["sensor"] = sensor; - // It works with RawJson too: - root["sensor"] = RawJson(sensor); + // It works with serialized() too: + obj["sensor"] = serialized(sensor); // You can also concatenate strings - // WARNING: the content of the String will be duplicated in the JsonBuffer. - root[String("sen") + "sor"] = String("gp") + "s"; + // WARNING: the content of the String will be duplicated in the JsonDocument. + obj[String("sen") + "sor"] = String("gp") + "s"; // You can compare the content of a JsonObject with a String - if (root["sensor"] == sensor) { + if (obj["sensor"] == sensor) { // ... } // Lastly, you can print the resulting JSON to a String String output; - root.printTo(output); + serializeJson(doc, output); } void loop() { @@ -64,11 +67,11 @@ void loop() { // See also // -------- // -// The website arduinojson.org contains the documentation for all the functions +// https://arduinojson.org/ contains the documentation for all the functions // used above. It also includes an FAQ that will help you solve any problem. -// Please check it out at: https://arduinojson.org/ // // The book "Mastering ArduinoJson" contains a quick C++ course that explains // how your microcontroller stores strings in memory. On several occasions, it // shows how you can avoid String in your program. -// Please check it out at: https://arduinojson.org/book/ \ No newline at end of file +// Learn more at https://arduinojson.org/book/ +// Use the coupon code TWENTY for a 20% discount â¤â¤â¤â¤â¤ diff --git a/lib/ArduinoJson-6.x/fuzzing/CMakeLists.txt b/lib/ArduinoJson-6.x/fuzzing/CMakeLists.txt new file mode 100644 index 0000000000..5d5a268a51 --- /dev/null +++ b/lib/ArduinoJson-6.x/fuzzing/CMakeLists.txt @@ -0,0 +1,17 @@ +# ArduinoJson - arduinojson.org +# Copyright Benoit Blanchon 2014-2019 +# MIT License + +if(MSVC) + add_compile_options(-D_CRT_SECURE_NO_WARNINGS) +endif() + +add_executable(msgpack_fuzzer + msgpack_fuzzer.cpp + fuzzer_main.cpp +) + +add_executable(json_fuzzer + json_fuzzer.cpp + fuzzer_main.cpp +) diff --git a/lib/ArduinoJson-6.x/fuzzing/Makefile b/lib/ArduinoJson-6.x/fuzzing/Makefile new file mode 100644 index 0000000000..ce4ede3226 --- /dev/null +++ b/lib/ArduinoJson-6.x/fuzzing/Makefile @@ -0,0 +1,22 @@ +# CAUTION: this file is invoked by https://github.com/google/oss-fuzz + +CXXFLAGS += -I../src -DARDUINOJSON_DEBUG + +all: \ + $(OUT)/json_fuzzer \ + $(OUT)/json_fuzzer_seed_corpus.zip \ + $(OUT)/json_fuzzer.options \ + $(OUT)/msgpack_fuzzer \ + $(OUT)/msgpack_fuzzer_seed_corpus.zip \ + $(OUT)/msgpack_fuzzer.options + +$(OUT)/%_fuzzer: %_fuzzer.cpp $(shell find ../src -type f) + $(CXX) $(CXXFLAGS) $< -o$@ $(LIB_FUZZING_ENGINE) + +$(OUT)/%_fuzzer_seed_corpus.zip: %_seed_corpus/* + zip -j $@ $? + +$(OUT)/%_fuzzer.options: + @echo "[libfuzzer]" > $@ + @echo "max_len = 256" >> $@ + @echo "timeout = 10" >> $@ diff --git a/lib/ArduinoJson-6.x/fuzzing/fuzzer_main.cpp b/lib/ArduinoJson-6.x/fuzzing/fuzzer_main.cpp new file mode 100644 index 0000000000..5dc7916a3c --- /dev/null +++ b/lib/ArduinoJson-6.x/fuzzing/fuzzer_main.cpp @@ -0,0 +1,50 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +// This file is NOT use by Google's OSS fuzz +// I only use it to reproduce the bugs found + +#include // size_t +#include // fopen et al. +#include // exit +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); + +std::vector read(const char* path) { + FILE* f = fopen(path, "rb"); + if (!f) { + std::cerr << "Failed to open " << path << std::endl; + exit(1); + } + + fseek(f, 0, SEEK_END); + size_t size = ftell(f); + fseek(f, 0, SEEK_SET); + + std::vector buffer(size); + if (fread(buffer.data(), 1, size, f) != size) { + fclose(f); + std::cerr << "Failed to read " << path << std::endl; + exit(1); + } + + fclose(f); + return buffer; +} + +int main(int argc, const char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: msgpack_fuzzer files" << std::endl; + return 1; + } + + for (int i = 1; i < argc; i++) { + std::cout << "Loading " << argv[i] << std::endl; + std::vector buffer = read(argv[i]); + LLVMFuzzerTestOneInput(buffer.data(), buffer.size()); + } + return 0; +} diff --git a/lib/ArduinoJson/fuzzing/my_corpus/.gitignore b/lib/ArduinoJson-6.x/fuzzing/json_corpus/.gitignore similarity index 100% rename from lib/ArduinoJson/fuzzing/my_corpus/.gitignore rename to lib/ArduinoJson-6.x/fuzzing/json_corpus/.gitignore diff --git a/lib/ArduinoJson-6.x/fuzzing/json_fuzzer.cpp b/lib/ArduinoJson-6.x/fuzzing/json_fuzzer.cpp new file mode 100644 index 0000000000..db9a3d6c15 --- /dev/null +++ b/lib/ArduinoJson-6.x/fuzzing/json_fuzzer.cpp @@ -0,0 +1,11 @@ +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + DynamicJsonDocument doc(4096); + DeserializationError error = deserializeJson(doc, data, size); + if (!error) { + std::string json; + serializeJson(doc, json); + } + return 0; +} diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/Comments.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/Comments.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/Comments.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/Comments.json diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/EmptyArray.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/EmptyArray.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/EmptyArray.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/EmptyArray.json diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/EmptyObject.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/EmptyObject.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/EmptyObject.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/EmptyObject.json diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/ExcessiveNesting.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/ExcessiveNesting.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/ExcessiveNesting.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/ExcessiveNesting.json diff --git a/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/IntegerOverflow.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/IntegerOverflow.json new file mode 100644 index 0000000000..3a33cb89d3 --- /dev/null +++ b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/IntegerOverflow.json @@ -0,0 +1 @@ +9720730739393920739 diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/Numbers.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/Numbers.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/Numbers.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/Numbers.json diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/OpenWeatherMap.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/OpenWeatherMap.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/OpenWeatherMap.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/OpenWeatherMap.json diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/Strings.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/Strings.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/Strings.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/Strings.json diff --git a/lib/ArduinoJson/fuzzing/seed_corpus/WeatherUnderground.json b/lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/WeatherUnderground.json similarity index 100% rename from lib/ArduinoJson/fuzzing/seed_corpus/WeatherUnderground.json rename to lib/ArduinoJson-6.x/fuzzing/json_seed_corpus/WeatherUnderground.json diff --git a/lib/ArduinoJson-6.x/fuzzing/msgpack_corpus/.gitignore b/lib/ArduinoJson-6.x/fuzzing/msgpack_corpus/.gitignore new file mode 100644 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/lib/ArduinoJson-6.x/fuzzing/msgpack_corpus/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/lib/ArduinoJson-6.x/fuzzing/msgpack_fuzzer.cpp b/lib/ArduinoJson-6.x/fuzzing/msgpack_fuzzer.cpp new file mode 100644 index 0000000000..e62359114a --- /dev/null +++ b/lib/ArduinoJson-6.x/fuzzing/msgpack_fuzzer.cpp @@ -0,0 +1,11 @@ +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + DynamicJsonDocument doc(4096); + DeserializationError error = deserializeMsgPack(doc, data, size); + if (!error) { + std::string json; + serializeMsgPack(doc, json); + } + return 0; +} diff --git a/lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/array16 b/lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/array16 new file mode 100644 index 0000000000000000000000000000000000000000..714ba99e70cbed2056b4e4b04c86bb1a2ff7311b GIT binary patch literal 15 Wcmcb^z_c_YH76&3X?cE8P6_}q=LTf} literal 0 HcmV?d00001 diff --git a/lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/array32 b/lib/ArduinoJson-6.x/fuzzing/msgpack_seed_corpus/array32 new file mode 100644 index 0000000000000000000000000000000000000000..6e3ed7b1b81742fbb90a4135004b55a9a45a5769 GIT binary patch literal 15 Vcmcc1z`($C3P>=Va`5 maintainer=Benoit Blanchon sentence=An efficient and elegant JSON library for Arduino. -paragraph=ArduinoJson supports ✔ serialization, ✔ deserialization, ✔ fixed allocation, ✔ zero-copy, ✔ streams, and more. It is the most popular Arduino library on GitHub â¤â¤â¤â¤â¤. Check out arduinojson.org for a comprehensive documentation. +paragraph=ArduinoJson supports ✔ serialization, ✔ deserialization, ✔ MessagePack, ✔ fixed allocation, ✔ zero-copy, ✔ streams, and more. It is the most popular Arduino library on GitHub â¤â¤â¤â¤â¤. Check out arduinojson.org for a comprehensive documentation. category=Data Processing url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties architectures=* diff --git a/lib/ArduinoJson/scripts/build-arduino-package.sh b/lib/ArduinoJson-6.x/scripts/build-arduino-package.sh old mode 100755 new mode 100644 similarity index 100% rename from lib/ArduinoJson/scripts/build-arduino-package.sh rename to lib/ArduinoJson-6.x/scripts/build-arduino-package.sh diff --git a/lib/ArduinoJson/scripts/build-single-header.sh b/lib/ArduinoJson-6.x/scripts/build-single-header.sh similarity index 100% rename from lib/ArduinoJson/scripts/build-single-header.sh rename to lib/ArduinoJson-6.x/scripts/build-single-header.sh diff --git a/lib/ArduinoJson/scripts/create-build-envs.sh b/lib/ArduinoJson-6.x/scripts/create-build-envs.sh old mode 100755 new mode 100644 similarity index 100% rename from lib/ArduinoJson/scripts/create-build-envs.sh rename to lib/ArduinoJson-6.x/scripts/create-build-envs.sh diff --git a/lib/ArduinoJson/scripts/oss-fuzz/.gitignore b/lib/ArduinoJson-6.x/scripts/oss-fuzz/.gitignore similarity index 100% rename from lib/ArduinoJson/scripts/oss-fuzz/.gitignore rename to lib/ArduinoJson-6.x/scripts/oss-fuzz/.gitignore diff --git a/lib/ArduinoJson/scripts/oss-fuzz/Vagrantfile b/lib/ArduinoJson-6.x/scripts/oss-fuzz/Vagrantfile similarity index 79% rename from lib/ArduinoJson/scripts/oss-fuzz/Vagrantfile rename to lib/ArduinoJson-6.x/scripts/oss-fuzz/Vagrantfile index 252c191c1b..85d0733fbe 100644 --- a/lib/ArduinoJson/scripts/oss-fuzz/Vagrantfile +++ b/lib/ArduinoJson-6.x/scripts/oss-fuzz/Vagrantfile @@ -2,11 +2,16 @@ Vagrant.configure(2) do |config| config.vm.box = "ubuntu/xenial64" - config.vm.synced_folder "E:\\Git\\Arduino\\libraries\\ArduinoJson", "/host/ArduinoJson" + config.vm.synced_folder "../..", "/host/ArduinoJson" config.vm.synced_folder "E:\\Git\\oss-fuzz", "/host/oss-fuzz" config.vm.network "forwarded_port", guest: 8001, host: 8001 + config.vm.provider "virtualbox" do |v| + v.memory = 2048 + v.cpus = 2 + end + config.vm.provision "shell", privileged: false, inline: <<-SHELL set -x @@ -18,10 +23,6 @@ Vagrant.configure(2) do |config| git clone https://github.com/google/fuzzer-test-suite.git FTS ./FTS/tutorial/install-deps.sh # Get deps ./FTS/tutorial/install-clang.sh # Get fresh clang binaries - # Get libFuzzer sources and build it - svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer - Fuzzer/build.sh - sudo mv libFuzzer.a /usr/local/lib/ echo "export PROJECT_NAME='arduinojson'" >> $HOME/.profile echo "export CC='clang'" >> $HOME/.profile diff --git a/lib/ArduinoJson-6.x/scripts/publish-particle-library.sh b/lib/ArduinoJson-6.x/scripts/publish-particle-library.sh new file mode 100644 index 0000000000..e37e7965e9 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/publish-particle-library.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -eu + +SOURCE_DIR="$(dirname "$0")/.." +WORK_DIR=$(mktemp -d) +trap 'rm -rf "$WORK_DIR"' EXIT + +cp "$SOURCE_DIR/README.md" "$WORK_DIR/README.md" +cp "$SOURCE_DIR/CHANGELOG.md" "$WORK_DIR/CHANGELOG.md" +cp "$SOURCE_DIR/library.properties" "$WORK_DIR/library.properties" +cp "$SOURCE_DIR/LICENSE.md" "$WORK_DIR/LICENSE.txt" +cp -r "$SOURCE_DIR/src" "$WORK_DIR/" +cp -r "$SOURCE_DIR/examples" "$WORK_DIR/" + +cd "$WORK_DIR" +particle library upload +particle library publish diff --git a/lib/ArduinoJson-6.x/scripts/publish.sh b/lib/ArduinoJson-6.x/scripts/publish.sh new file mode 100644 index 0000000000..2d2f06930a --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/publish.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -eu + +cd "$(dirname "$0")/.." + +VERSION="$1" +DATE=$(date +%F) +TAG="v$VERSION" +VERSION_REGEX="[0-9a-z\\.\\-]+" + +update_version_in_source () { + IFS=".-" read MAJOR MINOR REVISION EXTRA < <(echo "$VERSION") + UNDERLINE=$(printf -- '-%.0s' $(seq 1 ${#TAG})) + + sed -i~ -bE "s/version=$VERSION_REGEX/version=$VERSION/; s|ardu-badge.com/ArduinoJson/$VERSION_REGEX|ardu-badge.com/ArduinoJson/$VERSION|; " README.md + rm README.md*~ + + sed -i~ -bE "4s/HEAD/$TAG ($DATE)/; 5s/-+/$UNDERLINE/" CHANGELOG.md + rm CHANGELOG.md*~ + + sed -i~ -bE "s/\"version\":.*$/\"version\": \"$VERSION\",/" library.json + rm library.json*~ + + sed -i~ -bE "s/version=.*$/version=$VERSION/" library.properties + rm library.properties*~ + + sed -i~ -bE "s/version: .*$/version: $VERSION.{build}/" appveyor.yml + rm appveyor.yml*~ + + sed -i~ -bE \ + -e "s/ARDUINOJSON_VERSION .*$/ARDUINOJSON_VERSION \"$VERSION\"/" \ + -e "s/ARDUINOJSON_VERSION_MAJOR .*$/ARDUINOJSON_VERSION_MAJOR $MAJOR/" \ + -e "s/ARDUINOJSON_VERSION_MINOR .*$/ARDUINOJSON_VERSION_MINOR $MINOR/" \ + -e "s/ARDUINOJSON_VERSION_REVISION .*$/ARDUINOJSON_VERSION_REVISION $REVISION/" \ + src/ArduinoJson/version.hpp + rm src/ArduinoJson/version.hpp*~ +} + +commit_new_version () { + git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml + git commit -m "Set version to $VERSION" +} + +add_tag () { + CHANGES=$(awk '/\* /{ FOUND=1; print; next } { if (FOUND) exit}' CHANGELOG.md) + git tag -m "ArduinoJson $VERSION"$'\n'"$CHANGES" "$TAG" +} + +push () { + git push --follow-tags +} + +update_version_in_source +commit_new_version +add_tag +push + +scripts/build-arduino-package.sh +scripts/build-single-header.sh +scripts/wandbox/publish.sh "../ArduinoJson-$TAG.h" diff --git a/lib/ArduinoJson/scripts/travis/arduino.sh b/lib/ArduinoJson-6.x/scripts/travis/arduino.sh old mode 100755 new mode 100644 similarity index 79% rename from lib/ArduinoJson/scripts/travis/arduino.sh rename to lib/ArduinoJson-6.x/scripts/travis/arduino.sh index 4de24c154a..f4c168a5d2 --- a/lib/ArduinoJson/scripts/travis/arduino.sh +++ b/lib/ArduinoJson-6.x/scripts/travis/arduino.sh @@ -1,4 +1,4 @@ -#!/bin/sh -eux +#!/bin/bash -eux /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 sleep 3 @@ -6,9 +6,13 @@ export DISPLAY=:1.0 mkdir -p /tmp/arduino curl -sS http://downloads.arduino.cc/arduino-$VERSION-linux64.tar.xz | tar xJ -C /tmp/arduino --strip 1 || -curl -sS http://downloads.arduino.cc/arduino-$VERSION-linux64.tgz | tar xz -C /tmp/arduino --strip 1 +curl -sS http://downloads.arduino.cc/arduino-$VERSION-linux64.tgz | tar xz -C /tmp/arduino --strip 1 export PATH=$PATH:/tmp/arduino/ - + +if [[ "$BOARD" =~ "arduino:samd:" ]]; then + arduino --install-boards arduino:samd +fi + ln -s $PWD /tmp/arduino/libraries/ArduinoJson for EXAMPLE in $PWD/examples/*/*.ino; do diff --git a/lib/ArduinoJson-6.x/scripts/travis/build.sh b/lib/ArduinoJson-6.x/scripts/travis/build.sh new file mode 100644 index 0000000000..34547bee69 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/travis/build.sh @@ -0,0 +1,14 @@ +#!/bin/sh -ex + +export CC="$_CC" +export CXX="$_CXX" + +if [ -n "$SANITIZE" ]; then + export CXXFLAGS="-fsanitize=$SANITIZE" + BUILD_TYPE="Debug" +else + BUILD_TYPE="Release" +fi + +cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE . +cmake --build . diff --git a/lib/ArduinoJson/scripts/travis/coverage.sh b/lib/ArduinoJson-6.x/scripts/travis/coverage.sh old mode 100755 new mode 100644 similarity index 53% rename from lib/ArduinoJson/scripts/travis/coverage.sh rename to lib/ArduinoJson-6.x/scripts/travis/coverage.sh index 20de1e6a67..f90c0fb73e --- a/lib/ArduinoJson/scripts/travis/coverage.sh +++ b/lib/ArduinoJson-6.x/scripts/travis/coverage.sh @@ -1,8 +1,6 @@ #!/bin/sh -eux -curl https://cmake.org/files/v3.4/cmake-3.4.0-Linux-x86_64.tar.gz | tar xz -C /tmp --strip 1 - -/tmp/bin/cmake -DCOVERAGE=true . +cmake -DCOVERAGE=true . make make test diff --git a/lib/ArduinoJson-6.x/scripts/travis/fuzz.sh b/lib/ArduinoJson-6.x/scripts/travis/fuzz.sh new file mode 100644 index 0000000000..1d4e32d916 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/travis/fuzz.sh @@ -0,0 +1,26 @@ +#!/bin/bash -eux + +ROOT_DIR=$(dirname $0)/../../ +INCLUDE_DIR=${ROOT_DIR}/src/ +FUZZING_DIR=${ROOT_DIR}/fuzzing/ +CXXFLAGS="-g -fprofile-instr-generate -fcoverage-mapping -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all" + +fuzz() { + NAME="$1" + FUZZER="${NAME}_fuzzer" + FUZZER_CPP="${FUZZING_DIR}/${NAME}_fuzzer.cpp" + CORPUS_DIR="${FUZZING_DIR}/${NAME}_corpus" + SEED_CORPUS_DIR="${FUZZING_DIR}/${NAME}_seed_corpus" + + clang++-${CLANG} ${CXXFLAGS} -o ${FUZZER} -I$INCLUDE_DIR ${FUZZER_CPP} + + export ASAN_OPTIONS="detect_leaks=0" + export LLVM_PROFILE_FILE="${FUZZER}.profraw" + ./${FUZZER} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=30 -timeout=1 + + llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER}.profdata + llvm-cov-${CLANG} report ./${FUZZER} -instr-profile=${FUZZER}.profdata +} + +fuzz json +fuzz msgpack diff --git a/lib/ArduinoJson-6.x/scripts/travis/platformio.sh b/lib/ArduinoJson-6.x/scripts/travis/platformio.sh new file mode 100644 index 0000000000..5182625293 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/travis/platformio.sh @@ -0,0 +1,22 @@ +#!/bin/sh -eux + +pip install --user platformio + +rm -r test + +case $BOARD in +uno) + platformio lib install 868 # SD library + platformio lib install 872 # Ethernet library + ;; +esp01) + platformio lib uninstall 161 || true + platformio lib uninstall 868 || true + platformio lib uninstall 872 || true + ;; +esac + +for EXAMPLE in $PWD/examples/*/*.ino; +do + platformio ci $EXAMPLE -l '.' -b $BOARD +done diff --git a/lib/ArduinoJson-6.x/scripts/travis/test.sh b/lib/ArduinoJson-6.x/scripts/travis/test.sh new file mode 100644 index 0000000000..031f3eed78 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/travis/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh -ex + +"$(dirname "$0")/build.sh" +ctest --output-on-failure . diff --git a/lib/ArduinoJson-6.x/scripts/wandbox/JsonGeneratorExample.cpp b/lib/ArduinoJson-6.x/scripts/wandbox/JsonGeneratorExample.cpp new file mode 100644 index 0000000000..9220eb83f0 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/wandbox/JsonGeneratorExample.cpp @@ -0,0 +1,60 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License +// +// This example shows how to generate a JSON document with ArduinoJson. + +#include +#include "ArduinoJson.h" + +int main() { + // Allocate the JSON document + // + // Inside the brackets, 200 is the RAM allocated to this document. + // Don't forget to change this value to match your requirement. + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<200> doc; + + // StaticJsonObject allocates memory on the stack, it can be + // replaced by DynamicJsonDocument which allocates in the heap. + // + // DynamicJsonDocument doc(200); + + // StaticJsonObject allocates memory on the stack, it can be + // replaced by DynamicJsonDocument which allocates in the heap. + // + // DynamicJsonDocument doc(200); + + // Add values in the document + // + doc["sensor"] = "gps"; + doc["time"] = 1351824120; + + // Add an array. + // + JsonArray data = doc.createNestedArray("data"); + data.add(48.756080); + data.add(2.302038); + + // Generate the minified JSON and send it to STDOUT + // + serializeJson(doc, std::cout); + // The above line prints: + // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]} + + // Start a new line + std::cout << std::endl; + + // Generate the prettified JSON and send it to STDOUT + // + serializeJsonPretty(doc, std::cout); + // The above line prints: + // { + // "sensor": "gps", + // "time": 1351824120, + // "data": [ + // 48.756080, + // 2.302038 + // ] + // } +} diff --git a/lib/ArduinoJson-6.x/scripts/wandbox/JsonParserExample.cpp b/lib/ArduinoJson-6.x/scripts/wandbox/JsonParserExample.cpp new file mode 100644 index 0000000000..f517717608 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/wandbox/JsonParserExample.cpp @@ -0,0 +1,59 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License +// +// This example shows how to deserialize a JSON document with ArduinoJson. + +#include +#include "ArduinoJson.h" + +int main() { + // Allocate the JSON document + // + // Inside the brackets, 200 is the capacity of the memory pool in bytes. + // Don't forget to change this value to match your JSON document. + // Use arduinojson.org/v6/assistant to compute the capacity. + StaticJsonDocument<300> doc; + + // StaticJsonDocument allocates memory on the stack, it can be + // replaced by DynamicJsonDocument which allocates in the heap. + // + // DynamicJsonDocument doc(200); + + // JSON input string. + // + // Using a char[], as shown here, enables the "zero-copy" mode. This mode uses + // the minimal amount of memory because the JsonDocument stores pointers to + // the input buffer. + // If you use another type of input, ArduinoJson must copy the strings from + // the input to the JsonDocument, so you need to increase the capacity of the + // JsonDocument. + char json[] = + "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}"; + + // Deserialize the JSON document + DeserializationError error = deserializeJson(doc, json); + + // Test if parsing succeeds. + if (error) { + std::cerr << "deserializeJson() failed: " << error.c_str() << std::endl; + return 1; + } + + // Fetch values. + // + // Most of the time, you can rely on the implicit casts. + // In other case, you can do doc["time"].as(); + const char* sensor = doc["sensor"]; + long time = doc["time"]; + double latitude = doc["data"][0]; + double longitude = doc["data"][1]; + + // Print values. + std::cout << sensor << std::endl; + std::cout << time << std::endl; + std::cout << latitude << std::endl; + std::cout << longitude << std::endl; + + return 0; +} diff --git a/lib/ArduinoJson-6.x/scripts/wandbox/MsgPackParserExample.cpp b/lib/ArduinoJson-6.x/scripts/wandbox/MsgPackParserExample.cpp new file mode 100644 index 0000000000..778011db2c --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/wandbox/MsgPackParserExample.cpp @@ -0,0 +1,68 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License +// +// This example shows how to generate a JSON document with ArduinoJson. + +#include +#include "ArduinoJson.h" + +int main() { + // Allocate the JSON document + // + // Inside the brackets, 300 is the size of the memory pool in bytes. + // Don't forget to change this value to match your JSON document. + // Use arduinojson.org/assistant to compute the capacity. + StaticJsonDocument<300> doc; + + // StaticJsonObject allocates memory on the stack, it can be + // replaced by DynamicJsonObject which allocates in the heap. + // + // DynamicJsonObject doc(200); + + // MessagePack input string. + // + // It's better to use a char[] as shown here. + // If you use a const char* or a String, ArduinoJson will + // have to make a copy of the input in the JsonBuffer. + uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115, + 164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100, + 97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148, + 112, 203, 64, 2, 106, 146, 230, 33, 49, 169}; + // This MessagePack document contains: + // { + // "sensor": "gps", + // "time": 1351824120, + // "data": [48.75608, 2.302038] + // } + + // doc of the object tree. + // + // It's a reference to the JsonObject, the actual bytes are inside the + // JsonBuffer with all the other nodes of the object tree. + // Memory is freed when jsonBuffer goes out of scope. + DeserializationError error = deserializeMsgPack(doc, input); + + // Test if parsing succeeds. + if (error) { + std::cerr << "deserializeMsgPack() failed: " << error.c_str() << std::endl; + return 1; + } + + // Fetch values. + // + // Most of the time, you can rely on the implicit casts. + // In other case, you can do doc["time"].as(); + const char* sensor = doc["sensor"]; + long time = doc["time"]; + double latitude = doc["data"][0]; + double longitude = doc["data"][1]; + + // Print values. + std::cout << sensor << std::endl; + std::cout << time << std::endl; + std::cout << latitude << std::endl; + std::cout << longitude << std::endl; + + return 0; +} diff --git a/lib/ArduinoJson-6.x/scripts/wandbox/publish.sh b/lib/ArduinoJson-6.x/scripts/wandbox/publish.sh new file mode 100644 index 0000000000..8706790302 --- /dev/null +++ b/lib/ArduinoJson-6.x/scripts/wandbox/publish.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -eu + +ARDUINOJSON_H="$1" + +read_string() { + jq --slurp --raw-input '.' "$1" +} + +compile() { + FILE_PATH="$(dirname $0)/$1.cpp" + cat >parameters.json <add(pool) : 0; +} + +template +inline void arrayAccept(const CollectionData *arr, Visitor &visitor) { + if (arr) + visitor.visitArray(*arr); + else + visitor.visitNull(); +} + +inline bool arrayEquals(const CollectionData *lhs, const CollectionData *rhs) { + if (lhs == rhs) return true; + if (!lhs || !rhs) return false; + + return lhs->equalsArray(*rhs); +} +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayImpl.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayImpl.hpp new file mode 100644 index 0000000000..df0149277f --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayImpl.hpp @@ -0,0 +1,22 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../Object/ObjectRef.hpp" +#include "ArrayRef.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +template +inline ArrayRef ArrayShortcuts::createNestedArray() const { + return impl()->addElement().template to(); +} + +template +inline ObjectRef ArrayShortcuts::createNestedObject() const { + return impl()->addElement().template to(); +} + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayIterator.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayIterator.hpp new file mode 100644 index 0000000000..c1d6096ffc --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayIterator.hpp @@ -0,0 +1,121 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../Variant/SlotFunctions.hpp" +#include "../Variant/VariantRef.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +class VariantPtr { + public: + VariantPtr(MemoryPool *pool, VariantData *data) : _variant(pool, data) {} + + VariantRef *operator->() { + return &_variant; + } + + VariantRef &operator*() { + return _variant; + } + + private: + VariantRef _variant; +}; + +class ArrayIterator { + public: + ArrayIterator() : _slot(0) {} + explicit ArrayIterator(MemoryPool *pool, VariantSlot *slot) + : _pool(pool), _slot(slot) {} + + VariantRef operator*() const { + return VariantRef(_pool, _slot->data()); + } + VariantPtr operator->() { + return VariantPtr(_pool, _slot->data()); + } + + bool operator==(const ArrayIterator &other) const { + return _slot == other._slot; + } + + bool operator!=(const ArrayIterator &other) const { + return _slot != other._slot; + } + + ArrayIterator &operator++() { + _slot = _slot->next(); + return *this; + } + + ArrayIterator &operator+=(size_t distance) { + _slot = _slot->next(distance); + return *this; + } + + VariantSlot *internal() { + return _slot; + } + + private: + MemoryPool *_pool; + VariantSlot *_slot; +}; + +class VariantConstPtr { + public: + VariantConstPtr(const VariantData *data) : _variant(data) {} + + VariantConstRef *operator->() { + return &_variant; + } + + VariantConstRef &operator*() { + return _variant; + } + + private: + VariantConstRef _variant; +}; + +class ArrayConstRefIterator { + public: + ArrayConstRefIterator() : _slot(0) {} + explicit ArrayConstRefIterator(const VariantSlot *slot) : _slot(slot) {} + + VariantConstRef operator*() const { + return VariantConstRef(_slot->data()); + } + VariantConstPtr operator->() { + return VariantConstPtr(_slot->data()); + } + + bool operator==(const ArrayConstRefIterator &other) const { + return _slot == other._slot; + } + + bool operator!=(const ArrayConstRefIterator &other) const { + return _slot != other._slot; + } + + ArrayConstRefIterator &operator++() { + _slot = _slot->next(); + return *this; + } + + ArrayConstRefIterator &operator+=(size_t distance) { + _slot = _slot->next(distance); + return *this; + } + + const VariantSlot *internal() { + return _slot; + } + + private: + const VariantSlot *_slot; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayRef.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayRef.hpp new file mode 100644 index 0000000000..e34b62335c --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayRef.hpp @@ -0,0 +1,153 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../Variant/VariantData.hpp" +#include "ArrayFunctions.hpp" +#include "ArrayIterator.hpp" + +// Returns the size (in bytes) of an array with n elements. +// Can be very handy to determine the size of a StaticMemoryPool. +#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \ + ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot)) + +namespace ARDUINOJSON_NAMESPACE { + +class ObjectRef; +template +class ElementProxy; + +template +class ArrayRefBase { + public: + operator VariantConstRef() const { + const void* data = _data; // prevent warning cast-align + return VariantConstRef(reinterpret_cast(data)); + } + + template + FORCE_INLINE void accept(Visitor& visitor) const { + arrayAccept(_data, visitor); + } + + FORCE_INLINE bool isNull() const { + return _data == 0; + } + + FORCE_INLINE size_t memoryUsage() const { + return _data ? _data->memoryUsage() : 0; + } + + FORCE_INLINE size_t nesting() const { + return _data ? _data->nesting() : 0; + } + + FORCE_INLINE size_t size() const { + return _data ? _data->size() : 0; + } + + protected: + ArrayRefBase(TData* data) : _data(data) {} + TData* _data; +}; + +class ArrayConstRef : public ArrayRefBase, + public Visitable { + friend class ArrayRef; + typedef ArrayRefBase base_type; + + public: + typedef ArrayConstRefIterator iterator; + + FORCE_INLINE iterator begin() const { + if (!_data) return iterator(); + return iterator(_data->head()); + } + + FORCE_INLINE iterator end() const { + return iterator(); + } + + FORCE_INLINE ArrayConstRef() : base_type(0) {} + FORCE_INLINE ArrayConstRef(const CollectionData* data) : base_type(data) {} + + FORCE_INLINE bool operator==(ArrayConstRef rhs) const { + return arrayEquals(_data, rhs._data); + } + + FORCE_INLINE VariantConstRef operator[](size_t index) const { + return getElement(index); + } + + FORCE_INLINE VariantConstRef getElement(size_t index) const { + return VariantConstRef(_data ? _data->get(index) : 0); + } +}; + +class ArrayRef : public ArrayRefBase, + public ArrayShortcuts, + public Visitable { + typedef ArrayRefBase base_type; + + public: + typedef ArrayIterator iterator; + + FORCE_INLINE ArrayRef() : base_type(0), _pool(0) {} + FORCE_INLINE ArrayRef(MemoryPool* pool, CollectionData* data) + : base_type(data), _pool(pool) {} + + operator VariantRef() { + void* data = _data; // prevent warning cast-align + return VariantRef(_pool, reinterpret_cast(data)); + } + + operator ArrayConstRef() const { + return ArrayConstRef(_data); + } + + VariantRef addElement() const { + return VariantRef(_pool, arrayAdd(_data, _pool)); + } + + FORCE_INLINE iterator begin() const { + if (!_data) return iterator(); + return iterator(_pool, _data->head()); + } + + FORCE_INLINE iterator end() const { + return iterator(); + } + + // Copy a ArrayRef + FORCE_INLINE bool set(ArrayConstRef src) const { + if (!_data || !src._data) return false; + return _data->copyFrom(*src._data, _pool); + } + + FORCE_INLINE bool operator==(ArrayRef rhs) const { + return arrayEquals(_data, rhs._data); + } + + // Gets the value at the specified index. + FORCE_INLINE VariantRef getElement(size_t index) const { + return VariantRef(_pool, _data ? _data->get(index) : 0); + } + + // Removes element at specified position. + FORCE_INLINE void remove(iterator it) const { + if (!_data) return; + _data->remove(it.internal()); + } + + // Removes element at specified index. + FORCE_INLINE void remove(size_t index) const { + if (!_data) return; + _data->remove(index); + } + + private: + MemoryPool* _pool; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayShortcuts.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayShortcuts.hpp new file mode 100644 index 0000000000..221f3fd1a1 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ArrayShortcuts.hpp @@ -0,0 +1,47 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../Polyfills/attributes.hpp" +#include "../Polyfills/type_traits.hpp" + +namespace ARDUINOJSON_NAMESPACE { +// Forward declarations. +template +class ElementProxy; + +template +class ArrayShortcuts { + public: + // Returns the element at specified index if the variant is an array. + FORCE_INLINE ElementProxy operator[](size_t index) const; + + FORCE_INLINE ObjectRef createNestedObject() const; + + FORCE_INLINE ArrayRef createNestedArray() const; + + // Adds the specified value at the end of the array. + // + // bool add(TValue); + // TValue = bool, long, int, short, float, double, serialized, VariantRef, + // std::string, String, ObjectRef + template + FORCE_INLINE bool add(const T &value) const { + return impl()->addElement().set(value); + } + // + // bool add(TValue); + // TValue = char*, const char*, const __FlashStringHelper* + template + FORCE_INLINE bool add(T *value) const { + return impl()->addElement().set(value); + } + + private: + const TArray *impl() const { + return static_cast(this); + } +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ElementProxy.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ElementProxy.hpp new file mode 100644 index 0000000000..e1c784add7 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/ElementProxy.hpp @@ -0,0 +1,165 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../Configuration.hpp" +#include "../Operators/VariantOperators.hpp" + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4522) +#endif + +namespace ARDUINOJSON_NAMESPACE { + +template +class ElementProxy : public VariantOperators >, + public Visitable { + typedef ElementProxy this_type; + + public: + FORCE_INLINE ElementProxy(TArray array, size_t index) + : _array(array), _index(index) {} + + FORCE_INLINE this_type& operator=(const this_type& src) { + getUpstreamElement().set(src.as()); + return *this; + } + + // Replaces the value + // + // operator=(const TValue&) + // TValue = bool, long, int, short, float, double, serialized, VariantRef, + // std::string, String, ArrayRef, ObjectRef + template + FORCE_INLINE this_type& operator=(const T& src) { + getUpstreamElement().set(src); + return *this; + } + // + // operator=(TValue) + // TValue = char*, const char*, const __FlashStringHelper* + template + FORCE_INLINE this_type& operator=(T* src) { + getUpstreamElement().set(src); + return *this; + } + + FORCE_INLINE void clear() const { + getUpstreamElement().clear(); + } + + FORCE_INLINE bool isNull() const { + return getUpstreamElement().isNull(); + } + + template + FORCE_INLINE typename VariantAs::type as() const { + return getUpstreamElement().template as(); + } + + template + FORCE_INLINE bool is() const { + return getUpstreamElement().template is(); + } + + template + FORCE_INLINE typename VariantTo::type to() const { + return getUpstreamElement().template to(); + } + + // Replaces the value + // + // bool set(const TValue&) + // TValue = bool, long, int, short, float, double, serialized, VariantRef, + // std::string, String, ArrayRef, ObjectRef + template + FORCE_INLINE bool set(const TValue& value) const { + return getUpstreamElement().set(value); + } + // + // bool set(TValue) + // TValue = char*, const char*, const __FlashStringHelper* + template + FORCE_INLINE bool set(TValue* value) const { + return getUpstreamElement().set(value); + } + + template + void accept(Visitor& visitor) const { + return getUpstreamElement().accept(visitor); + } + + FORCE_INLINE size_t size() const { + return getUpstreamElement().size(); + } + + template + VariantRef getMember(TNestedKey* key) const { + return getUpstreamElement().getMember(key); + } + + template + VariantRef getMember(const TNestedKey& key) const { + return getUpstreamElement().getMember(key); + } + + template + VariantRef getOrAddMember(TNestedKey* key) const { + return getUpstreamElement().getOrAddMember(key); + } + + template + VariantRef getOrAddMember(const TNestedKey& key) const { + return getUpstreamElement().getOrAddMember(key); + } + + VariantRef addElement() const { + return getUpstreamElement().addElement(); + } + + VariantRef getElement(size_t index) const { + return getUpstreamElement().getElement(index); + } + + FORCE_INLINE void remove(size_t index) const { + getUpstreamElement().remove(index); + } + // remove(char*) const + // remove(const char*) const + // remove(const __FlashStringHelper*) const + template + FORCE_INLINE typename enable_if::value>::type remove( + TChar* key) const { + getUpstreamElement().remove(key); + } + // remove(const std::string&) const + // remove(const String&) const + template + FORCE_INLINE typename enable_if::value>::type remove( + const TString& key) const { + getUpstreamElement().remove(key); + } + + private: + FORCE_INLINE VariantRef getUpstreamElement() const { + return _array.getElement(_index); + } + + TArray _array; + const size_t _index; +}; + +template +inline ElementProxy ArrayShortcuts::operator[]( + size_t index) const { + return ElementProxy(*impl(), index); +} + +} // namespace ARDUINOJSON_NAMESPACE + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Array/Utilities.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/Utilities.hpp new file mode 100644 index 0000000000..4959916044 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Array/Utilities.hpp @@ -0,0 +1,66 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "ArrayRef.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +// Copy a 1D array to a JsonArray +template +inline bool copyArray(T (&src)[N], ArrayRef dst) { + return copyArray(src, N, dst); +} + +// Copy a 1D array to a JsonArray +template +inline bool copyArray(T* src, size_t len, ArrayRef dst) { + bool ok = true; + for (size_t i = 0; i < len; i++) { + ok &= dst.add(src[i]); + } + return ok; +} + +// Copy a 2D array to a JsonArray +template +inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) { + bool ok = true; + for (size_t i = 0; i < N1; i++) { + ArrayRef nestedArray = dst.createNestedArray(); + for (size_t j = 0; j < N2; j++) { + ok &= nestedArray.add(src[i][j]); + } + } + return ok; +} + +// Copy a JsonArray to a 1D array +template +inline size_t copyArray(ArrayConstRef src, T (&dst)[N]) { + return copyArray(src, dst, N); +} + +// Copy a JsonArray to a 1D array +template +inline size_t copyArray(ArrayConstRef src, T* dst, size_t len) { + size_t i = 0; + for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < len; + ++it) + dst[i++] = *it; + return i; +} + +// Copy a JsonArray to a 2D array +template +inline void copyArray(ArrayConstRef src, T (&dst)[N1][N2]) { + size_t i = 0; + for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < N1; + ++it) { + copyArray(it->as(), dst[i++]); + } +} + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionData.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionData.hpp new file mode 100644 index 0000000000..6b37d533a0 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionData.hpp @@ -0,0 +1,70 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +namespace ARDUINOJSON_NAMESPACE { + +class MemoryPool; +class VariantData; +class VariantSlot; + +class CollectionData { + VariantSlot *_head; + VariantSlot *_tail; + + public: + // Must be a POD! + // - no constructor + // - no destructor + // - no virtual + // - no inheritance + VariantSlot *addSlot(MemoryPool *); + + VariantData *add(MemoryPool *pool); + + template + VariantData *add(TAdaptedString key, MemoryPool *pool); + + void clear(); + + template + bool containsKey(const TAdaptedString &key) const; + + bool copyFrom(const CollectionData &src, MemoryPool *pool); + + bool equalsArray(const CollectionData &other) const; + bool equalsObject(const CollectionData &other) const; + + VariantData *get(size_t index) const; + + template + VariantData *get(TAdaptedString key) const; + + VariantSlot *head() const { + return _head; + } + + void remove(size_t index); + + template + void remove(TAdaptedString key) { + remove(getSlot(key)); + } + + void remove(VariantSlot *slot); + + size_t memoryUsage() const; + size_t nesting() const; + size_t size() const; + + private: + VariantSlot *getSlot(size_t index) const; + + template + VariantSlot *getSlot(TAdaptedString key) const; + + VariantSlot *getPreviousSlot(VariantSlot *) const; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionImpl.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionImpl.hpp new file mode 100644 index 0000000000..c6097e4741 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -0,0 +1,163 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../Variant/VariantData.hpp" +#include "CollectionData.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) { + VariantSlot* slot = pool->allocVariant(); + if (!slot) return 0; + + if (_tail) { + _tail->setNextNotNull(slot); + _tail = slot; + } else { + _head = slot; + _tail = slot; + } + + slot->clear(); + return slot; +} + +inline VariantData* CollectionData::add(MemoryPool* pool) { + return slotData(addSlot(pool)); +} + +template +inline VariantData* CollectionData::add(TAdaptedString key, MemoryPool* pool) { + VariantSlot* slot = addSlot(pool); + if (!slotSetKey(slot, key, pool)) return 0; + return slot->data(); +} + +inline void CollectionData::clear() { + _head = 0; + _tail = 0; +} + +template +inline bool CollectionData::containsKey(const TAdaptedString& key) const { + return getSlot(key) != 0; +} + +inline bool CollectionData::copyFrom(const CollectionData& src, + MemoryPool* pool) { + clear(); + for (VariantSlot* s = src._head; s; s = s->next()) { + VariantData* var; + if (s->key() != 0) { + if (s->ownsKey()) + var = add(RamStringAdapter(s->key()), pool); + else + var = add(ConstRamStringAdapter(s->key()), pool); + } else { + var = add(pool); + } + if (!var) return false; + if (!var->copyFrom(*s->data(), pool)) return false; + } + return true; +} + +inline bool CollectionData::equalsObject(const CollectionData& other) const { + size_t count = 0; + for (VariantSlot* slot = _head; slot; slot = slot->next()) { + VariantData* v1 = slot->data(); + VariantData* v2 = other.get(adaptString(slot->key())); + if (!variantEquals(v1, v2)) return false; + count++; + } + return count == other.size(); +} + +inline bool CollectionData::equalsArray(const CollectionData& other) const { + VariantSlot* s1 = _head; + VariantSlot* s2 = other._head; + for (;;) { + if (s1 == s2) return true; + if (!s1 || !s2) return false; + if (!variantEquals(s1->data(), s2->data())) return false; + s1 = s1->next(); + s2 = s2->next(); + } +} + +template +inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { + VariantSlot* slot = _head; + while (slot) { + if (key.equals(slot->key())) break; + slot = slot->next(); + } + return slot; +} + +inline VariantSlot* CollectionData::getSlot(size_t index) const { + return _head->next(index); +} + +inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const { + VariantSlot* current = _head; + while (current) { + VariantSlot* next = current->next(); + if (next == target) return current; + current = next; + } + return 0; +} + +template +inline VariantData* CollectionData::get(TAdaptedString key) const { + VariantSlot* slot = getSlot(key); + return slot ? slot->data() : 0; +} + +inline VariantData* CollectionData::get(size_t index) const { + VariantSlot* slot = getSlot(index); + return slot ? slot->data() : 0; +} + +inline void CollectionData::remove(VariantSlot* slot) { + if (!slot) return; + VariantSlot* prev = getPreviousSlot(slot); + VariantSlot* next = slot->next(); + if (prev) + prev->setNext(next); + else + _head = next; + if (!next) _tail = prev; +} + +inline void CollectionData::remove(size_t index) { + remove(getSlot(index)); +} + +inline size_t CollectionData::memoryUsage() const { + size_t total = 0; + for (VariantSlot* s = _head; s; s = s->next()) { + total += sizeof(VariantSlot) + s->data()->memoryUsage(); + if (s->ownsKey()) total += strlen(s->key()) + 1; + } + return total; +} + +inline size_t CollectionData::nesting() const { + size_t maxChildNesting = 0; + for (VariantSlot* s = _head; s; s = s->next()) { + size_t childNesting = s->data()->nesting(); + if (childNesting > maxChildNesting) maxChildNesting = childNesting; + } + return maxChildNesting + 1; +} + +inline size_t CollectionData::size() const { + return slotSize(_head); +} + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Configuration.hpp similarity index 65% rename from lib/ArduinoJson/src/ArduinoJson/Configuration.hpp rename to lib/ArduinoJson-6.x/src/ArduinoJson/Configuration.hpp index 82483adfa4..6e97b15df9 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Configuration.hpp @@ -1,9 +1,23 @@ // ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 +// Copyright Benoit Blanchon 2014-2019 // MIT License #pragma once +#if defined(_MSC_VER) +#define ARDUINOJSON_HAS_INT64 1 +#else +#define ARDUINOJSON_HAS_INT64 0 +#endif + +#if __cplusplus >= 201103L +#define ARDUINOJSON_HAS_LONG_LONG 1 +#define ARDUINOJSON_HAS_NULLPTR 1 +#else +#define ARDUINOJSON_HAS_LONG_LONG 0 +#define ARDUINOJSON_HAS_NULLPTR 0 +#endif + // Small or big machine? #ifndef ARDUINOJSON_EMBEDDED_MODE #if defined(ARDUINO) || defined(__IAR_SYSTEMS_ICC__) || defined(__XC) || \ @@ -25,9 +39,6 @@ #ifndef ARDUINOJSON_USE_LONG_LONG #define ARDUINOJSON_USE_LONG_LONG 0 #endif -#ifndef ARDUINOJSON_USE_INT64 -#define ARDUINOJSON_USE_INT64 0 -#endif // Embedded systems usually don't have std::string #ifndef ARDUINOJSON_ENABLE_STD_STRING @@ -53,22 +64,13 @@ // Use long long when available #ifndef ARDUINOJSON_USE_LONG_LONG -#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +#if ARDUINOJSON_HAS_LONG_LONG || ARDUINOJSON_HAS_INT64 #define ARDUINOJSON_USE_LONG_LONG 1 #else #define ARDUINOJSON_USE_LONG_LONG 0 #endif #endif -// Use _int64 on old versions of Visual Studio -#ifndef ARDUINOJSON_USE_INT64 -#if defined(_MSC_VER) && _MSC_VER <= 1700 -#define ARDUINOJSON_USE_INT64 1 -#else -#define ARDUINOJSON_USE_INT64 0 -#endif -#endif - // On a computer, we can use std::string #ifndef ARDUINOJSON_ENABLE_STD_STRING #define ARDUINOJSON_ENABLE_STD_STRING 1 @@ -88,28 +90,38 @@ #ifdef ARDUINO -// Enable support for Arduino String +// Enable support for Arduino's String class #ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING #define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 #endif -// Enable support for Arduino Stream +// Enable support for Arduino's Stream class #ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM #define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 #endif +// Enable support for Arduino's Print class +#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1 +#endif + #else // ARDUINO -// Disable support for Arduino String +// Enable support for Arduino's String class #ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING #define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 #endif -// Disable support for Arduino Stream +// Enable support for Arduino's Stream class #ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM #define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 #endif +// Enable support for Arduino's Print class +#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0 +#endif + #endif // ARDUINO #ifndef ARDUINOJSON_ENABLE_PROGMEM @@ -120,19 +132,19 @@ #endif #endif -#ifndef ARDUINOJSON_ENABLE_ALIGNMENT -#ifdef ARDUINO_ARCH_AVR -// alignment isn't needed for 8-bit AVR -#define ARDUINOJSON_ENABLE_ALIGNMENT 0 -#else -// but most processors need pointers to be align on word size -#define ARDUINOJSON_ENABLE_ALIGNMENT 1 +// Convert unicode escape sequence (\u0123) to UTF-8 +#ifndef ARDUINOJSON_DECODE_UNICODE +#define ARDUINOJSON_DECODE_UNICODE 0 #endif + +// Support NaN in JSON +#ifndef ARDUINOJSON_ENABLE_NAN +#define ARDUINOJSON_ENABLE_NAN 0 #endif -// Enable deprecated functions by default -#ifndef ARDUINOJSON_ENABLE_DEPRECATED -#define ARDUINOJSON_ENABLE_DEPRECATED 1 +// Support Infinity in JSON +#ifndef ARDUINOJSON_ENABLE_INFINITY +#define ARDUINOJSON_ENABLE_INFINITY 0 #endif // Control the exponentiation threshold for big numbers @@ -146,6 +158,16 @@ #define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 #endif -#if ARDUINOJSON_USE_LONG_LONG && ARDUINOJSON_USE_INT64 -#error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together +#ifndef ARDUINOJSON_LITTLE_ENDIAN +#if defined(_MSC_VER) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64) +#define ARDUINOJSON_LITTLE_ENDIAN 1 +#else +#define ARDUINOJSON_LITTLE_ENDIAN 0 +#endif +#endif + +#ifndef ARDUINOJSON_TAB +#define ARDUINOJSON_TAB " " #endif diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp new file mode 100644 index 0000000000..0551bd4179 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/ArduinoStreamReader.hpp @@ -0,0 +1,31 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_ARDUINO_STREAM + +#include + +namespace ARDUINOJSON_NAMESPACE { + +struct ArduinoStreamReader { + Stream& _stream; + + public: + explicit ArduinoStreamReader(Stream& stream) : _stream(stream) {} + + int read() { + // don't use _stream.read() as it ignores the timeout + uint8_t c; + return _stream.readBytes(&c, 1) ? c : -1; + } +}; + +inline ArduinoStreamReader makeReader(Stream& input) { + return ArduinoStreamReader(input); +} +} // namespace ARDUINOJSON_NAMESPACE + +#endif diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/CharPointerReader.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/CharPointerReader.hpp new file mode 100644 index 0000000000..2de68d1a08 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/CharPointerReader.hpp @@ -0,0 +1,67 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +namespace ARDUINOJSON_NAMESPACE { + +template +struct IsCharOrVoid { + static const bool value = + is_same::value || is_same::value || + is_same::value || is_same::value; +}; + +template +struct IsCharOrVoid : IsCharOrVoid {}; + +class UnsafeCharPointerReader { + const char* _ptr; + + public: + explicit UnsafeCharPointerReader(const char* ptr) + : _ptr(ptr ? ptr : reinterpret_cast("")) {} + + int read() { + return static_cast(*_ptr++); + } +}; + +class SafeCharPointerReader { + const char* _ptr; + const char* _end; + + public: + explicit SafeCharPointerReader(const char* ptr, size_t len) + : _ptr(ptr ? ptr : reinterpret_cast("")), _end(_ptr + len) {} + + int read() { + if (_ptr < _end) + return static_cast(*_ptr++); + else + return -1; + } +}; + +template +inline typename enable_if::value, + UnsafeCharPointerReader>::type +makeReader(TChar* input) { + return UnsafeCharPointerReader(reinterpret_cast(input)); +} + +template +inline + typename enable_if::value, SafeCharPointerReader>::type + makeReader(TChar* input, size_t n) { + return SafeCharPointerReader(reinterpret_cast(input), n); +} + +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +inline SafeCharPointerReader makeReader(const ::String& input) { + return SafeCharPointerReader(input.c_str(), input.length()); +} +#endif + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/DeserializationError.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/DeserializationError.hpp new file mode 100644 index 0000000000..8707b3b046 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/DeserializationError.hpp @@ -0,0 +1,113 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif + +namespace ARDUINOJSON_NAMESPACE { + +class DeserializationError { + // safe bool idiom + typedef void (DeserializationError::*bool_type)() const; + void safeBoolHelper() const {} + + public: + enum Code { + Ok, + IncompleteInput, + InvalidInput, + NoMemory, + NotSupported, + TooDeep + }; + + DeserializationError() {} + DeserializationError(Code c) : _code(c) {} + + // Compare with DeserializationError + friend bool operator==(const DeserializationError& lhs, + const DeserializationError& rhs) { + return lhs._code == rhs._code; + } + friend bool operator!=(const DeserializationError& lhs, + const DeserializationError& rhs) { + return lhs._code != rhs._code; + } + + // Compare with Code + friend bool operator==(const DeserializationError& lhs, Code rhs) { + return lhs._code == rhs; + } + friend bool operator==(Code lhs, const DeserializationError& rhs) { + return lhs == rhs._code; + } + friend bool operator!=(const DeserializationError& lhs, Code rhs) { + return lhs._code != rhs; + } + friend bool operator!=(Code lhs, const DeserializationError& rhs) { + return lhs != rhs._code; + } + + // Behaves like a bool + operator bool_type() const { + return _code != Ok ? &DeserializationError::safeBoolHelper : 0; + } + friend bool operator==(bool value, const DeserializationError& err) { + return static_cast(err) == value; + } + friend bool operator==(const DeserializationError& err, bool value) { + return static_cast(err) == value; + } + friend bool operator!=(bool value, const DeserializationError& err) { + return static_cast(err) != value; + } + friend bool operator!=(const DeserializationError& err, bool value) { + return static_cast(err) != value; + } + + // Returns internal enum, useful for switch statement + Code code() const { + return _code; + } + + const char* c_str() const { + switch (_code) { + case Ok: + return "Ok"; + case TooDeep: + return "TooDeep"; + case NoMemory: + return "NoMemory"; + case InvalidInput: + return "InvalidInput"; + case IncompleteInput: + return "IncompleteInput"; + case NotSupported: + return "NotSupported"; + default: + return "???"; + } + } + + private: + Code _code; +}; + +#if ARDUINOJSON_ENABLE_STD_STREAM +inline std::ostream& operator<<(std::ostream& s, + const DeserializationError& e) { + s << e.c_str(); + return s; +} + +inline std::ostream& operator<<(std::ostream& s, DeserializationError::Code c) { + s << DeserializationError(c).c_str(); + return s; +} +#endif + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/FlashStringReader.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/FlashStringReader.hpp new file mode 100644 index 0000000000..e8066ef600 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/FlashStringReader.hpp @@ -0,0 +1,48 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_PROGMEM + +namespace ARDUINOJSON_NAMESPACE { +class UnsafeFlashStringReader { + const char* _ptr; + + public: + explicit UnsafeFlashStringReader(const __FlashStringHelper* ptr) + : _ptr(reinterpret_cast(ptr)) {} + + int read() { + return pgm_read_byte_near(_ptr++); + } +}; + +class SafeFlashStringReader { + const char* _ptr; + const char* _end; + + public: + explicit SafeFlashStringReader(const __FlashStringHelper* ptr, size_t size) + : _ptr(reinterpret_cast(ptr)), _end(_ptr + size) {} + + int read() { + if (_ptr < _end) + return pgm_read_byte_near(_ptr++); + else + return -1; + } +}; + +inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) { + return UnsafeFlashStringReader(input); +} + +inline SafeFlashStringReader makeReader(const __FlashStringHelper* input, + size_t size) { + return SafeFlashStringReader(input, size); +} +} // namespace ARDUINOJSON_NAMESPACE + +#endif diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/IteratorReader.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/IteratorReader.hpp new file mode 100644 index 0000000000..f2605e48a3 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/IteratorReader.hpp @@ -0,0 +1,31 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +namespace ARDUINOJSON_NAMESPACE { + +template +class IteratorReader { + TIterator _ptr, _end; + + public: + explicit IteratorReader(TIterator begin, TIterator end) + : _ptr(begin), _end(end) {} + + int read() { + if (_ptr < _end) + return static_cast(*_ptr++); + else + return -1; + } +}; + +template +inline IteratorReader makeReader( + const TInput& input) { + return IteratorReader(input.begin(), + input.end()); +} +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/NestingLimit.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/NestingLimit.hpp new file mode 100644 index 0000000000..f1f4a5bc1e --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/NestingLimit.hpp @@ -0,0 +1,17 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../Configuration.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +struct NestingLimit { + NestingLimit() : value(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {} + explicit NestingLimit(uint8_t n) : value(n) {} + + uint8_t value; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/StdStreamReader.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/StdStreamReader.hpp new file mode 100644 index 0000000000..8996a77e10 --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/StdStreamReader.hpp @@ -0,0 +1,34 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_STD_STREAM + +#include + +namespace ARDUINOJSON_NAMESPACE { + +class StdStreamReader { + std::istream& _stream; + char _current; + + public: + explicit StdStreamReader(std::istream& stream) + : _stream(stream), _current(0) {} + + int read() { + return _stream.get(); + } + + private: + StdStreamReader& operator=(const StdStreamReader&); // Visual Studio C4512 +}; + +inline StdStreamReader makeReader(std::istream& input) { + return StdStreamReader(input); +} +} // namespace ARDUINOJSON_NAMESPACE + +#endif diff --git a/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/deserialize.hpp b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/deserialize.hpp new file mode 100644 index 0000000000..112d0823da --- /dev/null +++ b/lib/ArduinoJson-6.x/src/ArduinoJson/Deserialization/deserialize.hpp @@ -0,0 +1,76 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2019 +// MIT License + +#pragma once + +#include "../StringStorage/StringStorage.hpp" +#include "ArduinoStreamReader.hpp" +#include "CharPointerReader.hpp" +#include "DeserializationError.hpp" +#include "FlashStringReader.hpp" +#include "IteratorReader.hpp" +#include "NestingLimit.hpp" +#include "StdStreamReader.hpp" + +namespace ARDUINOJSON_NAMESPACE { + +template