From 81184436f3ccd1f09a3f3433da14585cb303c18e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 21 Jul 2019 16:01:43 +0200 Subject: [PATCH] Update IRremoteESP8266 library to 2.6.3.10 Update IRremoteESP8266 library to 2.6.3.10 --- lib/IRremoteESP8266-2.6.0/.travis.yml | 66 - .../examples/ControlSamsungAC/platformio.ini | 19 - .../examples/IRGCSendDemo/platformio.ini | 19 - .../examples/IRGCTCPServer/platformio.ini | 19 - .../examples/IRMQTTServer/platformio.ini | 42 - .../examples/IRServer/platformio.ini | 19 - .../examples/IRrecvDemo/platformio.ini | 19 - .../examples/IRrecvDump/platformio.ini | 19 - .../examples/IRrecvDumpV2/platformio.ini | 19 - .../examples/IRsendDemo/platformio.ini | 19 - .../examples/IRsendProntoDemo/platformio.ini | 19 - .../JVCPanasonicSendDemo/platformio.ini | 19 - .../examples/LGACSend/platformio.ini | 19 - .../examples/TurnOnArgoAC/platformio.ini | 19 - .../examples/TurnOnDaikinAC/platformio.ini | 19 - .../examples/TurnOnFujitsuAC/platformio.ini | 19 - .../TurnOnKelvinatorAC/platformio.ini | 19 - .../TurnOnMitsubishiAC/platformio.ini | 19 - .../TurnOnMitsubishiHeavyAc/platformio.ini | 19 - .../examples/TurnOnPanasonicAC/platformio.ini | 19 - .../examples/TurnOnToshibaAC/platformio.ini | 19 - .../examples/TurnOnTrotecAC/platformio.ini | 19 - lib/IRremoteESP8266-2.6.0/platformio.ini | 29 - lib/IRremoteESP8266-2.6.0/src/IRutils.h | 48 - lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp | 264 --- lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp | 117 - lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp | 556 ----- lib/IRremoteESP8266-2.6.0/src/ir_LG.h | 17 - lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp | 267 --- lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp | 162 -- .../test/IRrecv_test.cpp | 561 ----- .../test/ir_Electra_test.cpp | 99 - .../test/ir_Fujitsu_test.cpp | 560 ----- .../.github/CONTRIBUTING.md | 6 +- .../.github/Contributors.md | 4 +- .../.github/issue_template.md | 8 +- .../.gitignore | 4 + .../.gitmodules | 0 .../.style.yapf | 0 lib/IRremoteESP8266-2.6.3.10/.travis.yml | 73 + .../CPPLINT.cfg | 0 .../LICENSE.txt | 0 .../README.md | 31 +- .../ReleaseNotes.md | 88 + .../SupportedProtocols.md | 132 ++ .../ControlSamsungAC/ControlSamsungAC.ino | 31 +- .../examples/ControlSamsungAC/platformio.ini | 18 + .../DumbIRRepeater/DumbIRRepeater.ino | 130 ++ .../examples/DumbIRRepeater/platformio.ini | 18 + .../examples/IRGCSendDemo/IRGCSendDemo.ino | 4 +- .../examples/IRGCSendDemo/platformio.ini | 18 + .../examples/IRGCTCPServer/IRGCTCPServer.ino | 7 +- .../examples/IRGCTCPServer/platformio.ini | 18 + .../examples/IRMQTTServer/IRMQTTServer.h | 201 +- .../examples/IRMQTTServer/IRMQTTServer.ino | 1973 ++++++++--------- .../examples/IRMQTTServer/platformio.ini | 62 + .../examples/IRServer/IRServer.ino | 36 +- .../examples/IRServer/platformio.ini | 18 + .../examples/IRrecvDemo/IRrecvDemo.ino | 4 +- .../examples/IRrecvDemo/platformio.ini | 18 + .../examples/IRrecvDump/IRrecvDump.ino | 2 - .../examples/IRrecvDump/platformio.ini | 18 + .../examples/IRrecvDumpV2/IRrecvDumpV2.ino | 211 +- .../examples/IRrecvDumpV2/platformio.ini | 18 + .../examples/IRsendDemo/IRsendDemo.ino | 8 +- .../examples/IRsendDemo/platformio.ini | 18 + .../IRsendProntoDemo/IRsendProntoDemo.ino | 4 +- .../examples/IRsendProntoDemo/platformio.ini | 18 + .../JVCPanasonicSendDemo.ino | 4 +- .../JVCPanasonicSendDemo/platformio.ini | 18 + .../examples/LGACSend/LGACSend.ino | 0 .../examples/LGACSend/platformio.ini | 18 + .../SmartIRRepeater/SmartIRRepeater.ino | 143 ++ .../examples/SmartIRRepeater/platformio.ini | 18 + .../examples/TurnOnArgoAC/TurnOnArgoAC.ino | 6 +- .../examples/TurnOnArgoAC/platformio.ini | 18 + .../TurnOnDaikinAC/TurnOnDaikinAC.ino | 4 +- .../examples/TurnOnDaikinAC/platformio.ini | 18 + .../TurnOnFujitsuAC/TurnOnFujitsuAC.ino | 8 +- .../examples/TurnOnFujitsuAC/platformio.ini | 18 + .../TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino | 4 +- .../TurnOnKelvinatorAC/platformio.ini | 18 + .../TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino | 4 +- .../TurnOnMitsubishiAC/platformio.ini | 18 + .../TurnOnMitsubishiHeavyAc.ino | 4 +- .../TurnOnMitsubishiHeavyAc/platformio.ini | 18 + .../TurnOnPanasonicAC/TurnOnPanasonicAC.ino | 4 +- .../examples/TurnOnPanasonicAC/platformio.ini | 18 + .../TurnOnToshibaAC/TurnOnToshibaAC.ino | 4 +- .../examples/TurnOnToshibaAC/platformio.ini | 18 + .../TurnOnTrotecAC/TurnOnTrotecAC.ino | 4 +- .../examples/TurnOnTrotecAC/platformio.ini | 18 + .../keywords.txt | 355 ++- .../library.json | 20 +- .../library.properties | 12 +- .../pylintrc | 0 .../src/CPPLINT.cfg | 0 .../src/IRac.cpp | 1094 +++++++-- .../src/IRac.h | 110 +- .../src/IRrecv.cpp | 430 +++- .../src/IRrecv.h | 169 +- .../src/IRremoteESP8266.h | 101 +- .../src/IRsend.cpp | 377 +++- .../src/IRsend.h | 209 +- .../src/IRtimer.cpp | 0 .../src/IRtimer.h | 0 .../src/IRutils.cpp | 498 +++-- lib/IRremoteESP8266-2.6.3.10/src/IRutils.h | 60 + .../src/ir_Aiwa.cpp | 2 + lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.cpp | 438 ++++ .../src/ir_Argo.h | 129 +- .../src/ir_Carrier.cpp | 47 +- .../src/ir_Coolix.cpp | 223 +- .../src/ir_Coolix.h | 29 +- .../src/ir_Daikin.cpp | 1420 +++++++++--- .../src/ir_Daikin.h | 224 +- .../src/ir_Denon.cpp | 43 +- .../src/ir_Dish.cpp | 49 +- .../src/ir_Electra.cpp | 336 +++ lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.h | 102 + .../src/ir_Fujitsu.cpp | 722 ++++++ .../src/ir_Fujitsu.h | 100 +- .../src/ir_GICable.cpp | 40 +- .../src/ir_GlobalCache.cpp | 12 +- .../src/ir_Goodweather.cpp | 464 ++++ .../src/ir_Goodweather.h | 137 ++ .../src/ir_Gree.cpp | 328 +-- .../src/ir_Gree.h | 84 +- .../src/ir_Haier.cpp | 473 ++-- .../src/ir_Haier.h | 125 +- .../src/ir_Hitachi.cpp | 249 +-- .../src/ir_Hitachi.h | 43 +- lib/IRremoteESP8266-2.6.3.10/src/ir_Inax.cpp | 83 + .../src/ir_JVC.cpp | 41 +- .../src/ir_Kelvinator.cpp | 399 ++-- .../src/ir_Kelvinator.h | 92 +- .../src/ir_LG.cpp | 15 +- lib/IRremoteESP8266-2.6.3.10/src/ir_LG.h | 15 + .../src/ir_Lasertag.cpp | 7 +- .../src/ir_Lego.cpp | 41 +- .../src/ir_Lutron.cpp | 13 +- .../src/ir_MWM.cpp | 12 +- .../src/ir_Magiquest.cpp | 0 .../src/ir_Magiquest.h | 0 .../src/ir_Midea.cpp | 229 +- .../src/ir_Midea.h | 53 +- .../src/ir_Mitsubishi.cpp | 352 ++- .../src/ir_Mitsubishi.h | 87 +- .../src/ir_MitsubishiHeavy.cpp | 366 +-- .../src/ir_MitsubishiHeavy.h | 41 +- .../src/ir_NEC.cpp | 46 +- .../src/ir_NEC.h | 10 +- .../src/ir_Neoclima.cpp | 554 +++++ .../src/ir_Neoclima.h | 154 ++ .../src/ir_Nikai.cpp | 45 +- .../src/ir_Panasonic.cpp | 438 ++-- .../src/ir_Panasonic.h | 93 +- .../src/ir_Pioneer.cpp | 55 +- .../src/ir_Pronto.cpp | 8 +- .../src/ir_RC5_RC6.cpp | 12 +- .../src/ir_RCMM.cpp | 13 +- .../src/ir_Samsung.cpp | 471 ++-- .../src/ir_Samsung.h | 89 +- .../src/ir_Sanyo.cpp | 6 - lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.cpp | 557 +++++ lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.h | 98 + .../src/ir_Sherwood.cpp | 12 +- .../src/ir_Sony.cpp | 19 +- .../src/ir_Tcl.cpp | 189 +- .../src/ir_Tcl.h | 22 +- .../src/ir_Teco.cpp | 161 +- .../src/ir_Teco.h | 12 +- .../src/ir_Toshiba.cpp | 232 +- .../src/ir_Toshiba.h | 64 +- .../src/ir_Trotec.cpp | 296 +++ .../src/ir_Trotec.h | 40 +- .../src/ir_Vestel.cpp | 260 +-- .../src/ir_Vestel.h | 50 +- .../src/ir_Whirlpool.cpp | 381 ++-- .../src/ir_Whirlpool.h | 82 +- .../src/ir_Whynter.cpp | 54 +- .../test/IRac_test.cpp | 516 ++++- .../test/IRrecv_test.cpp | 1209 ++++++++++ .../test/IRrecv_test.h | 0 .../test/IRsend_test.cpp | 120 +- .../test/IRsend_test.h | 0 .../test/IRutils_test.cpp | 117 +- .../test/Makefile | 57 +- .../test/ir_Aiwa_test.cpp | 0 .../test/ir_Argo_test.cpp | 221 ++ .../test/ir_Carrier_test.cpp | 0 .../test/ir_Coolix_test.cpp | 150 +- .../test/ir_Daikin_test.cpp | 528 ++++- .../test/ir_Denon_test.cpp | 32 +- .../test/ir_Dish_test.cpp | 0 .../test/ir_Electra_test.cpp | 250 +++ .../test/ir_Fujitsu_test.cpp | 798 +++++++ .../test/ir_GICable_test.cpp | 6 +- .../test/ir_GlobalCache_test.cpp | 0 .../test/ir_Goodweather_test.cpp | 511 +++++ .../test/ir_Gree_test.cpp | 99 +- .../test/ir_Haier_test.cpp | 132 +- .../test/ir_Hitachi_test.cpp | 46 +- .../test/ir_Inax_test.cpp | 119 + .../test/ir_JVC_test.cpp | 0 .../test/ir_Kelvinator_test.cpp | 40 +- .../test/ir_LG_test.cpp | 2 +- .../test/ir_Lasertag_test.cpp | 0 .../test/ir_Lego_test.cpp | 0 .../test/ir_Lutron_test.cpp | 2 +- .../test/ir_MWM_test.cpp | 0 .../test/ir_Magiquest_test.cpp | 0 .../test/ir_Midea_test.cpp | 40 +- .../test/ir_MitsubishiHeavy_test.cpp | 105 +- .../test/ir_Mitsubishi_test.cpp | 31 +- .../test/ir_NEC_test.cpp | 0 .../test/ir_Neoclima_test.cpp | 434 ++++ .../test/ir_Nikai_test.cpp | 0 .../test/ir_Panasonic_test.cpp | 71 +- .../test/ir_Pioneer_test.cpp | 6 +- .../test/ir_Pronto_test.cpp | 0 .../test/ir_RC5_RC6_test.cpp | 0 .../test/ir_RCMM_test.cpp | 0 .../test/ir_Samsung_test.cpp | 381 +++- .../test/ir_Sanyo_test.cpp | 0 .../test/ir_Sharp_test.cpp | 345 +++ .../test/ir_Sherwood_test.cpp | 0 .../test/ir_Sony_test.cpp | 20 +- .../test/ir_Tcl_test.cpp | 80 +- .../test/ir_Teco_test.cpp | 33 +- .../test/ir_Toshiba_test.cpp | 35 +- .../test/ir_Trotec_test.cpp | 179 ++ .../test/ir_Vestel_test.cpp | 57 +- .../test/ir_Whirlpool_test.cpp | 66 +- .../test/ir_Whynter_test.cpp | 8 +- .../tools/Makefile | 26 +- .../tools/RawToGlobalCache.sh | 0 .../tools/auto_analyse_raw_data.py | 8 +- .../tools/auto_analyse_raw_data_test.py | 28 +- .../tools/gc_decode.cpp | 0 .../tools/mkkeywords | 14 +- .../tools/mode2_decode.cpp | 0 .../tools/scrape_supported_devices.py | 255 +++ 243 files changed, 20273 insertions(+), 9299 deletions(-) delete mode 100644 lib/IRremoteESP8266-2.6.0/.travis.yml delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/platformio.ini delete mode 100644 lib/IRremoteESP8266-2.6.0/src/IRutils.h delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_LG.h delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp delete mode 100644 lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/.github/CONTRIBUTING.md (90%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/.github/Contributors.md (86%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/.github/issue_template.md (77%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/.gitignore (96%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/.gitmodules (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/.style.yapf (100%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/.travis.yml rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/CPPLINT.cfg (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/LICENSE.txt (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/README.md (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/ReleaseNotes.md (78%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/SupportedProtocols.md rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/ControlSamsungAC/ControlSamsungAC.ino (79%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/ControlSamsungAC/platformio.ini create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/DumbIRRepeater.ino create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRGCSendDemo/IRGCSendDemo.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRGCSendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRGCTCPServer/IRGCTCPServer.ino (97%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRGCTCPServer/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRMQTTServer/IRMQTTServer.h (54%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRMQTTServer/IRMQTTServer.ino (68%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRServer/IRServer.ino (80%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRServer/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRrecvDemo/IRrecvDemo.ino (94%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRrecvDump/IRrecvDump.ino (99%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDump/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRrecvDumpV2/IRrecvDumpV2.ino (51%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDumpV2/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRsendDemo/IRsendDemo.ino (94%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRsendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/IRsendProntoDemo/IRsendProntoDemo.ino (98%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/IRsendProntoDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/JVCPanasonicSendDemo/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/LGACSend/LGACSend.ino (100%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/LGACSend/platformio.ini create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/SmartIRRepeater.ino create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnArgoAC/TurnOnArgoAC.ino (93%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnArgoAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino (95%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnDaikinAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino (90%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnFujitsuAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnKelvinatorAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiHeavyAc/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnPanasonicAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnToshibaAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino (95%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/examples/TurnOnTrotecAC/platformio.ini rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/keywords.txt (86%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/library.json (79%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/library.properties (54%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/pylintrc (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/CPPLINT.cfg (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRac.cpp (50%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRac.h (73%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRrecv.cpp (58%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRrecv.h (63%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRremoteESP8266.h (88%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRsend.cpp (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRsend.h (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRtimer.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRtimer.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/IRutils.cpp (58%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/IRutils.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Aiwa.cpp (98%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Argo.h (52%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Carrier.cpp (67%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Coolix.cpp (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Coolix.h (84%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Daikin.cpp (51%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Daikin.h (66%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Denon.cpp (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Dish.cpp (70%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.cpp create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.h create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Fujitsu.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Fujitsu.h (51%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_GICable.cpp (70%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_GlobalCache.cpp (86%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Goodweather.cpp create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Goodweather.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Gree.cpp (61%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Gree.h (68%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Haier.cpp (66%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Haier.h (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Hitachi.cpp (64%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Hitachi.h (68%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Inax.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_JVC.cpp (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Kelvinator.cpp (59%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Kelvinator.h (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_LG.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_LG.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Lasertag.cpp (91%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Lego.cpp (74%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Lutron.cpp (90%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_MWM.cpp (94%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Magiquest.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Magiquest.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Midea.cpp (69%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Midea.h (75%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Mitsubishi.cpp (72%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Mitsubishi.h (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_MitsubishiHeavy.cpp (73%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_MitsubishiHeavy.h (86%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_NEC.cpp (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_NEC.h (85%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.cpp create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Nikai.cpp (58%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Panasonic.cpp (69%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Panasonic.h (67%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Pioneer.cpp (67%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Pronto.cpp (92%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_RC5_RC6.cpp (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_RCMM.cpp (94%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Samsung.cpp (64%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Samsung.h (53%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Sanyo.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.cpp create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.h rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Sherwood.cpp (66%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Sony.cpp (88%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Tcl.cpp (69%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Tcl.h (84%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Teco.cpp (61%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Teco.h (92%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Toshiba.cpp (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Toshiba.h (62%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/src/ir_Trotec.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Trotec.h (63%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Vestel.cpp (71%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Vestel.h (83%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Whirlpool.cpp (62%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Whirlpool.h (75%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/src/ir_Whynter.cpp (69%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/IRac_test.cpp (62%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/IRrecv_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/IRrecv_test.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/IRsend_test.cpp (84%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/IRsend_test.h (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/IRutils_test.cpp (77%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/Makefile (91%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Aiwa_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/ir_Argo_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Carrier_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Coolix_test.cpp (80%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Daikin_test.cpp (79%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Denon_test.cpp (91%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Dish_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/ir_Electra_test.cpp create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/ir_Fujitsu_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_GICable_test.cpp (96%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_GlobalCache_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/ir_Goodweather_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Gree_test.cpp (85%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Haier_test.cpp (90%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Hitachi_test.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/ir_Inax_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_JVC_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Kelvinator_test.cpp (93%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_LG_test.cpp (99%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Lasertag_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Lego_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Lutron_test.cpp (98%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_MWM_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Magiquest_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Midea_test.cpp (95%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_MitsubishiHeavy_test.cpp (87%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Mitsubishi_test.cpp (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_NEC_test.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/ir_Neoclima_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Nikai_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Panasonic_test.cpp (95%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Pioneer_test.cpp (95%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Pronto_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_RC5_RC6_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_RCMM_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Samsung_test.cpp (75%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Sanyo_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Sharp_test.cpp (52%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Sherwood_test.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Sony_test.cpp (96%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Tcl_test.cpp (78%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Teco_test.cpp (90%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Toshiba_test.cpp (96%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/test/ir_Trotec_test.cpp rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Vestel_test.cpp (88%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Whirlpool_test.cpp (93%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/test/ir_Whynter_test.cpp (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/tools/Makefile (87%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/tools/RawToGlobalCache.sh (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/tools/auto_analyse_raw_data.py (98%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/tools/auto_analyse_raw_data_test.py (97%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/tools/gc_decode.cpp (100%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/tools/mkkeywords (82%) rename lib/{IRremoteESP8266-2.6.0 => IRremoteESP8266-2.6.3.10}/tools/mode2_decode.cpp (100%) create mode 100644 lib/IRremoteESP8266-2.6.3.10/tools/scrape_supported_devices.py diff --git a/lib/IRremoteESP8266-2.6.0/.travis.yml b/lib/IRremoteESP8266-2.6.0/.travis.yml deleted file mode 100644 index ae2d9fe3c..000000000 --- a/lib/IRremoteESP8266-2.6.0/.travis.yml +++ /dev/null @@ -1,66 +0,0 @@ -language: c -env: - - BD=esp8266:esp8266:nodemcuv2:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled - - BD=esp8266:esp8266:d1_mini:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled -before_install: - - "/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 - - export DISPLAY=:1.0 - - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz - - tar xf arduino-1.8.8-linux64.tar.xz - - sudo mv arduino-1.8.8 /usr/local/share/arduino - - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino - - wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py -install: - - ln -s $PWD /usr/local/share/arduino/libraries/ - - git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager - - git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient - - git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson - - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs - - arduino --install-boards esp8266:esp8266 - - arduino --board $BD --save-prefs - - arduino --pref "compiler.warning_level=all" --save-prefs - - sudo apt-get install jq - - sudo pip install pylint -script: - # Check that everything compiles. - - arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino - - arduino --verify --board $BD $PWD/examples/IRGCSendDemo/IRGCSendDemo.ino - - arduino --verify --board $BD $PWD/examples/IRGCTCPServer/IRGCTCPServer.ino - - arduino --verify --board $BD $PWD/examples/IRServer/IRServer.ino - - arduino --verify --board $BD $PWD/examples/IRrecvDumpV2/IRrecvDumpV2.ino - - arduino --verify --board $BD $PWD/examples/IRsendDemo/IRsendDemo.ino - - arduino --verify --board $BD $PWD/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino - - arduino --verify --board $BD $PWD/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino - - arduino --verify --board $BD $PWD/examples/IRsendProntoDemo/IRsendProntoDemo.ino - - arduino --verify --board $BD $PWD/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino - - arduino --verify --board $BD $PWD/examples/LGACSend/LGACSend.ino - - arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino - - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino - - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino - - arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino - - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino - - # Also check the tools programs compile. - - (cd tools; make all) - # Check for lint issues. - - shopt -s nullglob - - python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino} - - pylint {src,test,tools}/*.py - - shopt -u nullglob - # Build and run the unit tests. - - (cd test; make run) - - (cd tools; make run_tests) - # Check the version numbers match. - - LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2) - - test ${LIB_VERSION} == "$(jq -r .version library.json)" - - grep -q "^version=${LIB_VERSION}$" library.properties - -notifications: - email: - on_success: change - on_failure: change diff --git a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini deleted file mode 100644 index 243b36a99..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/platformio.ini +++ /dev/null @@ -1,42 +0,0 @@ -[platformio] -lib_extra_dirs = ../../ -src_dir=. - -[common] -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 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} - -[env:d1_mini] -platform=espressif8266 -framework=arduino -board=d1_mini -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} - -[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-2.6.0/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRServer/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/LGACSend/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini deleted file mode 100644 index ec84f22f3..000000000 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/platformio.ini +++ /dev/null @@ -1,19 +0,0 @@ -[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-2.6.0/platformio.ini b/lib/IRremoteESP8266-2.6.0/platformio.ini deleted file mode 100644 index b6020c165..000000000 --- a/lib/IRremoteESP8266-2.6.0/platformio.ini +++ /dev/null @@ -1,29 +0,0 @@ -[platformio] -lib_extra_dirs = . -src_dir = examples/IRrecvDumpV2 - -[common] -build_flags = -lib_deps_builtin = -lib_deps_external = -lib_ldf_mode = chain+ - -[env:nodemcuv2] -platform = espressif8266 -framework = arduino -board = nodemcuv2 -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} - -[env:d1_mini] -platform = espressif8266 -framework = arduino -board = d1_mini -lib_ldf_mode = ${common.lib_ldf_mode} -build_flags = ${common.build_flags} -lib_deps = - ${common.lib_deps_builtin} - ${common.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/src/IRutils.h b/lib/IRremoteESP8266-2.6.0/src/IRutils.h deleted file mode 100644 index 0d0b677b5..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/IRutils.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef IRUTILS_H_ -#define IRUTILS_H_ - -// Copyright 2017 David Conran - -#ifndef UNIT_TEST -#include -#endif -#define __STDC_LIMIT_MACROS -#include -#ifndef ARDUINO -#include -#endif -#include "IRremoteESP8266.h" -#include "IRrecv.h" - -uint64_t reverseBits(uint64_t input, uint16_t nbits); -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. -String uint64ToString(uint64_t input, uint8_t base = 10); -String typeToString(const decode_type_t protocol, - const bool isRepeat = false); -void serialPrintUint64(uint64_t input, uint8_t base = 10); -String resultToSourceCode(const decode_results *results); -String resultToTimingInfo(const decode_results *results); -String resultToHumanReadableBasic(const decode_results *results); -String resultToHexidecimal(const decode_results *result); -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 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, - 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); -#endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp deleted file mode 100644 index d6711acd3..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* -Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote -Controls Argo Ulisse 13 DCI A/C -Copyright 2017 Schmolders -*/ - -#include "ir_Argo.h" -#include -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// Constants -// using SPACE modulation. MARK is always const 400u -const uint16_t kArgoHdrMark = 6400; -const uint16_t kArgoHdrSpace = 3300; -const uint16_t kArgoBitMark = 400; -const uint16_t kArgoOneSpace = 2200; -const uint16_t kArgoZeroSpace = 900; - -#if SEND_ARGO -// Send an Argo A/C message. -// -// Args: -// data: An array of kArgoStateLength bytes containing the IR command. -// -// Status: ALPHA / Untested. - -void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) { - // Check if we have enough bytes to send a proper message. - if (nbytes < kArgoStateLength) return; - // TODO(kaschmo): validate - sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, - kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. - data, nbytes, 38, false, repeat, kDutyDefault); -} -#endif // SEND_ARGO - -IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRArgoAC::begin() { _irsend.begin(); } - -#if SEND_ARGO -void IRArgoAC::send(const uint16_t repeat) { - 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; - - // 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]; - - sum = sum % 256; // modulo 256 - // Append sum to end of array - // Set const part of checksum bit 10 - argo[10] = 0b00000010; - argo[10] += sum << 2; // Shift up 2 bits and append to byte 10 - argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11 -} - -void IRArgoAC::stateReset() { - for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0; - - // Argo Message. Store MSB left. - // Default message: - argo[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble - argo[1] = 0b11110101; // LSB first: 0b10101111; //const preamble - // Keep payload 2-9 at zero - argo[10] = 0b00000010; // Const 01, checksum 6bit - argo[11] = 0b00000000; // Checksum 2bit - - this->off(); - this->setTemp(20); - this->setRoomTemp(25); - this->setCoolMode(kArgoCoolAuto); - this->setFan(kArgoFanAuto); -} - -uint8_t* IRArgoAC::getRaw() { - checksum(); // Ensure correct bit array before returning - return argo; -} - -void IRArgoAC::on() { - // state = ON; - ac_state = 1; - // Bit 5 of byte 9 is on/off - // in MSB first - argo[9] = argo[9] | 0b00100000; // Set ON/OFF bit to 1 -} - -void IRArgoAC::off() { - // state = OFF; - ac_state = 0; - // in MSB first - // bit 5 of byte 9 to off - argo[9] = argo[9] & 0b11011111; // Set on/off bit to 0 -} - -void IRArgoAC::setPower(bool state) { - if (state) - on(); - else - off(); -} - -uint8_t IRArgoAC::getPower() { return ac_state; } - -void IRArgoAC::setMax(bool state) { - max_mode = state; - if (max_mode) - argo[9] |= 0b00001000; - else - argo[9] &= 0b11110111; -} - -bool IRArgoAC::getMax() { return max_mode; } - -// 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; - // offset 4 degrees. "If I want 12 degrees, I need to send 8" - temp -= 4; - // 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] += temp << 6; // append to bit 6,7 - argo[3] += temp >> 2; // remove lowest to bits and append in 0-2 -} - -uint8_t IRArgoAC::getTemp() { return set_temp; } - -// 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; - // Mask out bits - argo[3] &= 0b11100111; - // Set fan mode at bit positions - argo[3] += fan << 3; -} - -uint8_t IRArgoAC::getFan() { return fan_mode; } - -void IRArgoAC::setFlap(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; - - // Set cool mode at bit positions - argo[2] += mode << 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; -} - -uint8_t IRArgoAC::getHeatMode() { return heat_mode; } - -void IRArgoAC::setNight(bool state) { - night_mode = state; - if (night_mode) - // Set bit at night position: bit 2 - argo[9] |= 0b00000100; - else - argo[9] &= 0b11111011; -} - -bool IRArgoAC::getNight() { return night_mode; } - -void IRArgoAC::setiFeel(bool state) { - ifeel_mode = state; - if (ifeel_mode) - // Set bit at iFeel position: bit 7 - argo[9] |= 0b10000000; - else - argo[9] &= 0b01111111; -} - -bool IRArgoAC::getiFeel() { return ifeel_mode; } - -void IRArgoAC::setTime() { - // TODO(kaschmo): use function call from checksum to set time first -} - -void IRArgoAC::setRoomTemp(uint8_t temp) { - temp -= 4; - // Mask out bits - argo[3] &= 0b00011111; - argo[4] &= 0b11111100; - - 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-2.6.0/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp deleted file mode 100644 index 0700ab698..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Electra.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2018, 2019 David Conran - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// EEEEEEE LL EEEEEEE CCCCC TTTTTTT RRRRRR AAA -// EE LL EE CC C TTT RR RR AAAAA -// EEEEE LL EEEEE CC TTT RRRRRR AA AA -// EE LL EE CC C TTT RR RR AAAAAAA -// EEEEEEE LLLLLLL EEEEEEE CCCCC TTT RR RR AA AA - -// Electra A/C added by crankyoldgit -// -// Equipment it seems compatible with: -// * - -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/527 -// https://github.com/markszabo/IRremoteESP8266/issues/642 - -// Constants -const uint16_t kElectraAcHdrMark = 9166; -const uint16_t kElectraAcBitMark = 646; -const uint16_t kElectraAcHdrSpace = 4470; -const uint16_t kElectraAcOneSpace = 1647; -const uint16_t kElectraAcZeroSpace = 547; -const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess. - -#if SEND_ELECTRA_AC -// Send a Electra message -// -// Args: -// data: Contents of the message to be sent. (Guessing MSBF order) -// nbits: Nr. of bits of data to be sent. Typically kElectraAcBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: Alpha / Needs testing against a real device. -// -void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) - sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark, - kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, - kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, - 38000, // Complete guess of the modulation frequency. - false, // Send data in LSB order per byte - 0, 50); -} -#endif - -#if DECODE_ELECTRA_AC -// Decode the supplied Electra A/C 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 kElectraAcBits. -// 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. -// -bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, - bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - if (strict) { - if (nbits != kElectraAcBits) - return false; // Not strictly a ELECTRA_AC message. - } - - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid ELECTRA_AC message. - - uint16_t offset = kStartOffset; - - // Message Header - if (!matchMark(results->rawbuf[offset++], kElectraAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kElectraAcHdrSpace)) return false; - - // Data Section - match_result_t data_result; - uint16_t dataBitsSoFar = 0; - // Keep reading bytes until we either run out of section 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, kElectraAcBitMark, - kElectraAcOneSpace, kElectraAcBitMark, - kElectraAcZeroSpace, kTolerance, 0, false); - if (data_result.success == false) return false; // Fail - results->state[i] = data_result.data; - } - - // Message Footer - if (!matchMark(results->rawbuf[offset++], kElectraAcBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kElectraAcMessageGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - // Verify the checksum. - if (sumBytes(results->state, (dataBitsSoFar / 8) - 1) != - results->state[(dataBitsSoFar / 8) - 1]) return false; - } - - // Success - results->decode_type = ELECTRA_AC; - 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_ELECTRA_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp deleted file mode 100644 index de1b97e87..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.cpp +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright 2017 Jonny Graham, David Conran -#include "ir_Fujitsu.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRutils.h" - -// Fujitsu A/C support added by Jonny Graham & David Conran - -// Equipment it seems compatible with: -// * Fujitsu ASYG30LFCA with remote AR-RAH2E -// * Fujitsu AST9RSGCW with remote AR-DB1 -// * - -// Ref: -// These values are based on averages of measurements -const uint16_t kFujitsuAcHdrMark = 3324; -const uint16_t kFujitsuAcHdrSpace = 1574; -const uint16_t kFujitsuAcBitMark = 448; -const uint16_t kFujitsuAcOneSpace = 1182; -const uint16_t kFujitsuAcZeroSpace = 390; -const uint16_t kFujitsuAcMinGap = 8100; - -#if SEND_FUJITSU_AC -// Send a Fujitsu A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. Typically one of: -// kFujitsuAcStateLength -// kFujitsuAcStateLength - 1 -// kFujitsuAcStateLengthShort -// kFujitsuAcStateLengthShort - 1 -// repeat: Nr. of times the message is to be repeated. -// (Default = kFujitsuAcMinRepeat). -// -// Status: BETA / Appears to be working. -// -void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC - -// Code to emulate Fujitsu A/C IR remote control unit. - -// Initialise the object. -IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model) - : _irsend(pin) { - setModel(model); - stateReset(); -} - -void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) { - _model = model; - switch (model) { - case ARDB1: - _state_length = kFujitsuAcStateLength - 1; - _state_length_short = kFujitsuAcStateLengthShort - 1; - break; - default: - _state_length = kFujitsuAcStateLength; - _state_length_short = kFujitsuAcStateLengthShort; - } -} - -// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset() { - _temp = 24; - _fanSpeed = kFujitsuAcFanHigh; - _mode = kFujitsuAcModeCool; - _swingMode = kFujitsuAcSwingBoth; - _cmd = kFujitsuAcCmdTurnOn; - buildState(); -} - -// Configure the pin for output. -void IRFujitsuAC::begin() { _irsend.begin(); } - -#if SEND_FUJITSU_AC -// Send the current desired state to the IR LED. -void IRFujitsuAC::send(const uint16_t repeat) { - getRaw(); - _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); -} -#endif // SEND_FUJITSU_AC - -void IRFujitsuAC::buildState() { - remote_state[0] = 0x14; - remote_state[1] = 0x63; - remote_state[2] = 0x00; - remote_state[3] = 0x10; - 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; - break; - default: - switch (_model) { - case ARRAH2E: - remote_state[5] = 0xFE; - break; - case ARDB1: - remote_state[5] = 0xFC; - break; - } - fullCmd = true; - break; - } - if (fullCmd) { // long codes - uint8_t tempByte = _temp - kFujitsuAcMinTemp; - // Nr. of bytes in the message after this byte. - remote_state[6] = _state_length - 7; - - 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[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; - - 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; - } - // 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]; - // 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)) - return _state_length_short; - else - return _state_length; -} - -// Return a pointer to the internal state date of the remote. -uint8_t* IRFujitsuAC::getRaw() { - buildState(); - return remote_state; -} - -void IRFujitsuAC::buildFromState(const uint16_t length) { - switch (length) { - case kFujitsuAcStateLength - 1: - case kFujitsuAcStateLengthShort - 1: - setModel(ARDB1); - break; - default: - setModel(ARRAH2E); - } - switch (remote_state[6]) { - case 8: - setModel(ARDB1); - break; - case 9: - setModel(ARRAH2E); - break; - } - setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp); - if (remote_state[8] & 0x1) - setCmd(kFujitsuAcCmdTurnOn); - else - setCmd(kFujitsuAcCmdStayOn); - setMode(remote_state[9] & 0b111); - setFanSpeed(remote_state[10] & 0b111); - setSwing(remote_state[10] >> 4); - switch (remote_state[5]) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdStepVert: - setCmd(remote_state[5]); - break; - } -} - -bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAcStateLength) return false; - for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { - if (i < length) - remote_state[i] = newState[i]; - else - remote_state[i] = 0; - } - buildFromState(length); - return true; -} - -// Set the requested power state of the A/C to off. -void IRFujitsuAC::off() { _cmd = kFujitsuAcCmdTurnOff; } - -void IRFujitsuAC::stepHoriz() { - switch (_model) { - case ARDB1: - break; // This remote doesn't have a horizontal option. - default: - _cmd = kFujitsuAcCmdStepHoriz; - } -} - -void IRFujitsuAC::stepVert() { _cmd = kFujitsuAcCmdStepVert; } - -// Set the requested command of the A/C. -void IRFujitsuAC::setCmd(uint8_t cmd) { - switch (cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdTurnOn: - case kFujitsuAcCmdStayOn: - case kFujitsuAcCmdStepVert: - _cmd = cmd; - break; - case kFujitsuAcCmdStepHoriz: - if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal. - _cmd = cmd; - // FALLTHRU - default: - _cmd = kFujitsuAcCmdStayOn; - break; - } -} - -uint8_t IRFujitsuAC::getCmd() { return _cmd; } - -bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; } - -// 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; -} - -uint8_t IRFujitsuAC::getTemp() { return _temp; } - -// Set the speed of the fan -void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) { - if (fanSpeed > kFujitsuAcFanQuiet) - fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - _fanSpeed = fanSpeed; -} -uint8_t IRFujitsuAC::getFanSpeed() { return _fanSpeed; } - -// Set the requested climate operation mode of the a/c unit. -void IRFujitsuAC::setMode(uint8_t mode) { - if (mode > kFujitsuAcModeHeat) - mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - _mode = 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) { - case ARDB1: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) swingMode = kFujitsuAcSwingVert; - break; - case ARRAH2E: - default: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) swingMode = kFujitsuAcSwingBoth; - } - _swingMode = swingMode; -} - -uint8_t IRFujitsuAC::getSwing() { return _swingMode; } - -bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = state[length - 1]; - switch (length) { - case kFujitsuAcStateLengthShort: // ARRAH2E - return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 - sum = sumBytes(state, length - 1); - sum_complement = 0x9B; - break; - case kFujitsuAcStateLength: // ARRAH2E - sum = sumBytes(state + kFujitsuAcStateLengthShort, - length - 1 - kFujitsuAcStateLengthShort); - break; - default: // Includes ARDB1 short. - return true; // Assume the checksum is valid for other lengths. - } - return checksum == (uint8_t)(sum_complement - sum); // Does it match? -} - -// Convert a standard A/C mode into its native mode. -uint8_t IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kFujitsuAcModeCool; - case stdAc::opmode_t::kHeat: - return kFujitsuAcModeHeat; - case stdAc::opmode_t::kDry: - return kFujitsuAcModeDry; - case stdAc::opmode_t::kFan: - return kFujitsuAcModeFan; - default: - return kFujitsuAcModeAuto; - } -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - return kFujitsuAcFanQuiet; - case stdAc::fanspeed_t::kLow: - return kFujitsuAcFanLow; - case stdAc::fanspeed_t::kMedium: - return kFujitsuAcFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kFujitsuAcFanHigh; - default: - return kFujitsuAcFanAuto; - } -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRFujitsuAC::toString() { - String result = ""; -#else -std::string IRFujitsuAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kFujitsuAcModeAuto: - result += F(" (AUTO)"); - break; - case kFujitsuAcModeCool: - result += F(" (COOL)"); - break; - case kFujitsuAcModeHeat: - result += F(" (HEAT)"); - break; - case kFujitsuAcModeDry: - result += F(" (DRY)"); - break; - case kFujitsuAcModeFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFanSpeed()); - switch (getFanSpeed()) { - case kFujitsuAcFanAuto: - result += F(" (AUTO)"); - break; - case kFujitsuAcFanHigh: - result += F(" (HIGH)"); - break; - case kFujitsuAcFanMed: - result += F(" (MED)"); - break; - case kFujitsuAcFanLow: - result += F(" (LOW)"); - break; - case kFujitsuAcFanQuiet: - 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"); - } - result += F(", Command: "); - switch (getCmd()) { - case kFujitsuAcCmdStepHoriz: - result += F("Step vane horizontally"); - break; - case kFujitsuAcCmdStepVert: - result += F("Step vane vertically"); - break; - default: - result += F("N/A"); - } - return result; -} - -#if DECODE_FUJITSU_AC -// Decode a Fujitsu AC IR message if possible. -// Places successful decode information in the results pointer. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kFujitsuAcBits. -// 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: ALPHA / Untested. -// -// Ref: -// -bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits, - bool strict) { - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAcBits: - case kFujitsuAcBits - 8: - case kFujitsuAcMinBits: - case kFujitsuAcMinBits + 8: - break; - default: - return false; // Must be called with the correct nr. of bits. - } - } - - // Header - if (!matchMark(results->rawbuf[offset++], kFujitsuAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kFujitsuAcHdrSpace)) return false; - - // Data (Fixed signature) - match_result_t data_result = - matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8, - kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark, - kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; // Fail - if (data_result.data != 0x1010006314) return false; // Signature failed. - dataBitsSoFar += kFujitsuAcMinBits - 8; - offset += data_result.used; - results->state[0] = 0x14; - results->state[1] = 0x63; - results->state[2] = 0x00; - results->state[3] = 0x10; - results->state[4] = 0x10; - - // Keep reading bytes until we either run out of message or state to fill. - for (uint16_t i = 5; - offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Footer - if (offset > results->rawlen || - !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) - return false; - // The space is optional if we are out of capture. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - results->decode_type = FUJITSU_AC; - results->bits = dataBitsSoFar; - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAcMinBits: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFC) return false; - return true; // Success - case kFujitsuAcMinBits + 8: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFE) return false; - // The last byte needs to be the inverse of the penultimate byte. - if (results->state[5] != (uint8_t)~results->state[6]) return false; - return true; // Success - case kFujitsuAcBits - 8: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFC) return false; - break; - case kFujitsuAcBits: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - return true; // All good. -} -#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_LG.h b/lib/IRremoteESP8266-2.6.0/src/ir_LG.h deleted file mode 100644 index 25d56bc26..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_LG.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 David Conran - -#ifndef IR_LG_H_ -#define IR_LG_H_ - -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - -#define __STDC_LIMIT_MACROS -#include - -uint8_t calcLGChecksum(uint16_t data); - -#endif // IR_LG_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp deleted file mode 100644 index ae1b59c74..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sharp.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// SSSS H H AAA RRRR PPPP -// S H H A A R R P P -// SSS HHHHH AAAAA RRRR PPPP -// S H H A A R R P -// SSSS H H A A R R P - -// Equipment it seems compatible with: -// * Sharp LC-52D62U -// * -// - -// Constants -// period time = 1/38000Hz = 26.316 microseconds. -// Ref: -// GlobalCache's IR Control Tower data. -// http://www.sbprojects.com/knowledge/ir/sharp.php -const uint16_t kSharpTick = 26; -const uint16_t kSharpBitMarkTicks = 10; -const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; -const uint16_t kSharpOneSpaceTicks = 70; -const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; -const uint16_t kSharpZeroSpaceTicks = 30; -const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; -const uint16_t kSharpGapTicks = 1677; -const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; -// Address(5) + Command(8) + Expansion(1) + Check(1) -const uint64_t kSharpToggleMask = - ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; -const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; -const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; - -#if (SEND_SHARP || SEND_DENON) -// Send a (raw) Sharp message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically kSharpBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: BETA / Previously working fine. -// -// Notes: -// This procedure handles the inversion of bits required per protocol. -// The protocol spec says to send the LSB first, but legacy code & usage -// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() -// handles this for you, assuming you are using the correct/standard values. -// e.g. sendSharpRaw(encodeSharp(address, command)); -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// 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) { - 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. - // Note: Previously this used to be performed 3 times (normal, inverted, - // normal), however all data points to that being incorrect. - for (uint8_t n = 0; n < 2; n++) { - sendGeneric(0, 0, // No Header - kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, data, 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; - } - } -} - -// Encode a (raw) Sharp message from it's components. -// -// Args: -// address: The value of the address to be sent. -// command: The value of the address to be sent. (8 bits) -// expansion: The value of the expansion bit to use. (0 or 1, typically 1) -// check: The value of the check bit to use. (0 or 1, typically 0) -// MSBfirst: Flag indicating MSB first or LSB first order. (Default: false) -// Returns: -// An uint32_t containing the raw Sharp message for sendSharpRaw(). -// -// Status: BETA / Should work okay. -// -// Notes: -// Assumes the standard Sharp bit sizes. -// Historically sendSharp() sends address & command in -// MSB first order. This is actually incorrect. It should be sent in LSB -// order. The behaviour of sendSharp() hasn't been changed to maintain -// backward compatibility. -// -// Ref: -// 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) { - // Mask any unexpected bits. - address &= ((1 << kSharpAddressBits) - 1); - command &= ((1 << kSharpCommandBits) - 1); - expansion &= 1; - check &= 1; - - if (!MSBfirst) { // Correct bit order if needed. - address = reverseBits(address, kSharpAddressBits); - command = reverseBits(command, kSharpCommandBits); - } - // Concatinate all the bits. - return (address << (kSharpCommandBits + 2)) | (command << 2) | - (expansion << 1) | check; -} - -// Send a Sharp message -// -// Args: -// address: Address value to be sent. -// command: Command value to be sent. -// nbits: Nr. of bits of data to be sent. Typically kSharpBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: DEPRICATED / Previously working fine. -// -// Notes: -// This procedure has a non-standard invocation style compared to similar -// sendProtocol() routines. This is due to legacy, compatibility, & historic -// reasons. Normally the calling syntax version is like sendSharpRaw(). -// This procedure transmits the address & command in MSB first order, which is -// incorrect. This behaviour is left as-is to maintain backward -// compatibility with legacy code. -// In short, you should use sendSharpRaw(), encodeSharp(), and the correct -// values of address & command instead of using this, & the wrong values. -// -// Ref: -// 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) { - sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); -} -#endif // (SEND_SHARP || SEND_DENON) - -#if (DECODE_SHARP || DECODE_DENON) -// Decode the supplied Sharp message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. Typically kSharpBits. -// strict: Flag indicating if we should perform strict matching. -// expansion: Should we expect the expansion bit to be set. Default is true. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE / Working fine. -// -// Note: -// This procedure returns a value suitable for use in sendSharpRaw(). -// TODO(crankyoldgit): Need to ensure capture of the inverted message as it can -// be missed due to the interrupt timeout used to detect an end of message. -// Several compliance checks are disabled until that is resolved. -// Ref: -// 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) { - if (results->rawlen < 2 * nbits + kFooter - 1) - return false; // Not enough entries to be a Sharp message. - // Compliance - if (strict) { - if (nbits != kSharpBits) return false; // Request is out of spec. - // DISABLED - See TODO -#ifdef UNIT_TEST - // An in spec message has the data sent normally, then inverted. So we - // expect twice as many entries than to just get the results. - if (results->rawlen < 2 * (2 * nbits + kFooter)) return false; -#endif - } - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // No header - // But try to auto-calibrate off the initial mark signal. - if (!matchMark(results->rawbuf[offset], kSharpBitMark, 35)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset] * kRawTick / kSharpBitMarkTicks; - // Data - for (uint16_t i = 0; i < nbits; i++, offset++) { - // Use a higher tolerance value for kSharpBitMark as it is quite small. - if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35)) - return false; - if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick)) - data = (data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick)) - data <<= 1; // 0 - else - return false; - } - - // Footer - if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick)) - return false; - - // Compliance - if (strict) { - // Check the state of the expansion bit is what we expect. - if ((data & 0b10) >> 1 != expansion) return false; - // The check bit should be cleared in a normal message. - if (data & 0b1) return false; - // DISABLED - See TODO -#ifdef UNIT_TEST - // Grab the second copy of the data (i.e. inverted) - // Header - // i.e. The inter-data/command repeat gap. - if (!matchSpace(results->rawbuf[offset++], kSharpGapTicks * tick)) - return false; - - // Data - uint64_t second_data = 0; - for (uint16_t i = 0; i < nbits; i++, offset++) { - // Use a higher tolerance value for kSharpBitMark as it is quite small. - if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35)) - return false; - if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick)) - second_data = (second_data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick)) - second_data <<= 1; // 0 - else - return false; - } - // Footer - if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick)) - return false; - - // Check that second_data has been inverted correctly. - if (data != (second_data ^ kSharpToggleMask)) return false; -#endif // UNIT_TEST - } - - // Success - results->decode_type = SHARP; - results->bits = nbits; - results->value = data; - // Address & command are actually transmitted in LSB first order. - results->address = reverseBits(data, nbits) & kSharpAddressMask; - results->command = - reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); - return true; -} -#endif // (DECODE_SHARP || DECODE_DENON) diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp deleted file mode 100644 index b5c15e7fd..000000000 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2017 stufisher - -#include "ir_Trotec.h" -#include -#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 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) { - 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, - 0, // Repeats handled elsewhere - 50); - // More footer - enableIROut(36); - mark(kTrotecOneMark); - space(kTrotecGapEnd); - } -} -#endif // SEND_TROTEC - -IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRTrotecESP::begin() { _irsend.begin(); } - -#if SEND_TROTEC -void IRTrotecESP::send(const uint16_t repeat) { - checksum(); - _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); -} -#endif // SEND_TROTEC - -void IRTrotecESP::checksum() { - uint8_t sum = 0; - - 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++) remote_state[i] = 0x0; - - remote_state[0] = kTrotecIntro1; - remote_state[1] = kTrotecIntro2; - - setPower(false); - setTemp(kTrotecDefTemp); - setSpeed(kTrotecFanMed); - setMode(kTrotecAuto); -} - -uint8_t* IRTrotecESP::getRaw() { - checksum(); - return remote_state; -} - -void IRTrotecESP::setPower(const bool on) { - if (on) - remote_state[2] |= kTrotecPowerBit; - else - remote_state[2] &= ~kTrotecPowerBit; -} - -bool IRTrotecESP::getPower() { 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; } - -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 remote_state[2] & 0b00000011; } - -void IRTrotecESP::setTemp(const uint8_t celsius) { - uint8_t temp = std::max(celsius, kTrotecMinTemp); - temp = std::min(temp, kTrotecMaxTemp); - remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); -} - -uint8_t IRTrotecESP::getTemp() { - return (remote_state[3] & 0b01111111) + kTrotecMinTemp; -} - -void IRTrotecESP::setSleep(bool sleep) { - if (sleep) - remote_state[3] |= kTrotecSleepBit; - else - remote_state[3] &= ~kTrotecSleepBit; -} - -bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } - -void IRTrotecESP::setTimer(const uint8_t timer) { - if (timer) - remote_state[5] |= kTrotecTimerBit; - else - remote_state[5] &= ~kTrotecTimerBit; - remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; -} - -uint8_t IRTrotecESP::getTimer() { return remote_state[6]; } - -// Convert a standard A/C mode into its native mode. -uint8_t IRTrotecESP::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: - return kTrotecCool; - case stdAc::opmode_t::kDry: - return kTrotecDry; - case stdAc::opmode_t::kFan: - return kTrotecFan; - // Note: No Heat mode. - default: - return kTrotecAuto; - } -} - -// Convert a standard A/C Fan speed into its native fan speed. -uint8_t IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kTrotecFanLow; - case stdAc::fanspeed_t::kMedium: - return kTrotecFanMed; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kTrotecFanHigh; - default: - return kTrotecFanMed; - } -} diff --git a/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp deleted file mode 100644 index 85b6685f0..000000000 --- a/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.cpp +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRrecv_test.h" -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for the IRrecv object. -TEST(TestIRrecv, DefaultBufferSize) { - IRrecv irrecv_default(1); - EXPECT_EQ(kRawBuf, irrecv_default.getBufSize()); -} - -TEST(TestIRrecv, LargeBufferSize) { - IRrecv irrecv_large(3, 1024); - EXPECT_EQ(1024, irrecv_large.getBufSize()); -} - -TEST(TestIRrecv, SmallBufferSize) { - IRrecv irrecv_small(4, 80); - EXPECT_EQ(80, irrecv_small.getBufSize()); -} - -TEST(TestIRrecv, MediumBufferSize) { - IRrecv irrecv_medium(4, 512); - EXPECT_EQ(512, irrecv_medium.getBufSize()); -} - -TEST(TestIRrecv, IRrecvDestructor) { - IRrecv *irrecv_ptr = new IRrecv(1); - EXPECT_EQ(kRawBuf, irrecv_ptr->getBufSize()); - - delete irrecv_ptr; - irrecv_ptr = new IRrecv(1, 1234); - EXPECT_EQ(1234, irrecv_ptr->getBufSize()); - delete irrecv_ptr; - - irrecv_ptr = new IRrecv(1, 123); - EXPECT_EQ(123, irrecv_ptr->getBufSize()); - delete irrecv_ptr; -} - -// Tests for copyIrParams() - -TEST(TestCopyIrParams, CopyEmpty) { - irparams_t src; - irparams_t dst; - uint16_t test_size = 1234; - src.bufsize = test_size; - src.rawlen = 0; - src.rawbuf = new uint16_t[test_size]; - src.overflow = false; - dst.bufsize = 4567; - dst.rawlen = 123; - dst.rawbuf = new uint16_t[test_size]; - dst.overflow = true; - // Confirm we are looking at different memory for the buffers. - ASSERT_NE(src.rawbuf, dst.rawbuf); - - IRrecv irrecv(4); - irrecv.copyIrParams(&src, &dst); - - ASSERT_EQ(src.bufsize, dst.bufsize); - ASSERT_EQ(src.rawlen, dst.rawlen); - ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. - ASSERT_EQ(src.overflow, dst.overflow); - // Contents of the buffers needs to match. - EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); -} - -TEST(TestCopyIrParams, CopyNonEmpty) { - irparams_t src; - irparams_t dst; - uint16_t test_size = 1234; - src.bufsize = test_size; - src.rawlen = 67; - src.rawbuf = new uint16_t[test_size]; - src.rawbuf[0] = 0xF00D; - src.rawbuf[1] = 0xBEEF; - src.rawbuf[test_size - 1] = 0xDEAD; - src.overflow = true; - dst.bufsize = 0; - dst.rawlen = 0; - dst.rawbuf = new uint16_t[test_size]; - dst.overflow = false; - // Confirm we are looking at different memory for the buffers. - ASSERT_NE(src.rawbuf, dst.rawbuf); - // and that they differ before we test. - EXPECT_NE(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); - - IRrecv irrecv(4); - irrecv.copyIrParams(&src, &dst); - - ASSERT_EQ(src.bufsize, dst.bufsize); - EXPECT_EQ(test_size, dst.bufsize); - ASSERT_EQ(src.rawlen, dst.rawlen); - EXPECT_EQ(67, dst.rawlen); - ASSERT_EQ(src.overflow, dst.overflow); - EXPECT_TRUE(dst.overflow); - ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. - // Contents of the buffers needs to match. - EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); - // Check the canary values. - EXPECT_EQ(0xF00D, dst.rawbuf[0]); - EXPECT_EQ(0xBEEF, dst.rawbuf[1]); - EXPECT_EQ(0xDEAD, dst.rawbuf[test_size - 1]); -} - -// Tests for decode(). - -// Test decode of a NEC message. -TEST(TestDecode, DecodeNEC) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendNEC(0x807F40BF); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(kNECBits, irsend.capture.bits); - EXPECT_EQ(0x807F40BF, irsend.capture.value); -} - -// Test decode of a JVC message. -TEST(TestDecode, DecodeJVC) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendJVC(0xC2B8); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(JVC, irsend.capture.decode_type); - EXPECT_EQ(kJvcBits, irsend.capture.bits); - EXPECT_EQ(0xC2B8, irsend.capture.value); -} - -// Test decode of a LG message. -TEST(TestDecode, DecodeLG) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendLG(0x4B4AE51); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(LG, irsend.capture.decode_type); - EXPECT_EQ(kLgBits, irsend.capture.bits); - EXPECT_EQ(0x4B4AE51, irsend.capture.value); - - irsend.reset(); - irsend.sendLG(0xB4B4AE51, kLg32Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(LG, irsend.capture.decode_type); - EXPECT_EQ(kLg32Bits, irsend.capture.bits); - EXPECT_EQ(0xB4B4AE51, irsend.capture.value); -} - -// Test decode of a Panasonic message. -TEST(TestDecode, DecodePanasonic) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendPanasonic64(0x40040190ED7C); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodePanasonic(&irsend.capture, kPanasonicBits, true)); - EXPECT_EQ(PANASONIC, irsend.capture.decode_type); - EXPECT_EQ(kPanasonicBits, irsend.capture.bits); - EXPECT_EQ(0x40040190ED7C, irsend.capture.value); -} - -// Test decode of a Samsun message. -TEST(TestDecode, DecodeSamsung) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSAMSUNG(0xE0E09966); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SAMSUNG, irsend.capture.decode_type); - EXPECT_EQ(kSamsungBits, irsend.capture.bits); - EXPECT_EQ(0xE0E09966, irsend.capture.value); -} - -// Test decode of a Sherwood message. -TEST(TestDecode, DecodeSherwood) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSherwood(0x807F40BF); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - // Sherwood codes are really NEC codes. - EXPECT_EQ(NEC, irsend.capture.decode_type); - EXPECT_EQ(kNECBits, irsend.capture.bits); - EXPECT_EQ(0x807F40BF, irsend.capture.value); -} - -// Test decode of a Whynter message. -TEST(TestDecode, DecodeWhynter) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendWhynter(0x87654321); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(WHYNTER, irsend.capture.decode_type); - EXPECT_EQ(kWhynterBits, irsend.capture.bits); - EXPECT_EQ(0x87654321, irsend.capture.value); -} - -// Test decode of Sony messages. -TEST(TestDecode, DecodeSony) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - // Synthesised Normal Sony 20-bit message. - irsend.reset(); - irsend.sendSony(irsend.encodeSony(kSony20Bits, 0x1, 0x1, 0x1)); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(kSony20Bits, irsend.capture.bits); - EXPECT_EQ(0x81080, irsend.capture.value); - - // Synthesised Normal Sony 15-bit message. - irsend.reset(); - irsend.sendSony(irsend.encodeSony(kSony15Bits, 21, 1), kSony15Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(kSony15Bits, irsend.capture.bits); - EXPECT_EQ(0x5480, irsend.capture.value); - - // Synthesised Normal Sony 12-bit message. - irsend.reset(); - irsend.sendSony(irsend.encodeSony(kSony12Bits, 21, 1), kSony12Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SONY, irsend.capture.decode_type); - EXPECT_EQ(kSony12Bits, irsend.capture.bits); - EXPECT_EQ(0xA90, irsend.capture.value); -} - -// Test decode of Sharp messages. -TEST(TestDecode, DecodeSharp) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSharpRaw(0x454A); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SHARP, irsend.capture.decode_type); - EXPECT_EQ(kSharpBits, irsend.capture.bits); - EXPECT_EQ(0x454A, irsend.capture.value); -} - -// Test decode of Sanyo messages. -TEST(TestDecode, DecodeSanyo) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendSanyoLC7461(0x2468DCB56A9); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type); - EXPECT_EQ(kSanyoLC7461Bits, irsend.capture.bits); - EXPECT_EQ(0x2468DCB56A9, irsend.capture.value); -} - -// Test decode of RC-MM messages. -TEST(TestDecode, DecodeRCMM) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - // Normal RCMM 24-bit message. - irsend.reset(); - irsend.sendRCMM(0xe0a600); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RCMM, irsend.capture.decode_type); - EXPECT_EQ(kRCMMBits, irsend.capture.bits); - EXPECT_EQ(0xe0a600, irsend.capture.value); - - // Normal RCMM 12-bit message. - irsend.reset(); - irsend.sendRCMM(0x600, 12); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RCMM, irsend.capture.decode_type); - EXPECT_EQ(12, irsend.capture.bits); - EXPECT_EQ(0x600, irsend.capture.value); - - // Normal RCMM 32-bit message. - irsend.reset(); - irsend.sendRCMM(0x28e0a600, 32); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RCMM, irsend.capture.decode_type); - EXPECT_EQ(32, irsend.capture.bits); - EXPECT_EQ(0x28e0a600, irsend.capture.value); -} - -// Test decode of Mitsubishi messages. -TEST(TestDecode, DecodeMitsubishi) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendMitsubishi(0xC2B8); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type); - EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); - EXPECT_EQ(0xC2B8, irsend.capture.value); -} - -// Test decode of RC-5/RC-5X messages. -TEST(TestDecode, DecodeRC5) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - // Normal RC-5 12-bit message. - irsend.reset(); - irsend.sendRC5(0x175); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC5, irsend.capture.decode_type); - EXPECT_EQ(kRC5Bits, irsend.capture.bits); - EXPECT_EQ(0x175, irsend.capture.value); - // Synthesised Normal RC-5X 13-bit message. - irsend.reset(); - irsend.sendRC5(irsend.encodeRC5X(0x02, 0x41, true), kRC5XBits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC5X, irsend.capture.decode_type); - EXPECT_EQ(kRC5XBits, irsend.capture.bits); - EXPECT_EQ(0x1881, irsend.capture.value); -} - -// Test decode of RC-6 messages. -TEST(TestDecode, DecodeRC6) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - // Normal RC-6 Mode 0 (20-bit) message. - irsend.reset(); - irsend.sendRC6(0x175); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC6, irsend.capture.decode_type); - EXPECT_EQ(kRC6Mode0Bits, irsend.capture.bits); - EXPECT_EQ(0x175, irsend.capture.value); - - // Normal RC-6 36-bit message. - irsend.reset(); - irsend.sendRC6(0xC800F742A, kRC6_36Bits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(RC6, irsend.capture.decode_type); - EXPECT_EQ(kRC6_36Bits, irsend.capture.bits); - EXPECT_EQ(0xC800F742A, irsend.capture.value); -} - -// Test decode of Dish messages. -TEST(TestDecode, DecodeDish) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendDISH(0x9C00); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DISH, irsend.capture.decode_type); - EXPECT_EQ(kDishBits, irsend.capture.bits); - EXPECT_EQ(0x9C00, irsend.capture.value); -} - -// Test decode of Denon messages. -TEST(TestDecode, DecodeDenon) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - // Normal Denon 15-bit message. (Sharp) - irsend.reset(); - irsend.sendDenon(0x2278); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, irsend.capture.bits); - EXPECT_EQ(0x2278, irsend.capture.value); - // Legacy Denon 14-bit message. - irsend.reset(); - irsend.sendDenon(0x1278, kDenonLegacyBits); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_BITS, 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.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(DENON, irsend.capture.decode_type); - EXPECT_EQ(DENON_48_BITS, irsend.capture.bits); - EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); -} - -// Test decode of Coolix messages. -TEST(TestDecode, DecodeCoolix) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendCOOLIX(0x123456); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(COOLIX, irsend.capture.decode_type); - EXPECT_EQ(kCoolixBits, irsend.capture.bits); - EXPECT_EQ(0x123456, irsend.capture.value); -} - -// Test decode of Aiwa messages. -TEST(TestDecode, DecodeAiwa) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - irsend.reset(); - irsend.sendAiwaRCT501(0x7F); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type); - EXPECT_EQ(kAiwaRcT501Bits, irsend.capture.bits); - EXPECT_EQ(0x7F, irsend.capture.value); -} - -// Test matchData() on space encoded data. -TEST(TestMatchData, SpaceEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t space_encoded_raw[11] = {500, 500, 500, 1500, 499, 499, - 501, 1501, 499, 1490, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(space_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1500, 500, 500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(space_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1000, 500, 500); - ASSERT_FALSE(result.success); -} - -// Test matchData() on mark encoded data. -TEST(TestMatchData, MarkEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t mark_encoded_raw[11] = {500, 500, 1500, 500, 499, 499, - 1501, 501, 1499, 490, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(mark_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - // MSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - // LSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500, - kTolerance, kMarkExcess, false); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b11010, result.data); // Bits reversed of the previous test. - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(mark_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - // MSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500); - ASSERT_FALSE(result.success); - // LSBF order. - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500, - kTolerance, kMarkExcess, false); - ASSERT_FALSE(result.success); -} - -// Test matchData() on "equal total bit time" encoded data. -TEST(TestMatchData, EqualTotalBitTimeEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t equal_encoded_raw[11] = {500, 1500, 1500, 500, 499, 1499, - 1501, 501, 1499, 490, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(equal_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 1500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(equal_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); - ASSERT_FALSE(result.success); -} - -// Test matchData() on arbitrary encoded data. -TEST(TestMatchData, ArbitraryEncoded) { - IRsendTest irsend(0); - IRrecv irrecv(1); - irsend.begin(); - - uint16_t arbitrary_encoded_raw[11] = {500, 1500, 3000, 1000, 499, 1499, - 3001, 1001, 2999, 990, 500}; - match_result_t result; - - irsend.reset(); - irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = - irrecv.matchData(irsend.capture.rawbuf + 1, 5, 3000, 1000, 500, 1500); - ASSERT_TRUE(result.success); - EXPECT_EQ(0b01011, result.data); - EXPECT_EQ(10, result.used); - - irsend.reset(); - irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); - irsend.makeDecodeResult(); - result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); - ASSERT_FALSE(result.success); -} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp deleted file mode 100644 index 7d6d0c915..000000000 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Electra_test.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 David Conran - -#include "IRrecv.h" -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#include "gtest/gtest.h" - -// Tests for sendElectraAC(). - -// Test sending typical data only. -TEST(TestSendElectraAC, SendDataOnly) { - IRsendTest irsend(0); - irsend.begin(); - uint8_t data[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, - 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x05, 0x0D}; - - irsend.sendElectraAC(data); - EXPECT_EQ( - "f38000d50" - "m9166s4470" - "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" - "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" - "m646s547m646s1647m646s1647m646s547m646s1647m646s1647m646s1647m646s1647" - "m646s547m646s547m646s547m646s1647m646s547m646s1647m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s1647m646s1647m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" - "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" - "m646s1647m646s547m646s1647m646s547m646s547m646s547m646s547m646s547" - "m646s1647m646s547m646s1647m646s1647m646s547m646s547m646s547m646s547" - "m646s100000", - irsend.outputStr()); -} - -// Tests for decodeElectraAC(). -// Decode normal ElectraAC messages. - -TEST(TestDecodeElectraAC, SyntheticDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Synthesised Normal ElectraAC message. - irsend.reset(); - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, - 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x05, 0x0D}; - irsend.sendElectraAC(expectedState); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - EXPECT_EQ(ELECTRA_AC, irsend.capture.decode_type); - EXPECT_EQ(kElectraAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} - -// Decode a recorded example -TEST(TestDecodeElectraAC, RealExampleDecode) { - IRsendTest irsend(0); - IRrecv irrecv(0); - irsend.begin(); - - // Real ElectraAC message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/527 - uint16_t rawData[211] = { - 9166, 4470, 642, 1632, 642, 1632, 668, 534, 666, 534, 668, 534, - 614, 536, 640, 1636, 640, 1646, 694, 1662, 612, 1628, 642, 1666, - 664, 532, 668, 534, 666, 534, 666, 532, 666, 1644, 642, 532, - 640, 1634, 668, 1632, 642, 538, 666, 1660, 610, 1666, 664, 1632, - 642, 1672, 610, 536, 666, 534, 694, 532, 666, 1636, 614, 538, - 666, 1632, 642, 536, 666, 544, 692, 534, 640, 558, 640, 534, - 640, 540, 666, 534, 638, 1666, 638, 1636, 640, 550, 666, 534, - 640, 540, 666, 534, 640, 540, 666, 536, 638, 540, 666, 536, - 638, 550, 664, 536, 638, 540, 664, 536, 638, 540, 666, 534, - 638, 1640, 664, 536, 692, 546, 664, 536, 664, 536, 664, 536, - 664, 546, 612, 532, 636, 538, 664, 536, 664, 546, 612, 538, - 638, 538, 638, 538, 664, 536, 690, 538, 662, 538, 664, 538, - 662, 548, 664, 536, 662, 538, 662, 562, 638, 564, 636, 564, - 636, 1668, 582, 556, 652, 572, 612, 568, 636, 564, 610, 570, - 636, 556, 616, 550, 656, 566, 610, 570, 632, 578, 608, 1640, - 662, 562, 642, 1686, 582, 570, 634, 566, 604, 576, 636, 566, - 610, 578, 634, 1664, 584, 590, 660, 1636, 610, 1642, 664, 590, - 610, 590, 636, 566, 634, 568, 686}; // UNKNOWN 9AD8CDB5 - uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, - 0x00, 0x20, 0x00, 0x00, 0x20, - 0x00, 0x05, 0x0D}; - - irsend.reset(); - irsend.sendRaw(rawData, 211, 38000); - irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(ELECTRA_AC, irsend.capture.decode_type); - EXPECT_EQ(kElectraAcBits, irsend.capture.bits); - EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); -} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp deleted file mode 100644 index b895e4d9b..000000000 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Fujitsu_test.cpp +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2017 Jonny Graham, David Conran - -#include "IRrecv_test.h" -#include "IRsend.h" -#include "IRsend_test.h" -#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); - 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()); - - 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()); -} - -TEST(TestIRFujitsuACClass, GetRawTurnOff) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setModel(ARRAH2E); - fujitsu.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()); - - fujitsu.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()); -} - -TEST(TestIRFujitsuACClass, GetRawStepHoriz) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.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()); -} - -TEST(TestIRFujitsuACClass, GetRawStepVert) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setModel(ARRAH2E); - fujitsu.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(); - uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C}; - EXPECT_TRUE(ArraysMatch(expected_ardb1, fujitsu.getRaw())); - 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()); -} - -TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingHoriz); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanQuiet); - fujitsu.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()); -} - -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 - // but it is sent by the RC anyway - fujitsu.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); - 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()); -} - -TEST(TestIRFujitsuACClass, SetRaw) { - IRFujitsuAC fujitsu = IRFujitsuAC(0); - EXPECT_EQ(kFujitsuAcStateLength, fujitsu.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()); - // 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()); -} - -TEST(TestSendFujitsuAC, GenerateMessage) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); - irsend.begin(); - - fujitsu.setCmd(kFujitsuAcCmdStayOn); - fujitsu.setSwing(kFujitsuAcSwingBoth); - fujitsu.setMode(kFujitsuAcModeCool); - fujitsu.setFanSpeed(kFujitsuAcFanHigh); - fujitsu.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()); - - irsend.reset(); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLength); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s8100", - irsend.outputStr()); -} - -TEST(TestSendFujitsuAC, GenerateShortMessage) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); - irsend.begin(); - - fujitsu.off(); - - EXPECT_EQ(kFujitsuAcCmdTurnOff, fujitsu.getCmd()); - - irsend.reset(); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" - "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" - "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", - irsend.outputStr()); -} - -// Issue #275 -TEST(TestSendFujitsuAC, Issue275) { - IRFujitsuAC fujitsu = IRFujitsuAC(4); - IRsendTest irsend(4); - fujitsu.begin(); - irsend.begin(); - irsend.reset(); - - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), kFujitsuAcStateLengthShort); - EXPECT_EQ( - "f38000d50" - // Header - "m3324s1574" - // 0 0 1 0 1 0 0 0 (0x28) - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - // 1 1 0 0 0 1 1 0 (0xC6) - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - // 0 0 0 0 0 0 0 0 (0x00) - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 0 0 0 1 0 0 0 (0x08) - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - // 0 1 0 0 0 0 0 0 (0x40) - "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" - // 1 0 1 1 1 1 1 1 (0xBF) - "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - // Footer - "m448s8100", irsend.outputStr()); - - irsend.reset(); - // Per report in Issue #275 - uint16_t off[115] = { - 3350, 1650, - 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, - 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, - 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, - 450, 1250, 450}; - irsend.sendRaw(off, 115, 38); - EXPECT_EQ( - "f38000d50" - // Header - "m3350s1650" - // 0 0 1 0 1 0 0 0 (0x28) - "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" - // 1 1 0 0 0 1 1 0 (0xC6) - "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" - // 0 0 0 0 0 0 0 0 (0x00) - "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 0 0 0 1 0 0 0 (0x08) - "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" - // 0 1 0 0 0 0 0 0 (0x40) - "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" - // 1 0 1 1 1 1 1 1 (0xBF) - "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" - // Footer - "m450", - irsend.outputStr()); -} - -TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { - IRsendTest irsend(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - IRrecv irrecv(0); - - irsend.begin(); - irsend.reset(); - - fujitsu.setModel(ARRAH2E); - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.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)); - - irsend.reset(); - - fujitsu.setModel(ARDB1); - fujitsu.setCmd(kFujitsuAcCmdTurnOff); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.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)); -} - -TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { - IRsendTest irsend(0); - IRFujitsuAC fujitsu = IRFujitsuAC(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()); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - uint8_t expected_arrah2e[kFujitsuAcStateLength] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, - 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; - EXPECT_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()); - - irsend.reset(); - - fujitsu.setModel(ARDB1); - irsend.sendFujitsuAC(fujitsu.getRaw(), fujitsu.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()); -} - -TEST(TestDecodeFujitsuAC, RealShortARDB1OffExample) { - IRsendTest irsend(0); - IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - - irsend.begin(); - - irsend.reset(); - // "Off" Message recorded from an AR-DB1 remote. - uint16_t rawData[99] = { - 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, - 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, - 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, - 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, - 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, - 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, - 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, - 420, 388, 436}; - irsend.sendRaw(rawData, 99, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); - uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; - EXPECT_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()); -} - -TEST(TestDecodeFujitsuAC, RealLongARDB1Example) { - IRsendTest irsend(0); - IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - - irsend.begin(); - irsend.reset(); - uint16_t rawData1[243] = { - 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, - 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, - 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, - 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, - 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, - 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, - 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, - 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, - 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, - 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, - 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, - 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, - 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, - 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, - 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, - 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, - 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, - 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, - 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, - 436, 1216, 436}; - irsend.sendRaw(rawData1, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected1[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; - EXPECT_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()); - - irsend.reset(); - uint16_t rawData2[243] = { - 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, - 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, - 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, - 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, - 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, - 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, - 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, - 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, - 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, - 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, - 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, - 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, - 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, - 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, - 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, - 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, - 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, - 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, - 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, - 436, 1214, 440}; - irsend.sendRaw(rawData2, 243, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); - uint8_t expected2[kFujitsuAcStateLength - 1] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, - 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; - EXPECT_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()); -} - -TEST(TestDecodeFujitsuAC, Issue414) { - IRsendTest irsend(0); - IRrecv irrecv(0); - IRFujitsuAC fujitsu = IRFujitsuAC(0); - - // Capture as supplied by arpmota - uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, - 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, - 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, - 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, - 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, - 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, - 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, - 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, - 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, - 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, - 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, - 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, - 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, - 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, - 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, - 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, - 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, - 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, - 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; - uint8_t state[16] = { - 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x2B}; - irsend.begin(); - irsend.reset(); - irsend.sendRaw(rawData, 259, 38000); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_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()); - - // Resend it using the state this time. - irsend.reset(); - irsend.sendFujitsuAC(state, 16); - irsend.makeDecodeResult(); - EXPECT_TRUE(irrecv.decode(&irsend.capture)); - ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); - ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); - EXPECT_TRUE(ArraysMatch(state, irsend.capture.state)); - EXPECT_EQ( - "f38000d50" - "m3324s1574" - "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" - "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" - "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" - "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" - "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" - "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" - "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" - "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" - "m448s8100", irsend.outputStr()); -} diff --git a/lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md b/lib/IRremoteESP8266-2.6.3.10/.github/CONTRIBUTING.md similarity index 90% rename from lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md rename to lib/IRremoteESP8266-2.6.3.10/.github/CONTRIBUTING.md index 9614d90a5..20bb97d94 100644 --- a/lib/IRremoteESP8266-2.6.0/.github/CONTRIBUTING.md +++ b/lib/IRremoteESP8266-2.6.3.10/.github/CONTRIBUTING.md @@ -33,8 +33,8 @@ Before creating bug reports, please check [this list](#before-submitting-a-bug-r #### Before Submitting A Bug Report -* **Check the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide).** You might be able to find the cause of the problem and fix it yourself. Most importantly, check if you can reproduce the problem in the latest version (a.k.a. 'master') of the library. -* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+repo%3Amarkszabo/IRremoteESP8266)** to see if the problem is already reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. +* **Check the [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide).** You might be able to find the cause of the problem and fix it yourself. Most importantly, check if you can reproduce the problem in the latest version (a.k.a. 'master') of the library. +* **Perform a [cursory search](https://github.com/issues?q=+is%3Aissue+repo%3Acrankyoldgit/IRremoteESP8266)** to see if the problem is already reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. #### How Do I Submit A (Good) Bug Report? @@ -53,7 +53,7 @@ Provide more context by answering these questions: * **Can you reproduce the problem in one of the code examples?** * **Did the problem start happening recently** (e.g. after updating to a new version of Arduino or the library) or was this always a problem? -* If the problem started happening recently, **can you reproduce the problem in an older version of the library?** What's the most recent version in which the problem doesn't happen? You can download older versions of the library from [the releases page](https://github.com/markszabo/IRremoteESP8266/releases). +* If the problem started happening recently, **can you reproduce the problem in an older version of the library?** What's the most recent version in which the problem doesn't happen? You can download older versions of the library from [the releases page](https://github.com/crankyoldgit/IRremoteESP8266/releases). * **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. Include details about your configuration, circuit and environment: diff --git a/lib/IRremoteESP8266-2.6.0/.github/Contributors.md b/lib/IRremoteESP8266-2.6.3.10/.github/Contributors.md similarity index 86% rename from lib/IRremoteESP8266-2.6.0/.github/Contributors.md rename to lib/IRremoteESP8266-2.6.3.10/.github/Contributors.md index af9734d69..a4958fe70 100644 --- a/lib/IRremoteESP8266-2.6.0/.github/Contributors.md +++ b/lib/IRremoteESP8266-2.6.3.10/.github/Contributors.md @@ -2,7 +2,7 @@ ### Main contributors & maintainers - [Mark Szabo](https://github.com/markszabo/) : Initial IR sending on ESP8266 - [Sébastien Warin](https://github.com/sebastienwarin/) (http://sebastien.warin.fr) : Initial IR receiving on ESP8266 -- [David Conran](https://github.com/crankyoldgit/) +- [David Conran](https://github.com/crankyoldgit/) : ESP32 support and pretty much everything else. - [Roi Dayan](https://github.com/roidayan/) - [Marcos de Alcântara Marinho](https://github.com/marcosamarinho/) - [Massimiliano Pinto](https://github.com/pintomax/) @@ -15,6 +15,6 @@ - [Fabien Valthier](https://github.com/hcoohb) - [Ajay Pala](https://github.com/ajaypala/) -All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). +All contributors can be found on the [contributors site](https://github.com/crankyoldgit/IRremoteESP8266/graphs/contributors). ### Contributors of the [original project](https://github.com/z3t0/Arduino-IRremote) can be found on the [original project's contributors page](https://github.com/z3t0/Arduino-IRremote/blob/master/Contributors.md) diff --git a/lib/IRremoteESP8266-2.6.0/.github/issue_template.md b/lib/IRremoteESP8266-2.6.3.10/.github/issue_template.md similarity index 77% rename from lib/IRremoteESP8266-2.6.0/.github/issue_template.md rename to lib/IRremoteESP8266-2.6.3.10/.github/issue_template.md index 024a0398c..d9b80dab6 100644 --- a/lib/IRremoteESP8266-2.6.0/.github/issue_template.md +++ b/lib/IRremoteESP8266-2.6.3.10/.github/issue_template.md @@ -1,6 +1,6 @@ -_(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_ +_(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_ -### Version/revison of the library used +### Version/revision of the library used _Typically located in the `library.json` & `src/IRremoteESP8266.h` files in the root directory of the library. e.g. v2.0.0, or 'master' as at 1st of June, 2017. etc._ @@ -30,9 +30,9 @@ _What can we do to (pref. reliably) repeat what is happening?_ _Include all relevant code snippets or links to the actual code files. Tip: [How to quote your code so it is still readable](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)._ #### Circuit diagram and hardware used (if applicable) -_Link to an image of the circuit diagram used. Part number of the IR receiver module etc._ +_Link to an image of the circuit diagram used. Part number of the IR receiver module etc. ESP8266 or ESP32 board type._ -### I have followed the steps in the [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions) +### I have followed the steps in the [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions) _Yes/No._ ### Has this library/code previously worked as expected for you? diff --git a/lib/IRremoteESP8266-2.6.0/.gitignore b/lib/IRremoteESP8266-2.6.3.10/.gitignore similarity index 96% rename from lib/IRremoteESP8266-2.6.0/.gitignore rename to lib/IRremoteESP8266-2.6.3.10/.gitignore index 23e21ca3e..4441365bc 100644 --- a/lib/IRremoteESP8266-2.6.0/.gitignore +++ b/lib/IRremoteESP8266-2.6.3.10/.gitignore @@ -8,8 +8,12 @@ # vi/vim **/*.swp +# vscode +.vscode + ## Build environments # Platformio +**/.pio/ **/.pioenvs/ **/.piolibdeps/ **/.clang_complete diff --git a/lib/IRremoteESP8266-2.6.0/.gitmodules b/lib/IRremoteESP8266-2.6.3.10/.gitmodules similarity index 100% rename from lib/IRremoteESP8266-2.6.0/.gitmodules rename to lib/IRremoteESP8266-2.6.3.10/.gitmodules diff --git a/lib/IRremoteESP8266-2.6.0/.style.yapf b/lib/IRremoteESP8266-2.6.3.10/.style.yapf similarity index 100% rename from lib/IRremoteESP8266-2.6.0/.style.yapf rename to lib/IRremoteESP8266-2.6.3.10/.style.yapf diff --git a/lib/IRremoteESP8266-2.6.3.10/.travis.yml b/lib/IRremoteESP8266-2.6.3.10/.travis.yml new file mode 100644 index 000000000..b873bff6e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/.travis.yml @@ -0,0 +1,73 @@ +language: c +env: + - BD=esp8266:esp8266:nodemcuv2:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled + # - BD=esp8266:esp8266:d1_mini:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled +before_install: + - "/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 + - export DISPLAY=:1.0 + - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz + - tar xf arduino-1.8.8-linux64.tar.xz + - sudo mv arduino-1.8.8 /usr/local/share/arduino + - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino + - wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py +install: + - ln -s $PWD /usr/local/share/arduino/libraries/ + - git clone https://github.com/tzapu/WiFiManager.git /usr/local/share/arduino/libraries/WiFiManager + - git clone https://github.com/knolleary/pubsubclient.git /usr/local/share/arduino/libraries/PubSubClient + - git clone https://github.com/bblanchon/ArduinoJson.git --branch 5.x /usr/local/share/arduino/libraries/ArduinoJson + - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs + - arduino --install-boards esp8266:esp8266 + - arduino --board $BD --save-prefs + - arduino --pref "compiler.warning_level=all" --save-prefs + - sudo apt-get install jq + - sudo apt-get purge python-enum34 + - sudo apt-get install pylint3 +script: echo Running checks +notifications: + email: + on_success: change + on_failure: change +jobs: + include: + - script: + # Check that everything compiles. (Part 1) + - arduino --verify --board $BD $PWD/examples/IRrecvDemo/IRrecvDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRGCSendDemo/IRGCSendDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRGCTCPServer/IRGCTCPServer.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRServer/IRServer.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRrecvDumpV2/IRrecvDumpV2.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRsendDemo/IRsendDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino 2> /dev/null + - script: + # Check that everything compiles. (Part 2) + - arduino --verify --board $BD $PWD/examples/IRsendProntoDemo/IRsendProntoDemo.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/LGACSend/LGACSend.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/DumbIRRepeater/DumbIRRepeater.ino 2> /dev/null + - arduino --verify --board $BD $PWD/examples/SmartIRRepeater/SmartIRRepeater.ino 2> /dev/null + - script: + # Check the version numbers match. + - LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2) + - test ${LIB_VERSION} == "$(jq -r .version library.json)" + - grep -q "^version=${LIB_VERSION}$" library.properties + # Check the tools programs compile. + - (cd tools; make all) + # Check for lint issues. + - shopt -s nullglob + - python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino} + - pylint3 -d F0001 {src,test,tools}/*.py + - shopt -u nullglob + # Build and run the unit tests. + - (cd test; make run) + - (cd tools; make run_tests) diff --git a/lib/IRremoteESP8266-2.6.0/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.3.10/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.6.0/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.3.10/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.6.0/LICENSE.txt b/lib/IRremoteESP8266-2.6.3.10/LICENSE.txt similarity index 100% rename from lib/IRremoteESP8266-2.6.0/LICENSE.txt rename to lib/IRremoteESP8266-2.6.3.10/LICENSE.txt diff --git a/lib/IRremoteESP8266-2.6.0/README.md b/lib/IRremoteESP8266-2.6.3.10/README.md similarity index 62% rename from lib/IRremoteESP8266-2.6.0/README.md rename to lib/IRremoteESP8266-2.6.3.10/README.md index 1eaaa21b4..f3249b801 100644 --- a/lib/IRremoteESP8266-2.6.0/README.md +++ b/lib/IRremoteESP8266-2.6.3.10/README.md @@ -1,18 +1,19 @@ # IRremote ESP8266 Library -[![Build Status](https://travis-ci.org/markszabo/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/markszabo/IRremoteESP8266) +[![Build Status](https://travis-ci.org/crankyoldgit/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/crankyoldgit/IRremoteESP8266) [![arduino-library-badge](https://www.ardu-badge.com/badge/IRremoteESP8266.svg?)](https://www.ardu-badge.com/IRremoteESP8266) -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Average time to resolve an issue") -[![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open") -[![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/crankyoldgit/IRremoteESP8266.svg)](http://isitmaintained.com/project/crankyoldgit/IRremoteESP8266 "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/crankyoldgit/IRremoteESP8266.svg)](http://isitmaintained.com/project/crankyoldgit/IRremoteESP8266 "Percentage of issues still open") +[![GitLicense](https://gitlicense.com/badge/crankyoldgit/IRremoteESP8266)](https://gitlicense.com/license/crankyoldgit/IRremoteESP8266) -This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc. +This library enables you to **send _and_ receive** infra-red signals on an [ESP8266](https://github.com/esp8266/Arduino) or an +[ESP32](https://github.com/espressif/arduino-esp32) using the [Arduino framework](https://www.arduino.cc/) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* demodulators etc. -## v2.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. +## v2.6.3 Now Available +Version 2.6.3 of the library is now [available](https://github.com/crankyoldgit/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. +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/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. #### Upgrading from pre-v2.5 The library has changed from using constants declared as `#define` to @@ -29,11 +30,15 @@ something you likely should not have. You should be able to quickly determine the new name from the old. e.g. `CONSTANT_NAME` to `kConstantName`. Use common sense or examining the library's code if this does affect code. +## Supported protocols +You can find the details of which protocols & devices are supported +[here](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md). + ## Troubleshooting -Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) first. +Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide) first. ## Frequently Asked Questions -Some common answers to common questions and problems are on our [F.A.Q. wiki page](https://github.com/markszabo/IRremoteESP8266/wiki/Frequently-Asked-Questions). +Some common answers to common questions and problems are on our [F.A.Q. wiki page](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions). ## Installation ##### Official releases via the Arduino IDE v1.8+ (Windows & Linux) @@ -43,7 +48,7 @@ Some common answers to common questions and problems are on our [F.A.Q. wiki pag 1. Select the version you wish to install and click _"Install"_. ##### Manual Installation for Windows -1. Click on _"Clone or Download"_ button, then _"[Download ZIP](https://github.com/markszabo/IRremoteESP8266/archive->master.zip)"_ on the page. +1. Click on _"Clone or Download"_ button, then _"[Download ZIP](https://github.com/crankyoldgit/IRremoteESP8266/archive->master.zip)"_ on the page. 1. Extract the contents of the downloaded zip file. 1. Rename the extracted folder to _"IRremoteESP8266"_. 1. Move this folder to your libraries directory. (under windows: `C:\Users\YOURNAME\Documents\Arduino\libraries\`) @@ -53,7 +58,7 @@ Some common answers to common questions and problems are on our [F.A.Q. wiki pag ##### Using Git to install library ( Linux ) ``` cd ~/Arduino/libraries -git clone https://github.com/markszabo/IRremoteESP8266.git +git clone https://github.com/crankyoldgit/IRremoteESP8266.git ``` ###### To Update to the latest version of the library ``` @@ -74,6 +79,6 @@ Available [here](.github/Contributors.md) ## Library History This library was originally based on Ken Shirriff's work (https://github.com/shirriff/Arduino-IRremote/) -[Mark Szabo](https://github.com/markszabo/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class). +[Mark Szabo](https://github.com/crankyoldgit/IRremoteESP8266) has updated the IRsend class to work on ESP8266 and [Sebastien Warin](https://github.com/sebastienwarin/IRremoteESP8266) the receiving & decoding part (IRrecv class). As of v2.0, the library was almost entirely re-written with the ESP8266's resources in mind. diff --git a/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md b/lib/IRremoteESP8266-2.6.3.10/ReleaseNotes.md similarity index 78% rename from lib/IRremoteESP8266-2.6.0/ReleaseNotes.md rename to lib/IRremoteESP8266-2.6.3.10/ReleaseNotes.md index 98416a12a..17e92adbf 100644 --- a/lib/IRremoteESP8266-2.6.0/ReleaseNotes.md +++ b/lib/IRremoteESP8266-2.6.3.10/ReleaseNotes.md @@ -1,5 +1,93 @@ # Release Notes +## _v2.6.3 (20190704)_ + +**[Bug Fixes]** +- IRMQTTServer: REPLAY_DECODED_AC_MESSAGE not working. (#784, #797) +- ESP32: Ensure `IRrecv`'s GPIO is set to input mode. (#774) + +**[Features]** +- IRMQTTServer: Show available sketch space for OTA uploads. (#795) +- Experimental detailed support for Electra/AUX protocol (#788) +- IRMQTTServer: Ability to resend existing climate state via MQTT & HTTP (#784) +- Daikin160: Add detailed & common a/c support. (#777) +- Experimental detailed support for Neoclima protocol. (#767) +- Gree: add WiFi and IFeel bits (#770) +- Handle A/Cs with toggles better. (#758) +- IRMQTTServer: Allow sending/receiving climate via JSON over MQTT. (#763) + +**[Misc]** +- Move converting of IR A/C messages out of example code. (#798) +- Reduce example code size and complexity (#790) +- Change `ControlSamsungAC` example to not use `sendExtended()` (#792) +- IRMQTTServer: Add MQTT_CLIMATE_IR_SEND_ON_RESTART compile-time flag. (#784) +- Refactor A/C's toString()'s to reduce code size. Saves ~3.5k (#782) +- Add sanity tests for unexpected conditions in IRrecv. (#773) +- IRMQTTServer: Fixed the HA config documentation (missing '-') (#776) +- Improve `mkkeywords` tool. (#766) +- Refactor with generic decode routines in `IRrecv` class. Saves ~7k. (#765) + + +## _v2.6.2 (20190616)_ + +**[Features]** +- Initial support for the ESP32 architecture & boards. (#742) +- Add changable GPIO settings to IRMQTTServer. (#730) +- IRMQTTServer: Enforce a repeat for all Coolix calls (#752) +- Basic DAIKIN 160bit send and decode. (#754) +- Add example code for a Smart(er) IR Repeater. (#740) +- Enforce Samsung A/C Quiet & Powerful mutual exclusivity. + +**[Misc]** +- IRMQTTServer: Add some memory alloc safety checks. (#749) +- Move some ToString() functions to IRac.cpp (#748) +- Increase tolerance value for TCL112AC protocol. (#745) +- Fix compiler warning in IRutils_test.cpp (#756) +- Scrape Supported Protocols and generate SupportedProtocols.md (#755) +- Make supported device info more organised. (#753) + + +## _v2.6.1 (20190609)_ + +**[Breaking Changes]** +- Major rework/breaking changes to Argo A/C support. (#705) + +**[Bug Fixes]** +- Correct `set/getQuiet` for Samsung A/C (#736) +- Add missing `on/off()` to IRCoolixAC class. (#725) +- Daikin `set/getEye()` uses wrong bit. (#711) +- IRMQTTServer: Continue to use same Temperature units. (#710) +- Fixed a bug with `setMode()`/`getMode()` for HAIER_AC. (#705) + +**[Features]** +- Add set/getPowerful for Samsung A/C (#736) +- Add `calibrate()` to all the A/C classes. (#735) +- IRMQTTServer: Add sequencing for sending MQTT IR commands. (#723) +- Add support for Fujitsu AR-REB1E & AR-JW2 remotes. (#718) +- Add Beta `decodeTrotec()` support. (#719) +- Add experimental `decodeArgo()` support. (#717) +- Support for Goodweather A/Cs. (#715) +- Add `DISABLE_CAPTURE_WHILE_TRANSMITTING` feature to IRMQTTServer. (#713) +- Support for Lixil Inax Toilet protocol. (#712) +- Add `set/getWeeklyTimerEnable()` to Daikin (#711) +- IRMQTTServer: Update Common A/C settings based on received IR messages. (#705) +- Add day of week to DAIKIN protocol (#699) +- Add limited support for Sharp A/C (#696) +- SAMSUNG_AC: Make sure special power mode messages are sent. (#695) +- Add `set/getPowerful()` (turbo) to DAIKIN216 (#693) + +**[Misc]** +- Add kPeriodOffset for CPU Freq of 160MHz. (#729) +- Example code for a Dumb IR repeater. (#737) +- Update swing handling for Fujitsu A/Cs. (#724) +- Add function to convert `decode_results` to `sendRaw()` array. (#721) +- Attempt to reduce heap fragmentation from strings. (#707) +- Update Fujitsu A/C example code to safer settings (#716) +- Enforce better `const` usage in IRUtils. (#708) +- Attempt to reduce heap fragmentation by A/C `toString()`s. (#694) +- Minor changes to DAIKIN216 timings and features. (#693) + + ## _v2.6.0 (20190430)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266-2.6.3.10/SupportedProtocols.md b/lib/IRremoteESP8266-2.6.3.10/SupportedProtocols.md new file mode 100644 index 000000000..2438abfeb --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/SupportedProtocols.md @@ -0,0 +1,132 @@ + +# IR Protocols supported by this library + +| Protocol | Brand | Model | A/C Model | Detailed A/C Support | +| --- | --- | --- | --- | --- | +| [Aiwa](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Aiwa.cpp) | **Aiwa** | RC-T501 RCU | | - | +| [Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.cpp) | **[Argo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Argo.h)** | Ulisse 13 DCI Mobile Split A/C | | Yes | +| [Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **Carrier/Surrey** | 42QG5A55970 remote
53NGK009/012 Inverter
619EGX0090E0 A/C
619EGX0120E0 A/C
619EGX0180E0 A/C
619EGX0220E0 A/C | | - | +| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Beko](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | BINR 070/071 split-type A/C
BINR 070/071 split-type A/C
RG57K7(B)/BGEF Remote
RG57K7(B)/BGEF Remote | | Yes | +| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | MS12FU-10HRDN1-QRD0GW(B) A/C
MS12FU-10HRDN1-QRD0GW(B) A/C
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
RG52D/BGE Remote
RG52D/BGE Remote | | Yes | +| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | ARC423A5 remote
ARC433** remote
ARC433B69 remote
ARC477A1 remote
FTE12HV2S A/C
FTXZ25NV1B A/C
FTXZ35NV1B A/C
FTXZ50NV1B A/C | | Yes | +| [Denon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Unknown** | | | - | +| [Dish](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - | +| [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[AUX](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | KFR-35GW/BpNFW=3 A/C
YKR-T/011 remote | | Yes | +| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-DB1 remote
AR-RAE1E remote
AR-RAH2E remote
AR-REB1E remote
AST9RSGCW A/C
ASYG30LFCA A/C
ASYG7LMCA A/C | ARDB1
ARJW2
ARRAH2E
ARREB1E | Yes | +| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu General](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AR-JW2 remote | ARDB1
ARJW2
ARRAH2E
ARREB1E | Yes | +| [GICable](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **Unknown** | | | - | +| [GlobalCache](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GlobalCache.cpp) | **Unknown** | | | - | +| [Goodweather](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Goodweather.cpp) | **[Goodweather](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Goodweather.h)** | ZH/JT-03 remote | | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[EKOKAI](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | A/C | | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[RusClimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | EACS/I-09HAR_X/N3 A/C
YAW1F remote | | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | | Yes | +| [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C
HSU07-HEA03 remote
YR-W02 remote | | Yes | +| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote
RAS-35THA6 remote
Series VI A/C (Circa 2007) | | Yes | +| [Inax](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - | +| [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - | +| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | KSV26CRC A/C
KSV26HRC A/C
KSV35CRC A/C
KSV35HRC A/C
KSV53HRC A/C
KSV62HRC A/C
KSV70CRC A/C
KSV70HRC A/C
KSV80HRC A/C
YALIF Remote | | Yes | +| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711A20083V remote
6711A20083V remote
AKB74395308 remote
AKB74395308 remote | | Yes | +| [Lasertag](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Unknown** | | | - | +| [Lego](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lego.cpp) | **LEGO Power Functions** | IR Receiver | | - | +| [Lutron](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lutron.cpp) | **Unknown** | | | - | +| [MWM](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MWM.cpp) | **Unknown** | | | - | +| [Magiquest](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Magiquest.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Magiquest.h)** | | | Yes | +| [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Pioneer System](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RUBO18GMFILCAD A/C (18K BTU)
RYBO12GMFILCAD A/C (12K BTU) | | Yes | +| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector
TV | | Yes | +| [MitsubishiHeavy](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote
RLA502A700B remote
SRKxxZJ-S A/C
SRKxxZM-S A/C
SRKxxZMXA-S A/C | | Yes | +| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | | | Yes | +| [Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.cpp) | **[Neoclima](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Neoclima.h)** | NS-09AHTI A/C
NS-09AHTI A/C
ZH/TY-01 remote
ZH/TY-01 remote | | Yes | +| [Nikai](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Nikai.cpp) | **Unknown** | | | - | +| [Panasonic](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Panasonic.cpp) | **[Panasonic](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Panasonic.h)** | A75C2311 remote (CKP)
A75C3704 remote
A75C3747 remote
A75C3747 remote
A75C3747 remote
A75C3747 remote
CKP series A/C
CS-ME10CKPG A/C
CS-ME12CKPG A/C
CS-ME14CKPG A/C
CS-YW9MKD A/C
CS-Z9RKR A/C
DKE series A/C
JKE series A/C
NKE series A/C
RKR series A/C
TV | CKP
DKE
JKE
LKE
NKE
RKR | Yes | +| [Pioneer](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Pioneer.cpp) | **Unknown** | | | - | +| [Pronto](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Pronto.cpp) | **Unknown** | | | - | +| [RC5_RC6](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_RC5_RC6.cpp) | **Unknown** | | | - | +| [RCMM](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_RCMM.cpp) | **Microsoft** | XBOX 360 | | - | +| [Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.cpp) | **[Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.h)** | AR12HSSDBWKNEU A/C
AR12KSFPEWQNET A/C
IEC-R03 remote
UA55H6300 TV | | Yes | +| [Sanyo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sanyo.cpp) | **Unknown** | | | - | +| [Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AY-ZP40KR A/C
LC-52D62U TV | | Yes | +| [Sherwood](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sherwood.cpp) | **Sherwood** | RC-138 remote
RD6505(B) Receiver | | - | +| [Sony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sony.cpp) | **Unknown** | | | - | +| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Leberg](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | LBS-TOR07 A/C | | Yes | +| [Teco](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.h)** | | | Yes | +| [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | Akita EVO II
RAS 18SKP-ES
RAS-B13N3KV2
RAS-B13N3KVP-E
WC-L03SE
WH-TA04NE | | Yes | +| [Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Unknown](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | | | Yes | +| [Vestel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Vestel.cpp) | **[Vestel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Vestel.h)** | BIOX CXP-9 A/C (9K BTU) | | Yes | +| [Whirlpool](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Whirlpool.cpp) | **[Whirlpool](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Whirlpool.h)** | DG11J1-04 remote
DG11J1-3A remote
DG11J1-91 remote
SPIS409L A/C
SPIS412L A/C
SPIW409L A/C
SPIW412L A/C
SPIW418L A/C | DG11J13A
DG11J191 | Yes | +| [Whynter](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Whynter.cpp) | **Whynter** | ARC-110WD A/C | | - | + + +## Send only protocols: + +- GLOBALCACHE +- PRONTO +- RAW +- SHERWOOD + + +## Send & decodable protocols: + +- AIWA_RC_T501 +- ARGO +- CARRIER_AC +- COOLIX +- DAIKIN +- DAIKIN160 +- DAIKIN2 +- DAIKIN216 +- DENON +- DISH +- ELECTRA_AC +- FUJITSU_AC +- GICABLE +- GOODWEATHER +- GREE +- HAIER_AC +- HAIER_AC_YRW02 +- HITACHI_AC +- HITACHI_AC1 +- HITACHI_AC2 +- INAX +- JVC +- KELVINATOR +- LASERTAG +- LEGOPF +- LG +- LG2 +- LUTRON +- MAGIQUEST +- MIDEA +- MITSUBISHI +- MITSUBISHI2 +- MITSUBISHI_AC +- MITSUBISHI_HEAVY_152 +- MITSUBISHI_HEAVY_88 +- MWM +- NEC +- NEC_LIKE +- NEOCLIMA +- NIKAI +- PANASONIC +- PANASONIC_AC +- PIONEER +- RC5 +- RC5X +- RC6 +- RCMM +- SAMSUNG +- SAMSUNG36 +- SAMSUNG_AC +- SANYO +- SANYO_LC7461 +- SHARP +- SHARP_AC +- SONY +- TCL112AC +- TECO +- TOSHIBA_AC +- TROTEC +- VESTEL_AC +- WHIRLPOOL_AC +- WHYNTER diff --git a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/ControlSamsungAC/ControlSamsungAC.ino similarity index 79% rename from lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/ControlSamsungAC/ControlSamsungAC.ino index df910fe87..4463013c1 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/ControlSamsungAC/ControlSamsungAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/ControlSamsungAC/ControlSamsungAC.ino @@ -6,7 +6,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -57,31 +55,29 @@ void setup() { } void loop() { - // Turn the A/C unit on and set to cooling mode. - // Power changes require we send an extended message. - Serial.println("Sending an extended IR command to A/C ..."); + // Turn the A/C unit on + Serial.println("Turn on the A/C ..."); ac.on(); + ac.send(); + printState(); + delay(15000); // wait 15 seconds + // and set to cooling mode. + Serial.println("Set the A/C mode to cooling ..."); ac.setMode(kSamsungAcCool); - ac.sendExtended(); + ac.send(); printState(); delay(15000); // wait 15 seconds // Increase the fan speed. - Serial.println("Sending a normal IR command to A/C ..."); + Serial.println("Set the fan to high and the swing on ..."); ac.setFan(kSamsungAcFanHigh); - ac.send(); - printState(); - delay(15000); - - // Change to swing the fan. - Serial.println("Sending a normal IR command to A/C ..."); ac.setSwing(true); ac.send(); printState(); delay(15000); // Change to Fan mode, lower the speed, and stop the swing. - Serial.println("Sending a normal IR command to A/C ..."); + Serial.println("Set the A/C to fan only with a low speed, & no swing ..."); ac.setSwing(false); ac.setMode(kSamsungAcFan); ac.setFan(kSamsungAcFanLow); @@ -90,10 +86,9 @@ void loop() { delay(15000); // Turn the A/C unit off. - // Power changes require we send an extended message. - Serial.println("Sending an extended IR command to A/C ..."); + Serial.println("Turn off the A/C ..."); ac.off(); - ac.sendExtended(); + ac.send(); printState(); delay(15000); // wait 15 seconds } diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/ControlSamsungAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/ControlSamsungAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/ControlSamsungAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/DumbIRRepeater.ino b/lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/DumbIRRepeater.ino new file mode 100644 index 000000000..80f5ce64a --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/DumbIRRepeater.ino @@ -0,0 +1,130 @@ +/* + * IRremoteESP8266: DumbIRRepeater.ino - Record and playback IR codes. + * Copyright 2019 David Conran (crankyoldgit) + * + * This program will try to capture incoming IR messages and replay them back. + * It doesn't use any of the advanced detection features, thus it will just + * replay the messages at fixed modulated frequency (kFrequency) and a 50% duty + * cycle. + * + * Note: + * This might NOT be the frequency of the incoming message, so some replayed + * messages may not work. The frequency of incoming messages & duty cycle is + * lost at the point of the Hardware IR demodulator. The ESP can't see it. + * + * W A R N I N G + * This code is just for educational/example use only. No help will be given + * to you to make it do something else, or to make it work with some + * weird device or circuit, or to make it more usable or practical. + * If it works for you. Great. If not, Congratulations on changing/fixing it. + * + * An IR detector/demodulator must be connected to the input, kRecvPin. + * An IR LED circuit must be connected to the output, kIrLedPin. + * + * Example circuit diagrams (both are needed): + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending + * + * Common mistakes & tips: + * * Don't just connect the IR LED directly to the pin, it won't + * have enough current to drive the IR LED effectively. + * * Make sure you have the IR LED polarity correct. + * See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity + * * Some digital camera/phones can be used to see if the IR LED is flashed. + * Replace the IR LED with a normal LED if you don't have a digital camera + * when debugging. + * * Avoid using the following pins unless you really know what you are doing: + * * Pin 0/D3: Can interfere with the boot/program mode & support circuits. + * * Pin 1/TX/TXD0: Any serial transmissions from the ESP will interfere. + * * Pin 3/RX/RXD0: Any serial transmissions to the ESP will interfere. + * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs + * for your first time. e.g. ESP-12 etc. + * + * Changes: + * Version 1.0: June, 2019 + * - Initial version. + */ + +#include +#include +#include +#include +#include + +// ==================== start of TUNEABLE PARAMETERS ==================== + +// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) +const uint16_t kRecvPin = 14; + +// GPIO to use to control the IR LED circuit. Recommended: 4 (D2). +const uint16_t kIrLedPin = 4; + +// The Serial connection baud rate. +// NOTE: Make sure you set your Serial Monitor to the same speed. +const uint32_t kBaudRate = 115200; + +// As this program is a special purpose capture/resender, let's use a larger +// than expected buffer so we can handle very large IR messages. +// i.e. Up to 512 bits. +const uint16_t kCaptureBufferSize = 1024; + +// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a +// message ended. +const uint8_t kTimeout = 50; // Milli-Seconds + +// kFrequency is the modulation frequency all messages will be replayed at. +const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz. + +// ==================== end of TUNEABLE PARAMETERS ==================== + +// The IR transmitter. +IRsend irsend(kIrLedPin); +// The IR receiver. +IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false); +// Somewhere to store the captured message. +decode_results results; + +// This section of code runs only once at start-up. +void setup() { + irrecv.enableIRIn(); // Start up the IR receiver. + irsend.begin(); // Start up the IR sender. + + Serial.begin(kBaudRate, SERIAL_8N1); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + + Serial.print("DumbIRRepeater is now running and waiting for IR input " + "on Pin "); + Serial.println(kRecvPin); + Serial.print("and will retransmit it on Pin "); + Serial.println(kIrLedPin); +} + +// The repeating section of the code +void loop() { + // Check if an IR message has been received. + if (irrecv.decode(&results)) { // We have captured something. + // The capture has stopped at this point. + + // Convert the results into an array suitable for sendRaw(). + // resultToRawArray() allocates the memory we need for the array. + uint16_t *raw_array = resultToRawArray(&results); + // Find out how many elements are in the array. + uint16_t length = getCorrectedRawLength(&results); + // Send it out via the IR LED circuit. + irsend.sendRaw(raw_array, length, kFrequency); + // Resume capturing IR messages. It was not restarted until after we sent + // the message so we didn't capture our own message. + irrecv.resume(); + // Deallocate the memory allocated by resultToRawArray(). + delete [] raw_array; + + // Display a crude timestamp & notification. + uint32_t now = millis(); + Serial.printf( + "%06u.%03u: A message that was %d entries long was retransmitted.\n", + now / 1000, now % 1000, length); + } + yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset. +} diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/DumbIRRepeater/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCSendDemo/IRGCSendDemo.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRGCSendDemo/IRGCSendDemo.ino index 03c80e18b..0b6ea5e84 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCSendDemo/IRGCSendDemo.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCSendDemo/IRGCSendDemo.ino @@ -17,7 +17,7 @@ * 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 + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -36,9 +36,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRGCSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCSendDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCSendDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCTCPServer/IRGCTCPServer.ino similarity index 97% rename from lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRGCTCPServer/IRGCTCPServer.ino index 69f7299fb..b69373d34 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRGCTCPServer/IRGCTCPServer.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCTCPServer/IRGCTCPServer.ino @@ -37,10 +37,13 @@ * can check your wifi router for it's address. */ -#ifndef UNIT_TEST #include -#endif +#if defined(ESP8266) #include +#endif // ESP8266 +#if defined(ESP32) +#include +#endif // ESP32 #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRGCTCPServer/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCTCPServer/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRGCTCPServer/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/IRMQTTServer.h similarity index 54% rename from lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h rename to lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/IRMQTTServer.h index 9494dbe2b..6f63cbd5e 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.h +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/IRMQTTServer.h @@ -18,17 +18,38 @@ #define MQTT_ENABLE true // Whether or not MQTT is used at all. #endif // MQTT_ENABLE +#ifndef EXAMPLES_ENABLE +// Whether or not examples are included. `false` saves ~2.5K of program space. +#define EXAMPLES_ENABLE true +#endif // EXAMPLES_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. +// Change if you need multiple independent send gpios & topics. (MQTT only) +const uint8_t kNrOfIrTxGpios = 1; +// Default GPIO the IR LED is connected to/controlled by. GPIO 4 = D2. +// For an ESP-01 we suggest you use RX/GPIO3/Pin 7. i.e. kDefaultIrLed = 3 +// Note: A value of -1 means unused. +const int8_t kDefaultIrLed = 4; // <=- CHANGE_ME (optional) -// 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) +// **DANGER** Optional flag to invert the output. (default = false) +// `false`: The LED is illuminated when the GPIO is HIGH. +// `true`: The LED is illuminated when GPIO is LOW rather than HIGH. +// Setting this to something other than the default could +// easily destroy your IR LED if you are overdriving it. +// Unless you *REALLY* know what you are doing, don't change this. +const bool kInvertTxOutput = false; + +// Default GPIO the IR demodulator is connected to/controlled by. GPIO 14 = D5. +const int8_t kDefaultIrRx = 14; // <=- CHANGE_ME (optional) + +// Enable/disable receiving/decoding IR messages entirely. +// Note: IR_RX costs about 40k+ of program memory. +#define IR_RX true + +// Should we use PULLUP on the IR Rx gpio? #define IR_RX_PULLUP false // --------------------- Network Related Settings ------------------------------ @@ -49,6 +70,8 @@ const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); // before we will connect. // The unset default is 8%. // (Uncomment to enable) +// Do you want/need mdns enabled? (https://en.wikipedia.org/wiki/Multicast_DNS) +#define MDNS_ENABLE true // `false` to disable and save ~21k of program space. // ----------------------- HTTP Related Settings ------------------------------- #define FIRMWARE_OTA true // Allow remote update of the firmware via http. @@ -56,8 +79,9 @@ const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0); // 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. + // Note: OTA & GPIO updates are always + // passworded. +// If you do not set a password, Firmware OTA & GPIO updates will be blocked. // ----------------------- MQTT Related Settings ------------------------------- #if MQTT_ENABLE @@ -71,13 +95,29 @@ const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. #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 +// Enable sending/receiving climate via JSON. `true` cost ~5k of program space. +#define MQTT_CLIMATE_JSON false +// Do we send an IR message when we reboot and recover the existing A/C state? +// If set to `false` you may miss requested state changes while the ESP was +// down. If set to `true`, it will resend the previous desired state sent to the +// A/C. Depending on your circumstances, you may need to change this. +#define MQTT_CLIMATE_IR_SEND_ON_RESTART false +#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. +// Enable(true)/Disable(false) the option to send a MQTT Discovery message for +// the AirCon/Climate system to Home Assistant. `false` saves ~1.5k. +#define MQTT_DISCOVERY_ENABLE true #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,16 +134,27 @@ 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 you 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[] = { - 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" @@ -119,10 +170,11 @@ const uint8_t gpioTable[] = { #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" +#define KEY_JSON "json" +#define KEY_RESEND "resend" // HTML arguments we will parse for IR code information. #define KEY_TYPE "type" // KEY_PROTOCOL is also checked too. @@ -130,6 +182,10 @@ const uint8_t gpioTable[] = { #define KEY_BITS "bits" #define KEY_REPEAT "repeats" +// GPIO html/config keys +#define KEY_TX_GPIO "tx" +#define KEY_RX_GPIO "rx" + // Text for Last Will & Testament status messages. const char* kLwtOnline = "Online"; const char* kLwtOffline = "Offline"; @@ -140,24 +196,36 @@ 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) +// Debug output is disabled if any of the IR pins are on the TX (D1) pin. +// See `isSerialGpioUsedByIr()`. +// Note: Debug costs ~6k of program space. #ifndef DEBUG -#define DEBUG true // Change to 'false' to disable all serial output. +#define DEBUG false // Change to 'true' for serial debug 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" +#define _MY_VERSION_ "v1.3.2-testing" + +const uint8_t kRebootTime = 15; // Seconds +const uint8_t kQuickDisplayTime = 2; // Seconds + +// Gpio related +#if defined(ESP8266) +const int8_t kTxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16}; +const int8_t kRxGpios[] = {-1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15}; +#endif // ESP8266 +#if defined(ESP32) +// Ref: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ +const int8_t kTxGpios[] = { + -1, 0, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, + 25, 26, 27, 32, 33}; +const int8_t kRxGpios[] = { + -1, 1, 2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, + 25, 26, 27, 32, 33, 34, 35, 36, 39}; +#endif // ESP32 -const uint8_t kSendTableSize = sizeof(gpioTable); // JSON stuff // Name of the json config file in SPIFFS. const char* kConfigFile = "/config.json"; @@ -169,45 +237,87 @@ const char* kMqttPrefixKey = "mqtt_prefix"; const char* kHostnameKey = "hostname"; const char* kHttpUserKey = "http_user"; const char* kHttpPassKey = "http_pass"; +const char* kCommandDelimiter = ","; + +// URLs +const char* kUrlRoot = "/"; +const char* kUrlAdmin = "/admin"; +const char* kUrlAircon = "/aircon"; +const char* kUrlSendDiscovery = "/send_discovery"; +const char* kUrlExamples = "/examples"; +const char* kUrlGpio = "/gpio"; +const char* kUrlGpioSet = "/gpio/set"; +const char* kUrlInfo = "/info"; +const char* kUrlReboot = "/quitquitquit"; +const char* kUrlWipe = "/reset"; #if MQTT_ENABLE const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds. const uint32_t kStatListenPeriodMs = 5 * 1000; // mSeconds +const int32_t kMaxPauseMs = 10000; // 10 Seconds. +const char* kSequenceDelimiter = ";"; +const char kPauseChar = 'P'; +#if defined(ESP8266) +const uint32_t kChipId = ESP.getChipId(); +#endif // ESP8266 +#if defined(ESP32) +const uint32_t kChipId = ESP.getEfuseMac(); // Discard the top 16 bits. +#endif // ESP32 + +const char* kClimateTopics = + "(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|" + KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|" + KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|" + KEY_FILTER "|" KEY_CLEAN "|" KEY_CELSIUS "|" KEY_RESEND +#if MQTT_CLIMATE_JSON + "|" KEY_JSON +#endif // MQTT_CLIMATE_JSON + ")
"; 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); +void mqttLog(const char* str); +bool mountSpiffs(void); bool reconnect(void); void receivingMQTT(String const topic_name, String const callback_str); void callback(char* topic, byte* payload, unsigned int length); void sendMQTTDiscovery(const char *topic); void doBroadcast(TimerMs *timer, const uint32_t interval, - const commonAcState_t state, const bool retain, + const stdAc::state_t state, const bool retain, const bool force); +#if MQTT_CLIMATE_JSON +stdAc::state_t jsonToState(const stdAc::state_t current, const String str); +void sendJsonState(const stdAc::state_t state, const String topic, + const bool retain = false, const bool ha_mode = true); +#endif // MQTT_CLIMATE_JSON #endif // MQTT_ENABLE bool isSerialGpioUsedByIr(void); void debug(const char *str); void saveWifiConfigCallback(void); void saveWifiConfig(void); void loadWifiConfigFile(void); +void doRestart(const char* str, const bool serial_only = false); String msToHumanString(uint32_t const msecs); String timeElapsed(uint32_t const msec); String timeSince(uint32_t const start); -String listOfSendGpios(void); +String gpioToString(const int16_t gpio); +uint8_t getDefaultIrSendIdx(void); +IRsend* getDefaultIrSendPtr(void); +int8_t getDefaultTxGpio(void); +String listOfTxGpios(void); bool hasUnsafeHTMLChars(String input); +String htmlHeader(const String title, const String h1_text = ""); +String htmlEnd(void); +String htmlButton(const String url, const String button, + const String text = ""); String htmlMenu(void); void handleRoot(void); String addJsReloadUrl(const String url, const uint16_t timeout_s, const bool notify); void handleExamples(void); -String 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); @@ -221,7 +331,7 @@ void handleAdmin(void); void handleInfo(void); void handleReset(void); void handleReboot(void); -bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, +bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType, const String str); uint16_t countValuesInStr(const String str, char sep); uint16_t * newCodeArray(const uint16_t size); @@ -241,18 +351,21 @@ void setup_wifi(void); void init_vars(void); void setup(void); void loop(void); +uint32_t maxSketchSpace(void); uint64_t getUInt64fromHex(char const *str); -bool sendIRCode(IRsend *irsend, int const ir_type, +bool sendIRCode(IRsend *irsend, decode_type_t 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, +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-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/IRMQTTServer.ino similarity index 68% rename from lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/IRMQTTServer.ino index 31e40432d..4b457cb29 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/IRMQTTServer.ino @@ -7,11 +7,13 @@ * resources. I'm *NOT* claiming complete Copyright ownership of all the code. * Likewise, feel free to borrow from this as much as you want. * - * 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. + * NOTE: An IR LED circuit SHOULD be connected to the ESP if + * you want to send IR messages. e.g. GPIO4 (D2) + * A compatible IR RX modules SHOULD be connected to ESP + * if you want to capture & decode IR nessages. e.g. GPIO14 (D5) + * See 'IR_RX' in IRMQTTServer.h. + * GPIOs are configurable from the http:///gpio + * page. * * WARN: This is *very* advanced & complicated example code. Not for beginners. * You are strongly suggested to try & look at other example code first @@ -29,21 +31,25 @@ * * - Arduino IDE: * o Install the following libraries via Library Manager - * - ArduinoJson (https://arduinojson.org/) (Version >= 5.x and < 6) + * - ArduinoJson (https://arduinojson.org/) (Version >= 5.0 and < 6.0) * - PubSubClient (https://pubsubclient.knolleary.net/) - * - WiFiManager (https://github.com/tzapu/WiFiManager) (Version >= 0.14) + * - WiFiManager (https://github.com/tzapu/WiFiManager) + * (ESP8266: Version >= 0.14, ESP32: 'development' branch.) * o You MUST change to have the following (or larger) value: * (with REPORT_RAW_UNKNOWNS 1024 or more is recommended) * #define MQTT_MAX_PACKET_SIZE 768 + * o Use the smallest non-zero SPIFFS size you can for your board. + * (See the Tools -> Flash Size menu) + * * - PlatformIO IDE: * If you are using PlatformIO, this should already been done for you in * the accompanying platformio.ini file. * * ## First Boot (Initial setup) - * The ESP8266 board will boot into the WiFiManager's AP mode. + * The ESP board will boot into the WiFiManager's AP mode. * i.e. It will create a WiFi Access Point with a SSID like: "ESP123456" etc. * Connect to that SSID. Then point your browser to http://192.168.4.1/ and - * configure the ESP8266 to connect to your desired WiFi network and associated + * configure the ESP to connect to your desired WiFi network and associated * required settings. It will remember these details on next boot if the device * connects successfully. * More information can be found here: @@ -55,6 +61,9 @@ * ## Normal Use (After initial setup) * Enter 'http:///gpio page to configure the GPIOs + * for the IR LED(s) and/or IR RX demodulator. + * * You can send URLs like the following, with similar data type limitations as * the MQTT formating in the next section. e.g: * http:///ir?type=7&code=E0E09966 @@ -102,6 +111,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. @@ -163,6 +185,13 @@ * acknowledge this via the relevant state topic for that command. * e.g. If the aircon/climate changes from power off to power on, it will * send an "on" payload to "ir_server/ac/stat/power" + * + * There is a special command available to force the ESP to resend the current + * A/C state in an IR message. To do so use the `resend` command MQTT topic, + * e.g. `ir_server/ac/cmnd/resend` with a payload message of `resend`. + * There is no corresponding "stat" message update for this particular topic, + * but a log message is produced indicating it was received. + * * NOTE: These "stat" messages have the MQTT retain flag set to on. Thus the * MQTT broker will remember them until reset/restarted etc. * @@ -195,43 +224,43 @@ * In HA's configuration.yaml, add: * * climate: - * platform: mqtt - * name: Living Room Aircon - * modes: - * - "off" - * - "auto" - * - "cool" - * - "heat" - * - "dry" - * - "fan_only" - * fan_modes: - * - "auto" - * - "min" - * - "low" - * - "medium" - * - "high" - * - "max" - * swing_modes: - * - "off" - * - "auto" - * - "highest" - * - "high" - * - "middle" - * - "low" - * - "lowest" - * power_command_topic: "ir_server/ac/cmnd/power" - * mode_command_topic: "ir_server/ac/cmnd/mode" - * mode_state_topic: "ir_server/ac/stat/mode" - * temperature_command_topic: "ir_server/ac/cmnd/temp" - * temperature_state_topic: "ir_server/ac/stat/temp" - * fan_mode_command_topic: "ir_server/ac/cmnd/fanspeed" - * fan_mode_state_topic: "ir_server/ac/stat/fanspeed" - * swing_mode_command_topic: "ir_server/ac/cmnd/swingv" - * swing_mode_state_topic: "ir_server/ac/stat/swingv" - * min_temp: 16 - * max_temp: 32 - * temp_step: 1 - * retain: false + * - platform: mqtt + * name: Living Room Aircon + * modes: + * - "off" + * - "auto" + * - "cool" + * - "heat" + * - "dry" + * - "fan_only" + * fan_modes: + * - "auto" + * - "min" + * - "low" + * - "medium" + * - "high" + * - "max" + * swing_modes: + * - "off" + * - "auto" + * - "highest" + * - "high" + * - "middle" + * - "low" + * - "lowest" + * power_command_topic: "ir_server/ac/cmnd/power" + * mode_command_topic: "ir_server/ac/cmnd/mode" + * mode_state_topic: "ir_server/ac/stat/mode" + * temperature_command_topic: "ir_server/ac/cmnd/temp" + * temperature_state_topic: "ir_server/ac/stat/temp" + * fan_mode_command_topic: "ir_server/ac/cmnd/fanspeed" + * fan_mode_state_topic: "ir_server/ac/stat/fanspeed" + * swing_mode_command_topic: "ir_server/ac/cmnd/swingv" + * swing_mode_state_topic: "ir_server/ac/stat/swingv" + * min_temp: 16 + * max_temp: 32 + * temp_step: 1 + * retain: false * * ### via HTTP: * Use the "http:///aircon/set" URL and pass on @@ -250,9 +279,13 @@ * `ir_server/log` * * ## Updates - * You can upload new firmware over the air (OTA) via the form on the device's - * main page. No need to connect to the device again via USB. \o/ - * Your WiFi settings should be remembered between updates. \o/ \o/ + * You can upload new firmware Over The Air (OTA) via the form on the device's + * "Admin" page. No need to connect to the device again via USB. \o/ + * Your settings should be remembered between updates. \o/ \o/ + * + * On boards with 1 Meg of flash should use an SPIFFS size of 64k if you want a + * hope of being able to load a firmware via OTA. + * Boards with only 512k flash have no chance of OTA with this firmware. * * ## Security * @@ -279,12 +312,21 @@ #include #include #include +#if defined(ESP8266) #include +#include +#include +#endif // ESP8266 +#if defined(ESP32) +#include +#include +#include +#include +#include +#endif // ESP32 #include #include -#include #include -#include #include #include #include @@ -303,13 +345,18 @@ #include #include +using irutils::msToString; + // Globals +#if defined(ESP8266) ESP8266WebServer server(kHttpPort); -#ifdef IR_RX -IRrecv irrecv(IR_RX, kCaptureBufferSize, kCaptureTimeout, true); -decode_results capture; // Somewhere to store inbound IR messages. -#endif // IR_RX +#endif // ESP8266 +#if defined(ESP32) +WebServer server(kHttpPort); +#endif // ESP32 +#if MDNS_ENABLE MDNSResponder mdns; +#endif // MDNS_ENABLE WiFiClient espClient; WiFiManager wifiManager; bool flagSaveWifiConfig = false; @@ -319,23 +366,28 @@ char Hostname[kHostnameLength + 1] = "ir_server"; // Default hostname. uint16_t *codeArray; uint32_t lastReconnectAttempt = 0; // MQTT last attempt reconnection number bool boot = true; -bool lockIr = false; // Primitive locking for gating the IR LED. +volatile bool lockIr = false; // Primitive locking for gating the IR LED. uint32_t sendReqCounter = 0; bool lastSendSucceeded = false; // Store the success status of the last send. uint32_t lastSendTime = 0; int8_t offset; // The calculated period offset for this chip and library. -IRsend *IrSendTable[kSendTableSize]; - -#ifdef IR_RX +IRsend *IrSendTable[kNrOfIrTxGpios]; +int8_t txGpioTable[kNrOfIrTxGpios] = {kDefaultIrLed}; +String lastClimateSource; +#if IR_RX +IRrecv *irrecv = NULL; +decode_results capture; // Somewhere to store inbound IR messages. +int8_t rx_gpio = kDefaultIrRx; String lastIrReceived = "None"; uint32_t lastIrReceivedTime = 0; uint32_t irRecvCounter = 0; #endif // IR_RX // Climate stuff -commonAcState_t climate; -commonAcState_t climate_prev; -IRac commonAc(gpioTable[0]); +stdAc::state_t climate; +stdAc::state_t climate_prev; +IRac *commonAc = NULL; + 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. @@ -368,7 +420,9 @@ 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. +#if MQTT_DISCOVERY_ENABLE String MqttDiscovery; +#endif // MQTT_DISCOVERY_ENABLE String MqttHAName; String MqttClientId; @@ -382,16 +436,29 @@ 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. + const int8_t kSerialTxGpio = 1; // The GPIO serial output is sent to. + // Note: *DOES NOT* control Serial output. +#if defined(ESP32) + const int8_t kSerialRxGpio = 3; // The GPIO serial input is received on. +#endif // ESP32 // Ensure we are not trodding on anything IR related. -#ifdef IR_RX - if (IR_RX == kSerialTxGpio) - return true; // Serial port is in use by IR capture. Abort. +#if IR_RX + switch (rx_gpio) { +#if defined(ESP32) + case kSerialRxGpio: +#endif // ESP32 + case kSerialTxGpio: + return true; // Serial port is in use by IR capture. Abort. + } #endif // IR_RX - for (uint8_t i = 0; i < kSendTableSize; i++) - if (gpioTable[i] == kSerialTxGpio) - return true; // Serial port is in use for IR sending. Abort. + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) + switch (txGpioTable[i]) { +#if defined(ESP32) + case kSerialRxGpio: +#endif // ESP32 + case kSerialTxGpio: + return true; // Serial port is in use for IR sending. Abort. + } return false; // Not in use as far as we can tell. } @@ -410,8 +477,27 @@ void saveWifiConfigCallback(void) { flagSaveWifiConfig = true; } -void saveWifiConfig(void) { - debug("Saving the wifi config."); +// Forcibly mount the SPIFFS. Formatting the SPIFFS if needed. +// +// Returns: +// A boolean indicating success or failure. +bool mountSpiffs(void) { + debug("Mounting SPIFFS..."); + if (SPIFFS.begin()) return true; // We mounted it okay. + // We failed the first time. + debug("Failed to mount SPIFFS!\nFormatting SPIFFS and trying again..."); + SPIFFS.format(); + if (!SPIFFS.begin()) { // Did we fail? + debug("DANGER: Failed to mount SPIFFS even after formatting!"); + delay(10000); // Make sure the debug message doesn't just float by. + return false; + } + return true; // Success! +} + +bool saveConfig(void) { + debug("Saving the config."); + bool success = false; DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); #if MQTT_ENABLE @@ -424,8 +510,15 @@ void saveWifiConfig(void) { json[kHostnameKey] = Hostname; json[kHttpUserKey] = HttpUsername; json[kHttpPassKey] = HttpPassword; +#if IR_RX + json[KEY_RX_GPIO] = static_cast(rx_gpio); +#endif // IR_RX + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { + const String key = KEY_TX_GPIO + String(i); + json[key] = static_cast(txGpioTable[i]); + } - if (SPIFFS.begin()) { + if (mountSpiffs()) { File configFile = SPIFFS.open(kConfigFile, "w"); if (!configFile) { debug("Failed to open config file for writing."); @@ -434,15 +527,17 @@ void saveWifiConfig(void) { json.printTo(configFile); configFile.close(); debug("Finished writing config file."); + success = true; } SPIFFS.end(); } + return success; } -void loadWifiConfigFile(void) { - debug("Trying to mount SPIFFS"); - if (SPIFFS.begin()) { - debug("mounted file system"); +bool loadConfigFile(void) { + bool success = false; + if (mountSpiffs()) { + debug("mounted the file system"); if (SPIFFS.exists(kConfigFile)) { debug("config file exists"); @@ -468,7 +563,17 @@ void loadWifiConfigFile(void) { strncpy(Hostname, json[kHostnameKey] | "", kHostnameLength); strncpy(HttpUsername, json[kHttpUserKey] | "", kUsernameLength); strncpy(HttpPassword, json[kHttpPassKey] | "", kPasswordLength); + // Read in the GPIO settings. +#if IR_RX + // Single RX gpio + rx_gpio = json[KEY_RX_GPIO] | kDefaultIrRx; +#endif // IR_RX + // Potentially multiple TX gpios + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) + txGpioTable[i] = json[String(KEY_TX_GPIO + String(i)).c_str()] | + kDefaultIrLed; debug("Recovered Json fields."); + success = true; } else { debug("Failed to load json config"); } @@ -480,89 +585,69 @@ void loadWifiConfigFile(void) { } 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; + return success; } String timeElapsed(uint32_t const msec) { - String result = msToHumanString(msec); + String result = msToString(msec); if (result.equalsIgnoreCase("Now")) return result; else - return result + " ago"; + return result + F(" ago"); } String timeSince(uint32_t const start) { if (start == 0) - return "Never"; + return F("Never"); uint32_t diff = 0; uint32_t now = millis(); if (start < now) diff = now - start; else diff = UINT32_MAX - start + now; - return msToHumanString(diff) + " ago"; + return msToString(diff) + F(" ago"); +} + +String gpioToString(const int16_t gpio) { + if (gpio == kGpioUnused) + return F("Unused"); + else + return String(gpio); +} + +int8_t getDefaultTxGpio(void) { + for (int8_t i = 0; i < kNrOfIrTxGpios; i++) + if (txGpioTable[i] != kGpioUnused) return txGpioTable[i]; + return kGpioUnused; } // Return a string containing the comma separated list of sending gpios. -String listOfSendGpios(void) { - String result = String(gpioTable[0]); - if (kSendTableSize > 1) result += " (default)"; - for (uint8_t i = 1; i < kSendTableSize; i++) { - result += ", " + String(gpioTable[i]); +String listOfTxGpios(void) { + bool found = false; + String result = ""; + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + if (i) result += ", "; + result += gpioToString(txGpioTable[i]); + if (!found && txGpioTable[i] == getDefaultTxGpio()) { + result += " (default)"; + found = true; + } } return result; } String htmlMenu(void) { - return F( - "
" - "" - "" - "" - "" - "" - "
" - "
"); + String html = F("
"); + html += htmlButton(kUrlRoot, F("Home")); + html += htmlButton(kUrlAircon, F("Aircon")); +#if EXAMPLES_ENABLE + html += htmlButton(kUrlExamples, F("Examples")); +#endif // EXAMPLES_ENABLE + html += htmlButton(kUrlInfo, F("System Info")); + html += htmlButton(kUrlAdmin, F("Admin")); + html += F("

"); + return html; } // Root web page with example usage etc. @@ -573,11 +658,8 @@ void handleRoot(void) { return server.requestAuthentication(); } #endif - String html = F( - "IR MQTT server" - "" - "

ESP8266 IR MQTT Server

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

Send a simple IR message

" @@ -590,6 +672,8 @@ void handleRoot(void) { "" "" "" + "" + "" "" "" "" @@ -650,8 +734,10 @@ void handleRoot(void) { "Type: " "" "" "


" @@ -686,11 +778,15 @@ void handleRoot(void) { "
" "" "String: (freq,array data) " + "1638,520,1638,520,1638,520,1638,520" +#endif // EXAMPLES_ENABLE + "'>" " " "
" "

" @@ -699,10 +795,14 @@ void handleRoot(void) { "
" "" "String: 1:1,1," + "63,20,63,20,63,20,1798" +#endif // EXAMPLES_ENABLE + "'>" " " "
" "

" @@ -711,15 +811,20 @@ void handleRoot(void) { "
" "" "String (comma separated): " + "0018,0018,0018,0018,0030,0018,0018,03f6" +#endif // EXAMPLES_ENABLE + "'>" " Repeats: " " " "
" - "
"); + "
"); + html += htmlEnd(); server.send(200, "text/html", html); } @@ -747,6 +852,7 @@ String addJsReloadUrl(const String url, const uint16_t timeout_s, return html; } +#if EXAMPLES_ENABLE // Web page with hardcoded example usage etc. void handleExamples(void) { #if HTML_PASSWORD_ENABLE @@ -755,11 +861,7 @@ void handleExamples(void) { return server.requestAuthentication(); } #endif - String html = F( - "IR MQTT examples" - "" - "

ESP8266 IR MQTT Server

" - "
" _MY_VERSION_ "
"); + String html = htmlHeader(F("IR MQTT examples")); html += htmlMenu(); html += F( "

Hardcoded examples

" @@ -792,106 +894,25 @@ void handleExamples(void) { "Change just the temp to 27C (via HTTP aircon interface)

" "

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

" - "

"); + "

"); + html += htmlEnd(); server.send(200, "text/html", html); } +#endif // EXAMPLES_ENABLE -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 htmlOptionItem(const String value, const String text, bool selected) { + String html = F(""); } html += F(""); @@ -937,14 +962,8 @@ String htmlSelectModel(const String name, const int16_t def) { String htmlSelectMode(const String name, const stdAc::opmode_t def) { String html = ""); return html; @@ -953,14 +972,8 @@ String htmlSelectMode(const String name, const stdAc::opmode_t def) { String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) { String html = ""); return html; @@ -969,14 +982,8 @@ String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) { String htmlSelectSwingv(const String name, const stdAc::swingv_t def) { String html = ""); return html; @@ -985,25 +992,42 @@ String htmlSelectSwingv(const String name, const stdAc::swingv_t def) { String htmlSelectSwingh(const String name, const stdAc::swingh_t def) { String html = ""); return html; } +String htmlHeader(const String title, const String h1_text) { + String html = F(""); + html += title; + html += F("

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

"); + return html; +} + +String htmlEnd(void) { + return F(""); +} + +String htmlButton(const String url, const String button, const String text) { + String html = F(" "); + html += text; + return html; +} + // Admin web page void handleAirCon(void) { - String html = F( - "AirCon control" - "" - "

Air Conditioner Control

"); + String html = htmlHeader(F("Air Conditioner Control")); html += htmlMenu(); html += "

Current Settings

" "
" @@ -1045,11 +1069,12 @@ void handleAirCon(void) { "" "Beep" + htmlSelectBool(KEY_BEEP, climate.beep) + "" + "Force resend" + htmlSelectBool(KEY_RESEND, false) + + "" "" "" "
"; - // Display the current settings. - html += F(""); + html += htmlEnd(); server.send(200, "text/html", html); } @@ -1061,63 +1086,66 @@ 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)); + bool force_resend = false; + for (uint16_t i = 0; i < server.args(); i++) { + if (server.argName(i).equals(KEY_RESEND)) + force_resend = IRac::strToBool(server.arg(i).c_str()); + else + result = updateClimate(result, server.argName(i), "", server.arg(i)); + } #if MQTT_ENABLE - sendClimate(climate, result, MqttClimateStat, - true, false, false); + sendClimate(climate, result, MqttClimateStat, true, false, force_resend); #else // MQTT_ENABLE - sendClimate(climate, result, "", false, false, false); + sendClimate(climate, result, "", false, false, force_resend); #endif // MQTT_ENABLE + lastClimateSource = F("HTTP"); // Update the old climate state with the new one. climate = result; // Redirect back to the aircon page. - String html = F( - "Update Aircon" - "" - "

Aircon updated!

"); - html += addJsReloadUrl("/aircon", 2, false); - html += F(""); + String html = htmlHeader(F("Aircon updated!")); + html += addJsReloadUrl(kUrlAircon, kQuickDisplayTime, false); + html += htmlEnd(); server.send(200, "text/html", html); } +String htmlDisabled(void) { + String html = F( + "Updates disabled until you set a password. " + "You will need to wipe & reset to set one.

"); + return html; +} + // Admin web page void handleAdmin(void) { - String html = F( - "IR MQTT server admin" - "" - "

Administration

"); + String html = htmlHeader(F("Administration")); html += htmlMenu(); - html += F( - "

Special commands

" + html += F("

Special commands

"); #if MQTT_ENABLE - " " - "Send a Climate MQTT discovery message to Home Assistant.

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

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

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

Update firmware

" - "Warning:
"); + "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." - "

"); + html += htmlDisabled(); else // default password has been changed, so allow it. html += F( "Updating your firmware may screw up your access to the device. " @@ -1128,16 +1156,21 @@ void handleAdmin(void) { "" ""); #endif // FIRMWARE_OTA - html += F(""); + html += htmlEnd(); server.send(200, "text/html", html); } +uint32_t maxSketchSpace(void) { +#if defined(ESP8266) + return (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; +#else // defined(ESP8266) + return UPDATE_SIZE_UNKNOWN; +#endif // defined(ESP8266) +} + // Info web page void handleInfo(void) { - String html = - "IR MQTT server info" - "" - "

Information

"; + String html = htmlHeader(F("IR MQTT server info")); html += htmlMenu(); html += "

General

" @@ -1149,13 +1182,22 @@ void handleInfo(void) { " " __TIME__ "
" "Period Offset: " + String(offset) + "us
" "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" +#if defined(ESP8266) "ESP8266 Core Version: " + ESP.getCoreVersion() + "
" - "IR Send GPIO(s): " + listOfSendGpios() + "
" + "Free Sketch Space: " + String(maxSketchSpace() >> 10) + "k
" +#endif // ESP8266 +#if defined(ESP32) + "ESP32 SDK Version: " + ESP.getSdkVersion() + "
" +#endif // ESP32 + "Cpu Freq: " + String(ESP.getCpuFreqMHz()) + "MHz
" + "IR Send GPIO(s): " + listOfTxGpios() + "
" + + irutils::addBoolToString(kInvertTxOutput, + "Inverting GPIO output", false) + "
" "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 + "IR Recv GPIO: " + gpioToString(rx_gpio) + #if IR_RX_PULLUP " (pullup)" #endif // IR_RX_PULLUP @@ -1191,7 +1233,7 @@ void handleInfo(void) { "Client id: " + MqttClientId + "
" "Command topic(s): " + listOfCommandTopics() + "
" "Acknowledgements topic: " + MqttAck + "
" -#ifdef IR_RX +#if IR_RX "IR Received topic: " + MqttRecv + "
" #endif // IR_RX "Log topic: " + MqttLog + "
" @@ -1199,8 +1241,9 @@ void handleInfo(void) { "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) + "' (" + + "Last MQTT command seen: (topic) '" + + irutils::htmlEscape(lastMqttCmdTopic) + + "' (payload) '" + irutils::htmlEscape(lastMqttCmd) + "' (" + timeSince(lastMqttCmdTime) + ")
" "Total published: " + String(mqttSentCounter) + "
" "Total received: " + String(mqttRecvCounter) + "
" @@ -1208,15 +1251,16 @@ void handleInfo(void) { #endif // MQTT_ENABLE "

Climate Information

" "

" - "IR Send GPIO: " + String(gpioTable[0]) + "
" + "IR Send GPIO: " + String(txGpioTable[0]) + "
" + "Last update source: " + lastClimateSource + "
" "Total sent: " + String(irClimateCounter) + "
" "Last send: " + String(hasClimateBeenSent ? (String(lastClimateSucceeded ? "Ok" : "FAILED") + " (" + timeElapsed(lastClimateIr.elapsed()) + ")") : "Never") + "
" #if MQTT_ENABLE - "State listen period: " + msToHumanString(kStatListenPeriodMs) + "
" - "State broadcast period: " + msToHumanString(kBroadcastPeriodMs) + "
" + "State listen period: " + msToString(kStatListenPeriodMs) + "
" + "State broadcast period: " + msToString(kBroadcastPeriodMs) + "
" "Last state broadcast: " + (hasBroadcastBeenSent ? timeElapsed(lastBroadcast.elapsed()) : String("Never")) + "
" @@ -1226,47 +1270,50 @@ void handleInfo(void) { 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 ")
" + "Command topics: " + MqttClimateCmnd + kClimateTopics + + "State topics: " + MqttClimateStat + kClimateTopics + #endif // MQTT_ENABLE "

" // Page footer "

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

"; - html += addJsReloadUrl("/info", 60, false); - html += ""; + html += addJsReloadUrl(kUrlInfo, 60, false); + html += htmlEnd(); server.send(200, "text/html", html); } + +void doRestart(const char* str, const bool serial_only) { +#if MQTT_ENABLE + if (!serial_only) + mqttLog(str); + else +#endif // MQTT_ENABLE + debug(str); + delay(2000); // Enough time for messages to be sent. + ESP.restart(); + delay(5000); // Enough time to ensure we don't return. +} + // Reset web page void handleReset(void) { #if HTML_PASSWORD_ENABLE if (!server.authenticate(HttpUsername, HttpPassword)) { - debug("Basic HTTP authentication failure for /reset."); + debug("Basic HTTP authentication failure for " + kUrlWipe); return server.requestAuthentication(); } #endif server.send(200, "text/html", - "Reset WiFi Config" - "" - "

Resetting the WiFiManager config back to defaults.

" + htmlHeader(F("Reset WiFi Config"), + F("Resetting the WiFiManager config back to defaults.")) + "

Device restarting. Try connecting in a few seconds.

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

Device restarting.

" + htmlHeader(F("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); + addJsReloadUrl(kUrlRoot, kRebootTime, true) + + htmlEnd()); + doRestart("Reboot requested"); } // Parse an Air Conditioner A/C Hex String/code and send it. @@ -1311,7 +1347,7 @@ void handleReboot() { // str: A hexadecimal string containing the state to be sent. // Returns: // bool: Successfully sent or not. -bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, +bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType, const String str) { uint8_t strOffset = 0; uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. @@ -1327,12 +1363,6 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, } switch (irType) { // Get the correct state size for the protocol. - case KELVINATOR: - stateSize = kKelvinatorStateLength; - break; - case TOSHIBA_AC: - stateSize = kToshibaACStateLength; - break; case DAIKIN: // Daikin has 2 different possible size states. // (The correct size, and a legacy shorter size.) @@ -1350,36 +1380,6 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, // 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; - 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; - case TROTEC: - stateSize = kTrotecStateLength; - break; - case ARGO: - stateSize = kArgoStateLength; - break; - case GREE: - stateSize = kGreeStateLength; - break; case FUJITSU_AC: // Fujitsu has four distinct & different size states, so make a best guess // which one we are being presented with based on the number of @@ -1396,23 +1396,16 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, // Lastly, it should never exceed the maximum "normal" size. stateSize = std::min(stateSize, kFujitsuAcStateLength); break; - case HAIER_AC: - stateSize = kHaierACStateLength; - break; - case HAIER_AC_YRW02: - stateSize = kHaierACYRW02StateLength; - break; - case HITACHI_AC: - stateSize = kHitachiAcStateLength; - break; - case HITACHI_AC1: - stateSize = kHitachiAc1StateLength; - break; - case HITACHI_AC2: - stateSize = kHitachiAc2StateLength; - break; - case WHIRLPOOL_AC: - stateSize = kWhirlpoolAcStateLength; + case MWM: + // MWM has variable size states, so make a best 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. + stateSize = inputLength / 2; // Every two hex chars is a byte. + // Use at least the minimum size. + stateSize = std::max(stateSize, (uint16_t) 3); + // Cap the maximum size. + stateSize = std::min(stateSize, kStateSizeMax); break; case SAMSUNG_AC: // Samsung has two distinct & different size states, so make a best guess @@ -1430,23 +1423,13 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, // Lastly, it should never exceed the maximum "extended" size. stateSize = std::min(stateSize, kSamsungAcExtendedStateLength); 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 - // hexadecimal digits provided. i.e. Zero-pad if you need to to get - // the correct length/byte size. - stateSize = inputLength / 2; // Every two hex chars is a byte. - // Use at least the minimum size. - stateSize = std::max(stateSize, (uint16_t) 3); - // Cap the maximum size. - stateSize = std::min(stateSize, kStateSizeMax); - break; - case TCL112AC: - stateSize = kTcl112AcStateLength; - break; - default: // Not a protocol we expected. Abort. - debug("Unexpected AirCon protocol detected. Ignoring."); - return false; + default: // Everything else. + stateSize = IRsend::defaultBits(irType) / 8; + if (!stateSize || !hasACState(irType)) { + // Not a protocol we expected. Abort. + debug("Unexpected AirCon protocol detected. Ignoring."); + return false; + } } if (inputLength > stateSize * 2) { debug("AirCon code to large for the given protocol."); @@ -1477,125 +1460,9 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType, *statePtr = c; } } - - // Make the appropriate call for the protocol type. - switch (irType) { -#if SEND_KELVINATOR - case KELVINATOR: - irsend->sendKelvinator(reinterpret_cast(state)); - break; -#endif -#if SEND_TOSHIBA_AC - case TOSHIBA_AC: - irsend->sendToshibaAC(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN - case DAIKIN: - irsend->sendDaikin(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN2 - case DAIKIN2: - irsend->sendDaikin2(reinterpret_cast(state)); - break; -#endif -#if SEND_DAIKIN216 - case DAIKIN216: - irsend->sendDaikin216(reinterpret_cast(state)); - break; -#endif // SEND_DAIKIN216 -#if SEND_MITSUBISHI_AC - case MITSUBISHI_AC: - 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)); - break; -#endif -#if SEND_ARGO - case ARGO: - irsend->sendArgo(reinterpret_cast(state)); - break; -#endif -#if SEND_GREE - case GREE: - irsend->sendGree(reinterpret_cast(state)); - break; -#endif -#if SEND_FUJITSU_AC - case FUJITSU_AC: - irsend->sendFujitsuAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_HAIER_AC - case HAIER_AC: - irsend->sendHaierAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HAIER_AC_YRW02 - case HAIER_AC_YRW02: - irsend->sendHaierACYRW02(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC - case HITACHI_AC: - irsend->sendHitachiAC(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC1 - case HITACHI_AC1: - irsend->sendHitachiAC1(reinterpret_cast(state)); - break; -#endif -#if SEND_HITACHI_AC2 - case HITACHI_AC2: - irsend->sendHitachiAC2(reinterpret_cast(state)); - break; -#endif -#if SEND_WHIRLPOOL_AC - case WHIRLPOOL_AC: - irsend->sendWhirlpoolAC(reinterpret_cast(state)); - break; -#endif -#if SEND_SAMSUNG_AC - case SAMSUNG_AC: - irsend->sendSamsungAC(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_ELECTRA_AC - case ELECTRA_AC: - irsend->sendElectraAC(reinterpret_cast(state)); - break; -#endif -#if SEND_PANASONIC_AC - case PANASONIC_AC: - irsend->sendPanasonicAC(reinterpret_cast(state)); - break; -#endif -#if SEND_MWM - case MWM: - irsend->sendMWM(reinterpret_cast(state), stateSize); - break; -#endif -#if SEND_TCL112AC - case TCL112AC: - irsend->sendTcl112Ac(reinterpret_cast(state)); - break; -#endif - default: - debug("Unexpected AirCon type in send request. Not sent."); - return false; + if (!irsend->send(irType, state, stateSize)) { + debug("Unexpected AirCon type in send request. Not sent."); + return false; } return true; // We were successful as far as we can tell. } @@ -1620,20 +1487,16 @@ uint16_t countValuesInStr(const String str, char sep) { // Args: // size: Nr. of uint16_t's need to be in the new array. // Returns: -// A Ptr to the new array. Restarts the ESP8266 if it fails. +// A Ptr to the new array. Restarts the ESP if it fails. uint16_t * newCodeArray(const uint16_t size) { uint16_t *result; result = reinterpret_cast(malloc(size * sizeof(uint16_t))); // Check we malloc'ed successfully. - if (result == NULL) { // malloc failed, so give up. - Serial.printf("\nCan't allocate %d bytes. (%d bytes free)\n", - size * sizeof(uint16_t), ESP.getFreeHeap()); - Serial.println("Giving up & forcing a reboot."); - ESP.restart(); // Reboot. - delay(500); // Wait for the restart to happen. - return result; // Should never get here, but just in case. - } + if (result == NULL) // malloc failed, so give up. + doRestart( + "FATAL: Can't allocate memory for an array for a new message! " + "Forcing a reboot!", true); // Send to serial only as we are in low mem return result; } @@ -1676,7 +1539,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) @@ -1795,6 +1657,16 @@ bool parseStringAndSendRaw(IRsend *irsend, const String str) { } #endif // SEND_RAW +uint8_t getDefaultIrSendIdx(void) { + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) + if (IrSendTable[i] != NULL) return i; + return 0; +} + +IRsend* getDefaultIrSendPtr(void) { + return IrSendTable[getDefaultIrSendIdx()]; +} + // Parse the URL args to find the IR code. void handleIr(void) { #if HTML_PASSWORD_ENABLE @@ -1805,7 +1677,7 @@ void handleIr(void) { #endif uint64_t data = 0; String data_str = ""; - int16_t ir_type = decode_type_t::NEC; // Default to NEC codes. + decode_type_t ir_type = decode_type_t::NEC; // Default to NEC codes. uint16_t nbits = 0; uint16_t repeat = 0; @@ -1823,17 +1695,104 @@ void handleIr(void) { } } debug("New code received via HTTP"); - lastSendSucceeded = sendIRCode(IrSendTable[0], ir_type, data, + lastSendSucceeded = sendIRCode(getDefaultIrSendPtr(), ir_type, data, data_str.c_str(), nbits, repeat); - String html = F( - "Send IR command" - "" - "

IR command sent!

"); - html += addJsReloadUrl("/", 2, true); - html += F(""); + String html = htmlHeader(F("IR command sent!")); + html += addJsReloadUrl(kUrlRoot, kQuickDisplayTime, true); + html += htmlEnd(); server.send(200, "text/html", html); } +// GPIO menu page +void handleGpio(void) { +#if HTML_PASSWORD_ENABLE + if (!server.authenticate(HttpUsername, HttpPassword)) { + debug("Basic HTTP authentication failure for /gpios."); + return server.requestAuthentication(); + } +#endif + String html = htmlHeader(F("GPIO config")); + html += F( + "
"); + html += htmlMenu(); + html += F("

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

"); + html += F("

Send

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

Receive

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


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

No changes detected!

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

Saved changes & rebooting.

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

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

"); + } + } + html += addJsReloadUrl(changed ? kUrlRoot : kUrlGpio, + changed ? kRebootTime : kQuickDisplayTime, + true); + html += htmlEnd(); + server.send(200, "text/html", html); + if (changed) doRestart("GPIOs were changed. Rebooting!"); +} + void handleNotFound(void) { String message = "File Not Found\n\n"; message += "URI: "; @@ -1850,7 +1809,7 @@ void handleNotFound(void) { void setup_wifi(void) { delay(10); - loadWifiConfigFile(); + loadConfigFile(); // We start by connecting to a WiFi network wifiManager.setTimeout(300); // Time out after 5 mins. // Set up additional parameters for WiFiManager config menu page. @@ -1896,7 +1855,7 @@ void setup_wifi(void) { kMqttPrefixKey, "Leave empty to use Hostname", MqttPrefix, kHostnameLength); wifiManager.addParameter(&custom_mqtt_prefix); - #endif // MQTT_ENABLE +#endif // MQTT_ENABLE #if USE_STATIC_IP // Use a static IP config rather than the one supplied via DHCP. wifiManager.setSTAStaticIPConfig(kIPAddress, kGateway, kSubnetMask); @@ -1906,13 +1865,9 @@ void setup_wifi(void) { #endif // MIN_SIGNAL_STRENGTH wifiManager.setRemoveDuplicateAPs(HIDE_DUPLIATE_NETWORKS); - if (!wifiManager.autoConnect()) { - debug("Wifi failed to connect and hit timeout. Rebooting..."); - delay(3000); + if (!wifiManager.autoConnect()) // Reboot. A.k.a. "Have you tried turning it Off and On again?" - ESP.reset(); - delay(5000); - } + doRestart("Wifi failed to connect and hit timeout. Rebooting...", true); #if MQTT_ENABLE strncpy(MqttServer, custom_mqtt_server.getValue(), kHostnameLength); @@ -1925,7 +1880,7 @@ void setup_wifi(void) { strncpy(HttpUsername, custom_http_username.getValue(), kUsernameLength); strncpy(HttpPassword, custom_http_password.getValue(), kPasswordLength); if (flagSaveWifiConfig) { - saveWifiConfig(); + saveConfig(); } debug("WiFi connected. IP address:"); debug(WiFi.localIP().toString().c_str()); @@ -1951,10 +1906,12 @@ void init_vars(void) { MqttClimateCmnd = MqttClimate + '/' + MQTT_CLIMATE_CMND + '/'; // Sub-topic for the climate stat topics. MqttClimateStat = MqttClimate + '/' + MQTT_CLIMATE_STAT + '/'; +#if MQTT_DISCOVERY_ENABLE MqttDiscovery = "homeassistant/climate/" + String(Hostname) + "/config"; +#endif // MQTT_DISCOVERY_ENABLE MqttHAName = String(Hostname) + "_aircon"; // Create a unique MQTT client id. - MqttClientId = String(Hostname) + String(ESP.getChipId(), HEX); + MqttClientId = String(Hostname) + String(kChipId, HEX); #endif // MQTT_ENABLE } @@ -1979,67 +1936,98 @@ void setup(void) { 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]); - IrSendTable[i]->begin(); - offset = IrSendTable[i]->calibrate(); - } -#ifdef IR_RX -#if IR_RX_PULLUP - pinMode(IR_RX, INPUT_PULLUP); -#endif // IR_RX_PULLUP -#if DECODE_HASH - // Ignore messages with less than minimum on or off pulses. - irrecv.setUnknownThreshold(kMinUnknownSize); -#endif // DECODE_HASH - irrecv.enableIRIn(); // Start the receiver -#endif // IR_RX + lastClimateSource = F("None"); #if DEBUG if (!isSerialGpioUsedByIr()) { +#if defined(ESP8266) // Use SERIAL_TX_ONLY so that the RX pin can be freed up for GPIO/IR use. Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(BAUD_RATE, SERIAL_8N1); +#endif // ESP8266 while (!Serial) // Wait for the serial connection to be establised. delay(50); Serial.println(); - debug("IRMQTTServer " _MY_VERSION_" has booted."); + debug("IRMQTTServer " _MY_VERSION_ " has booted."); } #endif // DEBUG setup_wifi(); +#if DEBUG + // After the config has been loaded, check again if we are using a Serial GPIO + if (isSerialGpioUsedByIr()) Serial.end(); +#endif // DEBUG + + // Initialise all the IR transmitters. + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { + if (txGpioTable[i] == kGpioUnused) { + IrSendTable[i] = NULL; + } else { + IrSendTable[i] = new IRsend(txGpioTable[i], kInvertTxOutput); + if (IrSendTable[i] == NULL) break; + IrSendTable[i]->begin(); + offset = IrSendTable[i]->calibrate(); + } + } +#if IR_RX + if (rx_gpio != kGpioUnused) + irrecv = new IRrecv(rx_gpio, kCaptureBufferSize, kCaptureTimeout, true); + if (irrecv != NULL) { +#if DECODE_HASH + // Ignore messages with less than minimum on or off pulses. + irrecv->setUnknownThreshold(kMinUnknownSize); +#endif // DECODE_HASH + irrecv->enableIRIn(IR_RX_PULLUP); // Start the receiver + } +#endif // IR_RX + commonAc = new IRac(txGpioTable[0], kInvertTxOutput); + // Wait a bit for things to settle. delay(500); lastReconnectAttempt = 0; +#if MDNS_ENABLE +#if defined(ESP8266) if (mdns.begin(Hostname, WiFi.localIP())) { +#else // ESP8266 + if (mdns.begin(Hostname)) { +#endif // ESP8266 debug("MDNS responder started"); } +#endif // MDNS_ENABLE // Setup the root web page. - server.on("/", handleRoot); + server.on(kUrlRoot, handleRoot); +#if EXAMPLES_ENABLE // Setup the examples web page. - server.on("/examples", handleExamples); + server.on(kUrlExamples, handleExamples); +#endif // EXAMPLES_ENABLE // Setup the page to handle web-based IR codes. server.on("/ir", handleIr); // Setup the aircon page. - server.on("/aircon", handleAirCon); + server.on(kUrlAircon, handleAirCon); // Setup the aircon update page. server.on("/aircon/set", handleAirConSet); // Setup the info page. - server.on("/info", handleInfo); + server.on(kUrlInfo, handleInfo); // Setup the admin page. - server.on("/admin", handleAdmin); + server.on(kUrlAdmin, handleAdmin); // Setup a reset page to cause WiFiManager information to be reset. - server.on("/reset", handleReset); + server.on(kUrlWipe, handleReset); // Reboot url - server.on("/quitquitquit", handleReboot); + server.on(kUrlReboot, handleReboot); + // Show & pick which gpios are used for what etc. + server.on(kUrlGpio, handleGpio); + // Parse and update the new gpios. + server.on(kUrlGpioSet, handleGpioSetting); #if MQTT_ENABLE +#if MQTT_DISCOVERY_ENABLE // MQTT Discovery url - server.on("/send_discovery", handleSendMqttDiscovery); + server.on(kUrlSendDiscovery, handleSendMqttDiscovery); +#endif // MQTT_DISCOVERY_ENABLE // Finish setup of the mqtt clent object. mqtt_client.setServer(MqttServer, atoi(MqttPort)); mqtt_client.setCallback(mqttCallback); @@ -2056,9 +2044,7 @@ void setup(void) { delay(1000); #endif // MQTT_ENABLE server.send(200, "text/html", - "Updating firmware." - "" - "

Updating firmware

" + htmlHeader(F("Updating firmware")) + "
" "

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

" "

The firmware is uploading and will try to flash itself. " @@ -2066,11 +2052,9 @@ void setup(void) { "

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

" + - addJsReloadUrl("/", 20, true) + - ""); - delay(1000); - ESP.restart(); - delay(1000); + addJsReloadUrl(kUrlRoot, 20, true) + + htmlEnd()); + doRestart("Post firmware reboot."); }, [](){ if (!server.authenticate(HttpUsername, HttpPassword)) { debug("Basic HTTP authentication failure for /update."); @@ -2078,12 +2062,12 @@ void setup(void) { } HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { - WiFiUDP::stopAll(); debug("Update:"); debug(upload.filename.c_str()); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & - 0xFFFFF000; - if (!Update.begin(maxSketchSpace)) { // start with max available size +#if defined(ESP8266) + WiFiUDP::stopAll(); +#endif // defined(ESP8266) + if (!Update.begin(maxSketchSpace())) { // start with max available #if DEBUG if (!isSerialGpioUsedByIr()) Update.printError(Serial); @@ -2138,9 +2122,9 @@ void unsubscribing(const String topic_name) { debug(topic_name.c_str()); } -void mqttLog(const String mesg) { - debug(mesg.c_str()); - mqtt_client.publish(MqttLog.c_str(), mesg.c_str()); +void mqttLog(const char* str) { + debug(str); + mqtt_client.publish(MqttLog.c_str(), str); mqttSentCounter++; } @@ -2174,7 +2158,7 @@ bool reconnect(void) { // Subscribing to topic(s) subscribing(MqttSend); - for (uint8_t i = 0; i < kSendTableSize; i++) { + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { subscribing(MqttSend + '_' + String(static_cast(i))); } // Climate command topics. @@ -2193,12 +2177,13 @@ bool reconnect(void) { // 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++) { + for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) { result += ", " + MqttSend + '_' + String(i); } return result; } +#if MQTT_DISCOVERY_ENABLE // MQTT Discovery web page void handleSendMqttDiscovery(void) { #if HTML_PASSWORD_ENABLE @@ -2208,9 +2193,7 @@ void handleSendMqttDiscovery(void) { } #endif // HTML_PASSWORD_ENABLE server.send(200, "text/html", - "Sending MQTT Discovery message" - "" - "

Sending MQTT Discovery message.

" + + htmlHeader(F("Sending MQTT Discovery message")) + htmlMenu() + "

The Home Assistant MQTT Discovery message is being sent to topic: " + MqttDiscovery + ". It will show up in Home Assistant in a few seconds." @@ -2218,29 +2201,32 @@ void handleSendMqttDiscovery(void) { "

Warning!

" "

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

" + - addJsReloadUrl("/", 15, true) + - ""); + addJsReloadUrl(kUrlRoot, kRebootTime, true) + + htmlEnd()); sendMQTTDiscovery(MqttDiscovery.c_str()); } +#endif // MQTT_DISCOVERY_ENABLE 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."); sendClimate(state, state, MqttClimateStat, retain, true, false); +#if MQTT_CLIMATE_JSON + sendJsonState(state, MqttClimateStat + KEY_JSON); +#endif // MQTT_CLIMATE_JSON 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" + uint8_t channel = getDefaultIrSendIdx(); // Default to first usable channel. debug("Receiving data by MQTT topic:"); debug(topic_name.c_str()); @@ -2255,10 +2241,18 @@ 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); + // Handle the special command for forcing a resend of the state via IR. + bool force_resend = false; + if (topic_name.equals(MqttClimateCmnd + KEY_RESEND) && + callback_str.equalsIgnoreCase(KEY_RESEND)) { + force_resend = true; + mqttLog("Climate resend requested."); + } + if (sendClimate(climate, updated, MqttClimateStat, + true, false, force_resend) && !force_resend) + 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"); @@ -2268,7 +2262,7 @@ void receivingMQTT(String const topic_name, String const 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++) { + for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) { debug(("Checking if " + topic_name + " ends with _" + String(i)).c_str()); if (topic_name.endsWith("_" + String(i))) { channel = i; @@ -2278,39 +2272,67 @@ void receivingMQTT(String const topic_name, String const callback_str) { } debug(("Using transmit channel " + String(static_cast(channel)) + - " / GPIO " + String(static_cast(gpioTable[channel]))).c_str()); + " / GPIO " + String(static_cast(txGpioTable[channel]))).c_str()); // Make a copy of the callback string as strtok destroys it. char* callback_c_str = strdup(callback_str.c_str()); debug("MQTT Payload (raw):"); debug(callback_c_str); - // 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; + // 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. + decode_type_t ir_type = (decode_type_t)atoi(strtok_r( + ircommand, kCommandDelimiter, &tok_ptr)); + 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]) + 1, nbits, repeat); + } + } + free(ircommand); } - // 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 @@ -2321,6 +2343,10 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { // constructing the PUBLISH packet. // Allocate the correct amount of memory for the payload copy byte* payload_copy = reinterpret_cast(malloc(length + 1)); + if (payload_copy == NULL) { + debug("Can't allocate memory for `payload_copy`. Skipping callback!"); + return; + } // Copy the payload to the new buffer memcpy(payload_copy, payload, length); @@ -2336,6 +2362,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) { free(payload_copy); } +#if MQTT_DISCOVERY_ENABLE void sendMQTTDiscovery(const char *topic) { if (mqtt_client.publish( topic, String( @@ -2374,6 +2401,7 @@ void sendMQTTDiscovery(const char *topic) { mqttLog("MQTT climate discovery FAILED to send."); } } +#endif // MQTT_DISCOVERY_ENABLE #endif // MQTT_ENABLE void loop(void) { @@ -2397,11 +2425,12 @@ void loop(void) { lastReconnectAttempt = 0; wasConnected = true; if (boot) { - mqttLog("IR Server just booted"); + mqttLog("IRMQTTServer " _MY_VERSION_ " just booted"); boot = false; } else { - mqttLog("IR Server just (re)connected to MQTT. " - "Lost connection about " + timeSince(lastConnectedTime)); + mqttLog(String( + "IRMQTTServer just (re)connected to MQTT. Lost connection about " + + timeSince(lastConnectedTime)).c_str()); } lastConnectedTime = now; debug("successful client mqtt connection"); @@ -2422,10 +2451,11 @@ void loop(void) { if (lockMqttBroadcast && statListenTime.elapsed() > kStatListenPeriodMs) { unsubscribing(MqttClimateStat + '+'); mqttLog("Finished listening for previous state."); - if (cmpClimate(climate, climate_prev)) { // Something changed. + if (IRac::cmpStates(climate, climate_prev)) { // Something changed. mqttLog("The state was recovered from MQTT broker. Updating."); sendClimate(climate_prev, climate, MqttClimateStat, - true, false, false); + true, false, false, MQTT_CLIMATE_IR_SEND_ON_RESTART); + lastClimateSource = F("MQTT (via retain)"); } lockMqttBroadcast = false; // Release the lock so we can broadcast again. } @@ -2433,15 +2463,16 @@ void loop(void) { doBroadcast(&lastBroadcast, kBroadcastPeriodMs, climate, false, false); } #endif // MQTT_ENABLE -#ifdef IR_RX +#if IR_RX // Check if an IR code has been received via the IR RX module. #if REPORT_UNKNOWNS - if (irrecv.decode(&capture)) { + if (irrecv != NULL && irrecv->decode(&capture)) { #else // REPORT_UNKNOWNS - if (irrecv.decode(&capture) && capture.decode_type != UNKNOWN) { + if (irrecv != NULL && irrecv->decode(&capture) && + capture.decode_type != UNKNOWN) { #endif // REPORT_UNKNOWNS lastIrReceivedTime = millis(); - lastIrReceived = String(capture.decode_type) + "," + + lastIrReceived = String(capture.decode_type) + kCommandDelimiter[0] + resultToHexidecimal(&capture); #if REPORT_RAW_UNKNOWNS if (capture.decode_type == UNKNOWN) { @@ -2461,14 +2492,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); @@ -2505,297 +2539,51 @@ uint64_t getUInt64fromHex(char const *str) { // repeat: Nr. of times the message is to be repeated. (Not all protcols.) // Returns: // bool: Successfully sent or not. -bool sendIRCode(IRsend *irsend, int const ir_type, +bool sendIRCode(IRsend *irsend, decode_type_t const ir_type, uint64_t const code, char const * code_str, uint16_t bits, uint16_t repeat) { + if (irsend == NULL) return false; + bool success = true; // Assume success. + // Ensure we have enough repeats. + repeat = std::max(IRsend::minRepeats(ir_type), repeat); + if (bits == 0) bits = IRsend::defaultBits(ir_type); // Create a pseudo-lock so we don't try to send two codes at the same time. while (lockIr) delay(20); lockIr = true; - bool success = true; // Assume success. + // Turn off IR capture if we need to. +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + if (irrecv != NULL) irrecv->disableIRIn(); // Stop the IR receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING // send the IR message. switch (ir_type) { -#if SEND_RC5 - case RC5: // 1 - if (bits == 0) - bits = kRC5Bits; - irsend->sendRC5(code, bits, repeat); - break; -#endif -#if SEND_RC6 - case RC6: // 2 - if (bits == 0) - bits = kRC6Mode0Bits; - irsend->sendRC6(code, bits, repeat); - break; -#endif -#if SEND_NEC - case NEC: // 3 - if (bits == 0) - bits = kNECBits; - irsend->sendNEC(code, bits, repeat); - break; -#endif -#if SEND_SONY - case SONY: // 4 - if (bits == 0) - bits = kSony12Bits; - repeat = std::max(repeat, kSonyMinRepeat); - irsend->sendSony(code, bits, repeat); - break; -#endif -#if SEND_PANASONIC - case PANASONIC: // 5 - if (bits == 0) - bits = kPanasonicBits; - irsend->sendPanasonic64(code, bits, repeat); - break; -#endif -#if SEND_JVC - case JVC: // 6 - if (bits == 0) - bits = kJvcBits; - irsend->sendJVC(code, bits, repeat); - break; -#endif -#if SEND_SAMSUNG - case SAMSUNG: // 7 - if (bits == 0) - bits = kSamsungBits; - irsend->sendSAMSUNG(code, bits, repeat); - break; -#endif -#if SEND_SAMSUNG36 - case SAMSUNG36: // 56 - if (bits == 0) - bits = kSamsung36Bits; - irsend->sendSamsung36(code, bits, repeat); - break; -#endif -#if SEND_WHYNTER - case WHYNTER: // 8 - if (bits == 0) - bits = kWhynterBits; - irsend->sendWhynter(code, bits, repeat); - break; -#endif -#if SEND_AIWA_RC_T501 - case AIWA_RC_T501: // 9 - if (bits == 0) - bits = kAiwaRcT501Bits; - repeat = std::max(repeat, kAiwaRcT501MinRepeats); - irsend->sendAiwaRCT501(code, bits, repeat); - break; -#endif -#if SEND_LG - case LG: // 10 - if (bits == 0) - bits = kLgBits; - irsend->sendLG(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI - case MITSUBISHI: // 12 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend->sendMitsubishi(code, bits, repeat); - break; -#endif -#if SEND_DISH - case DISH: // 13 - if (bits == 0) - bits = kDishBits; - repeat = std::max(repeat, kDishMinRepeat); - irsend->sendDISH(code, bits, repeat); - break; -#endif -#if SEND_SHARP - case SHARP: // 14 - if (bits == 0) - bits = kSharpBits; - irsend->sendSharpRaw(code, bits, repeat); - break; -#endif -#if SEND_COOLIX - case COOLIX: // 15 - if (bits == 0) - bits = kCoolixBits; - irsend->sendCOOLIX(code, bits, repeat); - break; -#endif - case DAIKIN: // 16 - case DAIKIN2: // 53 - case DAIKIN216: // 61 - 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(irsend, ir_type, code_str); - break; -#if SEND_DENON - case DENON: // 17 - if (bits == 0) - bits = DENON_BITS; - irsend->sendDenon(code, bits, repeat); - break; -#endif -#if SEND_SHERWOOD - case SHERWOOD: // 19 - if (bits == 0) - bits = kSherwoodBits; - repeat = std::max(repeat, kSherwoodMinRepeat); - irsend->sendSherwood(code, bits, repeat); - break; -#endif -#if SEND_RCMM - case RCMM: // 21 - if (bits == 0) - bits = kRCMMBits; - irsend->sendRCMM(code, bits, repeat); - break; -#endif -#if SEND_SANYO - case SANYO_LC7461: // 22 - if (bits == 0) - bits = kSanyoLC7461Bits; - irsend->sendSanyoLC7461(code, bits, repeat); - break; -#endif -#if SEND_RC5 - case RC5X: // 23 - if (bits == 0) - bits = kRC5XBits; - irsend->sendRC5(code, bits, repeat); - break; -#endif #if SEND_PRONTO - case PRONTO: // 25 + case decode_type_t::PRONTO: // 25 success = parseStringAndSendPronto(irsend, code_str, repeat); break; -#endif -#if SEND_NIKAI - case NIKAI: // 29 - if (bits == 0) - bits = kNikaiBits; - irsend->sendNikai(code, bits, repeat); - break; -#endif +#endif // SEND_PRONTO + case decode_type_t::RAW: // 30 #if SEND_RAW - case RAW: // 30 success = parseStringAndSendRaw(irsend, code_str); break; #endif #if SEND_GLOBALCACHE - case GLOBALCACHE: // 31 + case decode_type_t::GLOBALCACHE: // 31 success = parseStringAndSendGC(irsend, code_str); break; #endif -#if SEND_MIDEA - case MIDEA: // 34 - if (bits == 0) - bits = kMideaBits; - irsend->sendMidea(code, bits, repeat); - break; -#endif -#if SEND_MAGIQUEST - case MAGIQUEST: // 35 - if (bits == 0) - bits = kMagiquestBits; - irsend->sendMagiQuest(code, bits, repeat); - break; -#endif -#if SEND_LASERTAG - case LASERTAG: // 36 - if (bits == 0) - bits = kLasertagBits; - irsend->sendLasertag(code, bits, repeat); - break; -#endif -#if SEND_CARRIER_AC - case CARRIER_AC: // 37 - if (bits == 0) - bits = kCarrierAcBits; - irsend->sendCarrierAC(code, bits, repeat); - break; -#endif -#if SEND_MITSUBISHI2 - case MITSUBISHI2: // 39 - if (bits == 0) - bits = kMitsubishiBits; - repeat = std::max(repeat, kMitsubishiMinRepeat); - irsend->sendMitsubishi2(code, bits, repeat); - break; -#endif -#if SEND_GICABLE - case GICABLE: // 43 - if (bits == 0) - bits = kGicableBits; - repeat = std::max(repeat, kGicableMinRepeat); - irsend->sendGICable(code, bits, repeat); - break; -#endif -#if SEND_LUTRON - case LUTRON: // 47 - if (bits == 0) - bits = kLutronBits; - irsend->sendLutron(code, bits, repeat); - break; -#endif -#if SEND_PIONEER - case PIONEER: // 50 - if (bits == 0) - bits = kPioneerBits; - irsend->sendPioneer(code, bits, repeat); - break; -#endif -#if SEND_LG - case LG2: // 51 - if (bits == 0) - bits = kLgBits; - irsend->sendLG2(code, bits, repeat); - break; -#endif -#if SEND_VESTEL_AC - case VESTEL_AC: // 54 - if (bits == 0) - bits = kVestelAcBits; - irsend->sendVestelAc(code, bits, repeat); - break; -#endif -#if SEND_TECO - case TECO: // 55 - if (bits == 0) - bits = kTecoBits; - irsend->sendTeco(code, bits, repeat); - break; -#endif -#if SEND_LEGOPF - case LEGOPF: // 58 - if (bits == 0) - bits = kLegoPfBits; - irsend->sendLegoPf(code, bits, repeat); - break; -#endif - default: - // If we got here, we didn't know how to send it. - success = false; + default: // Everything else. + if (hasACState(ir_type)) // protocols with > 64 bits + success = parseStringAndSendAirCon(irsend, ir_type, code_str); + else // protocols with <= 64 bits + success = irsend->send(ir_type, code, bits, repeat); } +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture back on if we need to. + if (irrecv != NULL) irrecv->enableIRIn(); // Restart the receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING lastSendTime = millis(); // Release the lock. lockIr = false; @@ -2810,9 +2598,7 @@ bool sendIRCode(IRsend *irsend, int const 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 || - ir_type == RAW || + if (hasACState(ir_type) || ir_type == PRONTO || ir_type == RAW || ir_type == GLOBALCACHE) { debug("Code: "); debug(code_str); @@ -2820,11 +2606,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++; } @@ -2835,9 +2624,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++; } @@ -2882,62 +2674,135 @@ bool sendFloat(const String topic, const float_t temp, const bool retain) { #endif // MQTT_ENABLE } -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 MQTT_CLIMATE_JSON +void sendJsonState(const stdAc::state_t state, const String topic, + const bool retain, const bool ha_mode) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json[KEY_PROTOCOL] = typeToString(state.protocol); + json[KEY_MODEL] = state.model; + json[KEY_POWER] = IRac::boolToString(state.power); + json[KEY_MODE] = IRac::opmodeToString(state.mode); + // Home Assistant wants mode to be off if power is also off & vice-versa. + if (ha_mode && (state.mode == stdAc::opmode_t::kOff || !state.power)) { + json[KEY_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); + json[KEY_POWER] = IRac::boolToString(false); + } + json[KEY_CELSIUS] = IRac::boolToString(state.celsius); + json[KEY_TEMP] = state.degrees; + json[KEY_FANSPEED] = IRac::fanspeedToString(state.fanspeed); + json[KEY_SWINGV] = IRac::swingvToString(state.swingv); + json[KEY_SWINGH] = IRac::swinghToString(state.swingh); + json[KEY_QUIET] = IRac::boolToString(state.quiet); + json[KEY_TURBO] = IRac::boolToString(state.turbo); + json[KEY_ECONO] = IRac::boolToString(state.econo); + json[KEY_LIGHT] = IRac::boolToString(state.light); + json[KEY_FILTER] = IRac::boolToString(state.filter); + json[KEY_CLEAN] = IRac::boolToString(state.clean); + json[KEY_BEEP] = IRac::boolToString(state.beep); + json[KEY_SLEEP] = state.sleep; + + String payload = ""; + payload.reserve(200); + json.printTo(payload); + sendString(topic, payload, retain); +} + +stdAc::state_t jsonToState(const stdAc::state_t current, const String str) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.parseObject(str); + if (!json.success()) { + debug("json MQTT message did not parse. Skipping!"); + return current; + } + stdAc::state_t result = current; + if (json.containsKey(KEY_PROTOCOL)) + result.protocol = strToDecodeType(json[KEY_PROTOCOL]); + if (json.containsKey(KEY_MODEL)) + result.model = IRac::strToModel(json[KEY_MODEL]); + if (json.containsKey(KEY_MODE)) + result.mode = IRac::strToOpmode(json[KEY_MODE]); + if (json.containsKey(KEY_FANSPEED)) + result.fanspeed = IRac::strToFanspeed(json[KEY_FANSPEED]); + if (json.containsKey(KEY_SWINGV)) + result.swingv = IRac::strToSwingV(json[KEY_SWINGV]); + if (json.containsKey(KEY_SWINGH)) + result.swingh = IRac::strToSwingH(json[KEY_SWINGH]); + if (json.containsKey(KEY_TEMP)) + result.degrees = json[KEY_TEMP]; + if (json.containsKey(KEY_SLEEP)) + result.sleep = json[KEY_SLEEP]; + if (json.containsKey(KEY_POWER)) + result.power = IRac::strToBool(json[KEY_POWER]); + if (json.containsKey(KEY_QUIET)) + result.quiet = IRac::strToBool(json[KEY_QUIET]); + if (json.containsKey(KEY_TURBO)) + result.turbo = IRac::strToBool(json[KEY_TURBO]); + if (json.containsKey(KEY_ECONO)) + result.econo = IRac::strToBool(json[KEY_ECONO]); + if (json.containsKey(KEY_LIGHT)) + result.light = IRac::strToBool(json[KEY_LIGHT]); + if (json.containsKey(KEY_CLEAN)) + result.clean = IRac::strToBool(json[KEY_CLEAN]); + if (json.containsKey(KEY_FILTER)) + result.filter = IRac::strToBool(json[KEY_FILTER]); + if (json.containsKey(KEY_BEEP)) + result.beep = IRac::strToBool(json[KEY_BEEP]); + if (json.containsKey(KEY_CELSIUS)) + result.celsius = IRac::strToBool(json[KEY_CELSIUS]); + return result; +} +#endif // MQTT_CLIMATE_JSON + +stdAc::state_t updateClimate(stdAc::state_t current, const String str, + const String prefix, const String payload) { + stdAc::state_t result = current; +#if MQTT_CLIMATE_JSON + if (str.equals(prefix + KEY_JSON)) + result = jsonToState(result, payload.c_str()); + else +#endif // MQTT_CLIMATE_JSON if (str.equals(prefix + KEY_PROTOCOL)) - result.protocol = strToDecodeType(value.c_str()); + result.protocol = strToDecodeType(payload.c_str()); else if (str.equals(prefix + KEY_MODEL)) - result.model = IRac::strToModel(value.c_str()); + result.model = IRac::strToModel(payload.c_str()); else if (str.equals(prefix + KEY_POWER)) - result.power = IRac::strToBool(value.c_str()); + result.power = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_MODE)) - result.mode = IRac::strToOpmode(value.c_str()); + result.mode = IRac::strToOpmode(payload.c_str()); else if (str.equals(prefix + KEY_TEMP)) - result.degrees = value.toFloat(); + result.degrees = payload.toFloat(); else if (str.equals(prefix + KEY_FANSPEED)) - result.fanspeed = IRac::strToFanspeed(value.c_str()); + result.fanspeed = IRac::strToFanspeed(payload.c_str()); else if (str.equals(prefix + KEY_SWINGV)) - result.swingv = IRac::strToSwingV(value.c_str()); + result.swingv = IRac::strToSwingV(payload.c_str()); else if (str.equals(prefix + KEY_SWINGH)) - result.swingh = IRac::strToSwingH(value.c_str()); + result.swingh = IRac::strToSwingH(payload.c_str()); else if (str.equals(prefix + KEY_QUIET)) - result.quiet = IRac::strToBool(value.c_str()); + result.quiet = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_TURBO)) - result.turbo = IRac::strToBool(value.c_str()); + result.turbo = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_ECONO)) - result.econo = IRac::strToBool(value.c_str()); + result.econo = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_LIGHT)) - result.light = IRac::strToBool(value.c_str()); + result.light = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_BEEP)) - result.beep = IRac::strToBool(value.c_str()); + result.beep = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_FILTER)) - result.filter = IRac::strToBool(value.c_str()); + result.filter = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_CLEAN)) - result.clean = IRac::strToBool(value.c_str()); + result.clean = IRac::strToBool(payload.c_str()); + else if (str.equals(prefix + KEY_CELSIUS)) + result.celsius = IRac::strToBool(payload.c_str()); else if (str.equals(prefix + KEY_SLEEP)) - result.sleep = value.toInt(); - else if (str.equals(prefix + KEY_CLOCK)) - result.clock = value.toInt(); + result.sleep = payload.toInt(); return result; } -// 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; -} - -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; @@ -2954,7 +2819,8 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, diff = true; success &= sendBool(topic_prefix + KEY_POWER, next.power, retain); success &= sendString(topic_prefix + KEY_MODE, - (next.power ? opmodeToString(next.mode) : F("off")), + (next.power ? IRac::opmodeToString(next.mode) + : F("off")), retain); } if (prev.degrees != next.degrees || forceMQTT) { @@ -2968,17 +2834,17 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, if (prev.fanspeed != next.fanspeed || forceMQTT) { diff = true; success &= sendString(topic_prefix + KEY_FANSPEED, - fanspeedToString(next.fanspeed), retain); + IRac::fanspeedToString(next.fanspeed), retain); } if (prev.swingv != next.swingv || forceMQTT) { diff = true; success &= sendString(topic_prefix + KEY_SWINGV, - swingvToString(next.swingv), retain); + IRac::swingvToString(next.swingv), retain); } if (prev.swingh != next.swingh || forceMQTT) { diff = true; success &= sendString(topic_prefix + KEY_SWINGH, - swinghToString(next.swingh), retain); + IRac::swinghToString(next.swingh), retain); } if (prev.quiet != next.quiet || forceMQTT) { diff = true; @@ -3012,18 +2878,26 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, diff = true; success &= sendInt(topic_prefix + KEY_SLEEP, next.sleep, retain); } - if (diff && !forceMQTT) + if (diff && !forceMQTT) { debug("Difference in common A/C state detected."); - else +#if MQTT_CLIMATE_JSON + sendJsonState(next, MqttClimateStat + KEY_JSON); +#endif // MQTT_CLIMATE_JSON + } 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."); - lastClimateSucceeded = commonAc.sendAc( - next.protocol, next.model, next.power, next.mode, - next.degrees, next.celsius, next.fanspeed, next.swingv, next.swingh, - next.quiet, next.turbo, next.econo, next.light, next.filter, next.clean, - next.beep, next.sleep, -1); +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture off if we need to. + if (irrecv != NULL) irrecv->disableIRIn(); // Stop the IR receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + lastClimateSucceeded = commonAc->sendAc(next, &prev); +#if IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING + // Turn IR capture back on if we need to. + if (irrecv != NULL) irrecv->enableIRIn(); // Restart the receiver +#endif // IR_RX && DISABLE_CAPTURE_WHILE_TRANSMITTING if (lastClimateSucceeded) hasClimateBeenSent = true; success &= lastClimateSucceeded; lastClimateIr.reset(); @@ -3032,3 +2906,48 @@ bool sendClimate(const commonAcState_t prev, const commonAcState_t next, } return success; } + +#if USE_DECODED_AC_SETTINGS && IR_RX +// Decode and use a valid IR A/C remote that we understand enough to convert +// to a Common A/C format. +// Args: +// decode: A successful raw IR decode object. +// Returns: +// A boolean indicating success or failure. +bool decodeCommonAc(const decode_results *decode) { + if (!IRac::isProtocolSupported(decode->decode_type)) { + debug("Inbound IR messages isn't a supported common A/C protocol"); + return false; + } + stdAc::state_t state = climate; + debug("Converting inbound IR A/C message to common A/C"); + if (!IRAcUtils::decodeToState(decode, &state, &climate)) { + 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, + REPLAY_DECODED_AC_MESSAGE, REPLAY_DECODED_AC_MESSAGE); +#else // MQTT_ENABLE + sendClimate(climate, state, "", false, false, REPLAY_DECODED_AC_MESSAGE, + REPLAY_DECODED_AC_MESSAGE); +#endif // MQTT_ENABLE + climate = state; // Copy over the new climate state. + return true; +} +#endif // USE_DECODED_AC_SETTINGS && IR_RX diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/platformio.ini new file mode 100644 index 000000000..2d82260ad --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRMQTTServer/platformio.ini @@ -0,0 +1,62 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = -DMQTT_MAX_PACKET_SIZE=768 + +[common] +lib_deps_builtin = +lib_deps_external = + PubSubClient + ArduinoJson@<6.0 + +[common_esp8266] +lib_deps_external = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + WifiManager@>=0.14 + +[common_esp32] +lib_deps_external = + ${common.lib_deps_builtin} + ${common.lib_deps_external} + https://github.com/tzapu/WiFiManager.git#development + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 +lib_deps = ${common_esp8266.lib_deps_external} + +[env:d1_mini] +platform = espressif8266 +framework = arduino +board = d1_mini +lib_deps = ${common_esp8266.lib_deps_external} + +[env:d1_mini_no_mqtt] +platform = espressif8266 +framework = arduino +board = d1_mini +build_flags = + ${env.build_flags} + -DMQTT_ENABLE=false +lib_deps = ${common_esp8266.lib_deps_external} + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev +lib_deps = ${common_esp32.lib_deps_external} + +[env:esp01_1m] +platform = espressif8266 +framework = arduino +board = esp01_1m +build_flags = + ${env.build_flags} + -Wl,-Teagle.flash.1m64.ld +lib_deps = ${common_esp8266.lib_deps_external} diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRServer/IRServer.ino similarity index 80% rename from lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRServer/IRServer.ino index b378d3bd5..96fad95d2 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRServer/IRServer.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRServer/IRServer.ino @@ -1,15 +1,17 @@ /* * IRremoteESP8266: IRServer - demonstrates sending IR codes controlled from a webserver + * Version 0.3 May, 2019 * Version 0.2 June, 2017 * Copyright 2015 Mark Szabo + * Copyright 2019 David Conran * - * An IR LED circuit *MUST* be connected to the ESP8266 on a pin + * An IR LED circuit *MUST* be connected to the ESP on a pin * as specified by kIrLed below. * * TL;DR: The IR LED needs to be driven by a transistor for a good result. * * Suggested circuit: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -26,12 +28,17 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif +#if defined(ESP8266) #include #include #include +#endif // ESP8266 +#if defined(ESP32) +#include +#include +#include +#endif // ESP32 #include #include #include @@ -40,18 +47,27 @@ const char* kSsid = "....."; const char* kPassword = "....."; MDNSResponder mdns; +#if defined(ESP8266) ESP8266WebServer server(80); +#undef HOSTNAME +#define HOSTNAME "esp8266" +#endif // ESP8266 +#if defined(ESP32) +WebServer server(80); +#undef HOSTNAME +#define HOSTNAME "esp32" +#endif // ESP32 -const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2). +const uint16_t kIrLed = 4; // ESP GPIO pin to use. Recommended: 4 (D2). IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message. void handleRoot() { server.send(200, "text/html", "" \ - "ESP8266 Demo" \ + "" HOSTNAME " Demo" \ "" \ - "

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

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

" \ "

Send 0xFFE01F

" \ "

Send 0xFAB123

" \ @@ -104,7 +120,11 @@ void setup(void) { Serial.print("IP address: "); Serial.println(WiFi.localIP().toString()); - if (mdns.begin("esp8266", WiFi.localIP())) { +#if defined(ESP8266) + if (mdns.begin(HOSTNAME, WiFi.localIP())) { +#else // ESP8266 + if (mdns.begin(HOSTNAME)) { +#endif // ESP8266 Serial.println("MDNS responder started"); } diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRServer/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRServer/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRServer/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDemo/IRrecvDemo.ino similarity index 94% rename from lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDemo/IRrecvDemo.ino index 09babe4fe..5fd03f4b4 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDemo/IRrecvDemo.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDemo/IRrecvDemo.ino @@ -6,7 +6,7 @@ * An IR detector/demodulator must be connected to the input kRecvPin. * Copyright 2009 Ken Shirriff, http://arcfn.com * Example circuit diagram: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving * Changes: * Version 0.2 June, 2017 * Changed GPIO pin to the same as other examples. @@ -16,9 +16,7 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009 */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDemo/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDump/IRrecvDump.ino similarity index 99% rename from lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDump/IRrecvDump.ino index 34f10dc83..2a65cb624 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDump/IRrecvDump.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDump/IRrecvDump.ino @@ -22,9 +22,7 @@ * LG added by Darryl Smith (based on the JVC protocol) */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDump/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDump/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDump/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDumpV2/IRrecvDumpV2.ino similarity index 51% rename from lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDumpV2/IRrecvDumpV2.ino index 2dee0597c..f69c14aed 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -3,12 +3,14 @@ * An IR detector/demodulator must be connected to the input kRecvPin. * * Copyright 2009 Ken Shirriff, http://arcfn.com - * Copyright 2017 David Conran + * Copyright 2017-2019 David Conran * * Example circuit diagram: - * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving * * Changes: + * Version 0.5 June, 2019 + * - Move A/C description to IRac.cpp. * Version 0.4 July, 2018 * - Minor improvements and more A/C unit support. * Version 0.3 November, 2017 @@ -19,31 +21,11 @@ * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, */ -#ifndef UNIT_TEST #include -#endif #include #include +#include #include -// The following are only needed for extended decoding of A/C Messages -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - // ==================== start of TUNEABLE PARAMETERS ==================== // An IR detector/demodulator is connected to GPIO pin 14 @@ -114,171 +96,19 @@ const uint16_t kMinUnknownSize = 12; // Use turn on the save buffer feature for more complete capture coverage. 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) { - String description = ""; -#if DECODE_DAIKIN - if (results->decode_type == DAIKIN) { - IRDaikinESP ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_DAIKIN -#if DECODE_DAIKIN2 - if (results->decode_type == DAIKIN2) { - IRDaikin2 ac(0); - 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_DAIKIN216 -#if DECODE_FUJITSU_AC - if (results->decode_type == FUJITSU_AC) { - IRFujitsuAC ac(0); - ac.setRaw(results->state, results->bits / 8); - description = ac.toString(); - } -#endif // DECODE_FUJITSU_AC -#if DECODE_KELVINATOR - if (results->decode_type == KELVINATOR) { - IRKelvinatorAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_KELVINATOR -#if DECODE_MITSUBISHI_AC - if (results->decode_type == MITSUBISHI_AC) { - IRMitsubishiAC ac(0); - 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) { - IRToshibaAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_TOSHIBA_AC -#if DECODE_GREE - if (results->decode_type == GREE) { - IRGreeAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_GREE -#if DECODE_MIDEA - if (results->decode_type == MIDEA) { - IRMideaAC ac(0); - 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) { - IRHaierAC ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC_YRW02 - if (results->decode_type == HAIER_AC_YRW02) { - IRHaierACYRW02 ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_HAIER_AC_YRW02 -#if DECODE_SAMSUNG_AC - if (results->decode_type == SAMSUNG_AC) { - IRSamsungAc ac(0); - ac.setRaw(results->state, results->bits / 8); - description = ac.toString(); - } -#endif // DECODE_SAMSUNG_AC -#if DECODE_COOLIX - if (results->decode_type == COOLIX) { - IRCoolixAC ac(0); - 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) { - IRPanasonicAc ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_PANASONIC_AC -#if DECODE_HITACHI_AC - if (results->decode_type == HITACHI_AC) { - IRHitachiAc ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_HITACHI_AC -#if DECODE_WHIRLPOOL_AC - if (results->decode_type == WHIRLPOOL_AC) { - IRWhirlpoolAc ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_VESTEL_AC - if (results->decode_type == VESTEL_AC) { - IRVestelAc ac(0); - 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) { - IRTecoAc ac(0); - 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) { - IRTcl112Ac ac(0); - ac.setRaw(results->state); - description = ac.toString(); - } -#endif // DECODE_TCL112AC - // If we got a human-readable description of the message, display it. - if (description != "") Serial.println("Mesg Desc.: " + description); -} - -// The section of code run only once at start-up. +// This section of code runs only once at start-up. void setup() { +#if defined(ESP8266) Serial.begin(kBaudRate, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(kBaudRate, SERIAL_8N1); +#endif // ESP8266 while (!Serial) // Wait for the serial connection to be establised. delay(50); - Serial.println(); - Serial.print("IRrecvDumpV2 is now running and waiting for IR input on Pin "); - Serial.println(kRecvPin); - + Serial.printf("\nIRrecvDumpV2 is now running and waiting for IR input on Pin " + "%d\n", kRecvPin); #if DECODE_HASH // Ignore messages with less than minimum on or off pulses. irrecv.setUnknownThreshold(kMinUnknownSize); @@ -287,36 +117,33 @@ void setup() { } // The repeating section of the code -// void loop() { // Check if the IR code has been received. if (irrecv.decode(&results)) { // Display a crude timestamp. uint32_t now = millis(); Serial.printf("Timestamp : %06u.%03u\n", now / 1000, now % 1000); + // Check if we got an IR message tha was to big for our capture buffer. if (results.overflow) Serial.printf( "WARNING: IR code is too big for buffer (>= %d). " "This result shouldn't be trusted until this is resolved. " "Edit & increase kCaptureBufferSize.\n", kCaptureBufferSize); + // Display the library version the message was captured with. + Serial.println("Library : v" _IRREMOTEESP8266_VERSION_ "\n"); // Display the basic output of what we found. Serial.print(resultToHumanReadableBasic(&results)); - dumpACInfo(&results); // Display any extra A/C info if we have it. + // Display any extra A/C info if we have it. + String description = IRAcUtils::resultAcToString(&results); + if (description.length()) Serial.println("Mesg Desc.: " + description); yield(); // Feed the WDT as the text output can take a while to print. - - // Display the library version the message was captured with. - Serial.print("Library : v"); - Serial.println(_IRREMOTEESP8266_VERSION_); - Serial.println(); - // Output RAW timing info of the result. Serial.println(resultToTimingInfo(&results)); yield(); // Feed the WDT (again) - // Output the results as source code Serial.println(resultToSourceCode(&results)); - Serial.println(""); // Blank line between entries + Serial.println(); // Blank line between entries yield(); // Feed the WDT (again) } } diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDumpV2/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRrecvDumpV2/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendDemo/IRsendDemo.ino similarity index 94% rename from lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRsendDemo/IRsendDemo.ino index 19f118671..b9d995834 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendDemo/IRsendDemo.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendDemo/IRsendDemo.ino @@ -10,7 +10,7 @@ * 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 + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -28,9 +28,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include @@ -53,7 +51,11 @@ uint8_t samsungState[kSamsungAcStateLength] = { void setup() { irsend.begin(); +#if ESP8266 Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY); +#else // ESP8266 + Serial.begin(115200, SERIAL_8N1); +#endif // ESP8266 } void loop() { diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRsendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendProntoDemo/IRsendProntoDemo.ino similarity index 98% rename from lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/IRsendProntoDemo/IRsendProntoDemo.ino index 3bef2179e..09101c9dc 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/IRsendProntoDemo/IRsendProntoDemo.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendProntoDemo/IRsendProntoDemo.ino @@ -11,7 +11,7 @@ * 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 + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -29,9 +29,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/IRsendProntoDemo/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendProntoDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/IRsendProntoDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino b/lib/IRremoteESP8266-2.6.3.10/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino index ee2422915..cce72c35d 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/JVCPanasonicSendDemo/JVCPanasonicSendDemo.ino @@ -10,7 +10,7 @@ * 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 + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -28,9 +28,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/JVCPanasonicSendDemo/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/JVCPanasonicSendDemo/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/JVCPanasonicSendDemo/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino b/lib/IRremoteESP8266-2.6.3.10/examples/LGACSend/LGACSend.ino similarity index 100% rename from lib/IRremoteESP8266-2.6.0/examples/LGACSend/LGACSend.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/LGACSend/LGACSend.ino diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/LGACSend/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/LGACSend/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/LGACSend/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/SmartIRRepeater.ino b/lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/SmartIRRepeater.ino new file mode 100644 index 000000000..8dd202382 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/SmartIRRepeater.ino @@ -0,0 +1,143 @@ +/* + * IRremoteESP8266: SmartIRRepeater.ino - Record and playback IR codes. + * Copyright 2019 David Conran (crankyoldgit) + * + * This program will try to capture incoming IR messages and tries to + * intelligently replay them back. + * It uses the advanced detection features of the library, and the custom + * sending routines. Thus it will try to use the correct frequencies, + * duty cycles, and repeats as it thinks is required. + * Anything it doesn't understand, it will try to replay back as best it can, + * but at 38kHz. + * Note: + * That might NOT be the frequency of the incoming message, so some not + * recogised messages that are replayed may not work. The frequency & duty + * cycle of unknown incoming messages is lost at the point of the Hardware IR + * demodulator. The ESP can't see it. + * + * W A R N I N G + * This code is just for educational/example use only. No help will be given + * to you to make it do something else, or to make it work with some + * weird device or circuit, or to make it more usable or practical. + * If it works for you. Great. If not, Congratulations on changing/fixing it. + * + * An IR detector/demodulator must be connected to the input, kRecvPin. + * An IR LED circuit must be connected to the output, kIrLedPin. + * + * Example circuit diagrams (both are needed): + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-receiving + * https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending + * + * Common mistakes & tips: + * * Don't just connect the IR LED directly to the pin, it won't + * have enough current to drive the IR LED effectively. + * * Make sure you have the IR LED polarity correct. + * See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity + * * Some digital camera/phones can be used to see if the IR LED is flashed. + * Replace the IR LED with a normal LED if you don't have a digital camera + * when debugging. + * * Avoid using the following pins unless you really know what you are doing: + * * Pin 0/D3: Can interfere with the boot/program mode & support circuits. + * * Pin 1/TX/TXD0: Any serial transmissions from the ESP will interfere. + * * Pin 3/RX/RXD0: Any serial transmissions to the ESP will interfere. + * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs + * for your first time. e.g. ESP-12 etc. + * + * Changes: + * Version 1.0: June, 2019 + * - Initial version. + */ + +#include +#include +#include +#include +#include + +// ==================== start of TUNEABLE PARAMETERS ==================== + +// The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) +const uint16_t kRecvPin = 14; + +// GPIO to use to control the IR LED circuit. Recommended: 4 (D2). +const uint16_t kIrLedPin = 4; + +// The Serial connection baud rate. +// NOTE: Make sure you set your Serial Monitor to the same speed. +const uint32_t kBaudRate = 115200; + +// As this program is a special purpose capture/resender, let's use a larger +// than expected buffer so we can handle very large IR messages. +const uint16_t kCaptureBufferSize = 1024; // 1024 == ~511 bits + +// kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a +// message ended. +const uint8_t kTimeout = 50; // Milli-Seconds + +// kFrequency is the modulation frequency all UNKNOWN messages will be sent at. +const uint16_t kFrequency = 38000; // in Hz. e.g. 38kHz. + +// ==================== end of TUNEABLE PARAMETERS ==================== + +// The IR transmitter. +IRsend irsend(kIrLedPin); +// The IR receiver. +IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, false); +// Somewhere to store the captured message. +decode_results results; + +// This section of code runs only once at start-up. +void setup() { + irrecv.enableIRIn(); // Start up the IR receiver. + irsend.begin(); // Start up the IR sender. + + Serial.begin(kBaudRate, SERIAL_8N1); + while (!Serial) // Wait for the serial connection to be establised. + delay(50); + Serial.println(); + + Serial.print("SmartIRRepeater is now running and waiting for IR input " + "on Pin "); + Serial.println(kRecvPin); + Serial.print("and will retransmit it on Pin "); + Serial.println(kIrLedPin); +} + +// The repeating section of the code +void loop() { + // Check if an IR message has been received. + if (irrecv.decode(&results)) { // We have captured something. + // The capture has stopped at this point. + decode_type_t protocol = results.decode_type; + uint16_t size = results.bits; + bool success = true; + // Is it a protocol we don't understand? + if (protocol == decode_type_t::UNKNOWN) { // Yes. + // Convert the results into an array suitable for sendRaw(). + // resultToRawArray() allocates the memory we need for the array. + uint16_t *raw_array = resultToRawArray(&results); + // Find out how many elements are in the array. + size = getCorrectedRawLength(&results); + // Send it out via the IR LED circuit. + irsend.sendRaw(raw_array, size, kFrequency); + // Deallocate the memory allocated by resultToRawArray(). + delete [] raw_array; + } else if (hasACState(protocol)) { // Does the message require a state[]? + // It does, so send with bytes instead. + success = irsend.send(protocol, results.state, size / 8); + } else { // Anything else must be a simple message protocol. ie. <= 64 bits + success = irsend.send(protocol, results.value, size); + } + // Resume capturing IR messages. It was not restarted until after we sent + // the message so we didn't capture our own message. + irrecv.resume(); + + // Display a crude timestamp & notification. + uint32_t now = millis(); + Serial.printf( + "%06u.%03u: A %d-bit %s message was %ssuccessfully retransmitted.\n", + now / 1000, now % 1000, size, typeToString(protocol).c_str(), + success ? "" : "un"); + } + yield(); // Or delay(milliseconds); This ensures the ESP doesn't WDT reset. +} diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/SmartIRRepeater/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnArgoAC/TurnOnArgoAC.ino similarity index 93% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnArgoAC/TurnOnArgoAC.ino index 3993d1151..9a5457d0c 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnArgoAC/TurnOnArgoAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnArgoAC/TurnOnArgoAC.ino @@ -5,7 +5,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -44,7 +42,7 @@ void loop() { // Set up what we want to send. See ir_Argo.cpp for all the options. ac.setPower(true); ac.setFan(kArgoFan1); - ac.setCoolMode(kArgoCoolAuto); + ac.setMode(kArgoAuto); ac.setTemp(25); #if SEND_ARGO diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnArgoAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnArgoAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnArgoAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino similarity index 95% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino index b3ab757de..d9eb9005f 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnDaikinAC/TurnOnDaikinAC.ino @@ -6,7 +6,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -24,9 +24,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnDaikinAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnDaikinAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnDaikinAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino similarity index 90% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino index 823a3f485..010d84cac 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnFujitsuAC/TurnOnFujitsuAC.ino @@ -1,7 +1,5 @@ // Copyright 2017 Jonny Graham, 2018 David Conran -#ifndef UNIT_TEST #include -#endif #include #include #include @@ -30,11 +28,13 @@ void setup() { Serial.println("Default state of the remote."); printState(); Serial.println("Setting desired state for A/C."); - ac.setCmd(kFujitsuAcCmdTurnOn); - ac.setSwing(kFujitsuAcSwingBoth); + // See `fujitsu_ac_remote_model_t` in `ir_Fujitsu.h` for a list of models. + ac.setModel(ARRAH2E); + ac.setSwing(kFujitsuAcSwingOff); ac.setMode(kFujitsuAcModeCool); ac.setFanSpeed(kFujitsuAcFanHigh); ac.setTemp(24); // 24C + ac.setCmd(kFujitsuAcCmdTurnOn); } void loop() { diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnFujitsuAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnFujitsuAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnFujitsuAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino index b9b700741..ebdb7536b 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino @@ -6,7 +6,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnKelvinatorAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnKelvinatorAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnKelvinatorAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino index e719af68e..49bbb89ca 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino @@ -6,7 +6,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino index 2ad2d7bc3..dbece716f 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiHeavyAc/TurnOnMitsubishiHeavyAc.ino @@ -6,7 +6,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiHeavyAc/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiHeavyAc/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnMitsubishiHeavyAc/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino index ea39ac5e2..b7e399f09 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnPanasonicAC/TurnOnPanasonicAC.ino @@ -6,7 +6,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnPanasonicAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnPanasonicAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnPanasonicAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino similarity index 96% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino index d78178098..a37a07e5c 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino @@ -6,7 +6,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnToshibaAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnToshibaAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnToshibaAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino similarity index 95% rename from lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino rename to lib/IRremoteESP8266-2.6.3.10/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino index b7881eead..014272955 100644 --- a/lib/IRremoteESP8266-2.6.0/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnTrotecAC/TurnOnTrotecAC.ino @@ -5,7 +5,7 @@ * 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 +* https://github.com/crankyoldgit/IRremoteESP8266/wiki#ir-sending * * Common mistakes & tips: * * Don't just connect the IR LED directly to the pin, it won't @@ -23,9 +23,7 @@ * for your first time. e.g. ESP-12 etc. */ -#ifndef UNIT_TEST #include -#endif #include #include #include diff --git a/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnTrotecAC/platformio.ini b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnTrotecAC/platformio.ini new file mode 100644 index 000000000..1aba0afcc --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/examples/TurnOnTrotecAC/platformio.ini @@ -0,0 +1,18 @@ +[platformio] +src_dir = . + +[env] +lib_extra_dirs = ../../ +lib_ldf_mode = deep+ +lib_ignore = examples +build_flags = + +[env:nodemcuv2] +platform = espressif8266 +framework = arduino +board = nodemcuv2 + +[env:esp32dev] +platform = espressif32 +framework = arduino +board = esp32dev diff --git a/lib/IRremoteESP8266-2.6.0/keywords.txt b/lib/IRremoteESP8266-2.6.3.10/keywords.txt similarity index 86% rename from lib/IRremoteESP8266-2.6.0/keywords.txt rename to lib/IRremoteESP8266-2.6.3.10/keywords.txt index a498c5d61..84244f714 100644 --- a/lib/IRremoteESP8266-2.6.0/keywords.txt +++ b/lib/IRremoteESP8266-2.6.3.10/keywords.txt @@ -22,10 +22,13 @@ IRArgoAC KEYWORD1 IRCoolixAC KEYWORD1 +IRDaikin160 KEYWORD1 IRDaikin2 KEYWORD1 IRDaikin216 KEYWORD1 IRDaikinESP KEYWORD1 +IRElectraAc KEYWORD1 IRFujitsuAC KEYWORD1 +IRGoodweatherAc KEYWORD1 IRGreeAC KEYWORD1 IRHaierAC KEYWORD1 IRHaierACYRW02 KEYWORD1 @@ -35,8 +38,10 @@ IRMideaAC KEYWORD1 IRMitsubishiAC KEYWORD1 IRMitsubishiHeavy152Ac KEYWORD1 IRMitsubishiHeavy88Ac KEYWORD1 +IRNeoclimaAc KEYWORD1 IRPanasonicAc KEYWORD1 IRSamsungAc KEYWORD1 +IRSharpAc KEYWORD1 IRTcl112Ac KEYWORD1 IRTecoAc KEYWORD1 IRToshibaAC KEYWORD1 @@ -49,19 +54,32 @@ IRsend KEYWORD1 IRtimer KEYWORD1 TimerMs KEYWORD1 decode_results KEYWORD1 -ir_params_t KEYWORD1 +decode_type_t KEYWORD1 +fanspeed_t KEYWORD1 +fujitsu_ac_remote_model_t KEYWORD1 +irparams_t KEYWORD1 match_result_t KEYWORD1 +opmode_t KEYWORD1 +panasonic_ac_remote_model_t KEYWORD1 +state_t KEYWORD1 +swingh_t KEYWORD1 +swingv_t KEYWORD1 +whirlpool_ac_remote_model_t KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### _delayMicroseconds KEYWORD2 +_matchGeneric KEYWORD2 _setMode KEYWORD2 _setTemp KEYWORD2 +acBoolToString KEYWORD2 +acModeToString KEYWORD2 add KEYWORD2 argo KEYWORD2 begin KEYWORD2 +boolToString KEYWORD2 buildFromState KEYWORD2 buildState KEYWORD2 calcBlockChecksum KEYWORD2 @@ -73,36 +91,47 @@ calibrate KEYWORD2 cancelOffTimer KEYWORD2 cancelOnTimer KEYWORD2 cancelTimers KEYWORD2 +celsiusToFahrenheit KEYWORD2 checkZjsSig KEYWORD2 checkZmsSig KEYWORD2 checksum KEYWORD2 clearOnTimerFlag KEYWORD2 clearSensorTemp KEYWORD2 clearSleepTimerFlag KEYWORD2 +cmpStates KEYWORD2 compare KEYWORD2 +convertFan KEYWORD2 +convertMode KEYWORD2 +convertSwingH KEYWORD2 +convertSwingV KEYWORD2 coolix KEYWORD2 copyIrParams KEYWORD2 countBits KEYWORD2 daikin KEYWORD2 +daikin160 KEYWORD2 daikin2 KEYWORD2 daikin216 KEYWORD2 decode KEYWORD2 decodeAiwaRCT501 KEYWORD2 +decodeArgo KEYWORD2 decodeCOOLIX KEYWORD2 decodeCarrierAC KEYWORD2 decodeDISH KEYWORD2 decodeDaikin KEYWORD2 +decodeDaikin160 KEYWORD2 decodeDaikin2 KEYWORD2 decodeDaikin216 KEYWORD2 decodeDenon KEYWORD2 decodeElectraAC KEYWORD2 decodeFujitsuAC KEYWORD2 decodeGICable KEYWORD2 +decodeGoodweather KEYWORD2 decodeGree KEYWORD2 decodeHaierAC KEYWORD2 decodeHaierACYRW02 KEYWORD2 decodeHash KEYWORD2 decodeHitachiAC KEYWORD2 +decodeInax KEYWORD2 decodeJVC KEYWORD2 decodeKelvinator KEYWORD2 decodeLG KEYWORD2 @@ -117,6 +146,7 @@ decodeMitsubishi2 KEYWORD2 decodeMitsubishiAC KEYWORD2 decodeMitsubishiHeavy KEYWORD2 decodeNEC KEYWORD2 +decodeNeoclima KEYWORD2 decodeNikai KEYWORD2 decodePanasonic KEYWORD2 decodePanasonicAC KEYWORD2 @@ -130,18 +160,23 @@ decodeSamsungAC KEYWORD2 decodeSanyo KEYWORD2 decodeSanyoLC7461 KEYWORD2 decodeSharp KEYWORD2 +decodeSharpAc KEYWORD2 decodeSony KEYWORD2 decodeTcl112Ac KEYWORD2 decodeTeco KEYWORD2 +decodeToState KEYWORD2 decodeToshibaAC KEYWORD2 +decodeTrotec KEYWORD2 decodeVestelAc KEYWORD2 decodeWhirlpoolAC KEYWORD2 decodeWhynter KEYWORD2 +defaultBits KEYWORD2 disableIRIn KEYWORD2 disableOffTimer KEYWORD2 disableOnTimer KEYWORD2 disableSleepTimer KEYWORD2 elapsed KEYWORD2 +electra KEYWORD2 enableIRIn KEYWORD2 enableIROut KEYWORD2 enableOffTimer KEYWORD2 @@ -162,11 +197,13 @@ encodeSanyoLC7461 KEYWORD2 encodeSharp KEYWORD2 encodeSony KEYWORD2 encodeTime KEYWORD2 -fanspeed_t KEYWORD2 +fahrenheitToCelsius KEYWORD2 +fanspeedToString KEYWORD2 fixChecksum KEYWORD2 fixup KEYWORD2 fujitsu KEYWORD2 get3D KEYWORD2 +get8CHeat KEYWORD2 getBeep KEYWORD2 getBufSize KEYWORD2 getButton KEYWORD2 @@ -175,9 +212,9 @@ getClock KEYWORD2 getCmd KEYWORD2 getComfort KEYWORD2 getCommand KEYWORD2 -getCoolMode KEYWORD2 getCorrectedRawLength KEYWORD2 getCurrTime KEYWORD2 +getCurrentDay KEYWORD2 getCurrentTime KEYWORD2 getEcono KEYWORD2 getEye KEYWORD2 @@ -186,10 +223,13 @@ getFan KEYWORD2 getFanSpeed KEYWORD2 getFilter KEYWORD2 getFlap KEYWORD2 +getFollow KEYWORD2 +getFresh KEYWORD2 getFreshAir KEYWORD2 getFreshAirHigh KEYWORD2 getHealth KEYWORD2 -getHeatMode KEYWORD2 +getHold KEYWORD2 +getIFeel KEYWORD2 getIon KEYWORD2 getIonFilter KEYWORD2 getLed KEYWORD2 @@ -205,6 +245,7 @@ getOffTimerEnabled KEYWORD2 getOnTime KEYWORD2 getOnTimer KEYWORD2 getOnTimerEnabled KEYWORD2 +getOutsideQuiet KEYWORD2 getPower KEYWORD2 getPowerToggle KEYWORD2 getPowerful KEYWORD2 @@ -212,6 +253,7 @@ getPurify KEYWORD2 getQuiet KEYWORD2 getRClevel KEYWORD2 getRaw KEYWORD2 +getRoomTemp KEYWORD2 getSensor KEYWORD2 getSensorTemp KEYWORD2 getSilent KEYWORD2 @@ -224,7 +266,9 @@ getStateLength KEYWORD2 getStopClock KEYWORD2 getSuper KEYWORD2 getSwing KEYWORD2 +getSwingH KEYWORD2 getSwingHorizontal KEYWORD2 +getSwingV KEYWORD2 getSwingVertical KEYWORD2 getSwingVerticalAuto KEYWORD2 getSwingVerticalPosition KEYWORD2 @@ -235,9 +279,12 @@ getTime KEYWORD2 getTimer KEYWORD2 getTurbo KEYWORD2 getVane KEYWORD2 +getWeeklyTimerEnable KEYWORD2 +getWiFi KEYWORD2 getXFan KEYWORD2 getZoneFollow KEYWORD2 getiFeel KEYWORD2 +goodweather KEYWORD2 gree KEYWORD2 haier KEYWORD2 haierYrwo2 KEYWORD2 @@ -260,24 +307,28 @@ ledOn KEYWORD2 mark KEYWORD2 match KEYWORD2 matchAtLeast KEYWORD2 +matchBytes KEYWORD2 matchData KEYWORD2 +matchGeneric KEYWORD2 matchMark KEYWORD2 matchSpace KEYWORD2 midea KEYWORD2 +minRepeats KEYWORD2 mitsubishi KEYWORD2 mitsubishiHeavy152 KEYWORD2 mitsubishiHeavy88 KEYWORD2 -mode) KEYWORD2 +neoclima KEYWORD2 off KEYWORD2 on KEYWORD2 -opmode_t KEYWORD2 +opmodeToString KEYWORD2 panasonic KEYWORD2 -position) KEYWORD2 recoverSavedState KEYWORD2 renderTime KEYWORD2 reset KEYWORD2 +resultAcToString KEYWORD2 resultToHexidecimal KEYWORD2 resultToHumanReadableBasic KEYWORD2 +resultToRawArray KEYWORD2 resultToSourceCode KEYWORD2 resultToTimingInfo KEYWORD2 resume KEYWORD2 @@ -291,6 +342,7 @@ sendCOOLIX KEYWORD2 sendCarrierAC KEYWORD2 sendDISH KEYWORD2 sendDaikin KEYWORD2 +sendDaikin160 KEYWORD2 sendDaikin2 KEYWORD2 sendDaikin216 KEYWORD2 sendData KEYWORD2 @@ -301,12 +353,14 @@ sendFujitsuAC KEYWORD2 sendGC KEYWORD2 sendGICable KEYWORD2 sendGeneric KEYWORD2 +sendGoodweather KEYWORD2 sendGree KEYWORD2 sendHaierAC KEYWORD2 sendHaierACYRW02 KEYWORD2 sendHitachiAC KEYWORD2 sendHitachiAC1 KEYWORD2 sendHitachiAC2 KEYWORD2 +sendInax KEYWORD2 sendJVC KEYWORD2 sendKelvinator KEYWORD2 sendLG KEYWORD2 @@ -323,6 +377,7 @@ sendMitsubishiAC KEYWORD2 sendMitsubishiHeavy152 KEYWORD2 sendMitsubishiHeavy88 KEYWORD2 sendNEC KEYWORD2 +sendNeoclima KEYWORD2 sendNikai KEYWORD2 sendOff KEYWORD2 sendOn KEYWORD2 @@ -340,6 +395,7 @@ sendSamsung36 KEYWORD2 sendSamsungAC KEYWORD2 sendSanyoLC7461 KEYWORD2 sendSharp KEYWORD2 +sendSharpAc KEYWORD2 sendSharpRaw KEYWORD2 sendSherwood KEYWORD2 sendSony KEYWORD2 @@ -352,6 +408,7 @@ sendWhirlpoolAC KEYWORD2 sendWhynter KEYWORD2 serialPrintUint64 KEYWORD2 set3D KEYWORD2 +set8CHeat KEYWORD2 setAuto KEYWORD2 setBeep KEYWORD2 setButton KEYWORD2 @@ -360,8 +417,8 @@ setClock KEYWORD2 setCmd KEYWORD2 setComfort KEYWORD2 setCommand KEYWORD2 -setCoolMode KEYWORD2 setCurrTime KEYWORD2 +setCurrentDay KEYWORD2 setCurrentTime KEYWORD2 setEcono KEYWORD2 setEye KEYWORD2 @@ -370,10 +427,13 @@ setFan KEYWORD2 setFanSpeed KEYWORD2 setFilter KEYWORD2 setFlap KEYWORD2 +setFollow KEYWORD2 +setFresh KEYWORD2 setFreshAir KEYWORD2 setFreshAirHigh KEYWORD2 setHealth KEYWORD2 -setHeatMode KEYWORD2 +setHold KEYWORD2 +setIFeel KEYWORD2 setIon KEYWORD2 setIonFilter KEYWORD2 setLed KEYWORD2 @@ -387,6 +447,7 @@ setOffTimer KEYWORD2 setOffTimerActive KEYWORD2 setOnTimer KEYWORD2 setOnTimerActive KEYWORD2 +setOutsideQuiet KEYWORD2 setPower KEYWORD2 setPowerToggle KEYWORD2 setPowerful KEYWORD2 @@ -404,7 +465,9 @@ setStartClock KEYWORD2 setStopClock KEYWORD2 setSuper KEYWORD2 setSwing KEYWORD2 +setSwingH KEYWORD2 setSwingHorizontal KEYWORD2 +setSwingV KEYWORD2 setSwingVertical KEYWORD2 setTemp KEYWORD2 setTempRaw KEYWORD2 @@ -414,20 +477,22 @@ setTimerActive KEYWORD2 setTurbo KEYWORD2 setUnknownThreshold KEYWORD2 setVane KEYWORD2 +setWeeklyTimerEnable KEYWORD2 +setWiFi KEYWORD2 setXFan KEYWORD2 setZoneFollow KEYWORD2 setiFeel KEYWORD2 +sharp KEYWORD2 space KEYWORD2 -speed) KEYWORD2 stateReset KEYWORD2 stepHoriz KEYWORD2 stepVert KEYWORD2 strToBool KEYWORD2 +strToDecodeType KEYWORD2 strToModel KEYWORD2 sumBytes KEYWORD2 -swingh_t KEYWORD2 -swingv) KEYWORD2 -swingv_t KEYWORD2 +swinghToString KEYWORD2 +swingvToString KEYWORD2 tcl112 KEYWORD2 teco KEYWORD2 ticksHigh KEYWORD2 @@ -436,6 +501,8 @@ timeToString KEYWORD2 toString KEYWORD2 toggleRC5 KEYWORD2 toggleRC6 KEYWORD2 +toggleSwingHoriz KEYWORD2 +toggleSwingVert KEYWORD2 toshiba KEYWORD2 trotec KEYWORD2 typeToString KEYWORD2 @@ -450,6 +517,7 @@ xorBytes KEYWORD2 # Constants (LITERAL1) ####################################### +// LITERAL1 AIWA_RC_T501 LITERAL1 AIWA_RC_T501_BITS LITERAL1 ALLOW_DELAY_CALLS LITERAL1 @@ -477,12 +545,15 @@ ARGO_HEAT_BLINK LITERAL1 ARGO_HEAT_ON LITERAL1 ARGO_MAX_TEMP LITERAL1 ARGO_MIN_TEMP LITERAL1 +ARJW2 LITERAL1 ARRAH2E LITERAL1 +ARREB1E LITERAL1 CARRIER_AC LITERAL1 CARRIER_AC_BITS LITERAL1 COOLIX LITERAL1 COOLIX_BITS LITERAL1 DAIKIN LITERAL1 +DAIKIN160 LITERAL1 DAIKIN2 LITERAL1 DAIKIN216 LITERAL1 DAIKIN_AUTO LITERAL1 @@ -503,6 +574,7 @@ DECODE_ARGO LITERAL1 DECODE_CARRIER_AC LITERAL1 DECODE_COOLIX LITERAL1 DECODE_DAIKIN LITERAL1 +DECODE_DAIKIN160 LITERAL1 DECODE_DAIKIN2 LITERAL1 DECODE_DAIKIN216 LITERAL1 DECODE_DENON LITERAL1 @@ -511,6 +583,7 @@ DECODE_ELECTRA_AC LITERAL1 DECODE_FUJITSU_AC LITERAL1 DECODE_GICABLE LITERAL1 DECODE_GLOBALCACHE LITERAL1 +DECODE_GOODWEATHER LITERAL1 DECODE_GREE LITERAL1 DECODE_HAIER_AC LITERAL1 DECODE_HAIER_AC_YRW02 LITERAL1 @@ -518,6 +591,7 @@ DECODE_HASH LITERAL1 DECODE_HITACHI_AC LITERAL1 DECODE_HITACHI_AC1 LITERAL1 DECODE_HITACHI_AC2 LITERAL1 +DECODE_INAX LITERAL1 DECODE_JVC LITERAL1 DECODE_KELVINATOR LITERAL1 DECODE_LASERTAG LITERAL1 @@ -532,6 +606,7 @@ DECODE_MITSUBISHIHEAVY LITERAL1 DECODE_MITSUBISHI_AC LITERAL1 DECODE_MWM LITERAL1 DECODE_NEC LITERAL1 +DECODE_NEOCLIMA LITERAL1 DECODE_NIKAI LITERAL1 DECODE_PANASONIC LITERAL1 DECODE_PANASONIC_AC LITERAL1 @@ -545,6 +620,7 @@ DECODE_SAMSUNG36 LITERAL1 DECODE_SAMSUNG_AC LITERAL1 DECODE_SANYO LITERAL1 DECODE_SHARP LITERAL1 +DECODE_SHARP_AC LITERAL1 DECODE_SHERWOOD LITERAL1 DECODE_SONY LITERAL1 DECODE_TCL112AC LITERAL1 @@ -593,6 +669,7 @@ FUJITSU_AC_SWING_VERT LITERAL1 GICABLE LITERAL1 GICABLE_BITS LITERAL1 GLOBALCACHE LITERAL1 +GOODWEATHER LITERAL1 GREE LITERAL1 GREE_AUTO LITERAL1 GREE_COOL LITERAL1 @@ -682,6 +759,7 @@ HITACHI_AC2_STATE_LENGTH LITERAL1 HITACHI_AC_BITS LITERAL1 HITACHI_AC_STATE_LENGTH LITERAL1 ICACHE_RAM_ATTR LITERAL1 +INAX LITERAL1 JVC LITERAL1 JVC_BITS LITERAL1 KELVINATOR LITERAL1 @@ -749,6 +827,7 @@ MWM LITERAL1 NEC LITERAL1 NEC_BITS LITERAL1 NEC_LIKE LITERAL1 +NEOCLIMA LITERAL1 NIKAI LITERAL1 NIKAI_BITS LITERAL1 ONCE LITERAL1 @@ -781,6 +860,7 @@ SEND_ARGO LITERAL1 SEND_CARRIER_AC LITERAL1 SEND_COOLIX LITERAL1 SEND_DAIKIN LITERAL1 +SEND_DAIKIN160 LITERAL1 SEND_DAIKIN2 LITERAL1 SEND_DAIKIN216 LITERAL1 SEND_DENON LITERAL1 @@ -789,12 +869,14 @@ SEND_ELECTRA_AC LITERAL1 SEND_FUJITSU_AC LITERAL1 SEND_GICABLE LITERAL1 SEND_GLOBALCACHE LITERAL1 +SEND_GOODWEATHER LITERAL1 SEND_GREE LITERAL1 SEND_HAIER_AC LITERAL1 SEND_HAIER_AC_YRW02 LITERAL1 SEND_HITACHI_AC LITERAL1 SEND_HITACHI_AC1 LITERAL1 SEND_HITACHI_AC2 LITERAL1 +SEND_INAX LITERAL1 SEND_JVC LITERAL1 SEND_KELVINATOR LITERAL1 SEND_LASERTAG LITERAL1 @@ -809,6 +891,7 @@ SEND_MITSUBISHIHEAVY LITERAL1 SEND_MITSUBISHI_AC LITERAL1 SEND_MWM LITERAL1 SEND_NEC LITERAL1 +SEND_NEOCLIMA LITERAL1 SEND_NIKAI LITERAL1 SEND_PANASONIC LITERAL1 SEND_PANASONIC_AC LITERAL1 @@ -823,6 +906,7 @@ SEND_SAMSUNG36 LITERAL1 SEND_SAMSUNG_AC LITERAL1 SEND_SANYO LITERAL1 SEND_SHARP LITERAL1 +SEND_SHARP_AC LITERAL1 SEND_SHERWOOD LITERAL1 SEND_SONY LITERAL1 SEND_TCL112AC LITERAL1 @@ -833,6 +917,7 @@ SEND_VESTEL_AC LITERAL1 SEND_WHIRLPOOL_AC LITERAL1 SEND_WHYNTER LITERAL1 SHARP LITERAL1 +SHARP_AC LITERAL1 SHARP_BITS LITERAL1 SHERWOOD LITERAL1 SHERWOOD_BITS LITERAL1 @@ -868,6 +953,7 @@ TROTEC_MAX_TIMER LITERAL1 TROTEC_MIN_TEMP LITERAL1 UNKNOWN LITERAL1 UNUSED LITERAL1 +USE_IRAM_ATTR LITERAL1 VESTEL_AC LITERAL1 WHIRLPOOL_AC LITERAL1 WHYNTER LITERAL1 @@ -878,16 +964,17 @@ kAiwaRcT501PostBits LITERAL1 kAiwaRcT501PostData LITERAL1 kAiwaRcT501PreBits LITERAL1 kAiwaRcT501PreData LITERAL1 +kArgoAuto LITERAL1 kArgoBitMark LITERAL1 -kArgoCoolAuto LITERAL1 -kArgoCoolHum LITERAL1 -kArgoCoolOff LITERAL1 -kArgoCoolOn LITERAL1 +kArgoBits LITERAL1 +kArgoCool LITERAL1 kArgoDefaultRepeat LITERAL1 +kArgoDry LITERAL1 kArgoFan1 LITERAL1 kArgoFan2 LITERAL1 kArgoFan3 LITERAL1 kArgoFanAuto LITERAL1 +kArgoFanMask LITERAL1 kArgoFlap1 LITERAL1 kArgoFlap2 LITERAL1 kArgoFlap3 LITERAL1 @@ -896,15 +983,29 @@ kArgoFlap5 LITERAL1 kArgoFlap6 LITERAL1 kArgoFlapAuto LITERAL1 kArgoFlapFull LITERAL1 +kArgoGap LITERAL1 kArgoHdrMark LITERAL1 kArgoHdrSpace LITERAL1 +kArgoHeat LITERAL1 kArgoHeatAuto LITERAL1 +kArgoHeatBit LITERAL1 kArgoHeatBlink LITERAL1 -kArgoHeatOn LITERAL1 +kArgoIFeelBit LITERAL1 +kArgoMaxBit LITERAL1 +kArgoMaxRoomTemp LITERAL1 kArgoMaxTemp LITERAL1 kArgoMinTemp LITERAL1 +kArgoModeMask LITERAL1 +kArgoNightBit LITERAL1 +kArgoOff LITERAL1 kArgoOneSpace LITERAL1 +kArgoPowerBit LITERAL1 +kArgoRoomTempHighMask LITERAL1 +kArgoRoomTempLowMask LITERAL1 kArgoStateLength LITERAL1 +kArgoTempHighMask LITERAL1 +kArgoTempLowMask LITERAL1 +kArgoTempOffset LITERAL1 kArgoZeroSpace LITERAL1 kAuto LITERAL1 kCarrierAcBitMark LITERAL1 @@ -965,11 +1066,40 @@ kCoolixUnknown LITERAL1 kCoolixZeroSpace LITERAL1 kCoolixZeroSpaceTicks LITERAL1 kCoolixZoneFollowMask LITERAL1 +kDaikin160BitMark LITERAL1 +kDaikin160Bits LITERAL1 +kDaikin160ByteFan LITERAL1 +kDaikin160ByteMode LITERAL1 +kDaikin160BytePower LITERAL1 +kDaikin160ByteSwingV LITERAL1 +kDaikin160ByteTemp LITERAL1 +kDaikin160DefaultRepeat LITERAL1 +kDaikin160Freq LITERAL1 +kDaikin160Gap LITERAL1 +kDaikin160HdrMark LITERAL1 +kDaikin160HdrSpace LITERAL1 +kDaikin160MaskFan LITERAL1 +kDaikin160MaskMode LITERAL1 +kDaikin160MaskSwingV LITERAL1 +kDaikin160MaskTemp LITERAL1 +kDaikin160OneSpace LITERAL1 +kDaikin160Section1Length LITERAL1 +kDaikin160Section2Length LITERAL1 +kDaikin160Sections LITERAL1 +kDaikin160StateLength LITERAL1 +kDaikin160SwingVAuto LITERAL1 +kDaikin160SwingVHigh LITERAL1 +kDaikin160SwingVHighest LITERAL1 +kDaikin160SwingVLow LITERAL1 +kDaikin160SwingVLowest LITERAL1 +kDaikin160SwingVMiddle LITERAL1 +kDaikin160ZeroSpace LITERAL1 kDaikin216BitMark LITERAL1 kDaikin216Bits LITERAL1 kDaikin216ByteFan LITERAL1 kDaikin216ByteMode LITERAL1 kDaikin216BytePower LITERAL1 +kDaikin216BytePowerful LITERAL1 kDaikin216ByteSwingH LITERAL1 kDaikin216ByteSwingV LITERAL1 kDaikin216ByteTemp LITERAL1 @@ -1039,6 +1169,7 @@ kDaikinBitPower LITERAL1 kDaikinBitPowerful LITERAL1 kDaikinBitSensor LITERAL1 kDaikinBitSilent LITERAL1 +kDaikinBitWeeklyTimer LITERAL1 kDaikinBits LITERAL1 kDaikinBitsShort LITERAL1 kDaikinByteChecksum1 LITERAL1 @@ -1063,6 +1194,7 @@ kDaikinByteSensor LITERAL1 kDaikinByteSilent LITERAL1 kDaikinByteSwingH LITERAL1 kDaikinByteTemp LITERAL1 +kDaikinByteWeeklyTimer LITERAL1 kDaikinCool LITERAL1 kDaikinCurBit LITERAL1 kDaikinCurIndex LITERAL1 @@ -1095,7 +1227,9 @@ kDaikinStateLengthShort LITERAL1 kDaikinTolerance LITERAL1 kDaikinUnusedTime LITERAL1 kDaikinZeroSpace LITERAL1 +kDefaultESP32Timer LITERAL1 kDefaultMessageGap LITERAL1 +kDenon48Bits LITERAL1 kDenonBitMark LITERAL1 kDenonBitMarkTicks LITERAL1 kDenonBits LITERAL1 @@ -1131,13 +1265,32 @@ kDishZeroSpaceTicks LITERAL1 kDry LITERAL1 kDutyDefault LITERAL1 kDutyMax LITERAL1 +kElectraAcAuto LITERAL1 kElectraAcBitMark LITERAL1 kElectraAcBits LITERAL1 +kElectraAcCool LITERAL1 +kElectraAcDry LITERAL1 +kElectraAcFan LITERAL1 +kElectraAcFanAuto LITERAL1 +kElectraAcFanHigh LITERAL1 +kElectraAcFanLow LITERAL1 +kElectraAcFanMask LITERAL1 +kElectraAcFanMed LITERAL1 kElectraAcHdrMark LITERAL1 kElectraAcHdrSpace LITERAL1 +kElectraAcHeat LITERAL1 +kElectraAcMaxTemp LITERAL1 kElectraAcMessageGap LITERAL1 +kElectraAcMinRepeat LITERAL1 +kElectraAcMinTemp LITERAL1 +kElectraAcModeMask LITERAL1 +kElectraAcOffsetTemp LITERAL1 kElectraAcOneSpace LITERAL1 +kElectraAcPowerMask LITERAL1 kElectraAcStateLength LITERAL1 +kElectraAcSwingHMask LITERAL1 +kElectraAcSwingVMask LITERAL1 +kElectraAcTempMask LITERAL1 kElectraAcZeroSpace LITERAL1 kFan LITERAL1 kFnvBasis32 LITERAL1 @@ -1145,9 +1298,13 @@ kFnvPrime32 LITERAL1 kFooter LITERAL1 kFujitsuAcBitMark LITERAL1 kFujitsuAcBits LITERAL1 +kFujitsuAcCmdEcono LITERAL1 +kFujitsuAcCmdPowerful LITERAL1 kFujitsuAcCmdStayOn LITERAL1 kFujitsuAcCmdStepHoriz LITERAL1 kFujitsuAcCmdStepVert LITERAL1 +kFujitsuAcCmdToggleSwingHoriz LITERAL1 +kFujitsuAcCmdToggleSwingVert LITERAL1 kFujitsuAcCmdTurnOff LITERAL1 kFujitsuAcCmdTurnOn LITERAL1 kFujitsuAcFanAuto LITERAL1 @@ -1191,6 +1348,58 @@ kGlobalCacheMinUsec LITERAL1 kGlobalCacheRptIndex LITERAL1 kGlobalCacheRptStartIndex LITERAL1 kGlobalCacheStartIndex LITERAL1 +kGoodweatherAuto LITERAL1 +kGoodweatherBitCommand LITERAL1 +kGoodweatherBitFan LITERAL1 +kGoodweatherBitLight LITERAL1 +kGoodweatherBitMark LITERAL1 +kGoodweatherBitMode LITERAL1 +kGoodweatherBitPower LITERAL1 +kGoodweatherBitSleep LITERAL1 +kGoodweatherBitSwing LITERAL1 +kGoodweatherBitTemp LITERAL1 +kGoodweatherBitTurbo LITERAL1 +kGoodweatherBits LITERAL1 +kGoodweatherCmdAirFlow LITERAL1 +kGoodweatherCmdDownTemp LITERAL1 +kGoodweatherCmdFan LITERAL1 +kGoodweatherCmdHold LITERAL1 +kGoodweatherCmdLight LITERAL1 +kGoodweatherCmdMode LITERAL1 +kGoodweatherCmdPower LITERAL1 +kGoodweatherCmdSleep LITERAL1 +kGoodweatherCmdSwing LITERAL1 +kGoodweatherCmdTimer LITERAL1 +kGoodweatherCmdTurbo LITERAL1 +kGoodweatherCmdUpTemp LITERAL1 +kGoodweatherCommandMask LITERAL1 +kGoodweatherCool LITERAL1 +kGoodweatherDry LITERAL1 +kGoodweatherFan LITERAL1 +kGoodweatherFanAuto LITERAL1 +kGoodweatherFanHigh LITERAL1 +kGoodweatherFanLow LITERAL1 +kGoodweatherFanMask LITERAL1 +kGoodweatherFanMed LITERAL1 +kGoodweatherHdrMark LITERAL1 +kGoodweatherHdrSpace LITERAL1 +kGoodweatherHeat LITERAL1 +kGoodweatherLightMask LITERAL1 +kGoodweatherMinRepeat LITERAL1 +kGoodweatherModeMask LITERAL1 +kGoodweatherOneSpace LITERAL1 +kGoodweatherPowerMask LITERAL1 +kGoodweatherSleepMask LITERAL1 +kGoodweatherSwingFast LITERAL1 +kGoodweatherSwingMask LITERAL1 +kGoodweatherSwingOff LITERAL1 +kGoodweatherSwingSlow LITERAL1 +kGoodweatherTempMask LITERAL1 +kGoodweatherTempMax LITERAL1 +kGoodweatherTempMin LITERAL1 +kGoodweatherTurboMask LITERAL1 +kGoodweatherZeroSpace LITERAL1 +kGpioUnused LITERAL1 kGreeAuto LITERAL1 kGreeBitMark LITERAL1 kGreeBits LITERAL1 @@ -1207,6 +1416,7 @@ kGreeFanMin LITERAL1 kGreeHdrMark LITERAL1 kGreeHdrSpace LITERAL1 kGreeHeat LITERAL1 +kGreeIFeelMask LITERAL1 kGreeLightMask LITERAL1 kGreeMaxTemp LITERAL1 kGreeMinTemp LITERAL1 @@ -1230,6 +1440,7 @@ kGreeSwingPosMask LITERAL1 kGreeSwingUp LITERAL1 kGreeSwingUpAuto LITERAL1 kGreeTurboMask LITERAL1 +kGreeWiFiMask LITERAL1 kGreeXfanMask LITERAL1 kGreeZeroSpace LITERAL1 kHaierACBits LITERAL1 @@ -1265,8 +1476,10 @@ kHaierAcMaxTemp LITERAL1 kHaierAcMaxTime LITERAL1 kHaierAcMinGap LITERAL1 kHaierAcMinTemp LITERAL1 +kHaierAcModeMask LITERAL1 kHaierAcOneSpace LITERAL1 kHaierAcPrefix LITERAL1 +kHaierAcSleepBit LITERAL1 kHaierAcSwingChg LITERAL1 kHaierAcSwingDown LITERAL1 kHaierAcSwingOff LITERAL1 @@ -1334,6 +1547,15 @@ kHitachiAcOneSpace LITERAL1 kHitachiAcStateLength LITERAL1 kHitachiAcZeroSpace LITERAL1 kIdleState LITERAL1 +kInaxBitMark LITERAL1 +kInaxBits LITERAL1 +kInaxHdrMark LITERAL1 +kInaxHdrSpace LITERAL1 +kInaxMinGap LITERAL1 +kInaxMinRepeat LITERAL1 +kInaxOneSpace LITERAL1 +kInaxTick LITERAL1 +kInaxZeroSpace LITERAL1 kJvcBitMark LITERAL1 kJvcBitMarkTicks LITERAL1 kJvcBits LITERAL1 @@ -1666,6 +1888,60 @@ kNecRptSpaceTicks LITERAL1 kNecTick LITERAL1 kNecZeroSpace LITERAL1 kNecZeroSpaceTicks LITERAL1 +kNeoclima8CHeatMask LITERAL1 +kNeoclimaAuto LITERAL1 +kNeoclimaBitMark LITERAL1 +kNeoclimaBits LITERAL1 +kNeoclimaButton8CHeat LITERAL1 +kNeoclimaButtonAirFlow LITERAL1 +kNeoclimaButtonEye LITERAL1 +kNeoclimaButtonFanSpeed LITERAL1 +kNeoclimaButtonFollow LITERAL1 +kNeoclimaButtonFresh LITERAL1 +kNeoclimaButtonHold LITERAL1 +kNeoclimaButtonIon LITERAL1 +kNeoclimaButtonLight LITERAL1 +kNeoclimaButtonMask LITERAL1 +kNeoclimaButtonMode LITERAL1 +kNeoclimaButtonPower LITERAL1 +kNeoclimaButtonSleep LITERAL1 +kNeoclimaButtonSwing LITERAL1 +kNeoclimaButtonTempDown LITERAL1 +kNeoclimaButtonTempUp LITERAL1 +kNeoclimaButtonTurbo LITERAL1 +kNeoclimaCool LITERAL1 +kNeoclimaDry LITERAL1 +kNeoclimaEyeMask LITERAL1 +kNeoclimaFan LITERAL1 +kNeoclimaFanAuto LITERAL1 +kNeoclimaFanHigh LITERAL1 +kNeoclimaFanLow LITERAL1 +kNeoclimaFanMask LITERAL1 +kNeoclimaFanMed LITERAL1 +kNeoclimaFollowMe LITERAL1 +kNeoclimaFreshMask LITERAL1 +kNeoclimaHdrMark LITERAL1 +kNeoclimaHdrSpace LITERAL1 +kNeoclimaHeat LITERAL1 +kNeoclimaHoldMask LITERAL1 +kNeoclimaIonMask LITERAL1 +kNeoclimaLightMask LITERAL1 +kNeoclimaMaxTemp LITERAL1 +kNeoclimaMinGap LITERAL1 +kNeoclimaMinRepeat LITERAL1 +kNeoclimaMinTemp LITERAL1 +kNeoclimaModeMask LITERAL1 +kNeoclimaOneSpace LITERAL1 +kNeoclimaPowerMask LITERAL1 +kNeoclimaSleepMask LITERAL1 +kNeoclimaStateLength LITERAL1 +kNeoclimaSwingHMask LITERAL1 +kNeoclimaSwingVMask LITERAL1 +kNeoclimaSwingVOff LITERAL1 +kNeoclimaSwingVOn LITERAL1 +kNeoclimaTempMask LITERAL1 +kNeoclimaTurboMask LITERAL1 +kNeoclimaZeroSpace LITERAL1 kNikaiBitMark LITERAL1 kNikaiBitMarkTicks LITERAL1 kNikaiBits LITERAL1 @@ -1835,9 +2111,12 @@ kSamsungAcMinTemp LITERAL1 kSamsungAcModeMask LITERAL1 kSamsungAcOneSpace LITERAL1 kSamsungAcPowerMask1 LITERAL1 -kSamsungAcPowerMask2 LITERAL1 +kSamsungAcPowerMask6 LITERAL1 kSamsungAcPowerSection LITERAL1 -kSamsungAcQuietMask11 LITERAL1 +kSamsungAcPowerfulMask10 LITERAL1 +kSamsungAcPowerfulMask8 LITERAL1 +kSamsungAcQuietMask1 LITERAL1 +kSamsungAcQuietMask5 LITERAL1 kSamsungAcSectionGap LITERAL1 kSamsungAcSectionMark LITERAL1 kSamsungAcSectionSpace LITERAL1 @@ -1885,6 +2164,37 @@ kSanyoSa8650bHdrSpace LITERAL1 kSanyoSa8650bOneMark LITERAL1 kSanyoSa8650bRptLength LITERAL1 kSanyoSa8650bZeroMark LITERAL1 +kSharpAcAuto LITERAL1 +kSharpAcBitFanManual LITERAL1 +kSharpAcBitMark LITERAL1 +kSharpAcBitPower LITERAL1 +kSharpAcBitTempManual LITERAL1 +kSharpAcBits LITERAL1 +kSharpAcByteFan LITERAL1 +kSharpAcByteManual LITERAL1 +kSharpAcByteMode LITERAL1 +kSharpAcBytePower LITERAL1 +kSharpAcByteTemp LITERAL1 +kSharpAcCool LITERAL1 +kSharpAcDefaultRepeat LITERAL1 +kSharpAcDry LITERAL1 +kSharpAcFanAuto LITERAL1 +kSharpAcFanHigh LITERAL1 +kSharpAcFanMax LITERAL1 +kSharpAcFanMed LITERAL1 +kSharpAcFanMin LITERAL1 +kSharpAcGap LITERAL1 +kSharpAcHdrMark LITERAL1 +kSharpAcHdrSpace LITERAL1 +kSharpAcHeat LITERAL1 +kSharpAcMaskFan LITERAL1 +kSharpAcMaskMode LITERAL1 +kSharpAcMaskTemp LITERAL1 +kSharpAcMaxTemp LITERAL1 +kSharpAcMinTemp LITERAL1 +kSharpAcOneSpace LITERAL1 +kSharpAcStateLength LITERAL1 +kSharpAcZeroSpace LITERAL1 kSharpAddressBits LITERAL1 kSharpAddressMask LITERAL1 kSharpBitMark LITERAL1 @@ -1954,6 +2264,7 @@ kTcl112AcPowerMask LITERAL1 kTcl112AcStateLength LITERAL1 kTcl112AcTempMax LITERAL1 kTcl112AcTempMin LITERAL1 +kTcl112AcTolerance LITERAL1 kTcl112AcZeroSpace LITERAL1 kTecoAuto LITERAL1 kTecoBitMark LITERAL1 @@ -2006,6 +2317,8 @@ kToshibaAcOneSpace LITERAL1 kToshibaAcPower LITERAL1 kToshibaAcZeroSpace LITERAL1 kTrotecAuto LITERAL1 +kTrotecBitMark LITERAL1 +kTrotecBits LITERAL1 kTrotecCool LITERAL1 kTrotecDefTemp LITERAL1 kTrotecDefaultRepeat LITERAL1 @@ -2023,13 +2336,11 @@ kTrotecIntro2 LITERAL1 kTrotecMaxTemp LITERAL1 kTrotecMaxTimer LITERAL1 kTrotecMinTemp LITERAL1 -kTrotecOneMark LITERAL1 kTrotecOneSpace LITERAL1 kTrotecPowerBit LITERAL1 kTrotecSleepBit LITERAL1 kTrotecStateLength LITERAL1 kTrotecTimerBit LITERAL1 -kTrotecZeroMark LITERAL1 kTrotecZeroSpace LITERAL1 kUnknownThreshold LITERAL1 kVestelAcAuto LITERAL1 diff --git a/lib/IRremoteESP8266-2.6.0/library.json b/lib/IRremoteESP8266-2.6.3.10/library.json similarity index 79% rename from lib/IRremoteESP8266-2.6.0/library.json rename to lib/IRremoteESP8266-2.6.3.10/library.json index 95867de1d..7db457702 100644 --- a/lib/IRremoteESP8266-2.6.0/library.json +++ b/lib/IRremoteESP8266-2.6.3.10/library.json @@ -1,17 +1,18 @@ { "name": "IRremoteESP8266", - "version": "2.6.0", - "keywords": "infrared, ir, remote, esp8266", - "description": "Send and receive infrared signals with multiple protocols (ESP8266)", + "version": "2.6.3", + "keywords": "infrared, ir, remote, esp8266, esp32", + "description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)", "repository": { "type": "git", - "url": "https://github.com/markszabo/IRremoteESP8266.git" + "url": "https://github.com/crankyoldgit/IRremoteESP8266.git" }, "authors": [ { - "name": "Ken Shirriff", - "email": "zetoslab@gmail.com" + "name": "David Conran", + "url": "https://plus.google.com/+davidconran", + "maintainer": true }, { "name": "Mark Szabo", @@ -24,9 +25,8 @@ "maintainer": true }, { - "name": "David Conran", - "url": "https://plus.google.com/+davidconran", - "maintainer": true + "name": "Ken Shirriff", + "email": "zetoslab@gmail.com" }, { "name": "Roi Dayan", @@ -40,5 +40,5 @@ } ], "frameworks": "arduino", - "platforms": "espressif8266" + "platforms": ["espressif8266", "espressif32"] } diff --git a/lib/IRremoteESP8266-2.6.0/library.properties b/lib/IRremoteESP8266-2.6.3.10/library.properties similarity index 54% rename from lib/IRremoteESP8266-2.6.0/library.properties rename to lib/IRremoteESP8266-2.6.3.10/library.properties index f122067c5..ce12cef8b 100644 --- a/lib/IRremoteESP8266-2.6.0/library.properties +++ b/lib/IRremoteESP8266-2.6.3.10/library.properties @@ -1,9 +1,9 @@ name=IRremoteESP8266 -version=2.6.0 -author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran +version=2.6.3 +author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto -sentence=Send and receive infrared signals with multiple protocols (ESP8266) -paragraph=This library enables you to send and receive infra-red signals on an ESP8266. +sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32) +paragraph=This library enables you to send and receive infra-red signals on an ESP8266 or an ESP32. category=Device Control -url=https://github.com/markszabo/IRremoteESP8266 -architectures=esp8266 +url=https://github.com/crankyoldgit/IRremoteESP8266 +architectures=esp8266,esp32 diff --git a/lib/IRremoteESP8266-2.6.0/pylintrc b/lib/IRremoteESP8266-2.6.3.10/pylintrc similarity index 100% rename from lib/IRremoteESP8266-2.6.0/pylintrc rename to lib/IRremoteESP8266-2.6.3.10/pylintrc diff --git a/lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg b/lib/IRremoteESP8266-2.6.3.10/src/CPPLINT.cfg similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/CPPLINT.cfg rename to lib/IRremoteESP8266-2.6.3.10/src/CPPLINT.cfg diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.cpp b/lib/IRremoteESP8266-2.6.3.10/src/IRac.cpp similarity index 50% rename from lib/IRremoteESP8266-2.6.0/src/IRac.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/IRac.cpp index 782c147c2..6ea543ade 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRac.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRac.cpp @@ -8,16 +8,17 @@ #ifndef UNIT_TEST #include #endif - #include #ifndef ARDUINO #include #endif #include "IRsend.h" #include "IRremoteESP8266.h" +#include "IRutils.h" #include "ir_Argo.h" #include "ir_Coolix.h" #include "ir_Daikin.h" +#include "ir_Electra.h" #include "ir_Fujitsu.h" #include "ir_Haier.h" #include "ir_Hitachi.h" @@ -25,8 +26,10 @@ #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.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" @@ -34,7 +37,11 @@ #include "ir_Vestel.h" #include "ir_Whirlpool.h" -IRac::IRac(uint8_t pin) { _pin = pin; } +IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { + _pin = pin; + _inverted = inverted; + _modulation = use_modulation; +} // Is the given protocol supported by the IRac class? bool IRac::isProtocolSupported(const decode_type_t protocol) { @@ -48,15 +55,27 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_DAIKIN case decode_type_t::DAIKIN: #endif +#if SEND_DAIKIN160 + case decode_type_t::DAIKIN160: +#endif +#if SEND_DAIKIN176 + case decode_type_t::DAIKIN176: +#endif #if SEND_DAIKIN2 case decode_type_t::DAIKIN2: #endif #if SEND_DAIKIN216 case decode_type_t::DAIKIN216: #endif +#if SEND_ELECTRA_AC + case decode_type_t::ELECTRA_AC: +#endif #if SEND_FUJITSU_AC case decode_type_t::FUJITSU_AC: #endif +#if SEND_GOODWEATHER + case decode_type_t::GOODWEATHER: +#endif #if SEND_GREE case decode_type_t::GREE: #endif @@ -82,12 +101,18 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { case decode_type_t::MITSUBISHI_HEAVY_88: case decode_type_t::MITSUBISHI_HEAVY_152: #endif +#if SEND_NEOCLIMA + case decode_type_t::NEOCLIMA: +#endif #if SEND_PANASONIC_AC case decode_type_t::PANASONIC_AC: #endif #if SEND_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 @@ -118,19 +143,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)); @@ -218,6 +231,34 @@ void IRac::daikin(IRDaikinESP *ac, } #endif // SEND_DAIKIN +#if SEND_DAIKIN160 +void IRac::daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingVertical(ac->convertSwingV(swingv)); + ac->send(); +} +#endif // SEND_DAIKIN160 + +#if SEND_DAIKIN176 +void IRac::daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh) { + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); + ac->send(); +} +#endif // SEND_DAIKIN176 + #if SEND_DAIKIN2 void IRac::daikin2(IRDaikin2 *ac, const bool on, const stdAc::opmode_t mode, @@ -250,7 +291,7 @@ 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 quiet, const bool turbo) { ac->setPower(on); ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); @@ -258,25 +299,24 @@ void IRac::daikin216(IRDaikin216 *ac, 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, +#if SEND_ELECTRA_AC +void IRac::electra(IRElectraAc *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, - const bool quiet) { - ac->setModel(model); + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh) { + ac->setPower(on); 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); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. // No Turbo setting available. // No Light setting available. // No Econo setting available. @@ -285,11 +325,88 @@ void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, // No Beep setting available. // No Sleep setting available. // No Clock setting available. - if (!on) ac->off(); + ac->send(); +} +#endif // SEND_ELECTRA_AC + +#if SEND_FUJITSU_AC +void IRac::fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo) { + ac->setModel(model); + if (on) { + // Do all special messages (except "Off") first, + // These need to be sent separately. + switch (ac->getModel()) { + // Some functions are only available on some models. + case fujitsu_ac_remote_model_t::ARREB1E: + if (turbo) { + ac->setCmd(kFujitsuAcCmdPowerful); + // Powerful is a separate command. + ac->send(); + } + if (econo) { + ac->setCmd(kFujitsuAcCmdEcono); + // Econo is a separate command. + ac->send(); + } + break; + default: + {}; + } + // Normal operation. + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFanSpeed(ac->convertFan(fan)); + uint8_t swing = kFujitsuAcSwingOff; + if (swingv > stdAc::swingv_t::kOff) swing |= kFujitsuAcSwingVert; + if (swingh > stdAc::swingh_t::kOff) swing |= kFujitsuAcSwingHoriz; + ac->setSwing(swing); + if (quiet) ac->setFanSpeed(kFujitsuAcFanQuiet); + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + } else { + // Off is special case/message. We don't need to send other messages. + ac->off(); + } ac->send(); } #endif // SEND_FUJITSU_AC +#if SEND_GOODWEATHER +void IRac::goodweather(IRGoodweatherAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const bool turbo, const bool light, + const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwing(swingv == stdAc::swingv_t::kOff ? kGoodweatherSwingOff + : kGoodweatherSwingSlow); + ac->setTurbo(turbo); + ac->setLight(light); + // No Clean setting available. + ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. + // No Horizontal Swing setting available. + // No Econo setting available. + // No Filter setting available. + // No Beep setting available. + // No Quiet setting available. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_GOODWEATHER + #if SEND_GREE void IRac::gree(IRGreeAC *ac, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -307,6 +424,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. @@ -514,6 +632,32 @@ void IRac::mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac, } #endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA +void IRac::neoclima(IRNeoclimaAc *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 filter, + const int16_t sleep) { + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + ac->setSwingH(swingh != stdAc::swingh_t::kOff); + // No Quiet setting available. + ac->setTurbo(turbo); + ac->setLight(light); + // No Econo 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. + // No Clock setting available. + ac->setPower(on); + ac->send(); +} +#endif // SEND_NEOCLIMA + #if SEND_PANASONIC_AC void IRac::panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, @@ -546,23 +690,16 @@ 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)); ac->setSwing(swingv != stdAc::swingv_t::kOff); // No Horizontal swing setting available. ac->setQuiet(quiet); - if (turbo) ac->setFan(kSamsungAcFanTurbo); + ac->setPowerful(turbo); // No Light setting available. // No Econo setting available. // No Filter setting available. @@ -576,6 +713,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, @@ -724,6 +886,48 @@ void IRac::whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model, } #endif // SEND_WHIRLPOOL_AC +// Create a new state base on desired & previous states but handle +// any state changes for options that need to be toggled. +// Args: +// desired: The state_t structure describing the desired a/c state. +// prev: Ptr to the previous state_t structure. +// +// Returns: +// A stdAc::state_t with the needed settings. +stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev) { + stdAc::state_t result = desired; + // If we've been given a previous state AND the it's the same A/C basically. + if (prev != NULL && desired.protocol == prev->protocol && + desired.model == prev->model) { + // Check if we have to handle toggle settings for specific A/C protocols. + switch (desired.protocol) { + case decode_type_t::COOLIX: + if ((desired.swingv == stdAc::swingv_t::kOff) ^ + (prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle. + result.swingv = stdAc::swingv_t::kAuto; + else + result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle. + result.turbo = desired.turbo ^ prev->turbo; + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + result.sleep = (desired.sleep ^ prev->sleep) ? 0 : -1; + break; + case decode_type_t::WHIRLPOOL_AC: + result.power = desired.power ^ prev->power; + break; + case decode_type_t::PANASONIC_AC: + // CKP models use a power mode toggle. + if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) + result.power = desired.power ^ prev->power; + break; + default: + {}; + } + } + return result; +} + // Send A/C message for a given device using common A/C settings. // Args: // vendor: The type of A/C protocol to use. @@ -731,7 +935,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. @@ -757,11 +961,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. @@ -769,7 +973,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_ARGO case ARGO: { - IRArgoAC ac(_pin); + IRArgoAC ac(_pin, _inverted, _modulation); argo(&ac, on, mode, degC, fan, swingv, turbo, sleep); break; } @@ -777,7 +981,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_COOLIX case COOLIX: { - IRCoolixAC ac(_pin); + IRCoolixAC ac(_pin, _inverted, _modulation); coolix(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, econo, clean); break; @@ -786,16 +990,32 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_DAIKIN case DAIKIN: { - IRDaikinESP ac(_pin); + IRDaikinESP ac(_pin, _inverted, _modulation); daikin(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, econo, clean); break; } #endif // SEND_DAIKIN +#if SEND_DAIKIN160 + case DAIKIN160: + { + IRDaikin160 ac(_pin, _inverted, _modulation); + daikin160(&ac, on, mode, degC, fan, swingv); + break; + } +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + { + IRDaikin176 ac(_pin); + daikin176(&ac, on, mode, degC, fan, swingh); + break; + } +#endif // SEND_DAIKIN176 #if SEND_DAIKIN2 case DAIKIN2: { - IRDaikin2 ac(_pin); + IRDaikin2 ac(_pin, _inverted, _modulation); daikin2(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, light, econo, filter, clean, beep, sleep, clock); break; @@ -804,25 +1024,44 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_DAIKIN216 case DAIKIN216: { - IRDaikin216 ac(_pin); - daikin216(&ac, on, mode, degC, fan, swingv, swingh, quiet); + IRDaikin216 ac(_pin, _inverted, _modulation); + daikin216(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo); break; } #endif // SEND_DAIKIN216 +#if SEND_ELECTRA_AC + case ELECTRA_AC: + { + IRElectraAc ac(_pin, _inverted, _modulation); + ac.begin(); + electra(&ac, on, mode, degC, fan, swingv, swingh); + break; + } +#endif // SEND_ELECTRA_AC #if SEND_FUJITSU_AC case FUJITSU_AC: { - IRFujitsuAC ac(_pin); + IRFujitsuAC ac(_pin, (fujitsu_ac_remote_model_t)model, _inverted, + _modulation); 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, _inverted, _modulation); + ac.begin(); + goodweather(&ac, on, mode, degC, fan, swingv, turbo, light, sleep); + break; + } +#endif // SEND_GOODWEATHER #if SEND_GREE case GREE: { - IRGreeAC ac(_pin); + IRGreeAC ac(_pin, _inverted, _modulation); ac.begin(); gree(&ac, on, mode, degC, fan, swingv, light, turbo, clean, sleep); break; @@ -831,7 +1070,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_HAIER_AC case HAIER_AC: { - IRHaierAC ac(_pin); + IRHaierAC ac(_pin, _inverted, _modulation); ac.begin(); haier(&ac, on, mode, degC, fan, swingv, filter, sleep, clock); break; @@ -840,7 +1079,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_HAIER_AC_YRW02 case HAIER_AC_YRW02: { - IRHaierACYRW02 ac(_pin); + IRHaierACYRW02 ac(_pin, _inverted, _modulation); ac.begin(); haierYrwo2(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep); break; @@ -849,7 +1088,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_HITACHI_AC case HITACHI_AC: { - IRHitachiAc ac(_pin); + IRHitachiAc ac(_pin, _inverted, _modulation); ac.begin(); hitachi(&ac, on, mode, degC, fan, swingv, swingh); break; @@ -858,7 +1097,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_KELVINATOR case KELVINATOR: { - IRKelvinatorAC ac(_pin); + IRKelvinatorAC ac(_pin, _inverted, _modulation); ac.begin(); kelvinator(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, light, filter, clean); @@ -868,7 +1107,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_MIDEA case MIDEA: { - IRMideaAC ac(_pin); + IRMideaAC ac(_pin, _inverted, _modulation); ac.begin(); midea(&ac, on, mode, degC, fan, sleep); break; @@ -877,7 +1116,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_MITSUBISHI_AC case MITSUBISHI_AC: { - IRMitsubishiAC ac(_pin); + IRMitsubishiAC ac(_pin, _inverted, _modulation); ac.begin(); mitsubishi(&ac, on, mode, degC, fan, swingv, quiet, clock); break; @@ -886,7 +1125,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_MITSUBISHIHEAVY case MITSUBISHI_HEAVY_88: { - IRMitsubishiHeavy88Ac ac(_pin); + IRMitsubishiHeavy88Ac ac(_pin, _inverted, _modulation); ac.begin(); mitsubishiHeavy88(&ac, on, mode, degC, fan, swingv, swingh, turbo, econo, clean); @@ -894,17 +1133,27 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, } case MITSUBISHI_HEAVY_152: { - IRMitsubishiHeavy152Ac ac(_pin); + IRMitsubishiHeavy152Ac ac(_pin, _inverted, _modulation); ac.begin(); mitsubishiHeavy152(&ac, on, mode, degC, fan, swingv, swingh, quiet, turbo, econo, filter, clean, sleep); break; } #endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + case NEOCLIMA: + { + IRNeoclimaAc ac(_pin, _inverted, _modulation); + ac.begin(); + neoclima(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, filter, + sleep); + break; + } +#endif // SEND_NEOCLIMA #if SEND_PANASONIC_AC case PANASONIC_AC: { - IRPanasonicAc ac(_pin); + IRPanasonicAc ac(_pin, _inverted, _modulation); ac.begin(); panasonic(&ac, (panasonic_ac_remote_model_t)model, on, mode, degC, fan, swingv, swingh, quiet, turbo, clock); @@ -914,16 +1163,25 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_SAMSUNG_AC case SAMSUNG_AC: { - IRSamsungAc ac(_pin); + IRSamsungAc ac(_pin, _inverted, _modulation); ac.begin(); samsung(&ac, on, mode, degC, fan, swingv, quiet, turbo, clean, beep); break; } #endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + case SHARP_AC: + { + IRSharpAc ac(_pin, _inverted, _modulation); + ac.begin(); + sharp(&ac, on, mode, degC, fan); + break; + } +#endif // SEND_SHARP_AC #if SEND_TCL112AC case TCL112AC: { - IRTcl112Ac ac(_pin); + IRTcl112Ac ac(_pin, _inverted, _modulation); ac.begin(); tcl112(&ac, on, mode, degC, fan, swingv, swingh, turbo, light, econo, filter); @@ -933,7 +1191,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_TECO case TECO: { - IRTecoAc ac(_pin); + IRTecoAc ac(_pin, _inverted, _modulation); ac.begin(); teco(&ac, on, mode, degC, fan, swingv, sleep); break; @@ -942,7 +1200,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_TOSHIBA_AC case TOSHIBA_AC: { - IRToshibaAC ac(_pin); + IRToshibaAC ac(_pin, _inverted, _modulation); ac.begin(); toshiba(&ac, on, mode, degC, fan); break; @@ -951,7 +1209,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_TROTEC case TROTEC: { - IRTrotecESP ac(_pin); + IRTrotecESP ac(_pin, _inverted, _modulation); ac.begin(); trotec(&ac, on, mode, degC, fan, sleep); break; @@ -960,7 +1218,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_VESTEL_AC case VESTEL_AC: { - IRVestelAc ac(_pin); + IRVestelAc ac(_pin, _inverted, _modulation); ac.begin(); vestel(&ac, on, mode, degC, fan, swingv, turbo, filter, sleep, clock); break; @@ -969,7 +1227,7 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, #if SEND_WHIRLPOOL_AC case WHIRLPOOL_AC: { - IRWhirlpoolAc ac(_pin); + IRWhirlpoolAc ac(_pin, _inverted, _modulation); ac.begin(); whirlpool(&ac, (whirlpool_ac_remote_model_t)model, on, mode, degC, fan, swingv, turbo, light, sleep, clock); @@ -982,21 +1240,49 @@ bool IRac::sendAc(const decode_type_t vendor, const int16_t model, return true; // Success. } +// Send A/C message for a given device using state_t structures. +// Args: +// desired: The state_t structure describing the desired new a/c state. +// prev: Ptr to the previous state_t structure. +// +// Returns: +// boolean: True, if accepted/converted/attempted. False, if unsupported. +bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { + stdAc::state_t final = this->handleToggles(desired, prev); + return this->sendAc(final.protocol, final.model, final.power, final.mode, + final.degrees, final.celsius, final.fanspeed, + final.swingv, final.swingh, final.quiet, final.turbo, + final.econo, final.light, final.filter, final.clean, + final.beep, final.sleep, final.clock); +} + +// Compare two AirCon states. +// Returns: True if they differ, False if they don't. +// Note: Excludes clock. +bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) { + return a.protocol != b.protocol || a.model != b.model || a.power != b.power || + a.mode != b.mode || a.degrees != b.degrees || a.celsius != b.celsius || + a.fanspeed != b.fanspeed || a.swingv != b.swingv || + a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo || + a.econo != b.econo || a.light != b.light || a.filter != b.filter || + a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep; +} + stdAc::opmode_t IRac::strToOpmode(const char *str, const stdAc::opmode_t def) { - if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC")) return stdAc::opmode_t::kAuto; - else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + else if (!strcasecmp(str, "OFF") || !strcasecmp(str, "STOP")) return stdAc::opmode_t::kOff; - else if (!strcmp(str, "COOL") || !strcmp(str, "COOLING")) + else if (!strcasecmp(str, "COOL") || !strcasecmp(str, "COOLING")) return stdAc::opmode_t::kCool; - else if (!strcmp(str, "HEAT") || !strcmp(str, "HEATING")) + else if (!strcasecmp(str, "HEAT") || !strcasecmp(str, "HEATING")) return stdAc::opmode_t::kHeat; - else if (!strcmp(str, "DRY") || !strcmp(str, "DRYING") || - !strcmp(str, "DEHUMIDIFY")) + else if (!strcasecmp(str, "DRY") || !strcasecmp(str, "DRYING") || + !strcasecmp(str, "DEHUMIDIFY")) return stdAc::opmode_t::kDry; - else if (!strcmp(str, "FAN") || !strcmp(str, "FANONLY") || - !strcmp(str, "FAN_ONLY")) + else if (!strcasecmp(str, "FAN") || !strcasecmp(str, "FANONLY") || + !strcasecmp(str, "FAN_ONLY")) return stdAc::opmode_t::kFan; else return def; @@ -1004,20 +1290,20 @@ stdAc::opmode_t IRac::strToOpmode(const char *str, stdAc::fanspeed_t IRac::strToFanspeed(const char *str, const stdAc::fanspeed_t def) { - if (!strcmp(str, "AUTO") || !strcmp(str, "AUTOMATIC")) + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC")) return stdAc::fanspeed_t::kAuto; - else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || - !strcmp(str, "LOWEST")) + else if (!strcasecmp(str, "MIN") || !strcasecmp(str, "MINIMUM") || + !strcasecmp(str, "LOWEST")) return stdAc::fanspeed_t::kMin; - else if (!strcmp(str, "LOW")) + else if (!strcasecmp(str, "LOW")) return stdAc::fanspeed_t::kLow; - else if (!strcmp(str, "MED") || !strcmp(str, "MEDIUM") || - !strcmp(str, "MID")) + else if (!strcasecmp(str, "MED") || !strcasecmp(str, "MEDIUM") || + !strcasecmp(str, "MID")) return stdAc::fanspeed_t::kMedium; - else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + else if (!strcasecmp(str, "HIGH") || !strcasecmp(str, "HI")) return stdAc::fanspeed_t::kHigh; - else if (!strcmp(str, "MAX") || !strcmp(str, "MAXIMUM") || - !strcmp(str, "HIGHEST")) + else if (!strcasecmp(str, "MAX") || !strcasecmp(str, "MAXIMUM") || + !strcasecmp(str, "HIGHEST")) return stdAc::fanspeed_t::kMax; else return def; @@ -1025,26 +1311,26 @@ stdAc::fanspeed_t IRac::strToFanspeed(const char *str, 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")) + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC") || + !strcasecmp(str, "ON") || !strcasecmp(str, "SWING")) return stdAc::swingv_t::kAuto; - else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + else if (!strcasecmp(str, "OFF") || !strcasecmp(str, "STOP")) return stdAc::swingv_t::kOff; - else if (!strcmp(str, "MIN") || !strcmp(str, "MINIMUM") || - !strcmp(str, "LOWEST") || !strcmp(str, "BOTTOM") || - !strcmp(str, "DOWN")) + else if (!strcasecmp(str, "MIN") || !strcasecmp(str, "MINIMUM") || + !strcasecmp(str, "LOWEST") || !strcasecmp(str, "BOTTOM") || + !strcasecmp(str, "DOWN")) return stdAc::swingv_t::kLowest; - else if (!strcmp(str, "LOW")) + else if (!strcasecmp(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")) + else if (!strcasecmp(str, "MID") || !strcasecmp(str, "MIDDLE") || + !strcasecmp(str, "MED") || !strcasecmp(str, "MEDIUM") || + !strcasecmp(str, "CENTRE") || !strcasecmp(str, "CENTER")) return stdAc::swingv_t::kMiddle; - else if (!strcmp(str, "HIGH") || !strcmp(str, "HI")) + else if (!strcasecmp(str, "HIGH") || !strcasecmp(str, "HI")) return stdAc::swingv_t::kHigh; - else if (!strcmp(str, "HIGHEST") || !strcmp(str, "MAX") || - !strcmp(str, "MAXIMUM") || !strcmp(str, "TOP") || - !strcmp(str, "UP")) + else if (!strcasecmp(str, "HIGHEST") || !strcasecmp(str, "MAX") || + !strcasecmp(str, "MAXIMUM") || !strcasecmp(str, "TOP") || + !strcasecmp(str, "UP")) return stdAc::swingv_t::kHighest; else return def; @@ -1052,56 +1338,56 @@ stdAc::swingv_t IRac::strToSwingV(const char *str, 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")) + if (!strcasecmp(str, "AUTO") || !strcasecmp(str, "AUTOMATIC") || + !strcasecmp(str, "ON") || !strcasecmp(str, "SWING")) return stdAc::swingh_t::kAuto; - else if (!strcmp(str, "OFF") || !strcmp(str, "STOP")) + else if (!strcasecmp(str, "OFF") || !strcasecmp(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")) + else if (!strcasecmp(str, "LEFTMAX") || !strcasecmp(str, "LEFT MAX") || + !strcasecmp(str, "MAXLEFT") || !strcasecmp(str, "MAX LEFT") || + !strcasecmp(str, "FARLEFT") || !strcasecmp(str, "FAR LEFT")) return stdAc::swingh_t::kLeftMax; - else if (!strcmp(str, "LEFT")) + else if (!strcasecmp(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")) + else if (!strcasecmp(str, "MID") || !strcasecmp(str, "MIDDLE") || + !strcasecmp(str, "MED") || !strcasecmp(str, "MEDIUM") || + !strcasecmp(str, "CENTRE") || !strcasecmp(str, "CENTER")) return stdAc::swingh_t::kMiddle; - else if (!strcmp(str, "RIGHT")) + else if (!strcasecmp(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")) + else if (!strcasecmp(str, "RIGHTMAX") || !strcasecmp(str, "RIGHT MAX") || + !strcasecmp(str, "MAXRIGHT") || !strcasecmp(str, "MAX RIGHT") || + !strcasecmp(str, "FARRIGHT") || !strcasecmp(str, "FAR RIGHT")) return stdAc::swingh_t::kRightMax; else return def; } -// Assumes str is upper case or an integer >= 1. +// Assumes str is the model or an integer >= 1. int16_t IRac::strToModel(const char *str, const int16_t def) { // Fujitsu A/C models - if (!strcmp(str, "ARRAH2E")) { + if (!strcasecmp(str, "ARRAH2E")) { return fujitsu_ac_remote_model_t::ARRAH2E; - } else if (!strcmp(str, "ARDB1")) { + } else if (!strcasecmp(str, "ARDB1")) { return fujitsu_ac_remote_model_t::ARDB1; // Panasonic A/C families - } else if (!strcmp(str, "LKE") || !strcmp(str, "PANASONICLKE")) { + } else if (!strcasecmp(str, "LKE") || !strcasecmp(str, "PANASONICLKE")) { return panasonic_ac_remote_model_t::kPanasonicLke; - } else if (!strcmp(str, "NKE") || !strcmp(str, "PANASONICNKE")) { + } else if (!strcasecmp(str, "NKE") || !strcasecmp(str, "PANASONICNKE")) { return panasonic_ac_remote_model_t::kPanasonicNke; - } else if (!strcmp(str, "DKE") || !strcmp(str, "PANASONICDKE")) { + } else if (!strcasecmp(str, "DKE") || !strcasecmp(str, "PANASONICDKE")) { return panasonic_ac_remote_model_t::kPanasonicDke; - } else if (!strcmp(str, "JKE") || !strcmp(str, "PANASONICJKE")) { + } else if (!strcasecmp(str, "JKE") || !strcasecmp(str, "PANASONICJKE")) { return panasonic_ac_remote_model_t::kPanasonicJke; - } else if (!strcmp(str, "CKP") || !strcmp(str, "PANASONICCKP")) { + } else if (!strcasecmp(str, "CKP") || !strcasecmp(str, "PANASONICCKP")) { return panasonic_ac_remote_model_t::kPanasonicCkp; - } else if (!strcmp(str, "RKR") || !strcmp(str, "PANASONICRKR")) { + } else if (!strcasecmp(str, "RKR") || !strcasecmp(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")) { + } else if (!strcasecmp(str, "DG11J13A") || !strcasecmp(str, "DG11J104") || + !strcasecmp(str, "DG11J1-04")) { return whirlpool_ac_remote_model_t::DG11J13A; - } else if (!strcmp(str, "DG11J191")) { + } else if (!strcasecmp(str, "DG11J191")) { return whirlpool_ac_remote_model_t::DG11J191; } else { int16_t number = atoi(str); @@ -1112,14 +1398,564 @@ int16_t IRac::strToModel(const char *str, const int16_t 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")) + if (!strcasecmp(str, "ON") || !strcasecmp(str, "1") || + !strcasecmp(str, "YES") || !strcasecmp(str, "TRUE")) return true; - else if (!strcmp(str, "OFF") || !strcmp(str, "0") || - !strcmp(str, "NO") || !strcmp(str, "FALSE")) + else if (!strcasecmp(str, "OFF") || !strcasecmp(str, "0") || + !strcasecmp(str, "NO") || !strcasecmp(str, "FALSE")) return false; else return def; } + +String IRac::boolToString(const bool value) { + return value ? F("on") : F("off"); +} + +String IRac::opmodeToString(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kOff: + return F("off"); + case stdAc::opmode_t::kAuto: + return F("auto"); + case stdAc::opmode_t::kCool: + return F("cool"); + case stdAc::opmode_t::kHeat: + return F("heat"); + case stdAc::opmode_t::kDry: + return F("dry"); + case stdAc::opmode_t::kFan: + return F("fan_only"); + default: + return F("unknown"); + } +} + +String IRac::fanspeedToString(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kAuto: + return F("auto"); + case stdAc::fanspeed_t::kMax: + return F("max"); + case stdAc::fanspeed_t::kHigh: + return F("high"); + case stdAc::fanspeed_t::kMedium: + return F("medium"); + case stdAc::fanspeed_t::kLow: + return F("low"); + case stdAc::fanspeed_t::kMin: + return F("min"); + default: + return F("unknown"); + } +} + +String IRac::swingvToString(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kOff: + return F("off"); + case stdAc::swingv_t::kAuto: + return F("auto"); + case stdAc::swingv_t::kHighest: + return F("highest"); + case stdAc::swingv_t::kHigh: + return F("high"); + case stdAc::swingv_t::kMiddle: + return F("middle"); + case stdAc::swingv_t::kLow: + return F("low"); + case stdAc::swingv_t::kLowest: + return F("lowest"); + default: + return F("unknown"); + } +} + +String IRac::swinghToString(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kOff: + return F("off"); + case stdAc::swingh_t::kAuto: + return F("auto"); + case stdAc::swingh_t::kLeftMax: + return F("leftmax"); + case stdAc::swingh_t::kLeft: + return F("left"); + case stdAc::swingh_t::kMiddle: + return F("middle"); + case stdAc::swingh_t::kRight: + return F("right"); + case stdAc::swingh_t::kRightMax: + return F("rightmax"); + default: + return F("unknown"); + } +} + +namespace IRAcUtils { + // Display the human readable state of an A/C message if we can. + // Args: + // result: A Ptr to the captured `decode_results` that contains an A/C mesg. + // Returns: + // A string with the human description of the A/C message. "" if we can't. + String resultAcToString(const decode_results * const result) { + switch (result->decode_type) { +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ARGO +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_DAIKIN216 +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(0); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_FUJITSU_AC +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_KELVINATOR +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(0); + ac.setRaw(result->state); + return ac.toString(); + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_NEOCLIMA +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(0); + ac.setRaw(result->value); // Goodweather uses value instead of state. + return ac.toString(); + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_GREE +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(0); + ac.setRaw(result->value); // Midea uses value instead of state. + return ac.toString(); + } +#endif // DECODE_MIDEA +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC_YRW02 +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(0); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_SHARP_AC +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(0); + ac.setRaw(result->value); // Coolix uses value instead of state. + return ac.toString(); + } +#endif // DECODE_COOLIX +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + if (result->bits > kPanasonicAcShortBits) { + IRPanasonicAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } + return ""; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_HITACHI_AC + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(0); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(0); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_TECO +#if DECODE_TCL112AC + case decode_type_t::TCL112AC: { + IRTcl112Ac ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TCL112AC + default: + return ""; + } + } + + // Convert a valid IR A/C remote message that we understand enough into a + // Common A/C state. + // + // Args: + // decode: A PTR to a successful raw IR decode object. + // result: A PTR to a state structure to store the result in. + // prev: A PTR to a state structure which has the prev. state. (optional) + // Returns: + // A boolean indicating success or failure. + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev) { + if (decode == NULL || result == NULL) return false; // Safety check. + switch (decode->decode_type) { +#if DECODE_ARGO + case decode_type_t::ARGO: { + IRArgoAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ARGO +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_COOLIX +#if DECODE_DAIKIN + case decode_type_t::DAIKIN: { + IRDaikinESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN +#if DECODE_DAIKIN160 + case decode_type_t::DAIKIN160: { + IRDaikin160 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + case decode_type_t::DAIKIN176: { + IRDaikin176 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN2 + case decode_type_t::DAIKIN2: { + IRDaikin2 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN2 +#if DECODE_DAIKIN216 + case decode_type_t::DAIKIN216: { + IRDaikin216 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_DAIKIN216 +#if DECODE_ELECTRA_AC + case decode_type_t::ELECTRA_AC: { + IRElectraAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_ELECTRA_AC +#if DECODE_FUJITSU_AC + case decode_type_t::FUJITSU_AC: { + IRFujitsuAC ac(kGpioUnused); + ac.setRaw(decode->state, decode->bits / 8); + *result = ac.toCommon(); + break; + } +#endif // DECODE_FUJITSU_AC +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HAIER_AC_YRW02 +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) +#if DECODE_KELVINATOR + case decode_type_t::KELVINATOR: { + IRKelvinatorAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_KELVINATOR +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_MIDEA +#if DECODE_MITSUBISHI_AC + case decode_type_t::MITSUBISHI_AC: { + IRMitsubishiAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHIHEAVY + case decode_type_t::MITSUBISHI_HEAVY_88: { + IRMitsubishiHeavy88Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } + case decode_type_t::MITSUBISHI_HEAVY_152: { + IRMitsubishiHeavy152Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHIHEAVY +#if DECODE_NEOCLIMA + case decode_type_t::NEOCLIMA: { + IRNeoclimaAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_NEOCLIMA +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_PANASONIC_AC +#if DECODE_SAMSUNG_AC + case decode_type_t::SAMSUNG_AC: { + IRSamsungAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SAMSUNG_AC +#if DECODE_SHARP_AC + case decode_type_t::SHARP_AC: { + IRSharpAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_SHARP_AC +#if DECODE_TCL112AC + case decode_type_t::TCL112AC: { + IRTcl112Ac ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TCL112AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TOSHIBA_AC +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_TROTEC +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_VESTEL_AC +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_WHIRLPOOL_AC + default: + return false; + } + return true; + } +} // namespace IRAcUtils diff --git a/lib/IRremoteESP8266-2.6.0/src/IRac.h b/lib/IRremoteESP8266-2.6.3.10/src/IRac.h similarity index 73% rename from lib/IRremoteESP8266-2.6.0/src/IRac.h rename to lib/IRremoteESP8266-2.6.3.10/src/IRac.h index ce8d50507..74b2e01ab 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRac.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRac.h @@ -6,14 +6,13 @@ #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_Electra.h" +#include "ir_Goodweather.h" #include "ir_Gree.h" #include "ir_Haier.h" #include "ir_Hitachi.h" @@ -21,8 +20,10 @@ #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.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" @@ -30,9 +31,14 @@ #include "ir_Vestel.h" #include "ir_Whirlpool.h" +// Constants +const int8_t kGpioUnused = -1; + +// Class class IRac { public: - explicit IRac(uint8_t pin); + explicit IRac(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); 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, @@ -42,23 +48,31 @@ class IRac { const bool light, const bool filter, const bool clean, const bool beep, const int16_t sleep = -1, const int16_t clock = -1); - + bool sendAc(const stdAc::state_t desired, const stdAc::state_t *prev = NULL); + static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b); static bool strToBool(const char *str, const bool def = false); static int16_t strToModel(const char *str, const int16_t def = -1); static stdAc::opmode_t strToOpmode( - const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto); + 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); + 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); + 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); + const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff); + static String boolToString(const bool value); + static String opmodeToString(const stdAc::opmode_t mode); + static String fanspeedToString(const stdAc::fanspeed_t speed); + static String swingvToString(const stdAc::swingv_t swingv); + static String swinghToString(const stdAc::swingh_t swingh); #ifndef UNIT_TEST private: #endif - uint8_t _pin; + uint16_t _pin; + bool _inverted; + bool _modulation; #if SEND_ARGO void argo(IRArgoAC *ac, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -81,6 +95,18 @@ class IRac { const bool quiet, const bool turbo, const bool econo, const bool clean); #endif // SEND_DAIKIN +#if SEND_DAIKIN160 +void daikin160(IRDaikin160 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 +void daikin176(IRDaikin176 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingh_t swingh); +#endif // SEND_DAIKIN176 #if SEND_DAIKIN2 void daikin2(IRDaikin2 *ac, const bool on, const stdAc::opmode_t mode, @@ -96,15 +122,31 @@ 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 quiet, const bool turbo); #endif // SEND_DAIKIN216 +#if SEND_ELECTRA_AC +void electra(IRElectraAc *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_ELECTRA_AC #if SEND_FUJITSU_AC void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const 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, @@ -169,6 +211,13 @@ void daikin216(IRDaikin216 *ac, const bool filter, const bool clean, const int16_t sleep = -1); #endif // SEND_MITSUBISHIHEAVY +#if SEND_NEOCLIMA + void neoclima(IRNeoclimaAc *ac, const bool on, const stdAc::opmode_t mode, + const 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 filter, + const int16_t sleep = -1); +#endif // SEND_NEOCLIMA #if SEND_PANASONIC_AC void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -181,8 +230,13 @@ 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 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, @@ -222,27 +276,13 @@ void daikin216(IRDaikin216 *ac, const bool turbo, const bool light, const int16_t sleep = -1, const int16_t clock = -1); #endif // SEND_WHIRLPOOL_AC +static stdAc::state_t handleToggles(const stdAc::state_t desired, + const stdAc::state_t *prev = NULL); }; // 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; +namespace IRAcUtils { + String resultAcToString(const decode_results * const results); + bool decodeToState(const decode_results *decode, stdAc::state_t *result, + const stdAc::state_t *prev = NULL); +} // namespace IRAcUtils #endif // IRAC_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp b/lib/IRremoteESP8266-2.6.3.10/src/IRrecv.cpp similarity index 58% rename from lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/IRrecv.cpp index eac868084..5f3971439 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRrecv.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRrecv.cpp @@ -1,18 +1,23 @@ // Copyright 2009 Ken Shirriff // Copyright 2015 Mark Szabo // Copyright 2015 Sebastien Warin -// Copyright 2017 David Conran +// Copyright 2017, 2019 David Conran #include "IRrecv.h" #include #ifndef UNIT_TEST +#if defined(ESP8266) extern "C" { #include #include } +#endif // ESP8266 #include #endif #include +#ifdef UNIT_TEST +#include +#endif // UNIT_TEST #include "IRremoteESP8266.h" #include "IRutils.h" @@ -20,32 +25,68 @@ extern "C" { #undef ICACHE_RAM_ATTR #define ICACHE_RAM_ATTR #endif + +#ifndef USE_IRAM_ATTR +#if defined(ESP8266) +#define USE_IRAM_ATTR ICACHE_RAM_ATTR +#endif // ESP8266 +#if defined(ESP32) +#define USE_IRAM_ATTR IRAM_ATTR +#endif // ESP32 +#endif // USE_IRAM_ATTR + +#define ONCE 0 + +// Updated by David Conran (https://github.com/crankyoldgit) for receiving IR +// code on ESP32 // Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code // on ESP8266 -// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for // sending IR code on ESP8266 // Globals #ifndef UNIT_TEST +#if defined(ESP8266) static ETSTimer timer; -#endif +#endif // ESP8266 +#if defined(ESP32) +static hw_timer_t * timer = NULL; +#endif // ESP32 +#endif // UNIT_TEST + +#if defined(ESP32) +portMUX_TYPE irremote_mux = portMUX_INITIALIZER_UNLOCKED; +#endif // ESP32 volatile irparams_t irparams; irparams_t *irparams_save; // A copy of the interrupt state while decoding. #ifndef UNIT_TEST -static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) { +#if defined(ESP8266) +static void USE_IRAM_ATTR read_timeout(void *arg __attribute__((unused))) { os_intr_lock(); +#endif // ESP8266 +#if defined(ESP32) +static void USE_IRAM_ATTR read_timeout(void) { + portENTER_CRITICAL(&irremote_mux); +#endif // ESP32 if (irparams.rawlen) irparams.rcvstate = kStopState; +#if defined(ESP8266) os_intr_unlock(); +#endif // ESP8266 +#if defined(ESP32) + portEXIT_CRITICAL(&irremote_mux); +#endif // ESP32 } -static void ICACHE_RAM_ATTR gpio_intr() { - uint32_t now = system_get_time(); - uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); +static void USE_IRAM_ATTR gpio_intr() { + uint32_t now = micros(); static uint32_t start = 0; +#if defined(ESP8266) + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); os_timer_disarm(&timer); GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); +#endif // ESP8266 // Grab a local copy of rawlen to reduce instructions used in IRAM. // This is an ugly premature optimisation code-wise, but we do everything we @@ -74,8 +115,14 @@ static void ICACHE_RAM_ATTR gpio_intr() { irparams.rawlen++; start = now; -#define ONCE 0 + +#if defined(ESP8266) os_timer_arm(&timer, irparams.timeout, ONCE); +#endif // ESP8266 +#if defined(ESP32) + timerWrite(timer, 0); // Reset the timeout. + timerAlarmEnable(timer); +#endif // ESP32 } #endif // UNIT_TEST @@ -87,11 +134,21 @@ static void ICACHE_RAM_ATTR gpio_intr() { // bufsize: Nr. of entries to have in the capture buffer. (Default: kRawBuf) // timeout: Nr. of milli-Seconds of no signal before we stop capturing data. // (Default: kTimeoutMs) -// save_buffer: Use a second (save) buffer to decode from. (Def: false) +// save_buffer: Use a second (save) buffer to decode from. (Default: false) +// timer_num: Which ESP32 timer number to use? ESP32 only, otherwise unused. +// (Range: 0-3. Default: kDefaultESP32Timer) // Returns: // An IRrecv class object. -IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, - bool save_buffer) { +#if defined(ESP32) +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer, + const uint8_t timer_num) { + // There are only 4 timers. 0 to 3. + _timer_num = std::min(timer_num, (uint8_t)3); +#else // ESP32 +IRrecv::IRrecv(const uint16_t recvpin, const uint16_t bufsize, + const uint8_t timeout, const bool save_buffer) { +#endif // ESP32 irparams.recvpin = recvpin; irparams.bufsize = bufsize; // Ensure we are going to be able to store all possible values in the @@ -123,7 +180,7 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout, irparams_save = NULL; } #if DECODE_HASH - unknown_threshold = kUnknownThreshold; + _unknown_threshold = kUnknownThreshold; #endif // DECODE_HASH } @@ -134,35 +191,70 @@ IRrecv::~IRrecv(void) { delete[] irparams_save->rawbuf; delete irparams_save; } + disableIRIn(); +#if defined(ESP32) + if (timer != NULL) timerEnd(timer); // Cleanup the ESP32 timeout timer. +#endif // ESP32 } -// initialization -void IRrecv::enableIRIn() { - // initialize state machine variables +// Set up and (re)start the IR capture mechanism. +// +// Args: +// pullup: A flag indicating should the GPIO use the internal pullup resistor. +// (Default: `false`. i.e. No.) +void IRrecv::enableIRIn(const bool pullup) { + // ESP32's seem to require explicitly setting the GPIO to INPUT etc. + // This wasn't required on the ESP8266s, but it shouldn't hurt to make sure. + if (pullup) { +#ifndef UNIT_TEST + pinMode(irparams.recvpin, INPUT_PULLUP); + } else { + pinMode(irparams.recvpin, INPUT); +#endif // UNIT_TEST + } +#if defined(ESP32) + // Initialize the ESP32 timer. + timer = timerBegin(_timer_num, 80, true); // 80MHz / 80 = 1 uSec granularity. + // Set the timer so it only fires once, and set it's trigger in uSeconds. + timerAlarmWrite(timer, MS_TO_USEC(irparams.timeout), ONCE); + // Note: Interrupt needs to be attached before it can be enabled or disabled. + timerAttachInterrupt(timer, &read_timeout, true); +#endif // ESP32 + + // Initialize state machine variables resume(); #ifndef UNIT_TEST - // Initialize timer +#if defined(ESP8266) + // Initialize ESP8266 timer. os_timer_disarm(&timer); os_timer_setfn(&timer, reinterpret_cast(read_timeout), NULL); - +#endif // ESP8266 // Attach Interrupt attachInterrupt(irparams.recvpin, gpio_intr, CHANGE); -#endif +#endif // UNIT_TEST } -void IRrecv::disableIRIn() { +void IRrecv::disableIRIn(void) { #ifndef UNIT_TEST +#if defined(ESP8266) os_timer_disarm(&timer); +#endif // ESP8266 +#if defined(ESP32) + timerAlarmDisable(timer); +#endif // ESP32 detachInterrupt(irparams.recvpin); -#endif +#endif // UNIT_TEST } -void IRrecv::resume() { +void IRrecv::resume(void) { irparams.rcvstate = kIdleState; irparams.rawlen = 0; irparams.overflow = false; +#if defined(ESP32) + timerAlarmDisable(timer); +#endif // ESP32 } // Make a copy of the interrupt state & buffer data. @@ -196,12 +288,12 @@ void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) { // Obtain the maximum number of entries possible in the capture buffer. // i.e. It's size. -uint16_t IRrecv::getBufSize() { return irparams.bufsize; } +uint16_t IRrecv::getBufSize(void) { return irparams.bufsize; } #if DECODE_HASH // Set the minimum length we will consider for reporting UNKNOWN message types. -void IRrecv::setUnknownThreshold(uint16_t length) { - unknown_threshold = length; +void IRrecv::setUnknownThreshold(const uint16_t length) { + _unknown_threshold = length; } #endif // DECODE_HASH @@ -335,7 +427,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 @@ -523,6 +615,38 @@ 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_DAIKIN160 + DPRINTLN("Attempting Daikin160 decode"); + if (decodeDaikin160(results)) return true; +#endif // DECODE_DAIKIN160 +#if DECODE_NEOCLIMA + DPRINTLN("Attempting Neoclima decode"); + if (decodeNeoclima(results)) return true; +#endif // DECODE_NEOCLIMA +#if DECODE_DAIKIN176 + DPRINTLN("Attempting Daikin176 decode"); + if (decodeDaikin176(results)) return true; +#endif // DECODE_DAIKIN176 #if DECODE_HASH // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. @@ -583,6 +707,17 @@ bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance, DPRINT(measured); DPRINT(" <= "); DPRINTLN(ticksHigh(desired, tolerance, delta)); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST return (measured >= ticksLow(desired, tolerance, delta) && measured <= ticksHigh(desired, tolerance, delta)); } @@ -616,6 +751,17 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, DPRINT(", "); DPRINT(ticksLow(MS_TO_USEC(irparams.timeout), tolerance, delta)); DPRINTLN(")]"); +#ifdef UNIT_TEST + // Sanity checks that we don't have values that cause integer over/underflow. + // Only performed during testing so there is no performance hit in normal + // operation. + assert(ticksLow(desired, tolerance, delta) <= desired); + // Check if we overflowed. (UINT32_MAX >> 3 is approx 9 minutes!) + assert(ticksHigh(desired, tolerance, delta) < UINT32_MAX >> 3); + // Check if our high mark is below where we started. This could happen. + // If there is a legit case, then this should be removed. + assert(ticksHigh(desired, tolerance, delta) >= desired); +#endif // UNIT_TEST // We really should never get a value of 0, except as the last value // in the buffer. If that is the case, then assume infinity and return true. if (measured == 0) return true; @@ -702,7 +848,7 @@ int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) { */ bool IRrecv::decodeHash(decode_results *results) { // Require at least some samples to prevent triggering on noise - if (results->rawlen < unknown_threshold) return false; + if (results->rawlen < _unknown_threshold) return false; int32_t hash = kFnvBasis32; // 'rawlen - 2' to avoid the look ahead from going out of bounds. // Should probably be -3 to avoid comparing the trailing space entry, @@ -765,4 +911,236 @@ match_result_t IRrecv::matchData( return result; } +// Match & decode the typical data section of an IR message. +// The bytes are stored at result_ptr. The first byte in the result equates to +// the first byte encountered, and so on. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_ptr: A pointer to where to start storing the bytes we decoded. +// remaining: The size of the capture buffer are remaining. +// nbytes: Nr. of data bytes we expect. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// tolerance: Percentage error margin to allow. (Def: kTolerance) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance, const int16_t excess, + const bool MSBfirst) { + // Check if there is enough capture buffer to possibly have the desired bytes. + if (remaining < nbytes * 8 * 2) return 0; // Nope, so abort. + uint16_t offset = 0; + for (uint16_t byte_pos = 0; byte_pos < nbytes; byte_pos++) { + match_result_t result = matchData(data_ptr + offset, 8, onemark, onespace, + zeromark, zerospace, tolerance, excess, + MSBfirst); + if (result.success == false) return 0; // Fail + result_ptr[byte_pos] = (uint8_t)result.data; + offset += result.used; + } + return offset; +} + +// Match & decode a generic/typical IR message. +// The data is stored in result_bits_ptr or result_bytes_ptr depending on flag +// `use_bits`. +// Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean skip +// that requirement. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_bits_ptr: A pointer to where to start storing the bits we decoded. +// result_bytes_ptr: A pointer to where to start storing the bytes we decoded. +// use_bits: A flag indicating if we are to decode bits or bytes. +// remaining: The size of the capture buffer are remaining. +// nbits: Nr. of data bits we expect. +// hdrmark: Nr. of uSeconds for the expected header mark signal. +// hdrspace: Nr. of uSeconds for the expected header space signal. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// footermark: Nr. of uSeconds for the expected footer mark signal. +// footerspace: Nr. of uSeconds for the expected footer space/gap signal. +// atleast: Is the match on the footerspace a matchAtLeast or matchSpace? +// tolerance: Percentage error margin to allow. (Def: kTolerance) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::_matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_bytes_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + // If we are expecting byte sizes, check it's a factor of 8 or fail. + if (!use_bits && nbits % 8 != 0) return 0; + // Calculate how much remaining buffer is required. + uint16_t min_remaining = nbits * 2; + + if (hdrmark) min_remaining++; + if (hdrspace) min_remaining++; + if (footermark) min_remaining++; + // Don't need to extend for footerspace because it could be the end of message + + // Check if there is enough capture buffer to possibly have the message. + if (remaining < min_remaining) return 0; // Nope, so abort. + uint16_t offset = 0; + + // Header + if (hdrmark && !matchMark(*(data_ptr + offset++), hdrmark, tolerance, excess)) + return 0; + if (hdrspace && !matchSpace(*(data_ptr + offset++), hdrspace, tolerance, + excess)) + return 0; + + // Data + if (use_bits) { // Bits. + match_result_t result = IRrecv::matchData(data_ptr + offset, nbits, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst); + if (!result.success) return 0; + *result_bits_ptr = result.data; + offset += result.used; + } else { // bytes + uint16_t data_used = IRrecv::matchBytes(data_ptr + offset, result_bytes_ptr, + remaining - offset, nbits / 8, + onemark, onespace, + zeromark, zerospace, tolerance, + excess, MSBfirst); + if (!data_used) return 0; + offset += data_used; + } + // Footer + if (footermark && !matchMark(*(data_ptr + offset++), footermark, tolerance, + excess)) + return 0; + // If we have something still to match & haven't reached the end of the buffer + if (footerspace && offset < remaining) { + if (atleast) { + if (!matchAtLeast(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } else { + if (!matchSpace(*(data_ptr + offset), footerspace, tolerance, excess)) + return 0; + } + offset++; + } + return offset; +} + +// Match & decode a generic/typical <= 64bit IR message. +// The data is stored at result_ptr. +// Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean skip +// that requirement. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_ptr: A pointer to where to start storing the bits we decoded. +// remaining: The size of the capture buffer are remaining. +// nbits: Nr. of data bits we expect. +// hdrmark: Nr. of uSeconds for the expected header mark signal. +// hdrspace: Nr. of uSeconds for the expected header space signal. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// footermark: Nr. of uSeconds for the expected footer mark signal. +// footerspace: Nr. of uSeconds for the expected footer space/gap signal. +// atleast: Is the match on the footerspace a matchAtLeast or matchSpace? +// tolerance: Percentage error margin to allow. (Def: kTolerance) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, result_ptr, NULL, true, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} + +// Match & decode a generic/typical > 64bit IR message. +// The bytes are stored at result_ptr. The first byte in the result equates to +// the first byte encountered, and so on. +// Values of 0 for hdrmark, hdrspace, footermark, or footerspace mean skip +// that requirement. +// +// Args: +// data_ptr: A pointer to where we are at in the capture buffer. +// result_ptr: A pointer to where to start storing the bytes we decoded. +// remaining: The size of the capture buffer are remaining. +// nbits: Nr. of data bits we expect. +// hdrmark: Nr. of uSeconds for the expected header mark signal. +// hdrspace: Nr. of uSeconds for the expected header space signal. +// onemark: Nr. of uSeconds in an expected mark signal for a '1' bit. +// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. +// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. +// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. +// footermark: Nr. of uSeconds for the expected footer mark signal. +// footerspace: Nr. of uSeconds for the expected footer space/gap signal. +// atleast: Is the match on the footerspace a matchAtLeast or matchSpace? +// tolerance: Percentage error margin to allow. (Def: kTolerance) +// excess: Nr. of useconds. (Def: kMarkExcess) +// MSBfirst: Bit order to save the data in. (Def: true) +// Returns: +// A uint16_t: If successful, how many buffer entries were used. Otherwise 0. +uint16_t IRrecv::matchGeneric(volatile uint16_t *data_ptr, + uint8_t *result_ptr, + const uint16_t remaining, + const uint16_t nbits, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast, + const uint8_t tolerance, + const int16_t excess, + const bool MSBfirst) { + return _matchGeneric(data_ptr, NULL, result_ptr, false, remaining, nbits, + hdrmark, hdrspace, onemark, onespace, + zeromark, zerospace, footermark, footerspace, atleast, + tolerance, excess, MSBfirst); +} // End of IRrecv class ------------------- diff --git a/lib/IRremoteESP8266-2.6.0/src/IRrecv.h b/lib/IRremoteESP8266-2.6.3.10/src/IRrecv.h similarity index 63% rename from lib/IRremoteESP8266-2.6.0/src/IRrecv.h rename to lib/IRremoteESP8266-2.6.3.10/src/IRrecv.h index 0659f093e..f330d0a6c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRrecv.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRrecv.h @@ -51,6 +51,9 @@ const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1)); const uint32_t kFnvPrime32 = 16777619UL; const uint32_t kFnvBasis32 = 2166136261UL; +// Which of the ESP32 timers to use by default. (0-3) +const uint8_t kDefaultESP32Timer = 3; + #if DECODE_AC // Hitachi AC is the current largest state size. const uint16_t kStateSizeMax = kHitachiAc2StateLength; @@ -108,17 +111,24 @@ class decode_results { // main class for receiving IR class IRrecv { public: - explicit IRrecv(uint16_t recvpin, uint16_t bufsize = kRawBuf, - uint8_t timeout = kTimeoutMs, - bool save_buffer = false); // Constructor - ~IRrecv(); // Destructor +#if defined(ESP32) + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false, + const uint8_t timer_num = kDefaultESP32Timer); // Constructor +#else // ESP32 + explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf, + const uint8_t timeout = kTimeoutMs, + const bool save_buffer = false); // Constructor +#endif // ESP32 + ~IRrecv(void); // Destructor bool decode(decode_results *results, irparams_t *save = NULL); - void enableIRIn(); - void disableIRIn(); - void resume(); - uint16_t getBufSize(); + void enableIRIn(const bool pullup = false); + void disableIRIn(void); + void resume(void); + uint16_t getBufSize(void); #if DECODE_HASH - void setUnknownThreshold(uint16_t length); + void setUnknownThreshold(const uint16_t length); #endif static bool match(uint32_t measured, uint32_t desired, uint8_t tolerance = kTolerance, uint16_t delta = 0); @@ -133,8 +143,9 @@ class IRrecv { private: #endif irparams_t *irparams_save; + uint8_t _timer_num; #if DECODE_HASH - uint16_t unknown_threshold; + uint16_t _unknown_threshold; #endif // These are called by decode void copyIrParams(volatile irparams_t *src, irparams_t *dst); @@ -145,17 +156,68 @@ class IRrecv { uint16_t delta = 0); bool matchAtLeast(uint32_t measured, uint32_t desired, uint8_t tolerance = kTolerance, uint16_t delta = 0); + uint16_t _matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_bits_ptr, + uint8_t *result_ptr, + const bool use_bits, + const uint16_t remaining, + const uint16_t required, + const uint16_t hdrmark, + const uint32_t hdrspace, + const uint16_t onemark, + const uint32_t onespace, + const uint16_t zeromark, + const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kTolerance, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark, const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace, const uint8_t tolerance = kTolerance, const int16_t excess = kMarkExcess, const bool MSBfirst = true); + uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbytes, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint8_t tolerance = kTolerance, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, + uint64_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kTolerance, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); + uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr, + const uint16_t remaining, const uint16_t nbits, + const uint16_t hdrmark, const uint32_t hdrspace, + const uint16_t onemark, const uint32_t onespace, + const uint16_t zeromark, const uint32_t zerospace, + const uint16_t footermark, + const uint32_t footerspace, + const bool atleast = false, + const uint8_t tolerance = kTolerance, + const int16_t excess = kMarkExcess, + const bool MSBfirst = true); bool decodeHash(decode_results *results); #if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO) 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 +266,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 +294,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 +307,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 +315,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, @@ -269,6 +343,16 @@ class IRrecv { bool decodeDaikin(decode_results *results, const uint16_t nbits = kDaikinBits, const bool strict = true); #endif +#if DECODE_DAIKIN160 + bool decodeDaikin160(decode_results *results, + const uint16_t nbits = kDaikin160Bits, + const bool strict = true); +#endif // DECODE_DAIKIN160 +#if DECODE_DAIKIN176 + bool decodeDaikin176(decode_results *results, + const uint16_t nbits = kDaikin176Bits, + const bool strict = true); +#endif // DECODE_DAIKIN176 #if DECODE_DAIKIN2 bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits, bool strict = true); @@ -280,8 +364,13 @@ class IRrecv { #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); @@ -298,6 +387,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); @@ -312,12 +406,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, @@ -325,7 +421,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, @@ -337,7 +434,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, @@ -349,21 +447,28 @@ class IRrecv { bool strict = true); #endif #if DECODE_VESTEL_AC - bool decodeVestelAc(decode_results *results, uint16_t nbits = kVestelAcBits, - bool strict = true); + bool decodeVestelAc(decode_results *results, + const uint16_t nbits = kVestelAcBits, + const bool strict = true); #endif #if DECODE_TCL112AC - bool decodeTcl112Ac(decode_results *results, uint16_t nbits = kTcl112AcBits, - bool strict = true); + bool decodeTcl112Ac(decode_results *results, + const uint16_t nbits = kTcl112AcBits, + const bool strict = true); #endif #if DECODE_TECO - bool decodeTeco(decode_results *results, uint16_t nbits = kTecoBits, - bool strict = false); + bool decodeTeco(decode_results *results, const uint16_t nbits = kTecoBits, + const bool strict = false); #endif #if DECODE_LEGOPF bool decodeLegoPf(decode_results *results, const uint16_t nbits = kLegoPfBits, const bool strict = true); #endif +#if DECODE_NEOCLIMA +bool decodeNeoclima(decode_results *results, + const uint16_t nbits = kNeoclimaBits, + const bool strict = true); +#endif // DECODE_NEOCLIMA }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h b/lib/IRremoteESP8266-2.6.3.10/src/IRremoteESP8266.h similarity index 88% rename from lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h rename to lib/IRremoteESP8266-2.6.3.10/src/IRremoteESP8266.h index b532cb1c0..1af1bdd13 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRremoteESP8266.h @@ -26,7 +26,7 @@ * DISH decode by marcosamarinho * Gree Heatpump sending added by Ville Skyttä (scop) * (derived from https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp) - * Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for sending IR code on ESP8266 + * Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for sending IR code on ESP8266 * Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code on ESP8266 * * Updated by sillyfrog for Daikin, adopted from @@ -47,10 +47,11 @@ #include #ifdef UNIT_TEST #include -#endif +#include +#endif // UNIT_TEST // Library Version -#define _IRREMOTEESP8266_VERSION_ "2.6.0" +#define _IRREMOTEESP8266_VERSION_ "2.6.3" // 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! @@ -118,6 +119,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 @@ -130,6 +134,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 @@ -139,16 +146,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 @@ -225,6 +235,15 @@ #define DECODE_DAIKIN216 true #define SEND_DAIKIN216 true + +#define DECODE_DAIKIN160 true +#define SEND_DAIKIN160 true + +#define DECODE_NEOCLIMA true +#define SEND_NEOCLIMA true + +#define DECODE_DAIKIN176 true +#define SEND_DAIKIN176 true */ // Tasmota supported protocols (less protocols is less code size) @@ -266,19 +285,19 @@ #define SEND_SAMSUNG_AC false #define DECODE_WHYNTER false -#define SEND_WHYNTER false +#define SEND_WHYNTER true #define DECODE_AIWA_RC_T501 false -#define SEND_AIWA_RC_T501 false +#define SEND_AIWA_RC_T501 true #define DECODE_LG true #define SEND_LG true #define DECODE_SANYO false -#define SEND_SANYO false +#define SEND_SANYO true #define DECODE_MITSUBISHI false -#define SEND_MITSUBISHI false +#define SEND_MITSUBISHI true #define DECODE_MITSUBISHI2 false #define SEND_MITSUBISHI2 false @@ -287,7 +306,10 @@ #define SEND_DISH true #define DECODE_SHARP false -#define SEND_SHARP false +#define SEND_SHARP true + +#define DECODE_SHARP_AC false +#define SEND_SHARP_AC false #define DECODE_DENON false #define SEND_DENON false @@ -301,6 +323,9 @@ #define DECODE_FUJITSU_AC false #define SEND_FUJITSU_AC true +#define DECODE_INAX false +#define SEND_INAX false + #define DECODE_DAIKIN false #define SEND_DAIKIN false @@ -310,16 +335,19 @@ #define DECODE_GLOBALCACHE false // Not written. #define SEND_GLOBALCACHE false +#define DECODE_GOODWEATHER false +#define SEND_GOODWEATHER false + #define DECODE_GREE false #define SEND_GREE false #define DECODE_PRONTO false // Not written. #define SEND_PRONTO false -#define DECODE_ARGO false // Not written. +#define DECODE_ARGO false // Experimental #define SEND_ARGO false -#define DECODE_TROTEC false // Not implemented. +#define DECODE_TROTEC false #define SEND_TROTEC false #define DECODE_NIKAI false @@ -397,6 +425,15 @@ #define DECODE_DAIKIN216 false #define SEND_DAIKIN216 false +#define DECODE_DAIKIN160 false +#define SEND_DAIKIN160 false + +#define DECODE_NEOCLIMA false +#define SEND_NEOCLIMA false + +#define DECODE_DAIKIN176 false +#define SEND_DAIKIN176 false + #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 || \ @@ -404,7 +441,8 @@ DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \ DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ - DECODE_DAIKIN216) + DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ + DECODE_NEOCLIMA || DECODE_DAIKIN176) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -413,7 +451,7 @@ // Use millisecond 'delay()' calls where we can to avoid tripping the WDT. // Note: If you plan to send IR messages in the callbacks of the AsyncWebserver // library, you need to set ALLOW_DELAY_CALLS to false. -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/430 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/430 #define ALLOW_DELAY_CALLS true /* @@ -485,8 +523,14 @@ enum decode_type_t { MITSUBISHI_HEAVY_88, MITSUBISHI_HEAVY_152, // 60 DAIKIN216, + SHARP_AC, + GOODWEATHER, + INAX, + DAIKIN160, // 65 + NEOCLIMA, + DAIKIN176, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = DAIKIN216, + kLastDecodeType = DAIKIN176, }; // Message lengths & required repeat values @@ -496,9 +540,10 @@ const uint16_t kSingleRepeat = 1; const uint16_t kAiwaRcT501Bits = 15; const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat; const uint16_t kArgoStateLength = 12; +const uint16_t kArgoBits = kArgoStateLength * 8; const uint16_t kArgoDefaultRepeat = kNoRepeat; const uint16_t kCoolixBits = 24; -const uint16_t kCoolixDefaultRepeat = 1; +const uint16_t kCoolixDefaultRepeat = kSingleRepeat; const uint16_t kCarrierAcBits = 32; const uint16_t kCarrierAcMinRepeat = kNoRepeat; const uint16_t kDaikinStateLength = 35; @@ -509,15 +554,23 @@ const uint16_t kDaikinDefaultRepeat = kNoRepeat; const uint16_t kDaikin2StateLength = 39; const uint16_t kDaikin2Bits = kDaikin2StateLength * 8; const uint16_t kDaikin2DefaultRepeat = kNoRepeat; +const uint16_t kDaikin160StateLength = 20; +const uint16_t kDaikin160Bits = kDaikin160StateLength * 8; +const uint16_t kDaikin160DefaultRepeat = kNoRepeat; +const uint16_t kDaikin176StateLength = 22; +const uint16_t kDaikin176Bits = kDaikin176StateLength * 8; +const uint16_t kDaikin176DefaultRepeat = 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; const uint16_t kElectraAcStateLength = 13; const uint16_t kElectraAcBits = kElectraAcStateLength * 8; +const uint16_t kElectraAcMinRepeat = kNoRepeat; const uint16_t kFujitsuAcMinRepeat = kNoRepeat; const uint16_t kFujitsuAcStateLength = 16; const uint16_t kFujitsuAcStateLengthShort = 7; @@ -525,6 +578,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; @@ -541,6 +596,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; @@ -570,6 +627,9 @@ const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; const uint16_t kNikaiBits = 24; const uint16_t kNECBits = 32; +const uint16_t kNeoclimaStateLength = 12; +const uint16_t kNeoclimaBits = kNeoclimaStateLength * 8; +const uint16_t kNeoclimaMinRepeat = kNoRepeat; const uint16_t kPanasonicBits = 48; const uint32_t kPanasonicManufacturer = 0x4004; const uint16_t kPanasonicAcStateLength = 27; @@ -600,6 +660,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; @@ -616,6 +679,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; @@ -631,7 +695,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 @@ -700,9 +764,10 @@ const uint8_t kVestelAcBits = 56; // Create a no-op F() macro so the code base still compiles outside of the // Arduino framework. Thus we can safely use the Arduino 'F()' macro through-out // the code base. That macro stores constants in Flash (PROGMEM) memory. -// See: https://github.com/markszabo/IRremoteESP8266/issues/667 +// See: https://github.com/crankyoldgit/IRremoteESP8266/issues/667 #define F(x) x #endif // F +typedef std::string String; #endif // UNIT_TEST #endif // IRREMOTEESP8266_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp b/lib/IRremoteESP8266-2.6.3.10/src/IRsend.cpp similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/IRsend.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/IRsend.cpp index 22c0c874b..89cf3e512 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRsend.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRsend.cpp @@ -16,7 +16,7 @@ #include "IRtimer.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ -// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for // sending IR code on ESP8266 // IRsend ---------------------------------------------------------------------- @@ -491,178 +491,335 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) { } #endif // SEND_RAW +// Get the minimum number of repeats for a given protocol. +// Args: +// protocol: Protocol number/type of the message you want to send. +// Returns: +// int16_t: The number of repeats required. +uint16_t IRsend::minRepeats(const decode_type_t protocol) { + switch (protocol) { + // Single repeats + case AIWA_RC_T501: + case COOLIX: + case GICABLE: + case INAX: + case MITSUBISHI: + case MITSUBISHI2: + case MITSUBISHI_AC: + case SHERWOOD: + case TOSHIBA_AC: + return kSingleRepeat; + // Special + case DISH: + return kDishMinRepeat; + case SONY: + return kSonyMinRepeat; + default: + return kNoRepeat; + } +} + +// Get the default number of bits for a given protocol. +// Args: +// protocol: Protocol number/type you want the default nr. of bits for. +// Returns: +// int16_t: The number of bits. +uint16_t IRsend::defaultBits(const decode_type_t protocol) { + switch (protocol) { + case RC5: + return 12; + case LASERTAG: + case RC5X: + return 13; + case AIWA_RC_T501: + case DENON: + case SHARP: + return 15; + case DISH: + case GICABLE: + case JVC: + case LEGOPF: + case MITSUBISHI: + case MITSUBISHI2: + return 16; + case RC6: + case SONY: + return 20; + case COOLIX: + case INAX: + case NIKAI: + case RCMM: + return 24; + case LG: + case LG2: + return 28; + case CARRIER_AC: + case NEC: + case NEC_LIKE: + case SAMSUNG: + case SHERWOOD: + case WHYNTER: + return 32; + case LUTRON: + case TECO: + return 35; + case SAMSUNG36: + return 36; + case SANYO_LC7461: + return kSanyoLC7461Bits; // 42 + case GOODWEATHER: + case MIDEA: + case PANASONIC: + return 48; + case MAGIQUEST: + case VESTEL_AC: + return 56; + case PIONEER: + return 64; + case ARGO: + return kArgoBits; + case DAIKIN: + return kDaikinBits; + case DAIKIN160: + return kDaikin160Bits; + case DAIKIN176: + return kDaikin176Bits; + case DAIKIN2: + return kDaikin2Bits; + case DAIKIN216: + return kDaikin216Bits; + case ELECTRA_AC: + return kElectraAcBits; + case GREE: + return kGreeBits; + case HAIER_AC: + return kHaierACBits; + case HAIER_AC_YRW02: + return kHaierACYRW02Bits; + case HITACHI_AC: + return kHitachiAcBits; + case HITACHI_AC1: + return kHitachiAc1Bits; + case HITACHI_AC2: + return kHitachiAc2Bits; + case KELVINATOR: + return kKelvinatorBits; + case MITSUBISHI_AC: + return kMitsubishiACBits; + case MITSUBISHI_HEAVY_152: + return kMitsubishiHeavy152Bits; + case MITSUBISHI_HEAVY_88: + return kMitsubishiHeavy88Bits; + case NEOCLIMA: + return kNeoclimaBits; + case PANASONIC_AC: + return kNeoclimaBits; + case SAMSUNG_AC: + return kSamsungAcBits; + case SHARP_AC: + return kSharpAcBits; + case TCL112AC: + return kTcl112AcBits; + case TOSHIBA_AC: + return kToshibaACBits; + case TROTEC: + return kTrotecBits; + case WHIRLPOOL_AC: + return kWhirlpoolAcBits; + // No default amount of bits. + case FUJITSU_AC: + case MWM: + default: + return 0; + } +} + // Send a simple (up to 64 bits) IR message of a given type. // An unknown/unsupported type will do nothing. // Args: // type: Protocol number/type of the message you want to send. // data: The data you want to send (up to 64 bits). // nbits: How many bits long the message is to be. +// repeat: How many repeats to do? // Returns: // bool: True if it is a type we can attempt to send, false if not. -bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { +bool IRsend::send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat) { + uint16_t min_repeat = std::max(IRsend::minRepeats(type), repeat); switch (type) { #if SEND_AIWA_RC_T501 case AIWA_RC_T501: - sendAiwaRCT501(data, nbits); + sendAiwaRCT501(data, nbits, min_repeat); break; #endif #if SEND_CARRIER_AC case CARRIER_AC: - sendCarrierAC(data, nbits); + sendCarrierAC(data, nbits, min_repeat); break; #endif #if SEND_COOLIX case COOLIX: - sendCOOLIX(data, nbits); + sendCOOLIX(data, nbits, min_repeat); break; #endif #if SEND_DENON case DENON: - sendDenon(data, nbits); + sendDenon(data, nbits, min_repeat); break; #endif #if SEND_DISH case DISH: - sendDISH(data, nbits); + sendDISH(data, nbits, min_repeat); break; #endif #if SEND_GICABLE case GICABLE: - sendGICable(data, nbits); + sendGICable(data, nbits, min_repeat); + break; +#endif +#if SEND_GOODWEATHER + case GOODWEATHER: + sendGoodweather(data, nbits, min_repeat); break; #endif #if SEND_GREE case GREE: - sendGree(data, nbits); + sendGree(data, nbits, min_repeat); break; #endif +#if SEND_INAX + case INAX: + sendInax(data, nbits, min_repeat); + break; +#endif // SEND_INAX #if SEND_JVC case JVC: - sendJVC(data, nbits); + sendJVC(data, nbits, min_repeat); break; #endif #if SEND_LASERTAG case LASERTAG: - sendLasertag(data, nbits); + sendLasertag(data, nbits, min_repeat); break; #endif #if SEND_LEGOPF case LEGOPF: - sendLegoPf(data, nbits); + sendLegoPf(data, nbits, min_repeat); break; #endif #if SEND_LG case LG: - sendLG(data, nbits); + sendLG(data, nbits, min_repeat); break; case LG2: - sendLG2(data, nbits); + sendLG2(data, nbits, min_repeat); break; #endif #if SEND_LUTRON case LUTRON: - sendLutron(data, nbits); + sendLutron(data, nbits, min_repeat); break; #endif #if SEND_MAGIQUEST case MAGIQUEST: - sendMagiQuest(data, nbits); + sendMagiQuest(data, nbits, min_repeat); break; #endif #if SEND_MIDEA case MIDEA: - sendMidea(data, nbits); + sendMidea(data, nbits, min_repeat); break; #endif #if SEND_MITSUBISHI case MITSUBISHI: - sendMitsubishi(data, nbits); + sendMitsubishi(data, nbits, min_repeat); break; #endif #if SEND_MITSUBISHI2 case MITSUBISHI2: - sendMitsubishi2(data, nbits); + sendMitsubishi2(data, nbits, min_repeat); break; #endif #if SEND_NIKAI case NIKAI: - sendNikai(data, nbits); + sendNikai(data, nbits, min_repeat); break; #endif #if SEND_NEC case NEC: case NEC_LIKE: - sendNEC(data, nbits); + sendNEC(data, nbits, min_repeat); break; #endif #if SEND_PANASONIC case PANASONIC: - sendPanasonic64(data, nbits); + sendPanasonic64(data, nbits, min_repeat); break; #endif #if SEND_PIONEER case PIONEER: - sendPioneer(data, nbits); + sendPioneer(data, nbits, min_repeat); break; #endif #if SEND_RC5 case RC5: - sendRC5(data, nbits); + case RC5X: + sendRC5(data, nbits, min_repeat); break; #endif #if SEND_RC6 case RC6: - sendRC6(data, nbits); + sendRC6(data, nbits, min_repeat); break; #endif #if SEND_RCMM case RCMM: - sendRCMM(data, nbits); + sendRCMM(data, nbits, min_repeat); break; #endif #if SEND_SAMSUNG case SAMSUNG: - sendSAMSUNG(data, nbits); + sendSAMSUNG(data, nbits, min_repeat); break; #endif #if SEND_SAMSUNG36 case SAMSUNG36: - sendSamsung36(data, nbits); + sendSamsung36(data, nbits, min_repeat); break; #endif #if SEND_SANYO case SANYO_LC7461: - sendSanyoLC7461(data, nbits); + sendSanyoLC7461(data, nbits, min_repeat); break; #endif #if SEND_SHARP case SHARP: - sendSharpRaw(data, nbits); + sendSharpRaw(data, nbits, min_repeat); break; #endif #if SEND_SHERWOOD case SHERWOOD: - sendSherwood(data, nbits); + sendSherwood(data, nbits, min_repeat); break; #endif #if SEND_SONY case SONY: - sendSony(data, nbits); + sendSony(data, nbits, min_repeat); break; #endif #if SEND_TECO case TECO: - sendTeco(data, nbits); + sendTeco(data, nbits, min_repeat); break; #endif #if SEND_VESTEL_AC case VESTEL_AC: - sendVestelAc(data, nbits); + sendVestelAc(data, nbits, min_repeat); break; #endif #if SEND_WHYNTER case WHYNTER: - sendWhynter(data, nbits); + sendWhynter(data, nbits, min_repeat); break; #endif default: @@ -670,3 +827,153 @@ bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) { } return true; } + +// Send a complex (>= 64 bits) IR message of a given type. +// An unknown/unsupported type will do nothing. +// Args: +// type: Protocol number/type of the message you want to send. +// state: A pointer to the array of bytes that make up the state[]. +// nbytes: How many bytes are in the state. +// Returns: +// bool: True if it is a type we can attempt to send, false if not. +bool IRsend::send(const decode_type_t type, const unsigned char *state, + const uint16_t nbytes) { + switch (type) { +#if SEND_ARGO + case ARGO: + sendArgo(state, nbytes); + break; +#endif // SEND_ARGO +#if SEND_DAIKIN + case DAIKIN: + sendDaikin(state, nbytes); + break; +#endif // SEND_DAIKIN +#if SEND_DAIKIN160 + case DAIKIN160: + sendDaikin160(state, nbytes); + break; +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + case DAIKIN176: + sendDaikin176(state, nbytes); + break; +#endif // SEND_DAIKIN176 +#if SEND_DAIKIN2 + case DAIKIN2: + sendDaikin2(state, nbytes); + break; +#endif // SEND_DAIKIN2 +#if SEND_DAIKIN216 + case DAIKIN216: + sendDaikin216(state, nbytes); + break; +#endif // SEND_DAIKIN216 +#if SEND_ELECTRA_AC + case ELECTRA_AC: + sendElectraAC(state, nbytes); + break; +#endif // SEND_ELECTRA_AC +#if SEND_FUJITSU_AC + case FUJITSU_AC: + sendFujitsuAC(state, nbytes); + break; +#endif // SEND_FUJITSU_AC +#if SEND_GREE + case GREE: + sendGree(state, nbytes); + break; +#endif // SEND_GREE +#if SEND_HAIER_AC + case HAIER_AC: + sendHaierAC(state, nbytes); + break; +#endif // SEND_HAIER_AC +#if SEND_HAIER_AC_YRW02 + case HAIER_AC_YRW02: + sendHaierACYRW02(state, nbytes); + break; +#endif // SEND_HAIER_AC_YRW02 +#if SEND_HITACHI_AC + case HITACHI_AC: + sendHitachiAC(state, nbytes); + break; +#endif // SEND_HITACHI_AC +#if SEND_HITACHI_AC1 + case HITACHI_AC1: + sendHitachiAC1(state, nbytes); + break; +#endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC2 + case HITACHI_AC2: + sendHitachiAC2(state, nbytes); + break; +#endif // SEND_HITACHI_AC2 +#if SEND_KELVINATOR + case KELVINATOR: + sendKelvinator(state, nbytes); + break; +#endif // SEND_KELVINATOR +#if SEND_MITSUBISHI_AC + case MITSUBISHI_AC: + sendMitsubishiAC(state, nbytes); + break; +#endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHIHEAVY + case MITSUBISHI_HEAVY_88: + sendMitsubishiHeavy88(state, nbytes); + break; + case MITSUBISHI_HEAVY_152: + sendMitsubishiHeavy152(state, nbytes); + break; +#endif // SEND_MITSUBISHIHEAVY +#if SEND_MWM + case MWM: + sendMWM(state, nbytes); + break; +#endif // SEND_MWM +#if SEND_NEOCLIMA + case NEOCLIMA: + sendNeoclima(state, nbytes); + break; +#endif // SEND_NEOCLIMA +#if SEND_PANASONIC_AC + case PANASONIC_AC: + sendPanasonicAC(state, nbytes); + break; +#endif // SEND_PANASONIC_AC +#if SEND_SAMSUNG_AC + case SAMSUNG_AC: + sendSamsungAC(state, nbytes); + break; +#endif // SEND_SAMSUNG_AC +#if SEND_SHARP_AC + case SHARP_AC: + sendSharpAc(state, nbytes); + break; +#endif // SEND_SHARP_AC +#if SEND_TCL112AC + case TCL112AC: + sendTcl112Ac(state, nbytes); + break; +#endif // SEND_TCL112AC +#if SEND_TOSHIBA_AC + case TOSHIBA_AC: + sendToshibaAC(state, nbytes); + break; +#endif // SEND_TOSHIBA_AC +#if SEND_TROTEC + case TROTEC: + sendTrotec(state, nbytes); + break; +#endif // SEND_TROTEC +#if SEND_WHIRLPOOL_AC + case WHIRLPOOL_AC: + sendWhirlpoolAC(state, nbytes); + break; +#endif // SEND_WHIRLPOOL_AC + default: + return false; + } + return true; +} diff --git a/lib/IRremoteESP8266-2.6.0/src/IRsend.h b/lib/IRremoteESP8266-2.6.3.10/src/IRsend.h similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/IRsend.h rename to lib/IRremoteESP8266-2.6.3.10/src/IRsend.h index b065f6582..276a7be15 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRsend.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRsend.h @@ -9,7 +9,7 @@ #include "IRremoteESP8266.h" // Originally from https://github.com/shirriff/Arduino-IRremote/ -// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for +// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for // sending IR code on ESP8266 #if TEST || UNIT_TEST @@ -21,8 +21,17 @@ // Constants // Offset (in microseconds) to use in Period time calculations to account for // code excution time in producing the software PWM signal. -// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core +#if defined(ESP32) +// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz +const int8_t kPeriodOffset = -2; +#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens) +// Calculated on an ESP8266 NodeMCU v2 board using: +// v2.6.0 with v2.5.2 ESP core @ 160MHz +const int8_t kPeriodOffset = -2; +#else // (defined(ESP8266) && F_CPU == 160000000L) +// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz const int8_t kPeriodOffset = -5; +#endif // (defined(ESP8266) && F_CPU == 160000000L) const uint8_t kDutyDefault = 50; // Percentage const uint8_t kDutyMax = 100; // Percentage // delayMicroseconds() is only accurate to 16383us. @@ -70,6 +79,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 @@ -109,7 +140,12 @@ class IRsend { const uint8_t *dataptr, const uint16_t nbytes, const uint16_t frequency, const bool MSBfirst, const uint16_t repeat, const uint8_t dutycycle); - bool send(decode_type_t type, uint64_t data, uint16_t nbits); + static uint16_t minRepeats(const decode_type_t protocol); + static uint16_t defaultBits(const decode_type_t protocol); + bool send(const decode_type_t type, const uint64_t data, + const uint16_t nbits, const uint16_t repeat = kNoRepeat); + bool send(const decode_type_t type, const uint8_t state[], + const uint16_t nbytes); #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO) void sendNEC(uint64_t data, uint16_t nbits = kNECBits, uint16_t repeat = kNoRepeat); @@ -131,9 +167,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 +188,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 +225,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, @@ -228,9 +271,9 @@ class IRsend { uint16_t repeat = kMitsubishiMinRepeat); #endif #if SEND_MITSUBISHI_AC - void sendMitsubishiAC(unsigned char data[], - uint16_t nbytes = kMitsubishiACStateLength, - uint16_t repeat = kMitsubishiACMinRepeat); + void sendMitsubishiAC(const unsigned char data[], + const uint16_t nbytes = kMitsubishiACStateLength, + const uint16_t repeat = kMitsubishiACMinRepeat); #endif #if SEND_MITSUBISHIHEAVY void sendMitsubishiHeavy88( @@ -243,25 +286,40 @@ class IRsend { const uint16_t repeat = kMitsubishiHeavy152MinRepeat); #endif #if SEND_FUJITSU_AC - void sendFujitsuAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat = kFujitsuAcMinRepeat); + void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kFujitsuAcMinRepeat); #endif +#if SEND_INAX + void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits, + const uint16_t repeat = kInaxMinRepeat); +#endif // SEND_INAX #if SEND_GLOBALCACHE void sendGC(uint16_t buf[], uint16_t len); #endif #if SEND_KELVINATOR - void sendKelvinator(unsigned char data[], - uint16_t nbytes = kKelvinatorStateLength, - uint16_t repeat = kKelvinatorDefaultRepeat); + void sendKelvinator(const unsigned char data[], + const uint16_t nbytes = kKelvinatorStateLength, + const uint16_t repeat = kKelvinatorDefaultRepeat); #endif #if SEND_DAIKIN void sendDaikin(const unsigned char data[], const uint16_t nbytes = kDaikinStateLength, const uint16_t repeat = kDaikinDefaultRepeat); #endif +#if SEND_DAIKIN160 + void sendDaikin160(const unsigned char data[], + const uint16_t nbytes = kDaikin160StateLength, + const uint16_t repeat = kDaikin160DefaultRepeat); +#endif // SEND_DAIKIN160 +#if SEND_DAIKIN176 + void sendDaikin176(const unsigned char data[], + const uint16_t nbytes = kDaikin176StateLength, + const uint16_t repeat = kDaikin176DefaultRepeat); +#endif // SEND_DAIKIN176 #if SEND_DAIKIN2 - void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength, - uint16_t repeat = kDaikin2DefaultRepeat); + void sendDaikin2(const unsigned char data[], + const uint16_t nbytes = kDaikin2StateLength, + const uint16_t repeat = kDaikin2DefaultRepeat); #endif #if SEND_DAIKIN216 void sendDaikin216(const unsigned char data[], @@ -273,30 +331,37 @@ class IRsend { uint16_t repeat = kAiwaRcT501MinRepeats); #endif #if SEND_GREE - void sendGree(uint64_t data, uint16_t nbits = kGreeBits, - uint16_t repeat = kGreeDefaultRepeat); - void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength, - uint16_t repeat = kGreeDefaultRepeat); + void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits, + const uint16_t repeat = kGreeDefaultRepeat); + void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength, + const uint16_t repeat = kGreeDefaultRepeat); #endif +#if SEND_GOODWEATHER + void sendGoodweather(const uint64_t data, + const uint16_t nbits = kGoodweatherBits, + const uint16_t repeat = kGoodweatherMinRepeat); +#endif // SEND_GOODWEATHER #if SEND_PRONTO void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat); #endif #if SEND_ARGO - void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength, - uint16_t repeat = kArgoDefaultRepeat); + void sendArgo(const unsigned char data[], + const uint16_t nbytes = kArgoStateLength, + const uint16_t repeat = kArgoDefaultRepeat); #endif #if SEND_TROTEC - void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength, - uint16_t repeat = kTrotecDefaultRepeat); + void sendTrotec(const unsigned char data[], + const uint16_t nbytes = kTrotecStateLength, + const uint16_t repeat = kTrotecDefaultRepeat); #endif #if SEND_NIKAI void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits, uint16_t repeat = kNoRepeat); #endif #if SEND_TOSHIBA_AC - void sendToshibaAC(unsigned char data[], - uint16_t nbytes = kToshibaACStateLength, - uint16_t repeat = kToshibaACMinRepeat); + void sendToshibaAC(const unsigned char data[], + const uint16_t nbytes = kToshibaACStateLength, + const uint16_t repeat = kToshibaACMinRepeat); #endif #if SEND_MIDEA void sendMidea(uint64_t data, uint16_t nbits = kMideaBits, @@ -316,51 +381,52 @@ class IRsend { uint16_t repeat = kCarrierAcMinRepeat); #endif #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) - void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength, - uint16_t repeat = kHaierAcDefaultRepeat); + void sendHaierAC(const unsigned char data[], + const uint16_t nbytes = kHaierACStateLength, + const uint16_t repeat = kHaierAcDefaultRepeat); #endif #if SEND_HAIER_AC_YRW02 - void sendHaierACYRW02(unsigned char data[], - uint16_t nbytes = kHaierACYRW02StateLength, - uint16_t repeat = kHaierAcYrw02DefaultRepeat); + void sendHaierACYRW02(const unsigned char data[], + const uint16_t nbytes = kHaierACYRW02StateLength, + const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif #if SEND_HITACHI_AC - void sendHitachiAC(unsigned char data[], - uint16_t nbytes = kHitachiAcStateLength, - uint16_t repeat = kHitachiAcDefaultRepeat); + void sendHitachiAC(const unsigned char data[], + const uint16_t nbytes = kHitachiAcStateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); #endif #if SEND_HITACHI_AC1 - void sendHitachiAC1(unsigned char data[], - uint16_t nbytes = kHitachiAc1StateLength, - uint16_t repeat = kNoRepeat); + void sendHitachiAC1(const unsigned char data[], + const uint16_t nbytes = kHitachiAc1StateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_HITACHI_AC2 - void sendHitachiAC2(unsigned char data[], - uint16_t nbytes = kHitachiAc2StateLength, - uint16_t repeat = kNoRepeat); + void sendHitachiAC2(const unsigned char data[], + const uint16_t nbytes = kHitachiAc2StateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_GICABLE void sendGICable(uint64_t data, uint16_t nbits = kGicableBits, uint16_t repeat = kGicableMinRepeat); #endif #if SEND_WHIRLPOOL_AC - void sendWhirlpoolAC(unsigned char data[], - uint16_t nbytes = kWhirlpoolAcStateLength, - uint16_t repeat = kWhirlpoolAcDefaultRepeat); + void sendWhirlpoolAC(const unsigned char data[], + const uint16_t nbytes = kWhirlpoolAcStateLength, + const uint16_t repeat = kWhirlpoolAcDefaultRepeat); #endif #if SEND_LUTRON void sendLutron(uint64_t data, uint16_t nbits = kLutronBits, uint16_t repeat = kNoRepeat); #endif #if SEND_ELECTRA_AC - void sendElectraAC(unsigned char data[], - uint16_t nbytes = kElectraAcStateLength, - uint16_t repeat = kNoRepeat); + void sendElectraAC(const unsigned char data[], + const uint16_t nbytes = kElectraAcStateLength, + const uint16_t repeat = kNoRepeat); #endif #if SEND_PANASONIC_AC - void sendPanasonicAC(unsigned char data[], - uint16_t nbytes = kPanasonicAcStateLength, - uint16_t repeat = kPanasonicAcDefaultRepeat); + void sendPanasonicAC(const unsigned char data[], + const uint16_t nbytes = kPanasonicAcStateLength, + const uint16_t repeat = kPanasonicAcDefaultRepeat); #endif #if SEND_PIONEER void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits, @@ -368,8 +434,8 @@ class IRsend { uint64_t encodePioneer(uint16_t address, uint16_t command); #endif #if SEND_MWM - void sendMWM(unsigned char data[], uint16_t nbytes, - uint16_t repeat = kNoRepeat); + void sendMWM(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat = kNoRepeat); #endif #if SEND_VESTEL_AC void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, @@ -381,13 +447,18 @@ class IRsend { const uint16_t repeat = kTcl112AcDefaultRepeat); #endif #if SEND_TECO - void sendTeco(uint64_t data, uint16_t nbits = kTecoBits, - uint16_t repeat = kNoRepeat); + void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits, + const uint16_t repeat = kNoRepeat); #endif #if SEND_LEGOPF void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits, const uint16_t repeat = kLegoPfMinRepeat); #endif +#if SEND_NEOCLIMA + void sendNeoclima(const unsigned char data[], + const uint16_t nbytes = kNeoclimaStateLength, + const uint16_t repeat = kNeoclimaMinRepeat); +#endif // SEND_NEOCLIMA protected: diff --git a/lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp b/lib/IRremoteESP8266-2.6.3.10/src/IRtimer.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/IRtimer.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/IRtimer.cpp diff --git a/lib/IRremoteESP8266-2.6.0/src/IRtimer.h b/lib/IRremoteESP8266-2.6.3.10/src/IRtimer.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/IRtimer.h rename to lib/IRremoteESP8266-2.6.3.10/src/IRtimer.h diff --git a/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp b/lib/IRremoteESP8266-2.6.3.10/src/IRutils.cpp similarity index 58% rename from lib/IRremoteESP8266-2.6.0/src/IRutils.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/IRutils.cpp index d90925241..10ff9f1cd 100644 --- a/lib/IRremoteESP8266-2.6.0/src/IRutils.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRutils.cpp @@ -44,19 +44,19 @@ uint64_t reverseBits(uint64_t input, uint16_t nbits) { // Returns: // A string representation of the integer. // Note: Based on Arduino's Print::printNumber() -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String uint64ToString(uint64_t input, uint8_t base) { String result = ""; -#else -std::string uint64ToString(uint64_t input, uint8_t base) { - std::string result = ""; -#endif // prevent issues if called with base <= 1 if (base < 2) base = 10; // Check we have a base that we can actually print. // i.e. [0-9A-Z] == 36 if (base > 36) base = 10; + // Reserve some string space to reduce fragmentation. + // 16 bytes should store a uint64 in hex text which is the likely worst case. + // 64 bytes would be the worst case (base 2). + result.reserve(16); + do { char c = input % base; input /= base; @@ -82,133 +82,151 @@ 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. +// Convert a C-style str to a decode_type_t // // Args: -// str: An upper-case C-style string. +// str: A C-style string containing a protocol name or number. // Returns: // A decode_type_t enum. -decode_type_t strToDecodeType(const char *str) { - if (!strcmp(str, "UNKNOWN")) +decode_type_t strToDecodeType(const char * const str) { + if (!strcasecmp(str, "UNKNOWN")) return decode_type_t::UNKNOWN; - else if (!strcmp(str, "UNUSED")) + else if (!strcasecmp(str, "UNUSED")) return decode_type_t::UNUSED; - else if (!strcmp(str, "AIWA_RC_T501")) + else if (!strcasecmp(str, "AIWA_RC_T501")) return decode_type_t::AIWA_RC_T501; - else if (!strcmp(str, "ARGO")) + else if (!strcasecmp(str, "ARGO")) return decode_type_t::ARGO; - else if (!strcmp(str, "CARRIER_AC")) + else if (!strcasecmp(str, "CARRIER_AC")) return decode_type_t::CARRIER_AC; - else if (!strcmp(str, "COOLIX")) + else if (!strcasecmp(str, "COOLIX")) return decode_type_t::COOLIX; - else if (!strcmp(str, "DAIKIN")) + else if (!strcasecmp(str, "DAIKIN")) return decode_type_t::DAIKIN; - else if (!strcmp(str, "DAIKIN2")) + else if (!strcasecmp(str, "DAIKIN160")) + return decode_type_t::DAIKIN160; + else if (!strcasecmp(str, "DAIKIN176")) + return decode_type_t::DAIKIN176; + else if (!strcasecmp(str, "DAIKIN2")) return decode_type_t::DAIKIN2; - else if (!strcmp(str, "DAIKIN216")) + else if (!strcasecmp(str, "DAIKIN216")) return decode_type_t::DAIKIN216; - else if (!strcmp(str, "DENON")) + else if (!strcasecmp(str, "DENON")) return decode_type_t::DENON; - else if (!strcmp(str, "DISH")) + else if (!strcasecmp(str, "DISH")) return decode_type_t::DISH; - else if (!strcmp(str, "ELECTRA_AC")) + else if (!strcasecmp(str, "ELECTRA_AC")) return decode_type_t::ELECTRA_AC; - else if (!strcmp(str, "FUJITSU_AC")) + else if (!strcasecmp(str, "FUJITSU_AC")) return decode_type_t::FUJITSU_AC; - else if (!strcmp(str, "GICABLE")) + else if (!strcasecmp(str, "GICABLE")) return decode_type_t::GICABLE; - else if (!strcmp(str, "GLOBALCACHE")) + else if (!strcasecmp(str, "GLOBALCACHE")) return decode_type_t::GLOBALCACHE; - else if (!strcmp(str, "GREE")) + else if (!strcasecmp(str, "GOODWEATHER")) + return decode_type_t::GOODWEATHER; + else if (!strcasecmp(str, "GREE")) return decode_type_t::GREE; - else if (!strcmp(str, "HAIER_AC")) + else if (!strcasecmp(str, "HAIER_AC")) return decode_type_t::HAIER_AC; - else if (!strcmp(str, "HAIER_AC_YRW02")) + else if (!strcasecmp(str, "HAIER_AC_YRW02")) return decode_type_t::HAIER_AC_YRW02; - else if (!strcmp(str, "HITACHI_AC")) + else if (!strcasecmp(str, "HITACHI_AC")) return decode_type_t::HITACHI_AC; - else if (!strcmp(str, "HITACHI_AC1")) + else if (!strcasecmp(str, "HITACHI_AC1")) return decode_type_t::HITACHI_AC1; - else if (!strcmp(str, "HITACHI_AC2")) + else if (!strcasecmp(str, "HITACHI_AC2")) return decode_type_t::HITACHI_AC2; - else if (!strcmp(str, "JVC")) + else if (!strcasecmp(str, "INAX")) + return decode_type_t::INAX; + else if (!strcasecmp(str, "JVC")) return decode_type_t::JVC; - else if (!strcmp(str, "KELVINATOR")) + else if (!strcasecmp(str, "KELVINATOR")) return decode_type_t::KELVINATOR; - else if (!strcmp(str, "LEGOPF")) + else if (!strcasecmp(str, "LEGOPF")) return decode_type_t::LEGOPF; - else if (!strcmp(str, "LG")) + else if (!strcasecmp(str, "LG")) return decode_type_t::LG; - else if (!strcmp(str, "LG2")) + else if (!strcasecmp(str, "LG2")) return decode_type_t::LG2; - else if (!strcmp(str, "LASERTAG")) + else if (!strcasecmp(str, "LASERTAG")) return decode_type_t::LASERTAG; - else if (!strcmp(str, "LUTRON")) + else if (!strcasecmp(str, "LUTRON")) return decode_type_t::LUTRON; - else if (!strcmp(str, "MAGIQUEST")) + else if (!strcasecmp(str, "MAGIQUEST")) return decode_type_t::MAGIQUEST; - else if (!strcmp(str, "MIDEA")) + else if (!strcasecmp(str, "MIDEA")) return decode_type_t::MIDEA; - else if (!strcmp(str, "MITSUBISHI")) + else if (!strcasecmp(str, "MITSUBISHI")) return decode_type_t::MITSUBISHI; - else if (!strcmp(str, "MITSUBISHI2")) + else if (!strcasecmp(str, "MITSUBISHI2")) return decode_type_t::MITSUBISHI2; - else if (!strcmp(str, "MITSUBISHI_AC")) + else if (!strcasecmp(str, "MITSUBISHI_AC")) return decode_type_t::MITSUBISHI_AC; - else if (!strcmp(str, "MWM")) + else if (!strcasecmp(str, "MITSUBISHI_HEAVY_88")) + return decode_type_t::MITSUBISHI_HEAVY_88; + else if (!strcasecmp(str, "MITSUBISHI_HEAVY_152")) + return decode_type_t::MITSUBISHI_HEAVY_152; + else if (!strcasecmp(str, "MWM")) return decode_type_t::MWM; - else if (!strcmp(str, "NEC") || !strcmp(str, "NEC (NON-STRICT")) + else if (!strcasecmp(str, "NEOCLIMA")) + return decode_type_t::NEOCLIMA; + else if (!strcasecmp(str, "NEC")) return decode_type_t::NEC; - else if (!strcmp(str, "NIKAI")) + else if (!strcasecmp(str, "NEC_LIKE") || + !strcasecmp(str, "NEC (NON-STRICT)")) + return decode_type_t::NEC_LIKE; + else if (!strcasecmp(str, "NIKAI")) return decode_type_t::NIKAI; - else if (!strcmp(str, "PANASONIC")) + else if (!strcasecmp(str, "PANASONIC")) return decode_type_t::PANASONIC; - else if (!strcmp(str, "PANASONIC_AC")) + else if (!strcasecmp(str, "PANASONIC_AC")) return decode_type_t::PANASONIC_AC; - else if (!strcmp(str, "PIONEER")) + else if (!strcasecmp(str, "PIONEER")) return decode_type_t::PIONEER; - else if (!strcmp(str, "PRONTO")) + else if (!strcasecmp(str, "PRONTO")) return decode_type_t::PRONTO; - else if (!strcmp(str, "RAW")) + else if (!strcasecmp(str, "RAW")) return decode_type_t::RAW; - else if (!strcmp(str, "RC5")) + else if (!strcasecmp(str, "RC5")) return decode_type_t::RC5; - else if (!strcmp(str, "RC5X")) + else if (!strcasecmp(str, "RC5X")) return decode_type_t::RC5X; - else if (!strcmp(str, "RC6")) + else if (!strcasecmp(str, "RC6")) return decode_type_t::RC6; - else if (!strcmp(str, "RCMM")) + else if (!strcasecmp(str, "RCMM")) return decode_type_t::RCMM; - else if (!strcmp(str, "SAMSUNG")) + else if (!strcasecmp(str, "SAMSUNG")) return decode_type_t::SAMSUNG; - else if (!strcmp(str, "SAMSUNG36")) + else if (!strcasecmp(str, "SAMSUNG36")) return decode_type_t::SAMSUNG36; - else if (!strcmp(str, "SAMSUNG_AC")) + else if (!strcasecmp(str, "SAMSUNG_AC")) return decode_type_t::SAMSUNG_AC; - else if (!strcmp(str, "SANYO")) + else if (!strcasecmp(str, "SANYO")) return decode_type_t::SANYO; - else if (!strcmp(str, "SANYO_LC7461")) + else if (!strcasecmp(str, "SANYO_LC7461")) return decode_type_t::SANYO_LC7461; - else if (!strcmp(str, "SHARP")) + else if (!strcasecmp(str, "SHARP")) return decode_type_t::SHARP; - else if (!strcmp(str, "SHERWOOD")) + else if (!strcasecmp(str, "SHARP_AC")) + return decode_type_t::SHARP_AC; + else if (!strcasecmp(str, "SHERWOOD")) return decode_type_t::SHERWOOD; - else if (!strcmp(str, "SONY")) + else if (!strcasecmp(str, "SONY")) return decode_type_t::SONY; - else if (!strcmp(str, "TCL112AC")) + else if (!strcasecmp(str, "TCL112AC")) return decode_type_t::TCL112AC; - else if (!strcmp(str, "TECO")) + else if (!strcasecmp(str, "TECO")) return decode_type_t::TECO; - else if (!strcmp(str, "TOSHIBA_AC")) + else if (!strcasecmp(str, "TOSHIBA_AC")) return decode_type_t::TOSHIBA_AC; - else if (!strcmp(str, "TROTEC")) + else if (!strcasecmp(str, "TROTEC")) return decode_type_t::TROTEC; - else if (!strcmp(str, "VESTEL_AC")) + else if (!strcasecmp(str, "VESTEL_AC")) return decode_type_t::VESTEL_AC; - else if (!strcmp(str, "WHIRLPOOL_AC")) + else if (!strcasecmp(str, "WHIRLPOOL_AC")) return decode_type_t::WHIRLPOOL_AC; - else if (!strcmp(str, "WHYNTER")) + else if (!strcasecmp(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( @@ -219,86 +237,14 @@ decode_type_t strToDecodeType(const char *str) { 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. // isRepeat: A flag indicating if it is a repeat message of the protocol. // Returns: // A string containing the protocol name. -#ifdef ARDUINO // Arduino's & C++'s string implementations can't co-exist. String typeToString(const decode_type_t protocol, const bool isRepeat) { String result = ""; -#else -std::string typeToString(const decode_type_t protocol, const bool isRepeat) { - std::string result = ""; -#endif switch (protocol) { case UNUSED: result = F("UNUSED"); @@ -318,6 +264,12 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case DAIKIN: result = F("DAIKIN"); break; + case DAIKIN160: + result = F("DAIKIN160"); + break; + case DAIKIN176: + result = F("DAIKIN176"); + break; case DAIKIN2: result = F("DAIKIN2"); break; @@ -342,6 +294,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; @@ -360,6 +315,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; @@ -405,6 +363,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) { case MWM: result = F("MWM"); break; + case NEOCLIMA: + result = F("NEOCLIMA"); + break; case NEC: result = F("NEC"); break; @@ -459,6 +420,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; @@ -498,7 +462,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 DAIKIN160: + case DAIKIN176: case DAIKIN2: case DAIKIN216: case ELECTRA_AC: @@ -514,10 +481,13 @@ bool hasACState(const decode_type_t protocol) { case MITSUBISHI_HEAVY_88: case MITSUBISHI_HEAVY_152: case MWM: + case NEOCLIMA: case PANASONIC_AC: case SAMSUNG_AC: + case SHARP_AC: case TCL112AC: case TOSHIBA_AC: + case TROTEC: case WHIRLPOOL_AC: return true; default: @@ -531,7 +501,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; @@ -543,13 +513,10 @@ uint16_t getCorrectedRawLength(const decode_results *results) { // Return a string containing the key values of a decode_results structure // in a C/C++ code style format. -#ifdef ARDUINO -String resultToSourceCode(const decode_results *results) { +String resultToSourceCode(const decode_results * const results) { String output = ""; -#else -std::string resultToSourceCode(const decode_results *results) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(1536); // 1.5KB should cover most cases. // Start declaration output += F("uint16_t "); // variable type output += F("rawData["); // array name @@ -625,15 +592,12 @@ std::string resultToSourceCode(const decode_results *results) { // Dump out the decode_results structure. // -#ifdef ARDUINO -String resultToTimingInfo(const decode_results *results) { +String resultToTimingInfo(const decode_results * const results) { String output = ""; String value = ""; -#else -std::string resultToTimingInfo(const decode_results *results) { - std::string output = ""; - std::string value = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2048); // 2KB should cover most cases. + value.reserve(6); // Max value should be 2^17 = 131072 output += F("Raw Timing["); output += uint64ToString(results->rawlen - 1, 10); output += F("]:\n"); @@ -657,13 +621,10 @@ 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 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++) { @@ -679,13 +640,10 @@ std::string resultToHexidecimal(const decode_results *result) { // Dump out the decode_results structure. // -#ifdef ARDUINO -String resultToHumanReadableBasic(const decode_results *results) { +String resultToHumanReadableBasic(const decode_results * const results) { String output = ""; -#else -std::string resultToHumanReadableBasic(const decode_results *results) { - std::string output = ""; -#endif + // Reserve some space for the string to reduce heap fragmentation. + output.reserve(2 * kStateSizeMax + 50); // Should cover most cases. // Show Encoding standard output += F("Encoding : "); output += typeToString(results->decode_type, results->repeat); @@ -700,16 +658,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; } @@ -722,8 +707,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); @@ -766,3 +751,170 @@ 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; } + +namespace irutils { + String addLabeledString(const String value, const String label, + const bool precomma) { + String result = ""; + if (precomma) result += F(", "); + result += label; + result += F(": "); + return result + value; + } + + String addBoolToString(const bool value, const String label, + const bool precomma) { + return addLabeledString((value ? F("On") : F("Off")), label, precomma); + } + + String addIntToString(const uint16_t value, const String label, + const bool precomma) { + return addLabeledString(uint64ToString(value), label, precomma); + } + + String addTempToString(const uint16_t degrees, const bool celsius, + const bool precomma) { + String result = addIntToString(degrees, F("Temp"), precomma); + result += celsius ? 'C' : 'F'; + return result; + } + + String addModeToString(const uint8_t mode, const uint8_t automatic, + const uint8_t cool, const uint8_t heat, + const uint8_t dry, const uint8_t fan) { + String result = addIntToString(mode, F("Mode")); + result += F(" ("); + if (mode == automatic) result += F("AUTO"); + else if (mode == cool) result += F("COOL"); + else if (mode == heat) result += F("HEAT"); + else if (mode == dry) result += F("DRY"); + else if (mode == fan) result += F("FAN"); + else + result += F("UNKNOWN"); + return result + ')'; + } + + String addFanToString(const uint8_t speed, const uint8_t high, + const uint8_t low, const uint8_t automatic, + const uint8_t quiet, const uint8_t medium) { + String result = addIntToString(speed, F("Fan")); + result += F(" ("); + if (speed == high) result += F("High"); + else if (speed == low) result += F("Low"); + else if (speed == automatic) result += F("Auto"); + else if (speed == quiet) result += F("Quiet"); + else if (speed == medium) result += F("Medium"); + else + result += F("UNKNOWN"); + return result + ')'; + } + + // Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS. + // Args: + // unescaped: A string containing text to make HTML safe. + // Returns: + // A string that is HTML safe. + String htmlEscape(const String unescaped) { + String result = ""; + uint16_t ulen = unescaped.length(); + result.reserve(ulen); // The result will be at least the size of input. + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + switch (c) { + // ';!-"<>=&#{}() are all unsafe. + case '\'': + result += F("'"); + break; + case ';': + result += F(";"); + break; + case '!': + result += F("!"); + break; + case '-': + result += F("‐"); + break; + case '\"': + result += F("""); + break; + case '<': + result += F("<"); + break; + case '>': + result += F(">"); + break; + case '=': + result += F("&#equals;"); + break; + case '&': + result += F("&"); + break; + case '#': + result += F("#"); + break; + case '{': + result += F("{"); + break; + case '}': + result += F("}"); + break; + case '(': + result += F("("); + break; + case ')': + result += F(")"); + break; + default: + result += c; + } + } + return result; + } + + String msToString(uint32_t const msecs) { + uint32_t totalseconds = msecs / 1000; + if (totalseconds == 0) return F("Now"); + + // Note: uint32_t can only hold up to 45 days, so uint8_t is safe. + uint8_t days = totalseconds / (60 * 60 * 24); + uint8_t hours = (totalseconds / (60 * 60)) % 24; + uint8_t minutes = (totalseconds / 60) % 60; + uint8_t seconds = totalseconds % 60; + + String result = ""; + if (days) { + result += uint64ToString(days) + F(" day"); + if (days > 1) result += 's'; + } + if (hours) { + if (result.length()) result += ' '; + result += uint64ToString(hours) + F(" hour"); + if (hours > 1) result += 's'; + } + if (minutes) { + if (result.length()) result += ' '; + result += uint64ToString(minutes) + F(" minute"); + if (minutes > 1) result += 's'; + } + if (seconds) { + if (result.length()) result += ' '; + result += uint64ToString(seconds) + F(" second"); + if (seconds > 1) result += 's'; + } + return result; + } + + String minsToString(const uint16_t mins) { + String result = ""; + result.reserve(5); // 23:59 is the typical worst case. + if (mins / 60 < 10) result += '0'; // Zero pad the hours + result += uint64ToString(mins / 60) + ':'; + if (mins % 60 < 10) result += '0'; // Zero pad the minutes. + result += uint64ToString(mins % 60); + return result; + } +} // namespace irutils diff --git a/lib/IRremoteESP8266-2.6.3.10/src/IRutils.h b/lib/IRremoteESP8266-2.6.3.10/src/IRutils.h new file mode 100644 index 000000000..bb7a1266f --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/IRutils.h @@ -0,0 +1,60 @@ +#ifndef IRUTILS_H_ +#define IRUTILS_H_ + +// Copyright 2017 David Conran + +#ifndef UNIT_TEST +#include +#endif +#define __STDC_LIMIT_MACROS +#include +#ifndef ARDUINO +#include +#endif +#include "IRremoteESP8266.h" +#include "IRrecv.h" + +uint64_t reverseBits(uint64_t input, uint16_t nbits); +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 * const results); +String resultToTimingInfo(const decode_results * const results); +String resultToHumanReadableBasic(const decode_results * const results); +String resultToHexidecimal(const decode_results * const result); +bool hasACState(const decode_type_t protocol); +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); +namespace irutils { + String addBoolToString(const bool value, const String label, + const bool precomma = true); + String addIntToString(const uint16_t value, const String label, + const bool precomma = true); + String addLabeledString(const String value, const String label, + const bool precomma = true); + String addTempToString(const uint16_t degrees, const bool celsius = true, + const bool precomma = true); + String addModeToString(const uint8_t mode, const uint8_t automatic, + const uint8_t cool, const uint8_t heat, + const uint8_t dry, const uint8_t fan); + String addFanToString(const uint8_t speed, const uint8_t high, + const uint8_t low, const uint8_t automatic, + const uint8_t quiet, const uint8_t medium); + String htmlEscape(const String unescaped); + String msToString(uint32_t const msecs); + String minsToString(const uint16_t mins); +} // namespace irutils +#endif // IRUTILS_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Aiwa.cpp similarity index 98% rename from lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Aiwa.cpp index 617711a99..70dbc85b2 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Aiwa.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Aiwa.cpp @@ -12,6 +12,8 @@ // Based off the RC-T501 RCU // Added by David Conran. (Inspired by IRremoteESP8266's implementation: // https://github.com/z3t0/Arduino-IRremote) +// Supports: +// Brand: Aiwa, Model: RC-T501 RCU const uint16_t kAiwaRcT501PreBits = 26; const uint16_t kAiwaRcT501PostBits = 1; diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.cpp new file mode 100644 index 000000000..7a2ecec19 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.cpp @@ -0,0 +1,438 @@ +/* +Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote +Controls Argo Ulisse 13 DCI A/C +Copyright 2017 Schmolders +Copyright 2019 crankyoldgit +*/ + +#include "ir_Argo.h" +#include +#ifndef UNIT_TEST +#include +#endif // UNIT_TEST +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants +// using SPACE modulation. MARK is always const 400u +const uint16_t kArgoHdrMark = 6400; +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. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_ARGO +// Send an Argo A/C message. +// +// Args: +// data: An array of kArgoStateLength bytes containing the IR command. +// +// Status: ALPHA / Untested. + +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 + sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. + data, nbytes, 38, false, repeat, kDutyDefault); +} +#endif // SEND_ARGO + +IRArgoAC::IRArgoAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRArgoAC::begin(void) { _irsend.begin(); } + +#if SEND_ARGO +void IRArgoAC::send(const uint16_t repeat) { + this->checksum(); // Create valid checksum before sending + _irsend.sendArgo(argo, kArgoStateLength, repeat); +} +#endif // SEND_ARGO + +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) + 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); +} + +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; + argo[10] += sum << 2; // Shift up 2 bits and append to byte 10 + argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11 +} + +void IRArgoAC::stateReset(void) { + for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0; + + // Argo Message. Store MSB left. + // Default message: + argo[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble + argo[1] = 0b11110101; // LSB first: 0b10101111; //const preamble + // Keep payload 2-9 at zero + argo[10] = 0b00000010; // Const 01, checksum 6bit + argo[11] = 0b00000000; // Checksum 2bit + + this->off(); + this->setTemp(20); + this->setRoomTemp(25); + this->setMode(kArgoAuto); + this->setFan(kArgoFanAuto); +} + +uint8_t* IRArgoAC::getRaw(void) { + this->checksum(); // Ensure correct bit array before returning + return argo; +} + +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]|= kArgoPowerBit; // Set ON/OFF bit to 1 +} + +void IRArgoAC::off(void) { + // in MSB first + // bit 5 of byte 9 to off + argo[9] &= ~kArgoPowerBit; // Set on/off bit to 0 +} + +void IRArgoAC::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRArgoAC::getPower(void) { return argo[9] & kArgoPowerBit; } + +void IRArgoAC::setMax(const bool on) { + if (on) + argo[9] |= kArgoMaxBit; + else + argo[9] &= ~kArgoMaxBit; +} + +bool IRArgoAC::getMax(void) { return argo[9] & kArgoMaxBit; } + +// Set the temp in deg C +// Sending 0 equals +4 +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 -= 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] &= ~kArgoTempLowMask; + argo[3] &= ~kArgoTempHighMask; + + // 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(void) { + return (((argo[3] & kArgoTempHighMask) << 2 ) | (argo[2] >> 6)) + + kArgoTempOffset; +} + +// Set the speed of the fan +void IRArgoAC::setFan(const uint8_t fan) { + // Mask out bits + argo[3] &= ~kArgoFanMask; + // Set fan mode at bit positions + argo[3] |= (std::min(fan, kArgoFan3) << 3); +} + +uint8_t IRArgoAC::getFan(void) { return (argo[3] & kArgoFanMask) >> 3; } + +void IRArgoAC::setFlap(const uint8_t flap) { + flap_mode = flap; + // TODO(kaschmo): set correct bits for flap mode +} + +uint8_t IRArgoAC::getFlap(void) { return flap_mode; } + +uint8_t IRArgoAC::getMode(void) { + return (argo[2] & kArgoModeMask) >> 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); + } +} + +void IRArgoAC::setNight(const bool on) { + if (on) + // Set bit at night position: bit 2 + argo[9] |= kArgoNightBit; + else + argo[9] &= ~kArgoNightBit; +} + +bool IRArgoAC::getNight(void) { return argo[9] & kArgoNightBit; } + +void IRArgoAC::setiFeel(const bool on) { + if (on) + // Set bit at iFeel position: bit 7 + argo[9] |= kArgoIFeelBit; + else + argo[9] &= ~kArgoIFeelBit; +} + +bool IRArgoAC::getiFeel(void) { return argo[9] & kArgoIFeelBit; } + +void IRArgoAC::setTime(void) { + // TODO(kaschmo): use function call from checksum to set time first +} + +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] &= ~kArgoRoomTempLowMask; + argo[4] &= ~kArgoRoomTempHighMask; + + argo[3] += temp << 5; // Append to bit 5,6,7 + argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 +} + +uint8_t IRArgoAC::getRoomTemp(void) { + return ((argo[4] & kArgoRoomTempHighMask) << 3 | (argo[3] >> 5)) + + kArgoTempOffset; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRArgoAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kArgoCool; + case stdAc::opmode_t::kHeat: + return kArgoHeat; + case stdAc::opmode_t::kDry: + return kArgoDry; + case stdAc::opmode_t::kOff: + return kArgoOff; + // No fan mode. + default: + return kArgoAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kArgoFan1; + case stdAc::fanspeed_t::kMedium: + return kArgoFan2; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kArgoFan3; + default: + return kArgoFanAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRArgoAC::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: + return kArgoFlapFull; + case stdAc::swingv_t::kHigh: + return kArgoFlap5; + case stdAc::swingv_t::kMiddle: + return kArgoFlap4; + case stdAc::swingv_t::kLow: + return kArgoFlap3; + case stdAc::swingv_t::kLowest: + return kArgoFlap1; + default: + return kArgoFlapAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRArgoAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kArgoCool: return stdAc::opmode_t::kCool; + case kArgoHeat: return stdAc::opmode_t::kHeat; + case kArgoDry: return stdAc::opmode_t::kDry; + // No fan mode. + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRArgoAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kArgoFan3: return stdAc::fanspeed_t::kMax; + case kArgoFan2: return stdAc::fanspeed_t::kMedium; + case kArgoFan1: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRArgoAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::ARGO; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.turbo = this->getMax(); + result.sleep = this->getNight() ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRArgoAC::toString() { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addIntToString(getMode(), F("Mode")); + 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 += addIntToString(getFan(), F("Fan")); + 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 += addTempToString(getTemp()); + result += F(", Room "); + result += addTempToString(getRoomTemp(), true, false); + result += addBoolToString(getMax(), F("Max")); + result += addBoolToString(getiFeel(), F("iFeel")); + result += addBoolToString(getNight(), F("Night")); + 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 (strict && nbits != kArgoBits) return false; + + uint16_t offset = kStartOffset; + + // Match Header + Data + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kArgoHdrMark, kArgoHdrSpace, + kArgoBitMark, kArgoOneSpace, + kArgoBitMark, kArgoZeroSpace, + 0, 0, // Footer (None, allegedly. This seems very wrong.) + true, kTolerance, 0, false)) return false; + + // Compliance + // Verify we got a valid checksum. + if (strict && !IRArgoAC::validChecksum(results->state)) return false; + // Success + results->decode_type = decode_type_t::ARGO; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_ARGO diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.h similarity index 52% rename from lib/IRremoteESP8266-2.6.0/src/ir_Argo.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.h index 883c2ddfd..49c8c42e5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Argo.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Argo.h @@ -1,9 +1,15 @@ -/* Copyright 2017 Schmolders +// Copyright 2017 Schmolders // Adds support for Argo Ulisse 13 DCI Mobile Split ACs. -*/ + +// Supports: +// Brand: Argo, Model: Ulisse 13 DCI Mobile Split A/C + #ifndef IR_ARGO_H_ #define IR_ARGO_H_ +#ifndef UNIT_TEST +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" #ifdef UNIT_TEST @@ -33,19 +39,42 @@ // Constants. Store MSB left. -const uint8_t kArgoCoolOn = 0; // 0b000 -const uint8_t kArgoCoolOff = 3; // 0b110 -const uint8_t kArgoCoolAuto = 2; // 0b010 -const uint8_t kArgoCoolHum = 1; // 0b100 -const uint8_t kArgoHeatOn = 0; // 0b001 -const uint8_t kArgoHeatAuto = 1; // 0b101 -const uint8_t kArgoHeatBlink = 2; // 0b011 // ??no idea what mode that is -const uint8_t kArgoMinTemp = 10; // Celsius offset +4 -const uint8_t kArgoMaxTemp = 32; // Celsius +// byte[2] +const uint8_t kArgoHeatBit = 0b00100000; +const uint8_t kArgoModeMask = 0b00111000; +const uint8_t kArgoTempLowMask = 0b11000000; +const uint8_t kArgoCool = 0b000; // 0b000 (LSB) 0 +const uint8_t kArgoDry = 0b001; // 0b100 (LSB) 1 +const uint8_t kArgoAuto = 0b010; // 0b010 (LSB) 2 +const uint8_t kArgoOff = 0b011; // 0b110 (LSB) 3 +const uint8_t kArgoHeat = 0b100; // 0b001 (LSB) 4 +const uint8_t kArgoHeatAuto = 0b101; // 0b101 (LSB) 5 +// ?no idea what mode that is +const uint8_t kArgoHeatBlink = 0b110; // 0b011 (LSB) 6 + +// byte[3] +const uint8_t kArgoTempHighMask = 0b00000111; +const uint8_t kArgoFanMask = 0b00011000; +const uint8_t kArgoRoomTempLowMask = 0b11100000; const uint8_t kArgoFanAuto = 0; // 0b00 const uint8_t kArgoFan3 = 3; // 0b11 const uint8_t kArgoFan2 = 2; // 0b01 const uint8_t kArgoFan1 = 1; // 0b10 + +// byte[4] +const uint8_t kArgoRoomTempHighMask = 0b00000011; +const uint8_t kArgoTempOffset = 4; +const uint8_t kArgoMaxRoomTemp = ((1 << 5) - 1) + kArgoTempOffset; // 35C + +// byte[9] +const uint8_t kArgoPowerBit = 0b00100000; +const uint8_t kArgoMaxBit = 0b00001000; +const uint8_t kArgoNightBit = 0b00000100; +const uint8_t kArgoIFeelBit = 0b10000000; + +const uint8_t kArgoMinTemp = 10; // Celsius offset +4 +const uint8_t kArgoMaxTemp = 32; // Celsius + const uint8_t kArgoFlapAuto = 0; // 0b000 const uint8_t kArgoFlap1 = 1; // 0b100 const uint8_t kArgoFlap2 = 2; // 0b010 @@ -81,49 +110,58 @@ const uint8_t kArgoFlapFull = 7; // 0b111 class IRArgoAC { public: - explicit IRArgoAC(uint16_t pin); + explicit IRArgoAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_ARGO void send(const uint16_t repeat = kArgoDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_ARGO - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); - void setPower(bool state); - uint8_t getPower(); + void setPower(const bool on); + bool getPower(void); - void setTemp(uint8_t temp); - uint8_t getTemp(); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); - void setFan(uint8_t fan); - uint8_t getFan(); + void setFan(const uint8_t fan); + uint8_t getFan(void); - void setFlap(uint8_t flap); - uint8_t getFlap(); + void setFlap(const uint8_t flap); + uint8_t getFlap(void); - void setCoolMode(uint8_t mode); - uint8_t getCoolMode(); + void setMode(const uint8_t mode); + uint8_t getMode(void); - void setHeatMode(uint8_t mode); - uint8_t getHeatMode(); - uint8_t getMode(); + void setMax(const bool on); + bool getMax(void); - void setMax(bool state); - bool getMax(); + void setNight(const bool on); + bool getNight(void); - void setNight(bool state); - bool getNight(); + void setiFeel(const bool on); + bool getiFeel(void); - void setiFeel(bool state); - bool getiFeel(); + void setTime(void); + void setRoomTemp(const uint8_t degrees); + uint8_t getRoomTemp(void); - void setTime(); - void setRoomTemp(uint8_t temp); - - uint8_t* getRaw(); - uint8_t convertFan(const stdAc::fanspeed_t speed); - uint8_t convertSwingV(const stdAc::swingv_t position); + uint8_t* getRaw(void); + void setRaw(const uint8_t state[]); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kArgoStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kArgoStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); #ifndef UNIT_TEST private: @@ -133,20 +171,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-2.6.0/src/ir_Carrier.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Carrier.cpp similarity index 67% rename from lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Carrier.cpp index 350d61cc1..e31ddd55f 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Carrier.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Carrier.cpp @@ -1,23 +1,20 @@ // Copyright 2018 David Conran +// Supports: +// Brand: Carrier/Surrey, Model: 42QG5A55970 remote +// Brand: Carrier/Surrey, Model: 619EGX0090E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0120E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0180E0 A/C +// Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C +// Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter + #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// CCCCC AAA RRRRRR RRRRRR IIIII EEEEEEE RRRRRR -// CC C AAAAA RR RR RR RR III EE RR RR -// CC AA AA RRRRRR RRRRRR III EEEEE RRRRRR -// CC C AAAAAAA RR RR RR RR III EE RR RR -// CCCCC AA AA RR RR RR RR IIIII EEEEEEE RR RR - -// Suits Carrier/Surrey HVAC models: -// 42QG5A55970 (remote) -// 619EGX0090E0 / 619EGX0120E0 / 619EGX0180E0 / 619EGX0220E0 (indoor units) -// 53NGK009/012 (inverter) - // Constants // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/385 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/385 const uint16_t kCarrierAcHdrMark = 8532; const uint16_t kCarrierAcHdrSpace = 4228; const uint16_t kCarrierAcBitMark = 628; @@ -77,22 +74,16 @@ bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t nbits, for (uint8_t i = 0; i < 3; i++) { prev_data = data; - // Header - if (!matchMark(results->rawbuf[offset++], kCarrierAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kCarrierAcHdrSpace)) - return false; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kCarrierAcBitMark, - kCarrierAcOneSpace, kCarrierAcBitMark, kCarrierAcZeroSpace); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kCarrierAcBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kCarrierAcGap)) - return false; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kCarrierAcHdrMark, kCarrierAcHdrSpace, + kCarrierAcBitMark, kCarrierAcOneSpace, + kCarrierAcBitMark, kCarrierAcZeroSpace, + kCarrierAcBitMark, kCarrierAcGap, true); + if (!used) return false; + offset += used; // Compliance. if (strict) { // Check if the data is an inverted copy of the previous data. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Coolix.cpp similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Coolix.cpp index 2659a1d88..2b845733c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Coolix.cpp @@ -1,5 +1,5 @@ // Copyright bakrus -// Copyright 2017 David Conran +// Copyright 2017,2019 David Conran #include "ir_Coolix.h" #include @@ -10,18 +10,16 @@ #include "IRsend.h" #include "IRutils.h" -// CCCCC OOOOO OOOOO LL IIIII XX XX -// CC C OO OO OO OO LL III XX XX -// CC OO OO OO OO LL III XXXX -// CC C OO OO OO OO LL III XX XX -// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX - // Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit // // Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. +// Brand: Beko, Model: RG57K7(B)/BGEF Remote +// Brand: Beko, Model: BINR 070/071 split-type A/C +// Brand: Midea, Model: RG52D/BGE Remote +// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C +// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016) // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/484 // Constants // Pulse parms are *50-100 for the Mark and *50+100 for the space @@ -41,6 +39,12 @@ const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks; const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_COOLIX // Send a Coolix message // @@ -90,8 +94,10 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { // Supports: // RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 -IRCoolixAC::IRCoolixAC(uint16_t pin) : _irsend(pin) { stateReset(); } +// https://github.com/crankyoldgit/IRremoteESP8266/issues/484 +IRCoolixAC::IRCoolixAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } void IRCoolixAC::stateReset() { setRaw(kCoolixDefaultState); } @@ -200,6 +206,14 @@ void IRCoolixAC::setPower(const bool power) { } } +void IRCoolixAC::on(void) { + this->setPower(true); +} + +void IRCoolixAC::off(void) { + this->setPower(false); +} + bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; } void IRCoolixAC::setSwing() { @@ -260,18 +274,30 @@ void IRCoolixAC::clearSensorTemp() { void IRCoolixAC::setMode(const uint8_t mode) { uint32_t actualmode = mode; + switch (actualmode) { + case kCoolixAuto: + case kCoolixDry: + if (this->getFan() == kCoolixFanAuto) + // No kCoolixFanAuto in Dry/Auto mode. + this->setFan(kCoolixFanAuto0, false); + break; + case kCoolixCool: + case kCoolixHeat: + case kCoolixFan: + if (this->getFan() == kCoolixFanAuto0) + // kCoolixFanAuto0 only in Dry/Auto mode. + this->setFan(kCoolixFanAuto, false); + break; + default: // Anything else, go with Auto mode. + this->setMode(kCoolixAuto); + return; + } // Fan mode is a special case of Dry. if (mode == kCoolixFan) actualmode = kCoolixDry; - switch (actualmode) { - case kCoolixCool: - case kCoolixAuto: - case kCoolixHeat: - case kCoolixDry: - recoverSavedState(); - remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); - // Force the temp into a known-good state. - setTemp(getTemp()); - } + recoverSavedState(); + remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); + // Force the temp into a known-good state. + setTemp(getTemp()); if (mode == kCoolixFan) setTempRaw(kCoolixFanTempCode); } @@ -286,15 +312,33 @@ uint8_t IRCoolixAC::getFan() { return (getNormalState() & kCoolixFanMask) >> 13; } -void IRCoolixAC::setFan(const uint8_t speed) { +void IRCoolixAC::setFan(const uint8_t speed, const bool modecheck) { recoverSavedState(); uint8_t newspeed = speed; switch (speed) { + case kCoolixFanAuto: // Dry & Auto mode can't have this speed. + if (modecheck) { + switch (this->getMode()) { + case kCoolixAuto: + case kCoolixDry: + newspeed = kCoolixFanAuto0; + } + } + break; + case kCoolixFanAuto0: // Only Dry & Auto mode can have this speed. + if (modecheck) { + switch (this->getMode()) { + case kCoolixAuto: + case kCoolixDry: + break; + default: + newspeed = kCoolixFanAuto; + } + } + break; case kCoolixFanMin: case kCoolixFanMed: case kCoolixFanMax: - case kCoolixFanAuto: - case kCoolixFanAuto0: case kCoolixFanZoneFollow: case kCoolixFanFixed: break; @@ -337,21 +381,88 @@ 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. Utilise the previous +// state if supplied. +stdAc::state_t IRCoolixAC::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result; + // Start with the previous state if given it. + if (prev != NULL) { + result = *prev; + } else { + // Set defaults for non-zero values that are not implicitly set for when + // there is no previous state. + result.swingv = stdAc::swingv_t::kOff; + result.sleep = -1; + } + // Not supported. + result.model = -1; // No models used. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.clock = -1; + + // Supported. + result.protocol = decode_type_t::COOLIX; + result.celsius = true; + result.power = this->getPower(); + // Power off state no other state info. Use the previous state if we have it. + if (!result.power) return result; + // Handle the special single command (Swing/Turbo/Light/Clean/Sleep) toggle + // messages. These have no other state info so use the rest of the previous + // state if we have it for them. + if (this->getSwing()) { + result.swingv = result.swingv != stdAc::swingv_t::kOff ? + stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto; // Invert swing. + return result; + } else if (this->getTurbo()) { + result.turbo = !result.turbo; + return result; + } else if (this->getLed()) { + result.light = !result.light; + return result; + } else if (this->getClean()) { + result.clean = !result.clean; + return result; + } else if (this->getSleep()) { + result.sleep = result.sleep >= 0 ? -1 : 0; // Invert sleep. + return result; + } + // Back to "normal" stateful messages. + result.mode = this->toCommonMode(this->getMode()); + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + return result; +} + // Convert the internal state into a human readable string. -#ifdef ARDUINO String IRCoolixAC::toString() { String result = ""; -#else -std::string IRCoolixAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) { - result += F("On"); - } else { - result += F("Off"); - return result; // If it's off, there is no other info. - } + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + if (!getPower()) return result; // If it's off, there is no other info. // Special modes. if (getSwing()) { result += F(", Swing: Toggle"); @@ -373,29 +484,10 @@ std::string IRCoolixAC::toString() { result += F(", Clean: Toggle"); return result; } - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kCoolixAuto: - result += F(" (AUTO)"); - break; - case kCoolixCool: - result += F(" (COOL)"); - break; - case kCoolixHeat: - result += F(" (HEAT)"); - break; - case kCoolixDry: - result += F(" (DRY)"); - break; - case kCoolixFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Fan: "); - result += uint64ToString(getFan()); + result += addModeToString(getMode(), kCoolixAuto, + kCoolixCool, kCoolixHeat, + kCoolixDry, kCoolixFan); + result += addIntToString(getFan(), F("Fan")); switch (getFan()) { case kCoolixFanAuto: result += F(" (AUTO)"); @@ -421,16 +513,9 @@ std::string IRCoolixAC::toString() { default: result += F(" (UNKNOWN)"); } - if (getMode() != kCoolixFan) { // Fan mode doesn't have a temperature. - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += 'C'; - } - result += F(", Zone Follow: "); - if (getZoneFollow()) - result += F("On"); - else - result += F("Off"); + // Fan mode doesn't have a temperature. + if (getMode() != kCoolixFan) result += addTempToString(getTemp()); + result += addBoolToString(getZoneFollow(), F("Zone Follow")); result += F(", Sensor Temp: "); if (getSensorTemp() > kCoolixSensorTempMax) result += F("Ignored"); diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Coolix.h similarity index 84% rename from lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Coolix.h index d85db98d7..0c2e44676 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Coolix.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Coolix.h @@ -9,8 +9,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,16 +16,14 @@ #include "IRsend_test.h" #endif -// CCCCC OOOOO OOOOO LL IIIII XX XX -// CC C OO OO OO OO LL III XX XX -// CC OO OO OO OO LL III XXXX -// CC C OO OO OO OO LL III XX XX -// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX - // Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. +// Brand: Beko, Model: RG57K7(B)/BGEF Remote +// Brand: Beko, Model: BINR 070/071 split-type A/C +// Brand: Midea, Model: RG52D/BGE Remote +// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C +// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016) // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/484 // Kudos: // Hamper: For the breakdown and mapping of the bit values. @@ -90,11 +86,13 @@ const uint32_t kCoolixDefaultState = 0b101100101011111111001000; // 0xB2BFC8 // Classes class IRCoolixAC { public: - explicit IRCoolixAC(uint16_t pin); + explicit IRCoolixAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); void stateReset(); #if SEND_COOLIX void send(const uint16_t repeat = kCoolixDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_COOLIX void begin(); void on(); @@ -106,7 +104,7 @@ class IRCoolixAC { void setSensorTemp(const uint8_t desired); uint8_t getSensorTemp(); void clearSensorTemp(); - void setFan(const uint8_t fan); + void setFan(const uint8_t speed, const bool modecheck = true); uint8_t getFan(); void setMode(const uint8_t mode); uint8_t getMode(); @@ -125,11 +123,10 @@ 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); -#ifdef ARDUINO + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(const stdAc::state_t *prev = NULL); String toString(); -#else - std::string toString(); -#endif #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Daikin.cpp similarity index 51% rename from lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Daikin.cpp index 358dbd603..8bd9ea3fe 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Daikin.cpp @@ -6,6 +6,7 @@ http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ Copyright 2016 sillyfrog Copyright 2017 sillyfrog, crankyoldgit Copyright 2018-2019 crankyoldgit +Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) */ #include "ir_Daikin.h" @@ -21,17 +22,19 @@ Copyright 2018-2019 crankyoldgit #endif #include "IRutils.h" -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - // Constants // Ref: // https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote // http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol -// https://github.com/markszabo/IRremoteESP8266/issues/582 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582 + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::addFanToString; +using irutils::minsToString; #if SEND_DAIKIN // Send a Daikin A/C message. @@ -86,7 +89,9 @@ void IRsend::sendDaikin(const unsigned char data[], const uint16_t nbytes, } #endif // SEND_DAIKIN -IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } +IRDaikinESP::IRDaikinESP(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } void IRDaikinESP::begin(void) { _irsend.begin(); } @@ -312,17 +317,6 @@ bool IRDaikinESP::getEcono(void) { return remote[kDaikinByteEcono] & kDaikinBitEcono; } -void IRDaikinESP::setEye(const bool on) { - if (on) - remote[kDaikinByteEye] |= kDaikinBitEye; - else - remote[kDaikinByteEye] &= ~kDaikinBitEye; -} - -bool IRDaikinESP::getEye(void) { - return remote[kDaikinByteEye] & kDaikinBitEye; -} - void IRDaikinESP::setMold(const bool on) { if (on) remote[kDaikinByteMold] |= kDaikinBitMold; @@ -394,108 +388,38 @@ 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 4 bits - remote[kDaikinByteClockMinsHigh] &= 0xF0; - remote[kDaikinByteClockMinsHigh] |= ((mins >> 8) & 0x0F); + // only keep 3 bits + remote[kDaikinByteClockMinsHigh] &= 0xF8; + remote[kDaikinByteClockMinsHigh] |= ((mins >> 8) & 0x07); } uint16_t IRDaikinESP::getCurrentTime(void) { - return ((remote[kDaikinByteClockMinsHigh] & 0x0F) << 8) + + return ((remote[kDaikinByteClockMinsHigh] & 0x07) << 8) + remote[kDaikinByteClockMinsLow]; } -#ifdef ARDUINO -String IRDaikinESP::renderTime(const uint16_t timemins) { - String ret; -#else // ARDUINO -std::string IRDaikinESP::renderTime(const uint16_t timemins) { - std::string ret; -#endif // ARDUINO - ret = uint64ToString(timemins / 60) + ':'; - uint8_t mins = timemins % 60; - if (mins < 10) ret += '0'; - ret += uint64ToString(mins); - return ret; +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; } -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikinESP::toString(void) { - String result = ""; -#else // ARDUINO -std::string IRDaikinESP::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 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(", Powerful: "); - result += this->getPowerful() ? F("On") : F("Off"); - result += F(", Quiet: "); - result += this->getQuiet() ? F("On") : F("Off"); - result += F(", Sensor: "); - result += this->getSensor() ? F("On") : F("Off"); - result += F(", Eye: "); - result += this->getEye() ? F("On") : F("Off"); - result += F(", Mold: "); - result += this->getMold() ? F("On") : F("Off"); - result += F(", Comfort: "); - result += this->getComfort() ? F("On") : F("Off"); - result += F(", Swing (Horizontal): "); - result += this->getSwingHorizontal() ? F("On") : F("Off"); - result += F(", Swing (Vertical): "); - result += this->getSwingVertical() ? F("On") : F("Off"); - result += F(", Current Time: "); - result += this->renderTime(this->getCurrentTime()); - result += F(", On Time: "); - if (this->getOnTimerEnabled()) - result += this->renderTime(this->getOnTime()); +uint8_t IRDaikinESP::getCurrentDay(void) { + return ((remote[kDaikinByteClockMinsHigh] & 0x38) >> 3); +} + +void IRDaikinESP::setWeeklyTimerEnable(const bool on) { + if (on) + remote[kDaikinByteWeeklyTimer] &= ~kDaikinBitWeeklyTimer; // Clear the bit. else - result += F("Off"); - result += F(", Off Time: "); - if (this->getOffTimerEnabled()) - result += this->renderTime(this->getOffTime()); - else - result += F("Off"); - return result; + remote[kDaikinByteWeeklyTimer] |= kDaikinBitWeeklyTimer; // Set the bit. +} + +bool IRDaikinESP::getWeeklyTimerEnable(void) { + return !(remote[kDaikinByteWeeklyTimer] & kDaikinBitWeeklyTimer); } // Convert a standard A/C mode into its native mode. @@ -532,6 +456,104 @@ uint8_t IRDaikinESP::convertFan(const stdAc::fanspeed_t speed) { } } +// 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; +} + +// Convert the internal state into a human readable string. +String IRDaikinESP::toString(void) { + String result = ""; + result.reserve(230); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(getPowerful(), F("Powerful")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getSensor(), F("Sensor")); + result += addBoolToString(getMold(), F("Mold")); + result += addBoolToString(getComfort(), F("Comfort")); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); + result += addLabeledString(minsToString(this->getCurrentTime()), + F("Current Time")); + 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 += addLabeledString(getOnTimerEnabled() + ? minsToString(this->getOnTime()) : F("Off"), + F("On Time")); + result += addLabeledString(getOffTimerEnabled() + ? minsToString(this->getOffTime()) : F("Off"), + F("Off Time")); + result += addBoolToString(getWeeklyTimerEnable(), F("Weekly Timer")); + return result; +} + #if DECODE_DAIKIN // Decode the supplied Daikin A/C message. // Args: @@ -557,8 +579,6 @@ bool IRrecv::decodeDaikin(decode_results *results, const uint16_t nbits, uint16_t offset = kStartOffset; match_result_t data_result; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; // Header #1 - Doesn't count as data. data_result = matchData(&(results->rawbuf[offset]), kDaikinHeaderLength, @@ -568,56 +588,41 @@ bool IRrecv::decodeDaikin(decode_results *results, const uint16_t nbits, offset += data_result.used; if (data_result.success == false) return false; // Fail if (data_result.data) return false; // The header bits should be zero. - - // Read the Data sections. - // Keep reading bytes until we either run out of section or state to fill. - const uint8_t kSectionSize[kDaikinSections] = { - kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; - for (uint8_t section = 0, pos = 0; section < kDaikinSections; - section++) { - pos += kSectionSize[section]; - // Section Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, - kDaikinTolerance, kDaikinMarkExcess)) return false; - // Section Header - if (!matchMark(results->rawbuf[offset++], kDaikinHdrMark, - kDaikinTolerance, kDaikinMarkExcess)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikinHdrSpace, - kDaikinTolerance, kDaikinMarkExcess)) return false; - - // Section Data - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - // Read in a byte at a time. - data_result = - matchData(&(results->rawbuf[offset]), 8, - kDaikinBitMark, kDaikinOneSpace, - kDaikinBitMark, kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - } - // Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kDaikinGap)) - return false; - + if (!matchMark(results->rawbuf[offset++], kDaikinBitMark, + kDaikinTolerance, kDaikinMarkExcess)) return false; + if (!matchSpace(results->rawbuf[offset++], kDaikinZeroSpace + kDaikinGap, + kDaikinTolerance, kDaikinMarkExcess)) return false; + // Sections + const uint8_t ksectionSize[kDaikinSections] = { + kDaikinSection1Length, kDaikinSection2Length, kDaikinSection3Length}; + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikinSections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikinHdrMark, kDaikinHdrSpace, + kDaikinBitMark, kDaikinOneSpace, + kDaikinBitMark, kDaikinZeroSpace, + kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, + section >= kDaikinSections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } // Compliance if (strict) { // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kDaikinBits) return false; + if (pos * 8 != kDaikinBits) return false; // Validate the checksum. if (!IRDaikinESP::validChecksum(results->state)) return false; } // Success results->decode_type = DAIKIN; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. @@ -634,9 +639,9 @@ bool IRrecv::decodeDaikin(decode_results *results, const uint16_t nbits, // Status: BETA/Appears to work. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/582 -void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582 +void IRsend::sendDaikin2(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kDaikin2Section1Length) return; // Not enough bytes to send a partial message. @@ -667,10 +672,12 @@ void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes, // Supported Remotes: Daikin ARC477A1 remote // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/582 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582 // https://docs.google.com/spreadsheets/d/1f8EGfIbBUo2B-CzUFdrgKQprWakoYNKM80IKZN4KXQE/edit?usp=sharing // https://www.daikin.co.nz/sites/default/files/daikin-split-system-US7-FTXZ25-50NV1B.pdf -IRDaikin2::IRDaikin2(uint16_t pin) : _irsend(pin) { stateReset(); } +IRDaikin2::IRDaikin2(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } void IRDaikin2::begin() { _irsend.begin(); } @@ -1091,60 +1098,64 @@ 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() { String result = ""; -#else // ARDUINO -std::string IRDaikin2::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(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(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (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 (V): "); - result += uint64ToString(getSwingVertical()); + result.reserve(310); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addIntToString(getSwingVertical(), F("Swing (V)")); switch (getSwingVertical()) { case kDaikin2SwingVHigh: result += F(" (Highest)"); @@ -1169,8 +1180,7 @@ std::string IRDaikin2::toString() { default: result += F(" (Unknown)"); } - result += F(", Swing (H): "); - result += uint64ToString(getSwingHorizontal()); + result += addIntToString(getSwingHorizontal(), F("Swing (H)")); switch (getSwingHorizontal()) { case kDaikin2SwingHAuto: result += F(" (Auto)"); @@ -1179,25 +1189,16 @@ std::string IRDaikin2::toString() { result += F(" (Swing)"); break; } - result += F(", Clock: "); - result += IRDaikinESP::renderTime(getCurrentTime()); - result += F(", On Time: "); - if (getOnTimerEnabled()) - result += IRDaikinESP::renderTime(getOnTime()); - else - result += F("Off"); - result += F(", Off Time: "); - if (getOffTimerEnabled()) - result += IRDaikinESP::renderTime(getOffTime()); - else - result += F("Off"); - result += F(", Sleep Time: "); - if (getSleepTimerEnabled()) - result += IRDaikinESP::renderTime(getSleepTime()); - else - result += F("Off"); - result += F(", Beep: "); - result += uint64ToString(getBeep()); + result += addLabeledString(minsToString(getCurrentTime()), F("Clock")); + result += addLabeledString( + getOnTimerEnabled() ? minsToString(getOnTime()) : F("Off"), F("On Time")); + result += addLabeledString( + getOffTimerEnabled() ? minsToString(getOffTime()) : F("Off"), + F("Off Time")); + result += addLabeledString( + getSleepTimerEnabled() ? minsToString(getSleepTime()) : F("Off"), + F("Sleep Time")); + result += addIntToString(getBeep(), F("Beep")); switch (getBeep()) { case kDaikinBeepLoud: result += F(" (Loud)"); @@ -1211,8 +1212,7 @@ std::string IRDaikin2::toString() { default: result += F(" (UNKNOWN)"); } - result += F(", Light: "); - result += uint64ToString(getLight()); + result += addIntToString(getLight(), F("Light")); switch (getLight()) { case kDaikinLightBright: result += F(" (Bright)"); @@ -1226,27 +1226,17 @@ std::string IRDaikin2::toString() { default: result += F(" (UNKNOWN)"); } - result += F(", Mold: "); - result += (getMold() ? F("On") : F("Off")); - result += F(", Clean: "); - result += (getClean() ? F("On") : F("Off")); - result += F(", Fresh Air: "); - if (getFreshAir()) - result += (getFreshAirHigh() ? "High" : "On"); - else - result += F("Off"); - result += F(", Eye: "); - result += (getEye() ? F("On") : F("Off")); - result += F(", Eye Auto: "); - result += (getEyeAuto() ? F("On") : F("Off")); - result += F(", Quiet: "); - result += (getQuiet() ? F("On") : F("Off")); - result += F(", Powerful: "); - result += (getPowerful() ? F("On") : F("Off")); - result += ", Purify: "; - result += (getPurify() ? F("On") : F("Off")); - result += F(", Econo: "); - result += (getEcono() ? F("On") : F("Off")); + result += addBoolToString(getMold(), F("Mold")); + result += addBoolToString(getClean(), F("Clean")); + result += addLabeledString( + getFreshAir() ? (getFreshAirHigh() ? F("High") : F("On")) : F("Off"), + F("Fresh Air")); + result += addBoolToString(getEye(), F("Eye")); + result += addBoolToString(getEyeAuto(), F("Eye Auto")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); + result += addBoolToString(getPurify(), F("Purify")); + result += addBoolToString(getEcono(), F("Econo")); return result; } @@ -1276,11 +1266,8 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, if (strict && nbits != kDaikin2Bits) return false; uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; - uint8_t sectionSize[kDaikin2Sections] = {kDaikin2Section1Length, - kDaikin2Section2Length}; + const uint8_t ksectionSize[kDaikin2Sections] = {kDaikin2Section1Length, + kDaikin2Section2Length}; // Leader if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark, @@ -1289,53 +1276,33 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, kDaikin2Tolerance)) return false; // Sections - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kDaikin2Sections; - section++) { - pos += sectionSize[section]; - - // Section Header - if (!matchMark(results->rawbuf[offset++], kDaikin2HdrMark, - kDaikin2Tolerance)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace, - kDaikin2Tolerance)) 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, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, - kDaikin2ZeroSpace, kDaikin2Tolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Section Footer - if (!matchMark(results->rawbuf[offset++], kDaikin2BitMark, - kDaikin2Tolerance)) return false; - if (section < kDaikin2Sections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kDaikin2Gap, - kDaikin2Tolerance)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kDaikin2Gap, - kDaikin2Tolerance)) return false; - } + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin2Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin2HdrMark, kDaikin2HdrSpace, + kDaikin2BitMark, kDaikin2OneSpace, + kDaikin2BitMark, kDaikin2ZeroSpace, + kDaikin2BitMark, kDaikin2Gap, + section >= kDaikin2Sections - 1, + kDaikin2Tolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; } - // Compliance if (strict) { // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kDaikin2Bits) return false; + if (pos * 8 != kDaikin2Bits) return false; // Validate the checksum. if (!IRDaikin2::validChecksum(results->state)) return false; } // Success results->decode_type = DAIKIN2; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. @@ -1355,7 +1322,7 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, // - Daikin ARC433B69 remote. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/crankyoldgit/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) { @@ -1387,9 +1354,11 @@ void IRsend::sendDaikin216(const unsigned char data[], const uint16_t nbytes, // Supported Remotes: Daikin ARC433B69 remote // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/689 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 // https://github.com/danny-source/Arduino_DY_IRDaikin -IRDaikin216::IRDaikin216(uint16_t pin) : _irsend(pin) { stateReset(); } +IRDaikin216::IRDaikin216(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } void IRDaikin216::begin() { _irsend.begin(); } @@ -1557,10 +1526,13 @@ bool IRDaikin216::getSwingHorizontal(void) { // This is a horrible hack till someone works out the quiet mode bit. void IRDaikin216::setQuiet(const bool on) { - if (on) + if (on) { this->setFan(kDaikinFanQuiet); - else if (this->getFan() == 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. @@ -1568,14 +1540,750 @@ 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 += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); + 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/crankyoldgit/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; + const uint8_t ksectionSize[kDaikin216Sections] = {kDaikin216Section1Length, + kDaikin216Section2Length}; + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin216Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin216HdrMark, kDaikin216HdrSpace, + kDaikin216BitMark, kDaikin216OneSpace, + kDaikin216BitMark, kDaikin216ZeroSpace, + kDaikin216BitMark, kDaikin216Gap, + section >= kDaikin216Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (pos * 8 != kDaikin216Bits) return false; + // Validate the checksum. + if (!IRDaikin216::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN216; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN216 + +#if SEND_DAIKIN160 +// Send a Daikin 160 bit A/C message. +// +// Args: +// data: An array of kDaikin160StateLength bytes containing the IR command. +// +// Status: STABLE / Confirmed working. +// +// Supported devices: +// - Daikin ARC423A5 remote. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin160Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, data, + kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark, + kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + data + kDaikin160Section1Length, + nbytes - kDaikin160Section1Length, + kDaikin160Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN160 + +// Class for handling Daikin 160 bit / 20 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin ARC423A5 remote +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +IRDaikin160::IRDaikin160(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRDaikin160::begin() { _irsend.begin(); } + +// Verify the checksum is valid for a given state. +// Args: +// state: The array to verify the checksum of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin160Section1Length - 1 || + state[kDaikin160Section1Length - 1] != sumBytes( + state, kDaikin160Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin160Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin160Section1Length, + length - kDaikin160Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin160::checksum() { + remote_state[kDaikin160Section1Length - 1] = sumBytes( + remote_state, kDaikin160Section1Length - 1); + remote_state[kDaikin160StateLength - 1] = sumBytes( + remote_state + kDaikin160Section1Length, kDaikin160Section2Length - 1); +} + +void IRDaikin160::stateReset() { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x27; + remote_state[3] = 0xF0; + remote_state[4] = 0x0D; + // remote_state[6] is a checksum byte, it will be set by checksum(). + remote_state[7] = 0x11; + remote_state[8] = 0xDA; + remote_state[9] = 0x27; + remote_state[11] = 0xD3; + remote_state[12] = 0x30; + remote_state[13] = 0x11; + remote_state[16] = 0x1E; + remote_state[17] = 0x0A; + remote_state[18] = 0x08; + // remote_state[19] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin160::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin160::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin160StateLength; i++) + remote_state[i] = new_code[i]; +} + +#if SEND_DAIKIN160 +void IRDaikin160::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin160(remote_state, kDaikin160StateLength, repeat); +} +#endif // SEND_DAIKIN160 + +void IRDaikin160::on() { + remote_state[kDaikin160BytePower] |= kDaikinBitPower; +} + +void IRDaikin160::off() { + remote_state[kDaikin160BytePower] &= ~kDaikinBitPower; +} + +void IRDaikin160::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin160::getPower() { + return remote_state[kDaikin160BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin160::getMode() { + return (remote_state[kDaikin160ByteMode] & kDaikin160MaskMode) >> 4; +} + +void IRDaikin160::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikinCool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote_state[kDaikin160ByteMode] &= ~kDaikin160MaskMode; + remote_state[kDaikin160ByteMode] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin160::convertMode(const stdAc::opmode_t mode) { + return IRDaikinESP::convertMode(mode); +} + +// Set the temp in deg C +void IRDaikin160::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp) * 2 - 20; + remote_state[kDaikin160ByteTemp] &= ~kDaikin160MaskTemp; + remote_state[kDaikin160ByteTemp] |= degrees; +} + +uint8_t IRDaikin160::getTemp(void) { + return (((remote_state[kDaikin160ByteTemp] & kDaikin160MaskTemp) / 2 ) + 10); +} + +// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet +void IRDaikin160::setFan(const uint8_t fan) { + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + // Set the fan speed bits, leave *upper* 4 bits alone + remote_state[kDaikin160ByteFan] &= ~kDaikin160MaskFan; + remote_state[kDaikin160ByteFan] |= fanset; +} + +uint8_t IRDaikin160::getFan() { + uint8_t fan = remote_state[kDaikin160ByteFan] & kDaikin160MaskFan; + if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; + return fan; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin160::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanMin; + case stdAc::fanspeed_t::kLow: return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kMedium: return kDaikinFanMin + 2; + case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +void IRDaikin160::setSwingVertical(const uint8_t position) { + switch (position) { + case kDaikin160SwingVLowest: + case kDaikin160SwingVLow: + case kDaikin160SwingVMiddle: + case kDaikin160SwingVHigh: + case kDaikin160SwingVHighest: + case kDaikin160SwingVAuto: + remote_state[kDaikin160ByteSwingV] &= ~kDaikin160MaskSwingV; + remote_state[kDaikin160ByteSwingV] |= (position << 4); + break; + default: + setSwingVertical(kDaikin160SwingVAuto); + } +} + +uint8_t IRDaikin160::getSwingVertical(void) { + return remote_state[kDaikin160ByteSwingV] >> 4; +} + +// Convert a standard A/C vertical swing into its native version. +uint8_t IRDaikin160::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 kDaikin160SwingVHighest + 1 - (uint8_t)position; + default: + return kDaikin160SwingVAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRDaikin160::toCommonSwingV(const uint8_t setting) { + switch (setting) { + case kDaikin160SwingVHighest: return stdAc::swingv_t::kHighest; + case kDaikin160SwingVHigh: return stdAc::swingv_t::kHigh; + case kDaikin160SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kDaikin160SwingVLow: return stdAc::swingv_t::kLow; + case kDaikin160SwingVLowest: return stdAc::swingv_t::kLowest; + default: + return stdAc::swingv_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin160::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN160; + 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()); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikin160::toString() { + String result = ""; + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kDaikinAuto, kDaikinCool, kDaikinHeat, + kDaikinDry, kDaikinFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kDaikinFanMax, kDaikinFanMin, + kDaikinFanAuto, kDaikinFanQuiet, kDaikinFanMed); + result += addIntToString(getSwingVertical(), F("Vent Position (V)")); + switch (getSwingVertical()) { + case kDaikin160SwingVHighest: result += F(" (Highest)"); break; + case kDaikin160SwingVHigh: result += F(" (High)"); break; + case kDaikin160SwingVMiddle: result += F(" (Middle)"); break; + case kDaikin160SwingVLow: result += F(" (Low)"); break; + case kDaikin160SwingVLowest: result += F(" (Lowest)"); break; + case kDaikin160SwingVAuto: result += F(" (Auto)"); break; + default: + result += F(" (Unknown)"); + } + return result; +} + +#if DECODE_DAIKIN160 +// Decode the supplied Daikin 160 bit A/C message. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of bits to expect in the data portion. (kDaikin160Bits) +// strict: Flag to indicate if we strictly adhere to the specification. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Supported devices: +// - Daikin ARC423A5 remote. +// +// Status: STABLE / Confirmed working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +bool IRrecv::decodeDaikin160(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) + return false; + + // Compliance + if (strict && nbits != kDaikin160Bits) return false; + + uint16_t offset = kStartOffset; + const uint8_t ksectionSize[kDaikin160Sections] = {kDaikin160Section1Length, + kDaikin160Section2Length}; + + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin160Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin160HdrMark, kDaikin160HdrSpace, + kDaikin160BitMark, kDaikin160OneSpace, + kDaikin160BitMark, kDaikin160ZeroSpace, + kDaikin160BitMark, kDaikin160Gap, + section >= kDaikin160Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + // Validate the checksum. + if (!IRDaikin160::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN160; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_DAIKIN160 +#if SEND_DAIKIN176 +// Send a Daikin 176 bit A/C message. +// +// Args: +// data: An array of kDaikin176StateLength bytes containing the IR command. +// +// Status: Alpha/Untested on a real device. +// +// Supported devices: +// - Daikin BRC4C153 remote. +// +void IRsend::sendDaikin176(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin176Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, data, + kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin176HdrMark, kDaikin176HdrSpace, kDaikin176BitMark, + kDaikin176OneSpace, kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + data + kDaikin176Section1Length, + nbytes - kDaikin176Section1Length, + kDaikin176Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN176 + +// Class for handling Daikin 176 bit / 22 byte A/C messages. +// +// Code by crankyoldgit. +// +// Supported Remotes: Daikin BRC4C153 remote +// +IRDaikin176::IRDaikin176(uint16_t pin) : _irsend(pin) { stateReset(); } + +void IRDaikin176::begin() { _irsend.begin(); } + +// 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 IRDaikin176::validChecksum(uint8_t state[], const uint16_t length) { + // Validate the checksum of section #1. + if (length <= kDaikin176Section1Length - 1 || + state[kDaikin176Section1Length - 1] != sumBytes( + state, kDaikin176Section1Length - 1)) + return false; + // Validate the checksum of section #2 (a.k.a. the rest) + if (length <= kDaikin176Section1Length + 1 || + state[length - 1] != sumBytes(state + kDaikin176Section1Length, + length - kDaikin176Section1Length - 1)) + return false; + return true; +} + +// Calculate and set the checksum values for the internal state. +void IRDaikin176::checksum() { + remote_state[kDaikin176Section1Length - 1] = sumBytes( + remote_state, kDaikin176Section1Length - 1); + remote_state[kDaikin176StateLength - 1] = sumBytes( + remote_state + kDaikin176Section1Length, kDaikin176Section2Length - 1); +} + +void IRDaikin176::stateReset() { + for (uint8_t i = 0; i < kDaikin176StateLength; i++) remote_state[i] = 0x00; + remote_state[0] = 0x11; + remote_state[1] = 0xDA; + remote_state[2] = 0x17; + remote_state[3] = 0x18; + remote_state[4] = 0x04; + // remote_state[6] is a checksum byte, it will be set by checksum(). + remote_state[7] = 0x11; + remote_state[8] = 0xDA; + remote_state[9] = 0x17; + remote_state[10] = 0x18; + remote_state[12] = 0x03; + remote_state[14] = 0x20; + remote_state[20] = 0x20; + // remote_state[21] is a checksum byte, it will be set by checksum(). +} + +uint8_t *IRDaikin176::getRaw() { + checksum(); // Ensure correct settings before sending. + return remote_state; +} + +void IRDaikin176::setRaw(const uint8_t new_code[]) { + for (uint8_t i = 0; i < kDaikin176StateLength; i++) + remote_state[i] = new_code[i]; +} + +#if SEND_DAIKIN176 +void IRDaikin176::send(const uint16_t repeat) { + checksum(); + _irsend.sendDaikin176(remote_state, kDaikin176StateLength, repeat); +} +#endif // SEND_DAIKIN176 + +void IRDaikin176::on() { + remote_state[kDaikin176BytePower] |= kDaikinBitPower; +} + +void IRDaikin176::off() { + remote_state[kDaikin176BytePower] &= ~kDaikinBitPower; +} + +void IRDaikin176::setPower(const bool state) { + if (state) + on(); + else + off(); +} + +bool IRDaikin176::getPower() { + return remote_state[kDaikin176BytePower] & kDaikinBitPower; +} + +uint8_t IRDaikin176::getMode() { + return (remote_state[kDaikin176ByteMode] & kDaikin176MaskMode) >> 4; +} + +void IRDaikin176::setMode(const uint8_t mode) { + switch (mode) { + case kDaikinAuto: + case kDaikin176Cool: + case kDaikinHeat: + case kDaikinFan: + case kDaikinDry: + remote_state[kDaikin176ByteMode] &= kDaikin176MaskMode; + remote_state[kDaikin176ByteMode] |= (mode << 4); + break; + default: + this->setMode(kDaikinAuto); + } +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRDaikin176::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kDaikin176Cool; + case stdAc::opmode_t::kHeat: + return kDaikinHeat; + case stdAc::opmode_t::kDry: + return kDaikinDry; + case stdAc::opmode_t::kFan: + return kDaikinFan; + default: + return kDaikinAuto; + } +} + +// Set the temp in deg C +void IRDaikin176::setTemp(const uint8_t temp) { + uint8_t degrees = std::max(temp, kDaikinMinTemp); + degrees = std::min(degrees, kDaikinMaxTemp) * 2 - 18; + remote_state[kDaikin176ByteTemp] &= ~kDaikin176MaskTemp; + remote_state[kDaikin176ByteTemp] |= degrees; +} + +uint8_t IRDaikin176::getTemp(void) { + return (((remote_state[kDaikin176ByteTemp] & kDaikin176MaskTemp) / 2 ) + 9); +} + +// Set the speed of the fan, 1 for Min or 3 for Max +void IRDaikin176::setFan(const uint8_t fan) { + uint8_t fanset; + if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) + fanset = fan; + else if (fan < kDaikinFanMin || fan > kDaikinFanMax) + fanset = kDaikinFanAuto; + else + fanset = 2 + fan; + // Set the fan speed bits, leave *lower* 4 bits alone + remote_state[kDaikin176ByteFan] &= ~kDaikin176MaskFan; + remote_state[kDaikin176ByteFan] |= (fanset << 4); +} + +uint8_t IRDaikin176::getFan() { + uint8_t fan = remote_state[kDaikin176ByteFan] >> 4; + return fan; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRDaikin176::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kDaikinFanMin; + case stdAc::fanspeed_t::kLow: return kDaikinFanMin + 1; + case stdAc::fanspeed_t::kMedium: return kDaikinFanMin + 2; + case stdAc::fanspeed_t::kHigh: return kDaikinFanMax - 1; + case stdAc::fanspeed_t::kMax: return kDaikinFanMax; + default: + return kDaikinFanAuto; + } +} + +void IRDaikin176::setSwingHorizontal(const uint8_t position) { + switch (position) { + case kDaikin176SwingHSwing: + remote_state[kDaikin176ByteSwingH] &= kDaikin176MaskSwingH; + remote_state[kDaikin176ByteSwingH] |= position; + break; + default: setSwingHorizontal(kDaikin176SwingHAuto); + } +} +uint8_t IRDaikin176::getSwingHorizontal() { + return remote_state[kDaikin176ByteSwingH] & kDaikin176MaskSwingH; +} + +// Convert a standard A/C horizontal swing into its native version. +uint8_t IRDaikin176::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kOff: + return kDaikin176SwingHSwing; + default: + return kDaikin176SwingHAuto; + } +} +// Convert a native horizontal swing to it's common equivalent. +stdAc::swingh_t IRDaikin176::toCommonSwingH(const uint8_t setting) { + switch (setting) { + case kDaikin176SwingHSwing: return stdAc::swingh_t::kOff; + default: return stdAc::swingh_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRDaikin176::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::DAIKIN176; + 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.swingh = this->toCommonSwingH(this->getSwingHorizontal()); + + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.quiet = false; + result.turbo = false; + result.light = false; + result.clean = false; + result.econo = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRDaikin176::toString() { + String result = ""; + result.reserve(120); // Reserve some heap for the string to reduce fragging. result += F("Power: "); if (this->getPower()) result += F("On"); @@ -1587,7 +2295,7 @@ std::string IRDaikin216::toString() { case kDaikinAuto: result += F(" (AUTO)"); break; - case kDaikinCool: + case kDaikinCool + 4: result += F(" (COOL)"); break; case kDaikinHeat: @@ -1616,97 +2324,79 @@ std::string IRDaikin216::toString() { case kDaikinFanMin: result += F(" (MIN)"); break; - case kDaikinFanMax: + case kDaikinFanMax - 2: 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 += (getQuiet() ? F("On") : F("Off")); + result += F(", Swing (H): "); + result += uint64ToString(getSwingHorizontal()); + switch (getSwingHorizontal()) { + case kDaikin176SwingHAuto: + result += F(" (Auto)"); + break; + case kDaikin176SwingHSwing: + result += F(" (Off)"); + break; + } return result; } -#if DECODE_DAIKIN216 -// Decode the supplied Daikin 216 bit A/C message. +#if DECODE_DAIKIN176 +// Decode the supplied Daikin 176 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) +// nbits: Nr. of bits to expect in the data portion. (kDaikin176Bits) // 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. +// - Daikin BRC4C153 remote. // -// Status: BETA / Should be working. +// Status: BETA / Probably works. // -// 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, + +bool IRrecv::decodeDaikin176(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; + if (strict && nbits != kDaikin176Bits) 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}; + const uint8_t ksectionSize[kDaikin176Sections] = {kDaikin176Section1Length, + kDaikin176Section2Length}; // 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++], kDaikin2HdrSpace)) 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, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Section Footer - if (!matchMark(results->rawbuf[offset++], kDaikin216BitMark)) 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; - } + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin176Sections; section++) { + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin176HdrMark, kDaikin176HdrSpace, + kDaikin176BitMark, kDaikin176OneSpace, + kDaikin176BitMark, kDaikin176ZeroSpace, + kDaikin176BitMark, kDaikin176Gap, + section >= kDaikin176Sections - 1, + kDaikinTolerance, kDaikinMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; } - // 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; + if (!IRDaikin176::validChecksum(results->state)) return false; } // Success - results->decode_type = decode_type_t::DAIKIN216; - results->bits = dataBitsSoFar; + results->decode_type = decode_type_t::DAIKIN176; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. return true; } -#endif // DECODE_DAIKIN216 +#endif // DECODE_DAIKIN176 diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Daikin.h similarity index 66% rename from lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Daikin.h index 038e8edd9..c8398ac69 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Daikin.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Daikin.h @@ -1,13 +1,22 @@ // Copyright 2016 sillyfrog // Copyright 2017 sillyfrog, crankyoldgit // Copyright 2018-2019 crankyoldgit + +// Supports: +// Brand: Daikin, Model: ARC433** remote +// Brand: Daikin, Model: ARC477A1 remote +// Brand: Daikin, Model: FTXZ25NV1B A/C +// Brand: Daikin, Model: FTXZ35NV1B A/C +// Brand: Daikin, Model: FTXZ50NV1B A/C +// Brand: Daikin, Model: ARC433B69 remote +// Brand: Daikin, Model: ARC423A5 remote +// Brand: Daikin, Model: FTE12HV2S A/C + #ifndef IR_DAIKIN_H_ #define IR_DAIKIN_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" @@ -16,12 +25,6 @@ #include "IRsend_test.h" #endif -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - /* Daikin AC map byte 6= @@ -29,7 +32,8 @@ byte 7= checksum of the first part (and last byte before a 29ms pause) byte 13=Current time, mins past midnight, low bits byte 14 - b0-b3=Current time, mins past midnight, high bits + 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 @@ -88,6 +92,7 @@ const uint8_t kDaikinFan = 0b110; const uint8_t kDaikinMinTemp = 10; // Celsius const uint8_t kDaikinMaxTemp = 32; // Celsius const uint8_t kDaikinFanMin = 1; +const uint8_t kDaikinFanMed = 3; const uint8_t kDaikinFanMax = 5; const uint8_t kDaikinFanAuto = 0b1010; const uint8_t kDaikinFanQuiet = 0b1011; @@ -122,6 +127,8 @@ const uint8_t kDaikinByteEcono = kDaikinByteSensor; const uint8_t kDaikinBitEcono = 0b00000100; const uint8_t kDaikinByteEye = kDaikinByteSensor; const uint8_t kDaikinBitEye = 0b10000000; +const uint8_t kDaikinByteWeeklyTimer = kDaikinByteSensor; +const uint8_t kDaikinBitWeeklyTimer = 0b10000000; const uint8_t kDaikinByteMold = 33; const uint8_t kDaikinBitMold = 0b00000010; const uint8_t kDaikinByteOffTimer = kDaikinBytePower; @@ -187,11 +194,11 @@ 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 = 3400; -const uint16_t kDaikin216HdrSpace = 1800; -const uint16_t kDaikin216BitMark = 380; -const uint16_t kDaikin216OneSpace = 1350; -const uint16_t kDaikin216ZeroSpace = 480; +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; @@ -208,7 +215,60 @@ const uint8_t kDaikin216ByteSwingV = 16; const uint8_t kDaikin216MaskSwingV = 0b00001111; const uint8_t kDaikin216ByteSwingH = 17; const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV; +const uint8_t kDaikin216BytePowerful = 21; +// Another variant of the protocol for the Daikin ARC423A5 remote. +const uint16_t kDaikin160Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin160HdrMark = 5000; +const uint16_t kDaikin160HdrSpace = 2145; +const uint16_t kDaikin160BitMark = 342; +const uint16_t kDaikin160OneSpace = 1786; +const uint16_t kDaikin160ZeroSpace = 700; +const uint16_t kDaikin160Gap = 29650; +const uint16_t kDaikin160Sections = 2; +const uint16_t kDaikin160Section1Length = 7; +const uint16_t kDaikin160Section2Length = kDaikin160StateLength - + kDaikin160Section1Length; +const uint8_t kDaikin160BytePower = 12; +const uint8_t kDaikin160ByteMode = kDaikin160BytePower; +const uint8_t kDaikin160MaskMode = 0b01110000; +const uint8_t kDaikin160ByteTemp = 16; +const uint8_t kDaikin160MaskTemp = 0b01111110; +const uint8_t kDaikin160ByteFan = 17; +const uint8_t kDaikin160MaskFan = 0b00001111; +const uint8_t kDaikin160ByteSwingV = 13; +const uint8_t kDaikin160MaskSwingV = 0b11110000; +const uint8_t kDaikin160SwingVLowest = 0x1; +const uint8_t kDaikin160SwingVLow = 0x2; +const uint8_t kDaikin160SwingVMiddle = 0x3; +const uint8_t kDaikin160SwingVHigh = 0x4; +const uint8_t kDaikin160SwingVHighest = 0x5; +const uint8_t kDaikin160SwingVAuto = 0xF; + +// Another variant of the protocol for the Daikin BRC4C153 remote. +const uint16_t kDaikin176Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin176HdrMark = 5070; +const uint16_t kDaikin176HdrSpace = 2140; +const uint16_t kDaikin176BitMark = 370; +const uint16_t kDaikin176OneSpace = 1780; +const uint16_t kDaikin176ZeroSpace = 710; +const uint16_t kDaikin176Gap = 29410; +const uint16_t kDaikin176Sections = 2; +const uint16_t kDaikin176Section1Length = 7; +const uint16_t kDaikin176Section2Length = kDaikin176StateLength - + kDaikin176Section1Length; +const uint8_t kDaikin176Cool = 0b111; +const uint8_t kDaikin176BytePower = 14; +const uint8_t kDaikin176ByteMode = 12; +const uint8_t kDaikin176MaskMode = 0b01110011; +const uint8_t kDaikin176ByteTemp = 17; +const uint8_t kDaikin176MaskTemp = 0b01111110; +const uint8_t kDaikin176ByteFan = 18; +const uint8_t kDaikin176MaskFan = 0b11110000; +const uint8_t kDaikin176ByteSwingH = 18; +const uint8_t kDaikin176MaskSwingH = 0b00001111; +const uint8_t kDaikin176SwingHAuto = 0x5; +const uint8_t kDaikin176SwingHSwing = 0x6; // Legacy defines. #define DAIKIN_COOL kDaikinCool @@ -225,10 +285,12 @@ const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV; class IRDaikinESP { public: - explicit IRDaikinESP(uint16_t pin); + explicit IRDaikinESP(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_DAIKIN void send(const uint16_t repeat = kDaikinDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif void begin(void); void on(void); @@ -253,8 +315,6 @@ class IRDaikinESP { bool getSensor(void); void setEcono(const bool on); bool getEcono(void); - void setEye(const bool on); - bool getEye(void); void setMold(const bool on); bool getMold(void); void setComfort(const bool on); @@ -269,6 +329,10 @@ class IRDaikinESP { 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); @@ -276,13 +340,10 @@ class IRDaikinESP { const uint16_t length = kDaikinStateLength); static uint8_t convertMode(const stdAc::opmode_t mode); static uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); String toString(void); - static String renderTime(const uint16_t timemins); -#else - std::string toString(void); - static std::string renderTime(const uint16_t timemins); -#endif #ifndef UNIT_TEST private: @@ -299,10 +360,12 @@ class IRDaikinESP { // Class to emulate a Daikin ARC477A1 remote. class IRDaikin2 { public: - explicit IRDaikin2(uint16_t pin); + explicit IRDaikin2(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_DAIKIN2 void send(const uint16_t repeat = kDaikin2DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif void begin(); void on(); @@ -368,13 +431,10 @@ class IRDaikin2 { 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); -#ifdef ARDUINO + static stdAc::swingv_t toCommonSwingV(const uint8_t setting); + static stdAc::swingh_t toCommonSwingH(const uint8_t setting); + stdAc::state_t toCommon(void); String toString(); - static String renderTime(uint16_t timemins); -#else - std::string toString(); - static std::string renderTime(uint16_t timemins); -#endif #ifndef UNIT_TEST private: @@ -393,10 +453,12 @@ class IRDaikin2 { // Class to emulate a Daikin ARC433B69 remote. class IRDaikin216 { public: - explicit IRDaikin216(uint16_t pin); + explicit IRDaikin216(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_DAIKIN216 void send(const uint16_t repeat = kDaikin216DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif void begin(); uint8_t* getRaw(); @@ -421,13 +483,10 @@ class IRDaikin216 { bool getSwingHorizontal(void); void setQuiet(const bool on); bool getQuiet(void); -#ifdef ARDUINO + void setPowerful(const bool on); + bool getPowerful(void); + stdAc::state_t toCommon(void); 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: @@ -441,4 +500,95 @@ class IRDaikin216 { void checksum(); }; +// Class to emulate a Daikin ARC423A5 remote. +class IRDaikin160 { + public: + explicit IRDaikin160(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_DAIKIN160 + void send(const uint16_t repeat = kDaikin160DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin160StateLength); + 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 uint8_t position); + uint8_t getSwingVertical(void); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static stdAc::swingv_t toCommonSwingV(const uint8_t setting); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin160StateLength]; + void stateReset(); + void checksum(); +}; +// Class to emulate a Daikin BRC4C153 remote. +class IRDaikin176 { + public: + explicit IRDaikin176(uint16_t pin); + +#if SEND_DAIKIN176 + void send(const uint16_t repeat = kDaikin176DefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif + void begin(); + uint8_t* getRaw(); + void setRaw(const uint8_t new_code[]); + static bool validChecksum(uint8_t state[], + const uint16_t length = kDaikin176StateLength); + 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 setSwingHorizontal(const uint8_t position); + uint8_t getSwingHorizontal(); + static uint8_t convertSwingH(const stdAc::swingh_t position); + static stdAc::swingh_t toCommonSwingH(const uint8_t setting); + stdAc::state_t toCommon(void); + String toString(void); + +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // # of bytes per command + uint8_t remote_state[kDaikin176StateLength]; + void stateReset(); + void checksum(); +}; + #endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Denon.cpp similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Denon.cpp index 6798e022e..8a32ae261 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Denon.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Denon.cpp @@ -6,12 +6,6 @@ #include "IRsend.h" #include "IRutils.h" -// DDDD EEEEE N N OOO N N -// D D E NN N O O NN N -// D D EEE N N N O O N N N -// D D E N NN O O N NN -// DDDD EEEEE N N OOO N N - // Original Denon support added by https://github.com/csBlueChip // Ported over by Massimiliano Pinto @@ -43,7 +37,7 @@ const uint64_t kDenonManufacturer = 0x2A4CULL; // // Args: // data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically DENON_BITS. +// nbits: Nr. of bits of data to be sent. Typically kDenonBits. // repeat: Nr. of additional times the message is to be sent. // // Status: BETA / Should be working. @@ -70,7 +64,7 @@ void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) { // // Args: // results: Ptr to the data to decode and where to store the decode result. -// nbits: Expected nr. of data bits. (Typically DENON_BITS) +// nbits: Expected nr. of data bits. (Typically kDenonBits) // Returns: // boolean: True if it can decode it, false if it can't. // @@ -82,8 +76,8 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) { // Compliance if (strict) { switch (nbits) { - case DENON_BITS: - case DENON_48_BITS: + case kDenonBits: + case kDenon48Bits: case kDenonLegacyBits: break; default: @@ -103,33 +97,18 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) { // We couldn't decode it as expected, so try the old legacy method. // NOTE: I don't think this following protocol actually exists. // Looks like a partial version of the Sharp protocol. - // Check we have enough data - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; if (strict && nbits != kDenonLegacyBits) return false; uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kDenonHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDenonHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kDenonHdrSpace)) return false; - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kDenonHdrSpaceTicks; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kDenonBitMarkTicks * m_tick, kDenonOneSpaceTicks * s_tick, - kDenonBitMarkTicks * m_tick, kDenonZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDenonBitMarkTicks * m_tick)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kDenonHdrMark, kDenonHdrSpace, + kDenonBitMark, kDenonOneSpace, + kDenonBitMark, kDenonZeroSpace, + kDenonBitMark, 0, false)) return false; // Success results->bits = nbits; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Dish.cpp similarity index 70% rename from lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Dish.cpp index 040aa3bf7..b217da763 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Dish.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Dish.cpp @@ -5,15 +5,12 @@ #include "IRsend.h" #include "IRutils.h" -// DDDD IIIII SSSS H H -// D D I S H H -// D D I SSS HHHHH -// D D I S H H -// DDDD IIIII SSSS H H - // DISH support originally by Todd Treece // http://unionbridge.org/design/ircommand +// Supports: +// Brand: DISH NETWORK, Model: echostar 301 + // Constants // Ref: // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp @@ -95,35 +92,17 @@ bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) { uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!match(results->rawbuf[offset], kDishHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDishHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kDishHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kDishHdrSpaceTicks; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kDishBitMarkTicks * m_tick, - kDishOneSpaceTicks * s_tick, kDishBitMarkTicks * m_tick, - kDishZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDishBitMarkTicks * m_tick)) - return false; - - // Compliance - if (strict) { - // The DISH protocol calls for a repeated message, so strictly speaking - // there should be a code following this. Only require it if we are set to - // strict matching. - if (!matchSpace(results->rawbuf[offset], kDishRptSpaceTicks * s_tick)) - return false; - } + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kDishHdrMark, kDishHdrSpace, + kDishBitMark, kDishOneSpace, + kDishBitMark, kDishZeroSpace, + kDishBitMark, + // The DISH protocol calls for a repeated message, so + // strictly speaking there should be a code following this. + // Only require it if we are set to strict matching. + strict ? kDishRptSpace : 0, false)) return false; // Success results->decode_type = DISH; diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.cpp new file mode 100644 index 000000000..4c61da34d --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.cpp @@ -0,0 +1,336 @@ +// Copyright 2018, 2019 David Conran + +#include "ir_Electra.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Electra A/C added by crankyoldgit +// + +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/527 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/642 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/778 +// https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp + +// Constants +const uint16_t kElectraAcHdrMark = 9166; +const uint16_t kElectraAcBitMark = 646; +const uint16_t kElectraAcHdrSpace = 4470; +const uint16_t kElectraAcOneSpace = 1647; +const uint16_t kElectraAcZeroSpace = 547; +const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess. + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_ELECTRA_AC +// Send a Electra message +// +// Args: +// data: Contents of the message to be sent. (Guessing MSBF order) +// nbits: Nr. of bits of data to be sent. Typically kElectraAcBits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: Alpha / Needs testing against a real device. +// +void IRsend::sendElectraAC(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + for (uint16_t r = 0; r <= repeat; r++) + sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark, + kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, + kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, + 38000, // Complete guess of the modulation frequency. + false, // Send data in LSB order per byte + 0, 50); +} +#endif + + +IRElectraAc::IRElectraAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + this->stateReset(); +} + +void IRElectraAc::stateReset(void) { + for (uint8_t i = 1; i < kElectraAcStateLength - 2; i++) + remote_state[i] = 0; + remote_state[0] = 0xC3; + remote_state[11] = 0x08; + // [12] is the checksum. +} + +void IRElectraAc::begin(void) { _irsend.begin(); } + +uint8_t IRElectraAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + if (length == 0) return state[0]; + return sumBytes(state, length - 1); +} + +bool IRElectraAc::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) + return true; // No checksum to compare with. Assume okay. + return (state[length - 1] == calcChecksum(state, length)); +} + +// Update the checksum for the internal state. +void IRElectraAc::checksum(uint16_t length) { + if (length < 2) return; + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +#if SEND_ELECTRA_AC +void IRElectraAc::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendElectraAC(remote_state, kElectraAcStateLength, repeat); +} +#endif // SEND_ELECTRA_AC + +uint8_t *IRElectraAc::getRaw(void) { + this->checksum(); + return remote_state; +} + +void IRElectraAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kElectraAcStateLength; i++) + remote_state[i] = new_code[i]; +} + +void IRElectraAc::on(void) { this->setPower(true); } + +void IRElectraAc::off(void) { this->setPower(false); } + +void IRElectraAc::setPower(const bool on) { + if (on) + remote_state[9] |= kElectraAcPowerMask; + else + remote_state[9] &= ~kElectraAcPowerMask; +} + +bool IRElectraAc::getPower(void) { + return remote_state[9] & kElectraAcPowerMask; +} + +void IRElectraAc::setMode(const uint8_t mode) { + switch (mode) { + case kElectraAcAuto: + case kElectraAcDry: + case kElectraAcCool: + case kElectraAcHeat: + case kElectraAcFan: + remote_state[6] &= ~kElectraAcModeMask; + remote_state[6] |= (mode << 5); + break; + default: + // If we get an unexpected mode, default to AUTO. + this->setMode(kElectraAcAuto); + } +} + +uint8_t IRElectraAc::getMode(void) { + return (remote_state[6] & kElectraAcModeMask) >> 5; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRElectraAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kElectraAcCool; + case stdAc::opmode_t::kHeat: + return kElectraAcHeat; + case stdAc::opmode_t::kDry: + return kElectraAcDry; + case stdAc::opmode_t::kFan: + return kElectraAcFan; + default: + return kElectraAcAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRElectraAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kElectraAcCool: return stdAc::opmode_t::kCool; + case kElectraAcHeat: return stdAc::opmode_t::kHeat; + case kElectraAcDry: return stdAc::opmode_t::kDry; + case kElectraAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Set the temp. in deg C +void IRElectraAc::setTemp(const uint8_t temp) { + uint8_t newtemp = std::max(kElectraAcMinTemp, temp); + newtemp = std::min(kElectraAcMaxTemp, newtemp); + remote_state[1] = (remote_state[1] & ~kElectraAcTempMask) | + ((newtemp - kElectraAcOffsetTemp) << 3); +} + +// Return the set temp. in deg C +uint8_t IRElectraAc::getTemp(void) { + return ((remote_state[1] & kElectraAcTempMask) >> 3) + kElectraAcOffsetTemp; +} + +// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed +void IRElectraAc::setFan(const uint8_t speed) { + switch (speed) { + case kElectraAcFanAuto: + case kElectraAcFanHigh: + case kElectraAcFanMed: + case kElectraAcFanLow: + remote_state[4] &= ~kElectraAcFanMask; + remote_state[4] |= (speed << 5); + break; + default: + // If we get an unexpected speed, default to Auto. + this->setFan(kElectraAcFanAuto); + } +} + +uint8_t IRElectraAc::getFan(void) { + return (remote_state[4] & kElectraAcFanMask) >> 5; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRElectraAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kElectraAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kElectraAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kElectraAcFanHigh; + default: + return kElectraAcFanAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRElectraAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kElectraAcFanHigh: return stdAc::fanspeed_t::kMax; + case kElectraAcFanMed: return stdAc::fanspeed_t::kMedium; + case kElectraAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +void IRElectraAc::setSwingV(const bool on) { + if (on) + remote_state[1] &= ~kElectraAcSwingVMask; + else + remote_state[1] |= kElectraAcSwingVMask; +} + +bool IRElectraAc::getSwingV(void) { + return !(remote_state[1] & kElectraAcSwingVMask); +} + +void IRElectraAc::setSwingH(const bool on) { + if (on) + remote_state[2] &= ~kElectraAcSwingHMask; + else + remote_state[2] |= kElectraAcSwingHMask; +} + +bool IRElectraAc::getSwingH(void) { + return !(remote_state[2] & kElectraAcSwingHMask); +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRElectraAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::ELECTRA_AC; + 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->getSwingV() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = this->getSwingH() ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + // Not supported. + result.model = -1; // No models used. + result.quiet = false; + result.turbo = false; + result.econo = false; + result.clean = false; + result.light = false; + result.filter = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRElectraAc::toString(void) { + String result = ""; + result.reserve(80); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kElectraAcAuto, kElectraAcCool, + kElectraAcHeat, kElectraAcDry, kElectraAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kElectraAcFanHigh, kElectraAcFanLow, + kElectraAcFanAuto, kElectraAcFanAuto, + kElectraAcFanMed); + result += addBoolToString(getSwingV(), F("Swing(V)")); + result += addBoolToString(getSwingH(), F("Swing(H)")); + return result; +} + +#if DECODE_ELECTRA_AC +// Decode the supplied Electra A/C 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 kElectraAcBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Known working. +// +bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, + bool strict) { + if (strict) { + if (nbits != kElectraAcBits) + return false; // Not strictly a ELECTRA_AC message. + } + + uint16_t offset = kStartOffset; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kElectraAcHdrMark, kElectraAcHdrSpace, + kElectraAcBitMark, kElectraAcOneSpace, + kElectraAcBitMark, kElectraAcZeroSpace, + kElectraAcBitMark, kElectraAcMessageGap, true, + kTolerance, 0, false)) return false; + + // Compliance + if (strict) { + // Verify the checksum. + if (!IRElectraAc::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = decode_type_t::ELECTRA_AC; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_ELECTRA_AC diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.h new file mode 100644 index 000000000..c9c6f018e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Electra.h @@ -0,0 +1,102 @@ +// Electra A/C +// +// Copyright 2019 David Conran + +#ifndef IR_ELECTRA_H_ +#define IR_ELECTRA_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Supports: +// Brand: AUX, Model: KFR-35GW/BpNFW=3 A/C +// Brand: AUX, Model: YKR-T/011 remote + +// Ref: +// https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp + +// Constants +// state[1] +const uint8_t kElectraAcTempMask = 0b11111000; +const uint8_t kElectraAcMinTemp = 16; // 16C +const uint8_t kElectraAcMaxTemp = 32; // 32C +const uint8_t kElectraAcOffsetTemp = 8; +const uint8_t kElectraAcSwingVMask = 0b00000111; +// state[2] +const uint8_t kElectraAcSwingHMask = 0b11100000; +// state[4] +const uint8_t kElectraAcFanMask = 0b11100000; +const uint8_t kElectraAcFanAuto = 0b101; +const uint8_t kElectraAcFanLow = 0b011; +const uint8_t kElectraAcFanMed = 0b010; +const uint8_t kElectraAcFanHigh = 0b001; +// state[6] +const uint8_t kElectraAcModeMask = 0b11100000; +const uint8_t kElectraAcAuto = 0b000; +const uint8_t kElectraAcCool = 0b001; +const uint8_t kElectraAcDry = 0b010; +const uint8_t kElectraAcHeat = 0b100; +const uint8_t kElectraAcFan = 0b110; +// state[9] +const uint8_t kElectraAcPowerMask = 0b00100000; + + +// Classes +class IRElectraAc { + public: + explicit IRElectraAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_ELECTRA_AC + void send(const uint16_t repeat = kElectraAcMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_ELECTRA_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setSwingV(const bool on); + bool getSwingV(void); + void setSwingH(const bool on); + bool getSwingH(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kElectraAcStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kElectraAcStateLength); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kElectraAcStateLength); + String toString(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); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kElectraAcStateLength]; + void checksum(const uint16_t length = kElectraAcStateLength); +}; +#endif // IR_ELECTRA_H_ diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Fujitsu.cpp new file mode 100644 index 000000000..43695b932 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Fujitsu.cpp @@ -0,0 +1,722 @@ +// Copyright 2017 Jonny Graham +// Copyright 2017-2019 David Conran +#include "ir_Fujitsu.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRsend.h" +#include "IRutils.h" + +// Fujitsu A/C support added by Jonny Graham & David Conran + +// 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: +// These values are based on averages of measurements +const uint16_t kFujitsuAcHdrMark = 3324; +const uint16_t kFujitsuAcHdrSpace = 1574; +const uint16_t kFujitsuAcBitMark = 448; +const uint16_t kFujitsuAcOneSpace = 1182; +const uint16_t kFujitsuAcZeroSpace = 390; +const uint16_t kFujitsuAcMinGap = 8100; + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#if SEND_FUJITSU_AC +// Send a Fujitsu A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. Typically one of: +// kFujitsuAcStateLength +// kFujitsuAcStateLength - 1 +// kFujitsuAcStateLengthShort +// kFujitsuAcStateLengthShort - 1 +// repeat: Nr. of times the message is to be repeated. +// (Default = kFujitsuAcMinRepeat). +// +// Status: STABLE / Known Good. +// +void IRsend::sendFujitsuAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, + kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, + kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, + repeat, 50); +} +#endif // SEND_FUJITSU_AC + +// Code to emulate Fujitsu A/C IR remote control unit. + +// Initialise the object. +IRFujitsuAC::IRFujitsuAC(const uint16_t pin, + const fujitsu_ac_remote_model_t model, + const bool inverted, const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + this->setModel(model); + this->stateReset(); +} + +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) { + _temp = 24; + _fanSpeed = kFujitsuAcFanHigh; + _mode = kFujitsuAcModeCool; + _swingMode = kFujitsuAcSwingBoth; + _cmd = kFujitsuAcCmdTurnOn; + this->buildState(); +} + +// Configure the pin for output. +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) { + this->buildState(); + _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); +} +#endif // SEND_FUJITSU_AC + +void IRFujitsuAC::buildState(void) { + remote_state[0] = 0x14; + remote_state[1] = 0x63; + remote_state[2] = 0x00; + remote_state[3] = 0x10; + remote_state[4] = 0x10; + bool fullCmd = false; + switch (_cmd) { + case kFujitsuAcCmdTurnOff: // 0x02 + case kFujitsuAcCmdEcono: // 0x09 + case kFujitsuAcCmdPowerful: // 0x39 + case kFujitsuAcCmdStepVert: // 0x6C + case kFujitsuAcCmdToggleSwingVert: // 0x6D + case kFujitsuAcCmdStepHoriz: // 0x79 + case kFujitsuAcCmdToggleSwingHoriz: // 0x7A + 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; + } + fullCmd = true; + break; + } + if (fullCmd) { // long codes + uint8_t tempByte = _temp - kFujitsuAcMinTemp; + // Nr. of bytes in the message after this byte. + remote_state[6] = _state_length - 7; + + remote_state[7] = 0x30; + remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4); + remote_state[9] = _mode | 0 << 4; // timer off + remote_state[10] = _fanSpeed; + remote_state[11] = 0; // timerOff values + remote_state[12] = 0; // timerOff/On values + remote_state[13] = 0; // timerOn values + remote_state[14] = 0; + uint8_t checksum = 0; + uint8_t checksum_complement = 0; + 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 + 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(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(void) { + this->buildState(); + return remote_state; +} + +void IRFujitsuAC::buildFromState(const uint16_t length) { + switch (length) { + case kFujitsuAcStateLength - 1: + case kFujitsuAcStateLengthShort - 1: + this->setModel(ARDB1); + // ARJW2 has horizontal swing. + if (this->getSwing(true) > kFujitsuAcSwingVert) this->setModel(ARJW2); + break; + default: + 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: + if (this->getModel() != fujitsu_ac_remote_model_t::ARJW2) + this->setModel(ARDB1); + break; + case 9: + if (this->getModel() != fujitsu_ac_remote_model_t::ARREB1E) + this->setModel(ARRAH2E); + break; + } + setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp); + if (remote_state[8] & 0x1) + setCmd(kFujitsuAcCmdTurnOn); + else + setCmd(kFujitsuAcCmdStayOn); + setMode(remote_state[9] & 0b111); + setFanSpeed(remote_state[10] & 0b111); + setSwing(remote_state[10] >> 4); + 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) { + if (length > kFujitsuAcStateLength) return false; + for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { + if (i < length) + remote_state[i] = newState[i]; + else + remote_state[i] = 0; + } + buildFromState(length); + return true; +} + +// Set the requested power state of the A/C to off. +void IRFujitsuAC::off(void) { this->setCmd(kFujitsuAcCmdTurnOff); } + +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(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(const uint8_t cmd) { + switch (cmd) { + case kFujitsuAcCmdTurnOff: + case kFujitsuAcCmdTurnOn: + case kFujitsuAcCmdStayOn: + case kFujitsuAcCmdStepVert: + case kFujitsuAcCmdToggleSwingVert: + _cmd = cmd; + break; + case kFujitsuAcCmdStepHoriz: + case kFujitsuAcCmdToggleSwingHoriz: + switch (_model) { + // Only these remotes have 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; + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } + break; + default: + _cmd = kFujitsuAcCmdStayOn; + } +} + +// 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. +} + +// 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(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(void) { return _temp; } + +// Set the speed of the fan +void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { + if (fanSpeed > kFujitsuAcFanQuiet) + _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(void) { return _fanSpeed; } + +// Set the requested climate operation mode of the a/c unit. +void IRFujitsuAC::setMode(const uint8_t mode) { + if (mode > kFujitsuAcModeHeat) + _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(void) { return _mode; } + +// Set the requested swing operation mode of the a/c unit. +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; + break; + // Has Horizontal support. + case ARRAH2E: + case ARJW2: + default: + // Set the mode to max if out of range + if (swingMode > kFujitsuAcSwingBoth) _swingMode = kFujitsuAcSwingBoth; + } + this->setCmd(kFujitsuAcCmdStayOn); // No special command involved. +} + +// 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[], const uint16_t length) { + uint8_t sum = 0; + uint8_t sum_complement = 0; + uint8_t checksum = state[length - 1]; + switch (length) { + case kFujitsuAcStateLengthShort: // ARRAH2E & ARREB1E + return state[length - 1] == (uint8_t)~state[length - 2]; + case kFujitsuAcStateLength - 1: // ARDB1 & ARJW2 + sum = sumBytes(state, length - 1); + sum_complement = 0x9B; + break; + case kFujitsuAcStateLength: // ARRAH2E & ARREB1E + sum = sumBytes(state + kFujitsuAcStateLengthShort, + length - 1 - kFujitsuAcStateLengthShort); + break; + default: // Includes ARDB1 & ARJW2 short. + return true; // Assume the checksum is valid for other lengths. + } + return checksum == (uint8_t)(sum_complement - sum); // Does it match? +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRFujitsuAC::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kFujitsuAcModeCool; + case stdAc::opmode_t::kHeat: + return kFujitsuAcModeHeat; + case stdAc::opmode_t::kDry: + return kFujitsuAcModeDry; + case stdAc::opmode_t::kFan: + return kFujitsuAcModeFan; + default: + return kFujitsuAcModeAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRFujitsuAC::convertFan(stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + return kFujitsuAcFanQuiet; + case stdAc::fanspeed_t::kLow: + return kFujitsuAcFanLow; + case stdAc::fanspeed_t::kMedium: + return kFujitsuAcFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kFujitsuAcFanHigh; + default: + return kFujitsuAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRFujitsuAC::toCommonMode(const uint8_t mode) { + switch (mode) { + case kFujitsuAcModeCool: return stdAc::opmode_t::kCool; + case kFujitsuAcModeHeat: return stdAc::opmode_t::kHeat; + case kFujitsuAcModeDry: return stdAc::opmode_t::kDry; + case kFujitsuAcModeFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kFujitsuAcFanHigh: return stdAc::fanspeed_t::kMax; + case kFujitsuAcFanMed: return stdAc::fanspeed_t::kMedium; + case kFujitsuAcFanLow: return stdAc::fanspeed_t::kLow; + case kFujitsuAcFanQuiet: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRFujitsuAC::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::FUJITSU_AC; + result.model = this->getModel(); + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFanSpeed()); + uint8_t swing = this->getSwing(); + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + result.swingv = (swing & kFujitsuAcSwingVert) ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = (swing & kFujitsuAcSwingHoriz) ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + break; + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + default: + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + } + + result.quiet = (this->getFanSpeed() == kFujitsuAcFanQuiet); + result.turbo = this->getCmd() == kFujitsuAcCmdPowerful; + result.econo = this->getCmd() == kFujitsuAcCmdEcono; + // Not supported. + result.light = false; + result.filter = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRFujitsuAC::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + fujitsu_ac_remote_model_t model = this->getModel(); + result += addIntToString(model, F("Model"), false); + 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 += addBoolToString(getPower(), F("Power")); + result += addModeToString(getMode(), kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + result += addTempToString(getTemp()); + result += addFanToString(getFanSpeed(), kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + 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 (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 += addBoolToString(getOutsideQuiet(), F("Outside Quiet")); + return result; +} + +#if DECODE_FUJITSU_AC +// Decode a Fujitsu AC IR message if possible. +// Places successful decode information in the results pointer. +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: The number of data bits to expect. Typically kFujitsuAcBits. +// 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: ALPHA / Untested. +// +// Ref: +// +bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits, + bool strict) { + uint16_t offset = kStartOffset; + uint16_t dataBitsSoFar = 0; + + // Have we got enough data to successfully decode? + if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1) + return false; // Can't possibly be a valid message. + + // Compliance + if (strict) { + switch (nbits) { + case kFujitsuAcBits: + case kFujitsuAcBits - 8: + case kFujitsuAcMinBits: + case kFujitsuAcMinBits + 8: + break; + default: + return false; // Must be called with the correct nr. of bits. + } + } + + // Header + if (!matchMark(results->rawbuf[offset++], kFujitsuAcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kFujitsuAcHdrSpace)) return false; + + // Data (Fixed signature) + match_result_t data_result = + matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8, + kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark, + kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) return false; // Fail + if (data_result.data != 0x1010006314) return false; // Signature failed. + dataBitsSoFar += kFujitsuAcMinBits - 8; + offset += data_result.used; + results->state[0] = 0x14; + results->state[1] = 0x63; + results->state[2] = 0x00; + results->state[3] = 0x10; + results->state[4] = 0x10; + + // Keep reading bytes until we either run out of message or state to fill. + for (uint16_t i = 5; + offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; + i++, dataBitsSoFar += 8, offset += data_result.used) { + data_result = matchData( + &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, + kFujitsuAcBitMark, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); + if (data_result.success == false) break; // Fail + results->state[i] = data_result.data; + } + + // Footer + if (offset > results->rawlen || + !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) + return false; + // The space is optional if we are out of capture. + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) + return false; + + // Compliance + if (strict) { + if (dataBitsSoFar != nbits) return false; + } + + results->decode_type = FUJITSU_AC; + results->bits = dataBitsSoFar; + + // Compliance + switch (dataBitsSoFar) { + case kFujitsuAcMinBits: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFC) return false; + return true; // Success + case kFujitsuAcMinBits + 8: + // Check if this values indicate that this should have been a long state + // message. + if (results->state[5] == 0xFE) return false; + // The last byte needs to be the inverse of the penultimate byte. + if (results->state[5] != (uint8_t)~results->state[6]) return false; + return true; // Success + case kFujitsuAcBits - 8: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFC) return false; + break; + case kFujitsuAcBits: + // Long messages of this size require this byte be correct. + if (results->state[5] != 0xFE) return false; + break; + default: + return false; // Unexpected size. + } + if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) + return false; + + // Success + return true; // All good. +} +#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Fujitsu.h similarity index 51% rename from lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Fujitsu.h index 78a4f8951..469e7ee9c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Fujitsu.h @@ -1,5 +1,16 @@ // Copyright 2017 Jonny Graham -// Copyright 2018 David Conran +// Copyright 2018-2019 David Conran + +// Supports: +// Brand: Fujitsu, Model: AR-RAH2E remote +// Brand: Fujitsu, Model: ASYG30LFCA A/C +// Brand: Fujitsu, Model: AR-DB1 remote +// Brand: Fujitsu, Model: AST9RSGCW A/C +// Brand: Fujitsu, Model: AR-REB1E remote +// Brand: Fujitsu, Model: ASYG7LMCA A/C +// Brand: Fujitsu, Model: AR-RAE1E remote +// Brand: Fujitsu General, Model: AR-JW2 remote + #ifndef IR_FUJITSU_H_ #define IR_FUJITSU_H_ @@ -7,8 +18,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" @@ -26,11 +35,15 @@ const uint8_t kFujitsuAcModeDry = 0x02; const uint8_t kFujitsuAcModeFan = 0x03; const uint8_t kFujitsuAcModeHeat = 0x04; -const uint8_t kFujitsuAcCmdStayOn = 0x00; -const uint8_t kFujitsuAcCmdTurnOn = 0x01; -const uint8_t kFujitsuAcCmdTurnOff = 0x02; -const uint8_t kFujitsuAcCmdStepHoriz = 0x79; -const uint8_t kFujitsuAcCmdStepVert = 0x6C; +const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000 +const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001 +const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010 +const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001 +const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001 +const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100 +const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101 +const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001 +const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010 const uint8_t kFujitsuAcFanAuto = 0x00; const uint8_t kFujitsuAcFanHigh = 0x01; @@ -70,45 +83,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, + const bool inverted = false, + const bool use_modulation = true); - void setModel(fujitsu_ac_remote_model_t model); - void stateReset(); + void setModel(const fujitsu_ac_remote_model_t model); + fujitsu_ac_remote_model_t getModel(void); + void stateReset(void); #if SEND_FUJITSU_AC void send(const uint16_t repeat = kFujitsuAcMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_FUJITSU_AC - void begin(); - void off(); - void stepHoriz(); - void stepVert(); - void setCmd(uint8_t cmd); - uint8_t getCmd(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFanSpeed(uint8_t fan); - uint8_t getFanSpeed(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwing(uint8_t mode); - uint8_t getSwing(); - uint8_t* getRaw(); + void begin(void); + void off(void); + void stepHoriz(void); + void toggleSwingHoriz(const bool update = true); + void stepVert(void); + void toggleSwingVert(const bool update = true); + void setCmd(const uint8_t cmd); + uint8_t getCmd(const bool raw = false); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFanSpeed(const uint8_t fan); + uint8_t getFanSpeed(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwing(const uint8_t mode); + uint8_t getSwing(const bool raw = false); + uint8_t* getRaw(void); bool setRaw(const uint8_t newState[], const uint16_t length); - uint8_t getStateLength(); - static bool validChecksum(uint8_t* state, uint16_t length); - bool getPower(); + 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); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -125,7 +150,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-2.6.0/src/ir_GICable.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_GICable.cpp similarity index 70% rename from lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_GICable.cpp index 229e4e5bb..6b3849b4c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_GICable.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_GICable.cpp @@ -1,4 +1,5 @@ // Copyright 2018 David Conran +// G.I. Cable #define __STDC_LIMIT_MACROS #include @@ -7,15 +8,9 @@ #include "IRsend.h" #include "IRutils.h" -// GGGG IIIII CCCCC AAA BBBBB LL EEEEEEE -// GG GG III CC C AAAAA BB B LL EE -// GG III CC AA AA BBBBBB LL EEEEE -// GG GG ... III ... CC C AAAAAAA BB BB LL EE -// GGGGGG ... IIIII ... CCCCC AA AA BBBBBB LLLLLLL EEEEEEE -// // Ref: // https://github.com/cyborg5/IRLib2/blob/master/IRLibProtocols/IRLib_P09_GICable.h -// https://github.com/markszabo/IRremoteESP8266/issues/447 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/447 // Constants const uint16_t kGicableHdrMark = 9000; @@ -71,32 +66,21 @@ void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) { // Status: Alpha / Not tested against a real device. bool IRrecv::decodeGICable(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) - return false; // Can't possibly be a valid GICABLE message. if (strict && nbits != kGicableBits) return false; // Not strictly an GICABLE message. uint64_t data = 0; uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset++], kGicableHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGicableHdrSpace)) return false; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kGicableBitMark, - kGicableOneSpace, kGicableBitMark, kGicableZeroSpace); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kGicableBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kGicableMinGap)) - return false; - + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kGicableHdrMark, kGicableHdrSpace, + kGicableBitMark, kGicableOneSpace, + kGicableBitMark, kGicableZeroSpace, + kGicableBitMark, kGicableMinGap, true); + if (!used) return false; + offset += used; // Compliance if (strict) { // We expect a repeat frame. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_GlobalCache.cpp similarity index 86% rename from lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_GlobalCache.cpp index daa9dd22c..8c9646970 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_GlobalCache.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_GlobalCache.cpp @@ -1,18 +1,12 @@ // Copyright 2016 Hisham Khalifa // Copyright 2017 David Conran -#include -#include "IRsend.h" - -// GGG L OOOO BBBB AA L CCCC AA CCCC H H EEEEEE -// G G L O O B B AAAA L C C AAAA C C H H E -// G L O O BBBBB A A L C A A C HHHHHH EEEE -// G GG L O O B BB AAAAAA L C C AAAAAA C C H H E -// GGGGG LLLLLL OOOO BBBBB A A LLLLLL CCCC A A CCCC H H EEEEEE - // Global Cache IR format sender originally added by Hisham Khalifa // (http://www.hishamkhalifa.com) +#include +#include "IRsend.h" + // Constants const uint16_t kGlobalCacheMaxRepeat = 50; const uint32_t kGlobalCacheMinUsec = 80; diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Goodweather.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Goodweather.cpp new file mode 100644 index 000000000..a196cb7ef --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Goodweather.cpp @@ -0,0 +1,464 @@ +// 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" + +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + +#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/crankyoldgit/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(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRGoodweatherAc::stateReset(void) { +} + +void IRGoodweatherAc::begin(void) { _irsend.begin(); } + +#if SEND_GOODWEATHER +void IRGoodweatherAc::send(const uint16_t repeat) { + _irsend.sendGoodweather(remote, kGoodweatherBits, repeat); +} +#endif // SEND_GOODWEATHER + +uint64_t IRGoodweatherAc::getRaw(void) { return remote; } + +void IRGoodweatherAc::setRaw(const uint64_t state) { remote = state; } + +void IRGoodweatherAc::on(void) { this->setPower(true); } + +void IRGoodweatherAc::off(void) { this->setPower(false); } + +void IRGoodweatherAc::setPower(const bool on) { + this->setCommand(kGoodweatherCmdPower); + if (on) + remote |= kGoodweatherPowerMask; + else + remote &= ~kGoodweatherPowerMask; +} + +bool IRGoodweatherAc::getPower(void) { return remote & kGoodweatherPowerMask; } + +// Set the temp. in deg C +void IRGoodweatherAc::setTemp(const uint8_t temp) { + uint8_t new_temp = std::max(kGoodweatherTempMin, temp); + new_temp = std::min(kGoodweatherTempMax, new_temp); + if (new_temp > this->getTemp()) this->setCommand(kGoodweatherCmdUpTemp); + if (new_temp < this->getTemp()) this->setCommand(kGoodweatherCmdDownTemp); + remote &= ~kGoodweatherTempMask; + remote |= (uint64_t)(new_temp - kGoodweatherTempMin) << kGoodweatherBitTemp; +} + +// Return the set temp. in deg C +uint8_t IRGoodweatherAc::getTemp(void) { + return ((remote & kGoodweatherTempMask) >> kGoodweatherBitTemp) + + kGoodweatherTempMin; +} + +// Set the speed of the fan +void IRGoodweatherAc::setFan(const uint8_t speed) { + switch (speed) { + case kGoodweatherFanAuto: + case kGoodweatherFanLow: + case kGoodweatherFanMed: + case kGoodweatherFanHigh: + this->setCommand(kGoodweatherCmdFan); + remote &= ~kGoodweatherFanMask; + remote |= ((uint64_t)speed << kGoodweatherBitFan); + break; + default: + this->setFan(kGoodweatherFanAuto); + } +} + +uint8_t IRGoodweatherAc::getFan() { + return (remote & kGoodweatherFanMask) >> kGoodweatherBitFan; +} + +void IRGoodweatherAc::setMode(const uint8_t mode) { + switch (mode) { + case kGoodweatherAuto: + case kGoodweatherDry: + case kGoodweatherCool: + case kGoodweatherFan: + case kGoodweatherHeat: + this->setCommand(kGoodweatherCmdMode); + remote &= ~kGoodweatherModeMask; + remote |= (uint64_t)mode << kGoodweatherBitMode; + break; + default: + // If we get an unexpected mode, default to AUTO. + this->setMode(kGoodweatherAuto); + } +} + +uint8_t IRGoodweatherAc::getMode() { + return (remote & kGoodweatherModeMask) >> kGoodweatherBitMode; +} + +void IRGoodweatherAc::setLight(const bool toggle) { + this->setCommand(kGoodweatherCmdLight); + if (toggle) + remote |= kGoodweatherLightMask; + else + remote &= ~kGoodweatherLightMask; +} + +bool IRGoodweatherAc::getLight() { return remote & kGoodweatherLightMask; } + +void IRGoodweatherAc::setSleep(const bool toggle) { + this->setCommand(kGoodweatherCmdSleep); + if (toggle) + remote |= kGoodweatherSleepMask; + else + remote &= ~kGoodweatherSleepMask; +} + +bool IRGoodweatherAc::getSleep() { return remote & kGoodweatherSleepMask; } + +void IRGoodweatherAc::setTurbo(const bool toggle) { + this->setCommand(kGoodweatherCmdTurbo); + if (toggle) + remote |= kGoodweatherTurboMask; + else + remote &= ~kGoodweatherTurboMask; +} + +bool IRGoodweatherAc::getTurbo() { return remote & kGoodweatherTurboMask; } + +void IRGoodweatherAc::setSwing(const uint8_t speed) { + switch (speed) { + case kGoodweatherSwingOff: + case kGoodweatherSwingSlow: + case kGoodweatherSwingFast: + this->setCommand(kGoodweatherCmdSwing); + remote &= ~kGoodweatherSwingMask; + remote |= ((uint64_t)speed << kGoodweatherBitSwing); + break; + default: + this->setSwing(kGoodweatherSwingOff); + } +} + +uint8_t IRGoodweatherAc::getSwing() { + return (remote & kGoodweatherSwingMask) >> kGoodweatherBitSwing; +} + +void IRGoodweatherAc::setCommand(const uint8_t cmd) { + if (cmd <= kGoodweatherCmdLight) { + remote &= ~kGoodweatherCommandMask; + remote |= (cmd << kGoodweatherBitCommand); + } +} + +uint8_t IRGoodweatherAc::getCommand() { + return (remote & kGoodweatherCommandMask) >> kGoodweatherBitCommand; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRGoodweatherAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kGoodweatherCool; + case stdAc::opmode_t::kHeat: + return kGoodweatherHeat; + case stdAc::opmode_t::kDry: + return kGoodweatherDry; + case stdAc::opmode_t::kFan: + return kGoodweatherFan; + default: + return kGoodweatherAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRGoodweatherAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kGoodweatherFanLow; + case stdAc::fanspeed_t::kMedium: + return kGoodweatherFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kGoodweatherFanHigh; + default: + return kGoodweatherFanAuto; + } +} + +// Convert a standard A/C Vertical Swing into its native version. +uint8_t IRGoodweatherAc::convertSwingV(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kHighest: + case stdAc::swingv_t::kHigh: + case stdAc::swingv_t::kMiddle: + return kGoodweatherSwingFast; + case stdAc::swingv_t::kLow: + case stdAc::swingv_t::kLowest: + case stdAc::swingv_t::kAuto: + return kGoodweatherSwingSlow; + default: + return kGoodweatherSwingOff; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRGoodweatherAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kGoodweatherCool: return stdAc::opmode_t::kCool; + case kGoodweatherHeat: return stdAc::opmode_t::kHeat; + case kGoodweatherDry: return stdAc::opmode_t::kDry; + case kGoodweatherFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRGoodweatherAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kGoodweatherFanHigh: return stdAc::fanspeed_t::kMax; + case kGoodweatherFanMed: return stdAc::fanspeed_t::kMedium; + case kGoodweatherFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRGoodweatherAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::GOODWEATHER; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() == kGoodweatherSwingOff ? + stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.sleep = this->getSleep() ? 0: -1; + // Not supported. + result.model = -1; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.econo = false; + result.filter = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRGoodweatherAc::toString() { + String result = ""; + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kGoodweatherAuto, kGoodweatherCool, + kGoodweatherHeat, kGoodweatherDry, kGoodweatherFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kGoodweatherFanHigh, kGoodweatherFanLow, + kGoodweatherFanAuto, kGoodweatherFanAuto, + kGoodweatherFanMed); + result += addLabeledString(getTurbo() ? F("Toggle") : F("-"), F("Turbo")); + result += addLabeledString(getLight() ? F("Toggle") : F("-"), F("Light")); + result += addLabeledString(getSleep() ? F("Toggle") : F("-"), F("Sleep")); + result += addIntToString(getSwing(), F("Swing")); + 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 += addIntToString(getCommand(), F("Command")); + 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-2.6.3.10/src/ir_Goodweather.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Goodweather.h new file mode 100644 index 000000000..76d559779 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Goodweather.h @@ -0,0 +1,137 @@ +// Goodweather A/C +// +// Copyright 2019 ribeirodanielf +// Copyright 2019 David Conran + +// Supports: +// Brand: Goodweather, Model: ZH/JT-03 remote + +#ifndef IR_GOODWEATHER_H_ +#define IR_GOODWEATHER_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Ref: +// https://github.com/crankyoldgit/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(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_GOODWEATHER + void send(const uint16_t repeat = kGoodweatherMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_GOODWEATHER + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(); + void setSwing(const uint8_t speed); + uint8_t getSwing(void); + void setSleep(const bool toggle); + bool getSleep(void); + void setTurbo(const bool toggle); + bool getTurbo(void); + void setLight(const bool toggle); + bool getLight(void); + void setCommand(const uint8_t cmd); + uint8_t getCommand(void); + uint64_t getRaw(void); + void setRaw(const uint64_t state); + uint8_t convertMode(const stdAc::opmode_t mode); + uint8_t convertFan(const stdAc::fanspeed_t speed); + uint8_t convertSwingV(const stdAc::swingv_t swingv); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint64_t remote; // The state of the IR remote in IR code form. +}; +#endif // IR_GOODWEATHER_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Gree.cpp similarity index 61% rename from lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Gree.cpp index 756f008d4..34fc5bc91 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Gree.cpp @@ -18,12 +18,6 @@ #include "IRutils.h" #include "ir_Kelvinator.h" -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE - // Constants // Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h const uint16_t kGreeHdrMark = 9000; @@ -35,6 +29,13 @@ const uint16_t kGreeMsgSpace = 19000; const uint8_t kGreeBlockFooter = 0b010; const uint8_t kGreeBlockFooterBits = 3; +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + #if SEND_GREE // Send a Gree Heat Pump message. // @@ -47,7 +48,8 @@ const uint8_t kGreeBlockFooterBits = 3; // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendGree(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kGreeStateLength) return; // Not enough bytes to send a proper message. @@ -80,7 +82,8 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { // // Ref: // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { +void IRsend::sendGree(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { if (nbits != kGreeBits) return; // Wrong nr. of bits to send a proper message. // Set IR carrier frequency @@ -110,9 +113,11 @@ void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { } #endif // SEND_GREE -IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRGreeAC::IRGreeAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +127,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 +140,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 +172,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 +203,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 +217,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 +242,61 @@ 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) { - remote_state[2] &= ~kGreeLightMask; - remote_state[2] |= (state << 5); +void IRGreeAC::setLight(const bool on) { + if (on) + remote_state[2] |= kGreeLightMask; + else + remote_state[2] &= ~kGreeLightMask; } -bool IRGreeAC::getLight() { return remote_state[2] & kGreeLightMask; } +bool IRGreeAC::getLight(void) { return remote_state[2] & kGreeLightMask; } -void IRGreeAC::setXFan(const bool state) { - remote_state[2] &= ~kGreeXfanMask; - remote_state[2] |= (state << 7); +void IRGreeAC::setIFeel(const bool on) { + if (on) + remote_state[5] |= kGreeIFeelMask; + else + remote_state[5] &= ~kGreeIFeelMask; } -bool IRGreeAC::getXFan() { return remote_state[2] & kGreeXfanMask; } +bool IRGreeAC::getIFeel(void) { return remote_state[5] & kGreeIFeelMask; } -void IRGreeAC::setSleep(const bool state) { - remote_state[0] &= ~kGreeSleepMask; - remote_state[0] |= (state << 7); +void IRGreeAC::setWiFi(const bool on) { + if (on) + remote_state[5] |= kGreeWiFiMask; + else + remote_state[5] &= ~kGreeWiFiMask; } -bool IRGreeAC::getSleep() { return remote_state[0] & kGreeSleepMask; } +bool IRGreeAC::getWiFi(void) { return remote_state[5] & kGreeWiFiMask; } -void IRGreeAC::setTurbo(const bool state) { - remote_state[2] &= ~kGreeTurboMask; - remote_state[2] |= (state << 4); +void IRGreeAC::setXFan(const bool on) { + if (on) + remote_state[2] |= kGreeXfanMask; + else + remote_state[2] &= ~kGreeXfanMask; } -bool IRGreeAC::getTurbo() { return remote_state[2] & kGreeTurboMask; } +bool IRGreeAC::getXFan(void) { return remote_state[2] & kGreeXfanMask; } + +void IRGreeAC::setSleep(const bool on) { + if (on) + remote_state[0] |= kGreeSleepMask; + else + remote_state[0] &= ~kGreeSleepMask; +} + +bool IRGreeAC::getSleep(void) { return remote_state[0] & kGreeSleepMask; } + +void IRGreeAC::setTurbo(const bool on) { + if (on) + remote_state[2] |= kGreeTurboMask; + else + 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 +328,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,79 +387,86 @@ 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 result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kGreeAuto: - result += F(" (AUTO)"); - break; - case kGreeCool: - result += F(" (COOL)"); - break; - case kGreeHeat: - result += F(" (HEAT)"); - break; - case kGreeDry: - result += F(" (DRY)"); - break; - case kGreeFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case 0: - result += F(" (AUTO)"); - break; - case kGreeFanMax: - result += F(" (MAX)"); - break; - } - result += F(", Turbo: "); - if (getTurbo()) - result += F("On"); - else - result += F("Off"); - result += F(", XFan: "); - if (getXFan()) - result += F("On"); - else - result += F("Off"); - result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing Vertical Mode: "); - if (getSwingVerticalAuto()) - result += F("Auto"); - else - result += F("Manual"); - result += F(", Swing Vertical Pos: "); - result += uint64ToString(getSwingVerticalPosition()); + result.reserve(150); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kGreeAuto, kGreeCool, kGreeHeat, + kGreeDry, kGreeFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kGreeFanMax, kGreeFanMin, kGreeFanAuto, + kGreeFanAuto, kGreeFanMed); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getIFeel(), F("IFeel")); + result += addBoolToString(getWiFi(), F("WiFi")); + result += addBoolToString(getXFan(), F("XFan")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addLabeledString(getSwingVerticalAuto() ? F("Auto") : F("Manual"), + F("Swing Vertical Mode")); + result += addIntToString(getSwingVerticalPosition(), F("Swing Vertical Pos")); switch (getSwingVerticalPosition()) { case kGreeSwingLastPos: result += F(" (Last Pos)"); @@ -458,31 +496,25 @@ bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) { if (strict && nbits != kGreeBits) return false; // Not strictly a Gree message. - uint32_t data; uint16_t offset = kStartOffset; // There are two blocks back-to-back in a full Gree IR message // sequence. - int8_t state_pos = 0; - match_result_t data_result; - // Header - if (!matchMark(results->rawbuf[offset++], kGreeHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGreeHdrSpace)) return false; - // Data Block #1 (32 bits) - data_result = - matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace, - kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record Data Block #1 in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; + uint16_t used; + // Header + Data Block #1 (32 bits) + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits / 2, + kGreeHdrMark, kGreeHdrSpace, + kGreeBitMark, kGreeOneSpace, + kGreeBitMark, kGreeZeroSpace, + 0, 0, false, + kTolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; // Block #1 footer (3 bits, B010) + match_result_t data_result; data_result = matchData(&(results->rawbuf[offset]), kGreeBlockFooterBits, kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false); @@ -490,40 +522,24 @@ bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) { if (data_result.data != kGreeBlockFooter) return false; offset += data_result.used; - // Inter-block gap. - if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGreeMsgSpace)) return false; - - // Data Block #2 (32 bits) - data_result = - matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace, - kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record Data Block #2 in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Footer. - if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], kGreeMsgSpace)) - return false; + // Inter-block gap + Data Block #2 (32 bits) + Footer + if (!matchGeneric(results->rawbuf + offset, results->state + 4, + results->rawlen - offset, nbits / 2, + kGreeBitMark, kGreeMsgSpace, + kGreeBitMark, kGreeOneSpace, + kGreeBitMark, kGreeZeroSpace, + kGreeBitMark, kGreeMsgSpace, true, + kTolerance, kMarkExcess, false)) return false; // Compliance if (strict) { - // Correct size/length) - if (state_pos != kGreeStateLength) return false; // Verify the message's checksum is correct. if (!IRGreeAC::validChecksum(results->state)) return false; } // Success results->decode_type = GREE; - results->bits = state_pos * 8; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Gree.h similarity index 68% rename from lib/IRremoteESP8266-2.6.0/src/ir_Gree.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Gree.h index c3c5916dc..c25f40b41 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Gree.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Gree.h @@ -1,6 +1,11 @@ -// Kelvinator A/C -// // Copyright 2016 David Conran +// Gree A/C +// +// Supports: +// Brand: Ultimate, Model: Heat Pump +// Brand: EKOKAI, Model: A/C +// Brand: RusClimate, Model: EACS/I-09HAR_X/N3 A/C +// Brand: RusClimate, Model: YAW1F remote #ifndef IR_GREE_H_ #define IR_GREE_H_ @@ -9,8 +14,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,12 +21,6 @@ #include "IRsend_test.h" #endif -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE - // Constants const uint8_t kGreeAuto = 0; const uint8_t kGreeCool = 1; @@ -44,11 +41,16 @@ const uint8_t kGreePower2Mask = 0b01000000; const uint8_t kGreeXfanMask = 0b10000000; // Byte 4 const uint8_t kGreeSwingPosMask = 0b00001111; +// byte 5 +const uint8_t kGreeIFeelMask = 0b00000100; +const uint8_t kGreeWiFiMask = 0b01000000; + 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 kGreeFanMed = 2; const uint8_t kGreeFanMax = 3; const uint8_t kGreeSwingLastPos = 0b00000000; @@ -85,46 +87,52 @@ const uint8_t kGreeSwingUpAuto = 0b00001011; // Classes class IRGreeAC { public: - explicit IRGreeAC(uint16_t pin); + explicit IRGreeAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_GREE void send(const uint16_t repeat = kGreeDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_GREE - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t new_mode); - uint8_t getMode(); - void setLight(const bool state); - bool getLight(); - void setXFan(const bool state); - bool getXFan(); - void setSleep(const bool state); - bool getSleep(); - void setTurbo(const bool state); - bool getTurbo(); + uint8_t getMode(void); + void setLight(const bool on); + bool getLight(void); + void setXFan(const bool on); + bool getXFan(void); + void setSleep(const bool on); + bool getSleep(void); + void setTurbo(const bool on); + bool getTurbo(void); + void setIFeel(const bool on); + bool getIFeel(void); + void setWiFi(const bool on); + bool getWiFi(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(); -#else - std::string toString(); -#endif + String toString(void); #ifndef UNIT_TEST private: @@ -135,7 +143,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-2.6.0/src/ir_Haier.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Haier.cpp similarity index 66% rename from lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Haier.cpp index f76bb3447..8cb24334c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Haier.cpp @@ -1,4 +1,5 @@ // Copyright 2018 crankyoldgit +// Code to emulate Haier protocol compatible devices. // The specifics of reverse engineering the protocols details: // * HSU07-HEA03 by kuzin2006. // * YR-W02/HSU-09HMC203 by non7top. @@ -6,27 +7,19 @@ #include "ir_Haier.h" #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRutils.h" -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR - // Supported devices: // * Haier HSU07-HEA03 Remote control. // * Haier YR-W02 Remote control // * Haier HSU-09HMC203 A/C unit. // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/404 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/404 // https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 -// https://github.com/markszabo/IRremoteESP8266/issues/485 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/485 // https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods // Constants @@ -37,6 +30,14 @@ const uint16_t kHaierAcOneSpace = 1650; const uint16_t kHaierAcZeroSpace = 650; const uint32_t kHaierAcMinGap = 150000; // Completely made up value. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::minsToString; + #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) // Send a Haier A/C message. (HSU07-HEA03 remote) // @@ -47,8 +48,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 +75,18 @@ 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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +95,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 +104,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 +117,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 +146,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 +169,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 +187,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 +213,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 +279,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 +288,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. @@ -298,23 +306,6 @@ void IRHaierAC::setSwing(const uint8_t state) { } } -// Convert a Haier time into a human readable string. -#ifdef ARDUINO -String IRHaierAC::timeToString(const uint16_t nr_mins) { - String result = ""; -#else -std::string IRHaierAC::timeToString(const uint16_t nr_mins) { - std::string result = ""; -#endif // ARDUINO - - if (nr_mins / 24 < 10) result += '0'; // Zero pad. - result += uint64ToString(nr_mins / 60); - result += ':'; - if (nr_mins % 60 < 10) result += '0'; // Zero pad. - result += uint64ToString(nr_mins % 60); - return result; -} - // Convert a standard A/C mode into its native mode. uint8_t IRHaierAC::convertMode(const stdAc::opmode_t mode) { switch (mode) { @@ -364,17 +355,69 @@ 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 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); + result += addIntToString(cmd, F("Command"), false); result += F(" ("); switch (cmd) { case kHaierAcCmdOff: @@ -414,41 +457,12 @@ std::string IRHaierAC::toString() { result += F("Unknown"); } result += ')'; - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kHaierAcAuto: - result += F(" (AUTO)"); - break; - case kHaierAcCool: - result += F(" (COOL)"); - break; - case kHaierAcHeat: - result += F(" (HEAT)"); - break; - case kHaierAcDry: - result += F(" (DRY)"); - break; - case kHaierAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kHaierAcFanAuto: - result += F(" (AUTO)"); - break; - case kHaierAcFanHigh: - result += F(" (MAX)"); - break; - } - result += F(", Swing: "); - result += uint64ToString(getSwing()); + result += addModeToString(getMode(), kHaierAcAuto, kHaierAcCool, kHaierAcHeat, + kHaierAcDry, kHaierAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHaierAcFanHigh, kHaierAcFanLow, + kHaierAcFanAuto, kHaierAcFanAuto, kHaierAcFanMed); + result += addIntToString(getSwing(), F("Swing")); result += F(" ("); switch (getSwing()) { case kHaierAcSwingOff: @@ -467,37 +481,24 @@ std::string IRHaierAC::toString() { result += F("Unknown"); } result += ')'; - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Health: "); - if (getHealth()) - result += F("On"); - else - result += F("Off"); - result += F(", Current Time: "); - result += timeToString(getCurrTime()); - result += F(", On Timer: "); - if (getOnTimer() >= 0) - result += timeToString(getOnTimer()); - else - result += F("Off"); - result += F(", Off Timer: "); - if (getOffTimer() >= 0) - result += timeToString(getOffTimer()); - else - result += F("Off"); - + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getHealth(), F("Health")); + result += addLabeledString(minsToString(getCurrTime()), F("Current Time")); + result += addLabeledString( + getOnTimer() >= 0 ? minsToString(getOnTimer()) : F("Off"), F("On Timer")); + result += addLabeledString( + getOffTimer() >= 0 ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); return result; } // End of IRHaierAC class. // Class for emulating a Haier YRW02 remote -IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); } +IRHaierACYRW02::IRHaierACYRW02(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +507,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 +517,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 +531,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 +558,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 +579,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 +599,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 +652,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 +666,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,22 +746,71 @@ 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 result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); + result.reserve(130); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); uint8_t cmd = getButton(); - result += F(", Button: "); - result += uint64ToString(cmd); + result += addIntToString(cmd, F("Button")); result += F(" ("); switch (cmd) { case kHaierAcYrw02ButtonPower: @@ -788,49 +844,14 @@ std::string IRHaierACYRW02::toString() { result += F("Unknown"); } result += ')'; - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kHaierAcYrw02Auto: - result += F(" (Auto)"); - break; - case kHaierAcYrw02Cool: - result += F(" (Cool)"); - break; - case kHaierAcYrw02Heat: - result += F(" (Heat)"); - break; - case kHaierAcYrw02Dry: - result += F(" (Dry)"); - break; - case kHaierAcYrw02Fan: - result += F(" (Fan)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kHaierAcYrw02FanAuto: - result += F(" (Auto)"); - break; - case kHaierAcYrw02FanHigh: - result += F(" (High)"); - break; - case kHaierAcYrw02FanLow: - result += F(" (Low)"); - break; - case kHaierAcYrw02FanMed: - result += F(" (Med)"); - break; - default: - result += F(" (Unknown)"); - } - result += F(", Turbo: "); - result += uint64ToString(getTurbo()); + result += addModeToString(getMode(), kHaierAcYrw02Auto, kHaierAcYrw02Cool, + kHaierAcYrw02Heat, kHaierAcYrw02Dry, + kHaierAcYrw02Fan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHaierAcYrw02FanHigh, kHaierAcYrw02FanLow, + kHaierAcYrw02FanAuto, kHaierAcYrw02FanAuto, + kHaierAcYrw02FanMed); + result += addIntToString(getTurbo(), F("Turbo")); result += F(" ("); switch (getTurbo()) { case kHaierAcYrw02TurboOff: @@ -846,8 +867,7 @@ std::string IRHaierACYRW02::toString() { result += F("Unknown"); } result += ')'; - result += F(", Swing: "); - result += uint64ToString(getSwing()); + result += addIntToString(getSwing(), F("Swing")); result += F(" ("); switch (getSwing()) { case kHaierAcYrw02SwingOff: @@ -872,17 +892,8 @@ std::string IRHaierACYRW02::toString() { result += F("Unknown"); } result += ')'; - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Health: "); - if (getHealth()) - result += F("On"); - else - result += F("Off"); - + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getHealth(), F("Health")); return result; } // End of IRHaierACYRW02 class. @@ -901,9 +912,6 @@ std::string IRHaierACYRW02::toString() { // bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - if (strict) { if (nbits != kHaierACBits) return false; // Not strictly a HAIER_AC message. @@ -914,27 +922,18 @@ bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, uint16_t offset = kStartOffset; - // Header + // Pre-Header if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; if (!matchSpace(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchSpace(results->rawbuf[offset++], kHaierAcHdrGap)) return false; - // Data - for (uint16_t i = 0; i < nbits / 8; i++) { - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 8, kHaierAcBitMark, - kHaierAcOneSpace, kHaierAcBitMark, kHaierAcZeroSpace); - if (data_result.success == false) return false; - offset += data_result.used; - results->state[i] = (uint8_t)data_result.data; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kHaierAcBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kHaierAcMinGap)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHaierAcHdr, kHaierAcHdrGap, + kHaierAcBitMark, kHaierAcOneSpace, + kHaierAcBitMark, kHaierAcZeroSpace, + kHaierAcBitMark, kHaierAcMinGap, true, + kTolerance, kMarkExcess)) return false; // Compliance if (strict) { diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Haier.h similarity index 78% rename from lib/IRremoteESP8266-2.6.0/src/ir_Haier.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Haier.h index 8f7b35196..ea5f67ab3 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Haier.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Haier.h @@ -1,13 +1,16 @@ // Copyright 2018 crankyoldgit // The specifics of reverse engineering the protocol details by kuzin2006 +// Supports: +// Brand: Haier, Model: HSU07-HEA03 remote +// Brand: Haier, Model: YR-W02 remote +// Brand: Haier, Model: HSU-09HMC203 A/C + #ifndef IR_HAIER_H_ #define IR_HAIER_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,16 +18,10 @@ #include "IRsend_test.h" #endif -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR - // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/404 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/404 // https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 -// https://github.com/markszabo/IRremoteESP8266/issues/485 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/485 // https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods // Constants @@ -56,6 +53,7 @@ const uint8_t kHaierAcSwingDown = 0b00000010; const uint8_t kHaierAcSwingChg = 0b00000011; // Byte 6 +const uint8_t kHaierAcModeMask = 0b11100000; const uint8_t kHaierAcAuto = 0; const uint8_t kHaierAcCool = 1; const uint8_t kHaierAcDry = 2; @@ -69,6 +67,9 @@ const uint8_t kHaierAcFanHigh = 3; const uint16_t kHaierAcMaxTime = (23 * 60) + 59; +// Byte 7 +const uint8_t kHaierAcSleepBit = 0b01000000; + // Legacy Haier AC defines. #define HAIER_AC_MIN_TEMP kHaierAcMinTemp #define HAIER_AC_DEF_TEMP kHaierAcDefTemp @@ -186,57 +187,56 @@ const uint8_t kHaierAcYrw02ButtonSleep = 0xB; class IRHaierAC { public: - explicit IRHaierAC(uint16_t pin); + explicit IRHaierAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_HAIER_AC void send(const uint16_t repeat = kHaierAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_HAIER_AC - void begin(); + void begin(void); void setCommand(const uint8_t command); - uint8_t getCommand(); + uint8_t getCommand(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); - uint8_t getMode(); + uint8_t getMode(void); void setMode(const uint8_t mode); - bool getSleep(); - void setSleep(const bool state); - bool getHealth(); - void setHealth(const bool state); + bool getSleep(void); + void setSleep(const bool on); + bool getHealth(void); + void setHealth(const bool on); - int16_t getOnTimer(); + int16_t getOnTimer(void); void setOnTimer(const uint16_t mins); - int16_t getOffTimer(); + int16_t getOffTimer(void); void setOffTimer(const uint16_t mins); - void cancelTimers(); + void cancelTimers(void); - uint16_t getCurrTime(); + uint16_t getCurrTime(void); void setCurrTime(const uint16_t mins); - uint8_t getSwing(); + uint8_t getSwing(void); void setSwing(const uint8_t state); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static bool validChecksum(uint8_t state[], const uint16_t length = kHaierACStateLength); 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); -#else - std::string toString(); - static std::string timeToString(const uint16_t nr_mins); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -245,61 +245,62 @@ 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); }; class IRHaierACYRW02 { public: - explicit IRHaierACYRW02(uint16_t pin); + explicit IRHaierACYRW02(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #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); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -308,8 +309,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-2.6.0/src/ir_Hitachi.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Hitachi.cpp similarity index 64% rename from lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Hitachi.cpp index b88189f4a..dedaa5696 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Hitachi.cpp @@ -15,14 +15,8 @@ #include "IRsend.h" #include "IRutils.h" -// HH HH IIIII TTTTTTT AAA CCCCC HH HH IIIII -// HH HH III TTT AAAAA CC C HH HH III -// HHHHHHH III TTT AA AA CC HHHHHHH III -// HH HH III TTT AAAAAAA CC C HH HH III -// HH HH IIIII TTT AA AA CCCCC HH HH IIIII - // Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 const uint16_t kHitachiAcHdrMark = 3300; const uint16_t kHitachiAcHdrSpace = 1700; const uint16_t kHitachiAc1HdrMark = 3400; @@ -32,6 +26,13 @@ const uint16_t kHitachiAcOneSpace = 1250; const uint16_t kHitachiAcZeroSpace = 500; const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + #if (SEND_HITACHI_AC || SEND_HITACHI_AC2) // Send a Hitachi A/C message. // @@ -43,9 +44,9 @@ const uint32_t kHitachiAcMinGap = kDefaultMessageGap; // Just a guess. // Status: ALPHA / Untested. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 -void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAcStateLength) return; // Not enough bytes to send a proper message. sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, @@ -69,10 +70,10 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, // Status: BETA / Appears to work. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/453 +// https://github.com/crankyoldgit/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, @@ -96,10 +97,10 @@ void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, // Status: BETA / Appears to work. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 +// https://github.com/crankyoldgit/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 +111,11 @@ 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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRHitachiAc::stateReset() { +void IRHitachiAc::stateReset(void) { remote_state[0] = 0x80; remote_state[1] = 0x08; remote_state[2] = 0x0C; @@ -130,7 +133,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 +151,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 +168,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 +177,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 +203,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 +225,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 +244,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 +253,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,68 +296,68 @@ 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 result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kHitachiAcAuto: - result += F(" (AUTO)"); - break; - case kHitachiAcCool: - result += F(" (COOL)"); - break; - case kHitachiAcHeat: - result += F(" (HEAT)"); - break; - case kHitachiAcDry: - result += F(" (DRY)"); - break; - case kHitachiAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kHitachiAcFanAuto: - result += F(" (AUTO)"); - break; - case kHitachiAcFanLow: - result += F(" (LOW)"); - break; - case kHitachiAcFanHigh: - result += F(" (HIGH)"); - break; - default: - result += F(" (UNKNOWN)"); - break; - } - result += F(", Swing (Vertical): "); - if (getSwingVertical()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing (Horizontal): "); - if (getSwingHorizontal()) - result += F("On"); - else - result += F("Off"); + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kHitachiAcAuto, kHitachiAcCool, + kHitachiAcHeat, kHitachiAcDry, kHitachiAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kHitachiAcFanHigh, kHitachiAcFanLow, + kHitachiAcFanAuto, kHitachiAcFanAuto, + kHitachiAcFanMed); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); return result; } @@ -373,10 +378,10 @@ std::string IRHitachiAc::toString() { // Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA // // 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) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/417 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/453 +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. @@ -391,57 +396,33 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, } } uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; - - // Header + uint16_t hmark; + uint32_t hspace; if (nbits == kHitachiAc1Bits) { - if (!matchMark(results->rawbuf[offset++], kHitachiAc1HdrMark, kTolerance)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAc1HdrSpace, kTolerance)) - return false; - } else { // Everything else. - if (!matchMark(results->rawbuf[offset++], kHitachiAcHdrMark, kTolerance)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAcHdrSpace, kTolerance)) - return false; + hmark = kHitachiAc1HdrMark; + hspace = kHitachiAc1HdrSpace; + } else { + hmark = kHitachiAcHdrMark; + hspace = kHitachiAcHdrSpace; } - // Data - // Keep reading bytes until we either run out of message 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, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, - kHitachiAcZeroSpace, kTolerance); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kHitachiAcBitMark, kTolerance)) - return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], kHitachiAcMinGap, kTolerance)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + hmark, hspace, + kHitachiAcBitMark, kHitachiAcOneSpace, + kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, true, + kTolerance)) return false; // Compliance if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - switch (dataBitsSoFar / 8) { - case kHitachiAcStateLength: - case kHitachiAc1StateLength: - case kHitachiAc2StateLength: - break; // Continue - default: - return false; - } - if (dataBitsSoFar / 8 == kHitachiAcStateLength && + if (nbits / 8 == kHitachiAcStateLength && !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) return false; } // Success - switch (dataBitsSoFar) { + switch (nbits) { case kHitachiAc1Bits: results->decode_type = HITACHI_AC1; break; @@ -452,7 +433,7 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, default: results->decode_type = HITACHI_AC; } - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Hitachi.h similarity index 68% rename from lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Hitachi.h index 532717447..b9bfd391c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Hitachi.h @@ -2,6 +2,11 @@ // // Copyright 2018 David Conran +// Supports: +// Brand: Hitachi, Model: RAS-35THA6 remote +// Brand: Hitachi, Model: LT0541-HTA remote +// Brand: Hitachi, Model: Series VI A/C (Circa 2007) + #ifndef IR_HITACHI_H_ #define IR_HITACHI_H_ @@ -9,8 +14,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -26,6 +29,7 @@ const uint8_t kHitachiAcDry = 5; const uint8_t kHitachiAcFan = 0xC; const uint8_t kHitachiAcFanAuto = 1; const uint8_t kHitachiAcFanLow = 2; +const uint8_t kHitachiAcFanMed = 3; const uint8_t kHitachiAcFanHigh = 5; const uint8_t kHitachiAcMinTemp = 16; // 16C const uint8_t kHitachiAcMaxTemp = 32; // 32C @@ -34,28 +38,30 @@ const uint8_t kHitachiAcAutoTemp = 23; // 23C // Classes class IRHitachiAc { public: - explicit IRHitachiAc(uint16_t pin); + explicit IRHitachiAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_HITACHI_AC void send(const uint16_t repeat = kHitachiAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_HITACHI_AC - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); void setPower(const bool on); - bool getPower(); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setSwingVertical(const bool on); - bool getSwingVertical(); + bool getSwingVertical(void); void setSwingHorizontal(const bool on); - bool getSwingHorizontal(); - uint8_t* getRaw(); + bool getSwingHorizontal(void); + uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kHitachiAcStateLength); static bool validChecksum(const uint8_t state[], @@ -64,11 +70,10 @@ class IRHitachiAc { 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 + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Inax.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Inax.cpp new file mode 100644 index 000000000..073580fae --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Inax.cpp @@ -0,0 +1,83 @@ +// Copyright 2019 David Conran (crankyoldgit) +// Support for an IR controlled Robot Toilet + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Supports: +// Brand: Lixil, Model: Inax DT-BA283 Toilet + +// Documentation: +// https://www.lixil-manual.com/GCW-1365-16050/GCW-1365-16050.pdf + +// Constants +// Ref: +// https://github.com/crankyoldgit/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/crankyoldgit/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: Stable / Known working. +// +bool IRrecv::decodeInax(decode_results *results, const uint16_t nbits, + const bool strict) { + if (strict && nbits != kInaxBits) + return false; // We expect Inax to be a certain sized message. + + uint64_t data = 0; + uint16_t offset = kStartOffset; + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kInaxHdrMark, kInaxHdrSpace, + kInaxBitMark, kInaxOneSpace, + kInaxBitMark, kInaxZeroSpace, + kInaxBitMark, kInaxMinGap, true)) return false; + // 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-2.6.0/src/ir_JVC.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_JVC.cpp similarity index 78% rename from lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_JVC.cpp index 47df29dc4..7038e9d3e 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_JVC.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_JVC.cpp @@ -7,12 +7,6 @@ #include "IRtimer.h" #include "IRutils.h" -// JJJJJ V V CCCC -// J V V C -// J V V C -// J J V V C -// J V CCCC - // JVC originally added by Kristian Lauszus // (Thanks to zenwheel and other people at the original blog post) @@ -119,40 +113,23 @@ bool IRrecv::decodeJVC(decode_results *results, uint16_t nbits, bool strict) { uint16_t offset = kStartOffset; bool isRepeat = true; - uint32_t m_tick; - uint32_t s_tick; // Header // (Optional as repeat codes don't have the header) if (matchMark(results->rawbuf[offset], kJvcHdrMark)) { isRepeat = false; - m_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrMarkTicks; + offset++; if (results->rawlen < 2 * nbits + 4) return false; // Can't possibly be a valid JVC message with a header. - if (!matchSpace(results->rawbuf[offset], kJvcHdrSpace)) return false; - s_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrSpaceTicks; - } else { - // We can't easily auto-calibrate as there is no header, so assume - // the default tick time. - m_tick = kJvcTick; - s_tick = kJvcTick; + if (!matchSpace(results->rawbuf[offset++], kJvcHdrSpace)) return false; } - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kJvcBitMarkTicks * m_tick, - kJvcOneSpaceTicks * s_tick, kJvcBitMarkTicks * m_tick, - kJvcZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kJvcBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kJvcMinGapTicks * s_tick)) - return false; - + // Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, + kJvcBitMark, kJvcOneSpace, + kJvcBitMark, kJvcZeroSpace, + kJvcBitMark, kJvcMinGap, true)) return false; // Success results->decode_type = JVC; results->bits = nbits; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Kelvinator.cpp similarity index 59% rename from lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Kelvinator.cpp index c69f4cb8a..f280d0161 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Kelvinator.cpp @@ -24,12 +24,6 @@ #include "IRsend.h" #include "IRutils.h" -// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR -// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR - // Constants const uint16_t kKelvinatorTick = 85; @@ -72,6 +66,13 @@ const uint8_t kKelvinatorXfan = 1 << kKelvinatorXfanOffset; const uint8_t kKelvinatorTurboOffset = 4; const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset; +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; + #if SEND_KELVINATOR // Send a Kelvinator A/C message. // @@ -82,8 +83,8 @@ const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset; // // Status: STABLE / Known working. // -void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendKelvinator(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kKelvinatorStateLength) return; // Not enough bytes to send a proper message. @@ -124,36 +125,38 @@ void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, } #endif // SEND_KELVINATOR -IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { stateReset(); } +IRKelvinatorAC::IRKelvinatorAC(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +199,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 +247,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,87 +376,67 @@ 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 result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kKelvinatorAuto: - result += F(" (AUTO)"); - break; - case kKelvinatorCool: - result += F(" (COOL)"); - break; - case kKelvinatorHeat: - result += F(" (HEAT)"); - break; - case kKelvinatorDry: - result += F(" (DRY)"); - break; - case kKelvinatorFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kKelvinatorFanAuto: - result += F(" (AUTO)"); - break; - case kKelvinatorFanMax: - result += F(" (MAX)"); - break; - } - result += F(", Turbo: "); - if (getTurbo()) - result += F("On"); - else - result += F("Off"); - result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); - result += F(", XFan: "); - if (getXFan()) - result += F("On"); - else - result += F("Off"); - result += F(", IonFilter: "); - if (getIonFilter()) - result += F("On"); - else - result += F("Off"); - result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing (Horizontal): "); - if (getSwingHorizontal()) - result += F("On"); - else - result += F("Off"); - result += F(", Swing (Vertical): "); - if (getSwingVertical()) - result += F("On"); - else - result += F("Off"); + result.reserve(160); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kKelvinatorAuto, kKelvinatorCool, + kKelvinatorHeat, kKelvinatorDry, kKelvinatorFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kKelvinatorFanMax, kKelvinatorFanMin, + kKelvinatorFanAuto, kKelvinatorFanAuto, + kKelvinatorBasicFanMax); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getXFan(), F("XFan")); + result += addBoolToString(getIonFilter(), F("IonFilter")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getSwingHorizontal(), F("Swing (Horizontal)")); + result += addBoolToString(getSwingVertical(), F("Swing (Vertical)")); return result; } @@ -458,7 +450,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 < @@ -467,101 +459,66 @@ bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits, if (strict && nbits != kKelvinatorBits) return false; // Not strictly a Kelvinator message. - uint32_t data; uint16_t offset = kStartOffset; // There are two messages back-to-back in a full Kelvinator IR message // sequence. - int8_t state_pos = 0; + int8_t pos = 0; for (uint8_t s = 0; s < 2; s++) { match_result_t data_result; - // Header - if (!matchMark(results->rawbuf[offset], kKelvinatorHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = - results->rawbuf[offset++] * kRawTick / kKelvinatorHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kKelvinatorHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kKelvinatorHdrSpaceTicks; - - // Data (Command) (32 bits) - data_result = matchData( - &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record command data in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; + uint16_t used; + // Header + Data Block #1 (32 bits) + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, 32, + kKelvinatorHdrMark, kKelvinatorHdrSpace, + kKelvinatorBitMark, kKelvinatorOneSpace, + kKelvinatorBitMark, kKelvinatorZeroSpace, + 0, 0, false, + kTolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += 4; // Command data footer (3 bits, B010) data_result = matchData( &(results->rawbuf[offset]), kKelvinatorCmdFooterBits, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); + kKelvinatorBitMark, kKelvinatorOneSpace, + kKelvinatorBitMark, kKelvinatorZeroSpace, + kTolerance, kMarkExcess, false); if (data_result.success == false) return false; if (data_result.data != kKelvinatorCmdFooter) return false; offset += data_result.used; // Interdata gap. - if (!matchMark(results->rawbuf[offset++], - kKelvinatorBitMarkTicks * mark_tick)) + if (!matchMark(results->rawbuf[offset++], kKelvinatorBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], - kKelvinatorGapSpaceTicks * space_tick)) + if (!matchSpace(results->rawbuf[offset++], kKelvinatorGapSpace)) return false; // Data (Options) (32 bits) - data_result = matchData( - &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record option data in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Inter-sequence gap. (Double length gap) - if (!matchMark(results->rawbuf[offset++], - kKelvinatorBitMarkTicks * mark_tick)) - return false; - if (s == 0) { - if (!matchSpace(results->rawbuf[offset++], - kKelvinatorGapSpaceTicks * space_tick * 2)) - return false; - } else { - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], - kKelvinatorGapSpaceTicks * 2 * space_tick)) - return false; - } + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, 32, + 0, 0, + kKelvinatorBitMark, kKelvinatorOneSpace, + kKelvinatorBitMark, kKelvinatorZeroSpace, + kKelvinatorBitMark, kKelvinatorGapSpace * 2, + s > 0, + kTolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + pos += 4; } // Compliance if (strict) { - // Correct size/length) - if (state_pos != kKelvinatorStateLength) return false; // Verify the message's checksum is correct. if (!IRKelvinatorAC::validChecksum(results->state)) return false; } // Success - results->decode_type = KELVINATOR; - results->bits = state_pos * 8; + results->decode_type = decode_type_t::KELVINATOR; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Kelvinator.h similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Kelvinator.h index ce830c70a..b30781649 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Kelvinator.h @@ -2,6 +2,18 @@ // // Copyright 2016 David Conran +// Supports: +// Brand: Kelvinator, Model: YALIF Remote +// Brand: Kelvinator, Model: KSV26CRC A/C +// Brand: Kelvinator, Model: KSV26HRC A/C +// Brand: Kelvinator, Model: KSV35CRC A/C +// Brand: Kelvinator, Model: KSV35HRC A/C +// Brand: Kelvinator, Model: KSV53HRC A/C +// Brand: Kelvinator, Model: KSV62HRC A/C +// Brand: Kelvinator, Model: KSV70CRC A/C +// Brand: Kelvinator, Model: KSV70HRC A/C +// Brand: Kelvinator, Model: KSV80HRC A/C + #ifndef IR_KELVINATOR_H_ #define IR_KELVINATOR_H_ @@ -9,8 +21,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,12 +28,6 @@ #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 -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR - // Constants const uint8_t kKelvinatorAuto = 0; const uint8_t kKelvinatorCool = 1; @@ -32,6 +36,7 @@ const uint8_t kKelvinatorFan = 3; const uint8_t kKelvinatorHeat = 4; const uint8_t kKelvinatorBasicFanMax = 3; const uint8_t kKelvinatorFanAuto = 0; +const uint8_t kKelvinatorFanMin = 1; const uint8_t kKelvinatorFanMax = 5; const uint8_t kKelvinatorMinTemp = 16; // 16C const uint8_t kKelvinatorMaxTemp = 30; // 30C @@ -129,49 +134,50 @@ const uint8_t kKelvinatorAutoTemp = 25; // 25C // Classes class IRKelvinatorAC { public: - explicit IRKelvinatorAC(uint16_t pin); + explicit IRKelvinatorAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_KELVINATOR void send(const uint16_t repeat = kKelvinatorDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_KELVINATOR - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - void setQuiet(bool state); - bool getQuiet(); - void setIonFilter(bool state); - bool getIonFilter(); - void setLight(bool state); - bool getLight(); - void setXFan(bool state); - bool getXFan(); - void setTurbo(bool state); - bool getTurbo(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingVertical(const bool on); + bool getSwingVertical(void); + void setSwingHorizontal(const bool on); + bool getSwingHorizontal(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setIonFilter(const bool on); + bool getIonFilter(void); + void setLight(const bool on); + bool getLight(void); + void setXFan(const bool on); + bool getXFan(void); + void setTurbo(const bool on); + bool getTurbo(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[]); static uint8_t calcBlockChecksum( const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2); static bool validChecksum(const uint8_t state[], const uint16_t length = kKelvinatorStateLength); uint8_t convertMode(const stdAc::opmode_t mode); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -182,7 +188,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-2.6.0/src/ir_LG.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_LG.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_LG.cpp index 36c85ff15..ded6fefad 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_LG.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_LG.cpp @@ -2,25 +2,18 @@ // Copyright 2015 cheaplin // Copyright 2017, 2018 David Conran +// Supports: +// Brand: LG, Model: 6711A20083V remote +// Brand: LG, Model: AKB74395308 remote + #include "ir_LG.h" #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - // LG decode originally added by Darryl Smith (based on the JVC protocol) // LG send originally added by https://github.com/chaeplin -// -// Known supported devices: -// IR Remotes: -// 6711A20083V -// AKB74395308 // Constants const uint16_t kLgTick = 50; diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_LG.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_LG.h new file mode 100644 index 000000000..01f81e2c1 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_LG.h @@ -0,0 +1,15 @@ +// Copyright 2017 David Conran + +// Supports: +// Brand: LG, Model: 6711A20083V remote +// Brand: LG, Model: AKB74395308 remote + +#ifndef IR_LG_H_ +#define IR_LG_H_ + +#define __STDC_LIMIT_MACROS +#include + +uint8_t calcLGChecksum(uint16_t data); + +#endif // IR_LG_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Lasertag.cpp similarity index 91% rename from lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Lasertag.cpp index b1cbdc9b1..f52b7477a 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Lasertag.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Lasertag.cpp @@ -1,16 +1,11 @@ // Copyright 2017 David Conran +// Lasertag #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG -// LL AAAAA SS EE RR RR TTT AAAAA GG GG -// LL AA AA SSSSS EEEEE RRRRRR TTT AA AA GG -// LL AAAAAAA SS EE RR RR TTT AAAAAAA GG GG -// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG - // Constants const uint16_t kLasertagMinSamples = 13; const uint16_t kLasertagTick = 333; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Lego.cpp similarity index 74% rename from lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Lego.cpp index b051aba51..932a568d0 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Lego.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Lego.cpp @@ -8,12 +8,12 @@ // LEGO // (LEGO is a Registrated Trademark of the Lego Group.) // -// Supported Devices: -// - LEGO Power Functions IR Receiver +// Supports: +// Brand: LEGO Power Functions, Model: IR Receiver // // Ref: -// - https://github.com/markszabo/IRremoteESP8266/issues/641 -// - https://github.com/markszabo/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf +// - https://github.com/crankyoldgit/IRremoteESP8266/issues/641 +// - https://github.com/crankyoldgit/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf // Constants const uint16_t kLegoPfBitMark = 158; @@ -81,32 +81,17 @@ bool IRrecv::decodeLegoPf(decode_results* results, uint64_t data = 0; uint16_t offset = kStartOffset; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kLegoPfHdrSpace)) return false; - // Data (Typically 16 bits) - data_result = - matchData(&(results->rawbuf[offset]), nbits, - kLegoPfBitMark, kLegoPfOneSpace, - kLegoPfBitMark, kLegoPfZeroSpace, - kTolerance, kMarkExcess, true); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t actualBits = data_result.used / 2; - - // Footer. - if (!matchMark(results->rawbuf[offset++], kLegoPfBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kLegoPfMinCommandLength)) - return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kLegoPfBitMark, kLegoPfHdrSpace, + kLegoPfBitMark, kLegoPfOneSpace, + kLegoPfBitMark, kLegoPfZeroSpace, + kLegoPfBitMark, kLegoPfMinCommandLength, + true)) return false; // Compliance - if (actualBits < nbits) return false; if (strict) { - if (actualBits != nbits) return false; // Not as we expected. // Verify the Longitudinal Redundancy Check (LRC) uint16_t lrc_data = data; uint8_t lrc = 0xF; @@ -119,7 +104,7 @@ bool IRrecv::decodeLegoPf(decode_results* results, // Success results->decode_type = LEGOPF; - results->bits = actualBits; + results->bits = nbits; results->value = data; results->address = ((data >> (nbits - 4)) & 0b11) + 1; // Channel Id results->command = (data >> 4) & 0xFF; // Stuff between Channel Id and LRC. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Lutron.cpp similarity index 90% rename from lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Lutron.cpp index 00eb9383b..7c9835047 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Lutron.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Lutron.cpp @@ -1,4 +1,5 @@ // Copyright 2018 David Conran +// Lutron #define __STDC_LIMIT_MACROS #include @@ -7,12 +8,6 @@ #include "IRsend.h" #include "IRutils.h" -// LL UU UU TTTTTTT RRRRRR OOOOO NN NN -// LL UU UU TTT RR RR OO OO NNN NN -// LL UU UU TTT RRRRRR OO OO NN N NN -// LL UU UU TTT RR RR OO OO NN NNN -// LLLLLLL UUUUU TTT RR RR OOOO0 NN NN - // Notes: // The Lutron protocol uses a sort of Run Length encoding to encode // its data. There is no header or footer per-se. @@ -22,7 +17,7 @@ // Constants // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/515 const uint16_t kLutronTick = 2288; const uint32_t kLutronGap = 150000; // Completely made up value. const uint16_t kLutronDelta = 400; // +/- 300 usecs. @@ -42,7 +37,7 @@ const uint16_t kLutronDelta = 400; // +/- 300 usecs. // So, assume the 1 and only have a normal payload of 35 bits. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/515 void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) { enableIROut(40000, 40); // 40Khz & 40% dutycycle. for (uint16_t r = 0; r <= repeat; r++) { @@ -73,7 +68,7 @@ void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) { // Notes: // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/515 bool IRrecv::decodeLutron(decode_results *results, uint16_t nbits, bool strict) { // Technically the smallest number of entries for the smallest message is '1'. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_MWM.cpp similarity index 94% rename from lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_MWM.cpp index 61eac49e2..b747b224b 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_MWM.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_MWM.cpp @@ -1,4 +1,7 @@ // Copyright 2018 Brett T. Warden + +// MWM + // derived from ir_Lasertag.cpp, Copyright 2017 David Conran #include @@ -6,12 +9,6 @@ #include "IRsend.h" #include "IRutils.h" -// MM MM WW WW MM MM -// MMM MMM WW WW MMM MMM -// MM M MM WW W WW MM M MM -// MM MM WWW WWW MM MM -// MM MM WW WW MM MM - // Constants const uint16_t kMWMMinSamples = 6; // Msgs are >=3 bytes, bytes have >=2 // samples @@ -37,7 +34,8 @@ const int16_t kMark = 0; // // Status: Implemented. // -void IRsend::sendMWM(uint8_t data[], uint16_t nbytes, uint16_t repeat) { +void IRsend::sendMWM(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < 3) return; // Shortest possible message is 3 bytes // Set 38kHz IR carrier frequency & a 1/4 (25%) duty cycle. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Magiquest.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Magiquest.cpp diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Magiquest.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/src/ir_Magiquest.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Magiquest.h diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Midea.cpp similarity index 69% rename from lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Midea.cpp index 8d5d9494f..e14f21434 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Midea.cpp @@ -1,4 +1,5 @@ // Copyright 2017 bwze, crankyoldgit +// Midea #include "ir_Midea.h" #include @@ -9,12 +10,6 @@ #include "IRsend.h" #include "IRutils.h" -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA - // Midea A/C added by (send) bwze/crankyoldgit & (decode) crankyoldgit // // Equipment it seems compatible with: @@ -42,6 +37,13 @@ const uint16_t kMideaMinGapTicks = const uint16_t kMideaMinGap = kMideaMinGapTicks * kMideaTick; const uint8_t kMideaTolerance = 30; // Percent +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_MIDEA // Send a Midea message // @@ -91,52 +93,56 @@ 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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +153,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 +171,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 +194,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 +255,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,65 +294,66 @@ 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 result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kMideaACAuto: - result += F(" (AUTO)"); - break; - case kMideaACCool: - result += F(" (COOL)"); - break; - case kMideaACHeat: - result += F(" (HEAT)"); - break; - case kMideaACDry: - result += F(" (DRY)"); - break; - case kMideaACFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp(true)); - result += F("C/"); + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMideaACAuto, kMideaACCool, kMideaACHeat, + kMideaACDry, kMideaACFan); + result += addTempToString(getTemp(true)); + result += '/'; result += uint64ToString(getTemp(false)); - result += F("F, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kMideaACFanAuto: - result += F(" (AUTO)"); - break; - case kMideaACFanLow: - result += F(" (LOW)"); - break; - case kMideaACFanMed: - result += F(" (MED)"); - break; - case kMideaACFanHigh: - result += F(" (HI)"); - break; - } - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); + result += 'F'; + result += addFanToString(getFan(), kMideaACFanHigh, kMideaACFanLow, + kMideaACFanAuto, kMideaACFanAuto, kMideaACFanMed); + result += addBoolToString(getSleep(), F("Sleep")); return result; } @@ -365,9 +370,6 @@ std::string IRMideaAC::toString() { // Status: Alpha / Needs testing against a real device. // bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits, bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - uint8_t min_nr_of_messages = 1; if (strict) { if (nbits != kMideaBits) return false; // Not strictly a MIDEA message. @@ -388,35 +390,16 @@ bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits, bool strict) { return false; // We can't possibly capture a Midea packet that big. for (uint8_t i = 0; i < min_nr_of_messages; i++) { - // Header - if (!matchMark(results->rawbuf[offset], kMideaHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kMideaHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kMideaHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kMideaHdrSpaceTicks; - - // Data (Normal) - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kMideaBitMarkTicks * m_tick, - kMideaOneSpaceTicks * s_tick, kMideaBitMarkTicks * m_tick, - kMideaZeroSpaceTicks * s_tick, kMideaTolerance); - if (data_result.success == false) return false; - offset += data_result.used; - if (i % 2 == 0) - data = data_result.data; - else - inverted = data_result.data; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMideaBitMarkTicks * m_tick, - kMideaTolerance)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kMideaMinGapTicks * s_tick, - kMideaTolerance)) - return false; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, i % 2 ? &inverted : &data, + results->rawlen - offset, nbits, + kMideaHdrMark, kMideaHdrSpace, + kMideaBitMark, kMideaOneSpace, + kMideaBitMark, kMideaZeroSpace, + kMideaBitMark, kMideaMinGap, false, kMideaTolerance); + if (!used) return false; + offset += used; } // Compliance diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Midea.h similarity index 75% rename from lib/IRremoteESP8266-2.6.0/src/ir_Midea.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Midea.h index ab14eb252..6689459a6 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Midea.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Midea.h @@ -1,4 +1,10 @@ // Copyright 2017 David Conran +// Midea + +// Supports: +// Brand: Pioneer System, Model: RYBO12GMFILCAD A/C (12K BTU) +// Brand: Pioneer System, Model: RUBO18GMFILCAD A/C (18K BTU) + #ifndef IR_MIDEA_H_ #define IR_MIDEA_H_ @@ -6,8 +12,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,12 +19,6 @@ #include "IRsend_test.h" #endif -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA - // Midea added by crankyoldgit & bwze // Ref: // https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing @@ -66,35 +64,36 @@ const uint64_t kMideaACChecksumMask = 0x0000FFFFFFFFFF00; class IRMideaAC { public: - explicit IRMideaAC(uint16_t pin); + explicit IRMideaAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_MIDEA void send(const uint16_t repeat = kMideaMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MIDEA - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp, const bool useCelsius = false); uint8_t getTemp(const bool useCelsius = false); void setFan(const uint8_t fan); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); - void setRaw(uint64_t newState); - uint64_t getRaw(); + uint8_t getMode(void); + void setRaw(const uint64_t newState); + uint64_t getRaw(void); static bool validChecksum(const uint64_t state); - void setSleep(const bool state); - bool getSleep(); + void setSleep(const bool on); + bool getSleep(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 + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -103,7 +102,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-2.6.0/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Mitsubishi.cpp similarity index 72% rename from lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Mitsubishi.cpp index ca9bef5d9..b1c8e702c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Mitsubishi.cpp @@ -2,6 +2,8 @@ // Copyright 2017-2018 David Conran // Copyright 2018 Denes Varga +// Mitsubishi + #include "ir_Mitsubishi.h" #include #ifndef ARDUINO @@ -11,12 +13,6 @@ #include "IRsend.h" #include "IRutils.h" -// MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII -// M M M I T S U U B B I S H H I -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII - // Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran @@ -42,7 +38,7 @@ const uint16_t kMitsubishiMinGap = kMitsubishiMinGapTicks * kMitsubishiTick; // Mitsubishi Projector (HC3000) // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/441 const uint16_t kMitsubishi2HdrMark = 8400; const uint16_t kMitsubishi2HdrSpace = kMitsubishi2HdrMark / 2; @@ -63,6 +59,14 @@ const uint16_t kMitsubishiAcZeroSpace = 420; const uint16_t kMitsubishiAcRptMark = 440; const uint16_t kMitsubishiAcRptSpace = 17100; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if SEND_MITSUBISHI // Send a Mitsubishi message // @@ -105,44 +109,23 @@ void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { // GlobalCache's Control Tower's Mitsubishi TV data. bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kFooter - 1) - return false; // Shorter than shortest possibly expected. if (strict && nbits != kMitsubishiBits) return false; // Request is out of spec. uint16_t offset = kStartOffset; uint64_t data = 0; - // No Header - // But try to auto-calibrate off the initial mark signal. - if (!matchMark(results->rawbuf[offset], kMitsubishiBitMark, 30)) return false; - // Calculate how long the common tick time is based on the initial mark. - uint32_t tick = results->rawbuf[offset] * kRawTick / kMitsubishiBitMarkTicks; - - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kMitsubishiBitMarkTicks * tick, - kMitsubishiOneSpaceTicks * tick, kMitsubishiBitMarkTicks * tick, - kMitsubishiZeroSpaceTicks * tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t actualBits = data_result.used / 2; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMitsubishiBitMarkTicks * tick, 30)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kMitsubishiMinGapTicks * tick)) - return false; - - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - + // Match Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, // No header + kMitsubishiBitMark, kMitsubishiOneSpace, + kMitsubishiBitMark, kMitsubishiZeroSpace, + kMitsubishiBitMark, kMitsubishiMinGap, + true, 30)) return false; // Success results->decode_type = MITSUBISHI; - results->bits = actualBits; + results->bits = nbits; results->value = data; results->address = 0; results->command = 0; @@ -168,7 +151,7 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, // i.e. Allegedly, the real remote requires the "Off" button pressed twice. // You will need to add a suitable gap yourself. // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/441 void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { for (uint16_t i = 0; i <= repeat; i++) { // First half of the data. @@ -203,7 +186,7 @@ void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { // * Mitsubishi HC3000 projector's remote. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/441 bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, bool strict) { if (results->rawlen < 2 * nbits + kHeader + (kFooter * 2) - 1) @@ -212,47 +195,34 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, return false; // Request is out of spec. uint16_t offset = kStartOffset; - uint64_t data = 0; - uint16_t actualBits = 0; + results->value = 0; // Header if (!matchMark(results->rawbuf[offset++], kMitsubishi2HdrMark)) return false; if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) return false; - for (uint8_t i = 1; i <= 2; i++) { - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits / 2, kMitsubishi2BitMark, - kMitsubishi2OneSpace, kMitsubishi2BitMark, kMitsubishi2ZeroSpace); - if (data_result.success == false) return false; - data <<= nbits / 2; - data += data_result.data; - offset += data_result.used; - actualBits += data_result.used / 2; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMitsubishi2BitMark)) - return false; - if (i % 2) { // Every odd data block, we expect a HDR space. - if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) - return false; - } else { // Every even data block, we expect Min Gap or end of the message. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kMitsubishi2MinGap)) - return false; - } + for (uint8_t i = 0; i < 2; i++) { + // Match Data + Footer + uint16_t used; + uint64_t data = 0; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits / 2, + 0, 0, // No header + kMitsubishi2BitMark, kMitsubishi2OneSpace, + kMitsubishi2BitMark, kMitsubishi2ZeroSpace, + kMitsubishi2BitMark, kMitsubishi2HdrSpace, + i % 2); + if (!used) return false; + offset += used; + results->value <<= (nbits / 2); + results->value += data; } - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - // Success results->decode_type = MITSUBISHI2; - results->bits = actualBits; - results->value = data; - results->address = data >> actualBits / 2; - results->command = data & ((1 << (actualBits / 2)) - 1); + results->bits = nbits; + results->address = results->value >> (nbits / 2); + results->command = results->value & ((1 << (nbits / 2)) - 1); return true; } #endif // DECODE_MITSUBISHI2 @@ -268,8 +238,8 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, // // Status: BETA / Appears to be working. // -void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +void IRsend::sendMitsubishiAC(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kMitsubishiACStateLength) return; // Not enough bytes to send a proper message. @@ -313,7 +283,7 @@ bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits, do { failure = false; // Header: - // Somtime happens that junk signals arrives before the real message + // Sometime happens that junk signals arrives before the real message bool headerFound = false; while (!headerFound && offset < (results->rawlen - (kMitsubishiACBits * 2 + 2))) { @@ -420,10 +390,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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +417,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 +458,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 +512,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 +538,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,68 +640,80 @@ uint8_t IRMitsubishiAC::convertSwingV(const stdAc::swingv_t position) { } } -#ifdef ARDUINO -String IRMitsubishiAC::timeToString(uint64_t time) { - String result = ""; -#else -std::string IRMitsubishiAC::timeToString(uint64_t time) { - std::string result = ""; -#endif // ARDUINO - if (time / 6 < 10) result += '0'; - result += uint64ToString(time / 6); - result += ':'; - if (time * 10 % 60 < 10) result += '0'; - result += uint64ToString(time * 10 % 60); +// 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; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRMitsubishiAC::toString() { +String IRMitsubishiAC::toString(void) { String result = ""; -#else -std::string IRMitsubishiAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - switch (getMode()) { - case MITSUBISHI_AC_AUTO: - result += F(" (AUTO)"); - break; - case MITSUBISHI_AC_COOL: - result += F(" (COOL)"); - break; - case MITSUBISHI_AC_DRY: - result += F(" (DRY)"); - break; - case MITSUBISHI_AC_HEAT: - result += F(" (HEAT)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, FAN: "); - switch (getFan()) { - case MITSUBISHI_AC_FAN_AUTO: - result += F("AUTO"); - break; - case MITSUBISHI_AC_FAN_MAX: - result += F("MAX"); - break; - case MITSUBISHI_AC_FAN_SILENT: - result += F("SILENT"); - break; - default: - result += uint64ToString(getFan()); - } - result += F(", VANE: "); - switch (getVane()) { + result.reserve(110); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMitsubishiAcAuto, kMitsubishiAcCool, + kMitsubishiAcHeat, kMitsubishiAcDry, + kMitsubishiAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kMitsubishiAcFanRealMax, + kMitsubishiAcFanRealMax - 3, + kMitsubishiAcFanAuto, kMitsubishiAcFanQuiet, + kMitsubishiAcFanRealMax - 2); + result += F(", Vane: "); + switch (this->getVane()) { case MITSUBISHI_AC_VANE_AUTO: result += F("AUTO"); break; @@ -730,16 +721,13 @@ std::string IRMitsubishiAC::toString() { result += F("AUTO MOVE"); break; default: - result += uint64ToString(getVane()); + result += uint64ToString(this->getVane()); } - result += F(", Time: "); - result += timeToString(getClock()); - result += F(", On timer: "); - result += timeToString(getStartClock()); - result += F(", Off timer: "); - result += timeToString(getStopClock()); + result += addLabeledString(minsToString(getClock() * 10), F("Time")); + result += addLabeledString(minsToString(getStartClock() * 10), F("On timer")); + result += addLabeledString(minsToString(getStopClock() * 10), F("Off timer")); result += F(", Timer: "); - switch (getTimer()) { + switch (this->getTimer()) { case kMitsubishiAcNoTimer: result += '-'; break; @@ -754,7 +742,7 @@ std::string IRMitsubishiAC::toString() { break; default: result += F("? ("); - result += getTimer(); + result += this->getTimer(); result += F(")\n"); } return result; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Mitsubishi.h similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Mitsubishi.h index c8dca5dbc..af2baeea1 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Mitsubishi.h @@ -1,5 +1,12 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran + +// Mitsubishi + +// Supports: +// Brand: Mitsubishi, Model: TV +// Brand: Mitsubishi, Model: HC3000 Projector + #ifndef IR_MITSUBISHI_H_ #define IR_MITSUBISHI_H_ @@ -7,8 +14,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -16,12 +21,6 @@ #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 -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII - // Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran @@ -35,6 +34,7 @@ const uint8_t kMitsubishiAcFanAuto = 0; const uint8_t kMitsubishiAcFanMax = 5; const uint8_t kMitsubishiAcFanRealMax = 4; const uint8_t kMitsubishiAcFanSilent = 6; +const uint8_t kMitsubishiAcFanQuiet = kMitsubishiAcFanSilent; const uint8_t kMitsubishiAcMinTemp = 16; // 16C const uint8_t kMitsubishiAcMaxTemp = 31; // 31C const uint8_t kMitsubishiAcVaneAuto = 0; @@ -61,59 +61,56 @@ const uint8_t kMitsubishiAcStartStopTimer = 7; class IRMitsubishiAC { public: - explicit IRMitsubishiAC(uint16_t pin); + explicit IRMitsubishiAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - static uint8_t calculateChecksum(uint8_t* data); + static uint8_t calculateChecksum(const uint8_t* data); - void stateReset(); + void stateReset(void); #if SEND_MITSUBISHI_AC void send(const uint16_t repeat = kMitsubishiACMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MITSUBISHI_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setVane(uint8_t mode); - uint8_t getVane(); - uint8_t* getRaw(); - void setRaw(uint8_t* data); - uint8_t getClock(); - void setClock(uint8_t clock); - uint8_t getStartClock(); - void setStartClock(uint8_t clock); - uint8_t getStopClock(); - void setStopClock(uint8_t clock); - uint8_t getTimer(); - void setTimer(uint8_t timer); + 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); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: IRsend _irsend; #else IRsendTest _irsend; -#endif -#ifdef ARDUINO - String timeToString(uint64_t time); -#else - std::string timeToString(uint64_t time); #endif uint8_t remote_state[kMitsubishiACStateLength]; - void checksum(); + void checksum(void); }; #endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_MitsubishiHeavy.cpp similarity index 73% rename from lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_MitsubishiHeavy.cpp index 9048124d4..98a681e05 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_MitsubishiHeavy.cpp @@ -22,7 +22,7 @@ #endif // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/crankyoldgit/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 @@ -34,6 +34,12 @@ const uint16_t kMitsubishiHeavyOneSpace = 420; const uint16_t kMitsubishiHeavyZeroSpace = 1220; const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_MITSUBISHIHEAVY // Send a MitsubishiHeavy 88 bit A/C message. // @@ -73,10 +79,12 @@ void IRsend::sendMitsubishiHeavy152(const unsigned char data[], #endif // SEND_MITSUBISHIHEAVY // Class for decoding and constructing MitsubishiHeavy152 AC messages. -IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac( - const uint16_t pin) : _irsend(pin) { stateReset(); } +IRMitsubishiHeavy152Ac::IRMitsubishiHeavy152Ac(const uint16_t pin, + const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRMitsubishiHeavy152Ac::begin() { _irsend.begin(); } +void IRMitsubishiHeavy152Ac::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHIHEAVY void IRMitsubishiHeavy152Ac::send(const uint16_t repeat) { @@ -303,7 +311,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,68 +385,118 @@ 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) { 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()); + result.reserve(180); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); + result += F(" ("); switch (this->getFan()) { case kMitsubishiHeavy152FanAuto: - result += F(" (Auto)"); + result += F("Auto"); break; case kMitsubishiHeavy152FanHigh: - result += F(" (High)"); + result += F("High"); break; case kMitsubishiHeavy152FanLow: - result += F(" (Low)"); + result += F("Low"); break; case kMitsubishiHeavy152FanMed: - result += F(" (Med)"); + result += F("Medium"); break; case kMitsubishiHeavy152FanMax: - result += F(" (Max)"); + result += F("Max"); break; case kMitsubishiHeavy152FanEcono: - result += F(" (Econo)"); + result += F("Econo"); break; case kMitsubishiHeavy152FanTurbo: - result += F(" (Turbo)"); + result += F("Turbo"); break; default: - result += F(" (UNKNOWN)"); + result += F("UNKNOWN"); } - result += F(", Swing (V): "); - result += uint64ToString(this->getSwingVertical()); + result += ')'; + result += addIntToString(getSwingVertical(), F("Swing (V)")); switch (this->getSwingVertical()) { case kMitsubishiHeavy152SwingVAuto: result += F(" (Auto)"); @@ -465,8 +522,7 @@ std::string IRMitsubishiHeavy152Ac::toString(void) { default: result += F(" (UNKNOWN)"); } - result += F(", Swing (H): "); - result += uint64ToString(this->getSwingHorizontal()); + result += addIntToString(getSwingHorizontal(), F("Swing (H)")); switch (this->getSwingHorizontal()) { case kMitsubishiHeavy152SwingHAuto: result += F(" (Auto)"); @@ -498,29 +554,24 @@ std::string IRMitsubishiHeavy152Ac::toString(void) { 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")); + result += addBoolToString(getSilent(), F("Silent")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getEcono(), F("Econo")); + result += addBoolToString(getNight(), F("Night")); + result += addBoolToString(getFilter(), F("Filter")); + result += addBoolToString(get3D(), F("3D")); + result += addBoolToString(getClean(), F("Clean")); return result; } // Class for decoding and constructing MitsubishiHeavy88 AC messages. -IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac( - const uint16_t pin) : _irsend(pin) { stateReset(); } +IRMitsubishiHeavy88Ac::IRMitsubishiHeavy88Ac(const uint16_t pin, + const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } -void IRMitsubishiHeavy88Ac::begin() { _irsend.begin(); } +void IRMitsubishiHeavy88Ac::begin(void) { _irsend.begin(); } #if SEND_MITSUBISHIHEAVY void IRMitsubishiHeavy88Ac::send(const uint16_t repeat) { @@ -737,7 +788,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,65 +846,104 @@ 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) { 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()); + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMitsubishiHeavyAuto, + kMitsubishiHeavyCool, kMitsubishiHeavyHeat, + kMitsubishiHeavyDry, kMitsubishiHeavyFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); + result += F(" ("); switch (this->getFan()) { case kMitsubishiHeavy88FanAuto: - result += F(" (Auto)"); + result += F("Auto"); break; case kMitsubishiHeavy88FanHigh: - result += F(" (High)"); + result += F("High"); break; case kMitsubishiHeavy88FanLow: - result += F(" (Low)"); + result += F("Low"); break; case kMitsubishiHeavy88FanMed: - result += F(" (Med)"); + result += F("Medium"); break; case kMitsubishiHeavy88FanEcono: - result += F(" (Econo)"); + result += F("Econo"); break; case kMitsubishiHeavy88FanTurbo: - result += F(" (Turbo)"); + result += F("Turbo"); break; default: - result += F(" (UNKNOWN)"); + result += F("UNKNOWN"); } - result += F(", Swing (V): "); - result += uint64ToString(this->getSwingVertical()); + result += ')'; + result += addIntToString(getSwingVertical(), F("Swing (V)")); switch (this->getSwingVertical()) { case kMitsubishiHeavy88SwingVAuto: result += F(" (Auto)"); @@ -880,8 +969,7 @@ std::string IRMitsubishiHeavy88Ac::toString(void) { default: result += F(" (UNKNOWN)"); } - result += F(", Swing (H): "); - result += uint64ToString(this->getSwingHorizontal()); + result += addIntToString(getSwingHorizontal(), F("Swing (H)")); switch (this->getSwingHorizontal()) { case kMitsubishiHeavy88SwingHAuto: result += F(" (Auto)"); @@ -916,14 +1004,10 @@ std::string IRMitsubishiHeavy88Ac::toString(void) { 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")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getEcono(), F("Econo")); + result += addBoolToString(get3D(), F("3D")); + result += addBoolToString(getClean(), F("Clean")); return result; } @@ -953,41 +1037,19 @@ bool IRrecv::decodeMitsubishiHeavy(decode_results* results, } } - 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; - + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kMitsubishiHeavyHdrMark, kMitsubishiHeavyHdrSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyOneSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyZeroSpace, + kMitsubishiHeavyBitMark, kMitsubishiHeavyGap, true, + kTolerance, 0, false); + if (used == 0) return false; + offset += used; // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - switch (actualBits) { + switch (nbits) { case kMitsubishiHeavy88Bits: if (strict && !(IRMitsubishiHeavy88Ac::checkZjsSig(results->state) && IRMitsubishiHeavy88Ac::validChecksum(results->state))) @@ -1005,7 +1067,7 @@ bool IRrecv::decodeMitsubishiHeavy(decode_results* results, } // Success - results->bits = actualBits; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_MitsubishiHeavy.h similarity index 86% rename from lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_MitsubishiHeavy.h index bcd85c6e0..c52eeb951 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_MitsubishiHeavy.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_MitsubishiHeavy.h @@ -1,12 +1,17 @@ // Copyright 2019 David Conran +// Supports: +// Brand: Mitsubishi Heavy Industries, Model: RLA502A700B remote +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZM-S A/C +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZMXA-S A/C +// Brand: Mitsubishi Heavy Industries, Model: RKX502A001C remote +// Brand: Mitsubishi Heavy Industries, Model: SRKxxZJ-S A/C + #ifndef IR_MITSUBISHIHEAVY_H_ #define IR_MITSUBISHIHEAVY_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,7 +20,7 @@ #endif // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/660 +// https://github.com/crankyoldgit/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 @@ -117,11 +122,14 @@ const uint8_t kMitsubishiHeavy88SwingVOff = 0b00000000; // 0x00 // Classes class IRMitsubishiHeavy152Ac { public: - explicit IRMitsubishiHeavy152Ac(const uint16_t pin); + explicit IRMitsubishiHeavy152Ac(const uint16_t pin, + const bool inverted = false, + const bool use_modulation = true); void stateReset(void); #if SEND_MITSUBISHIHEAVY void send(const uint16_t repeat = kMitsubishiHeavy152MinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_MITSUBISHIHEAVY void begin(void); void on(void); @@ -176,11 +184,12 @@ 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); -#ifdef ARDUINO + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); String toString(void); -#else // ARDUINO - std::string toString(void); -#endif // ARDUINO #ifndef UNIT_TEST private: @@ -190,12 +199,14 @@ 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 { public: - explicit IRMitsubishiHeavy88Ac(const uint16_t pin); + explicit IRMitsubishiHeavy88Ac(const uint16_t pin, + const bool inverted = false, + const bool use_modulation = true); void stateReset(void); #if SEND_MITSUBISHIHEAVY @@ -245,11 +256,11 @@ 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); -#ifdef ARDUINO + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); String toString(void); -#else // ARDUINO - std::string toString(void); -#endif // ARDUINO #ifndef UNIT_TEST private: @@ -259,6 +270,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-2.6.0/src/ir_NEC.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_NEC.cpp similarity index 78% rename from lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_NEC.cpp index 660b51109..9a3fdd2be 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_NEC.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_NEC.cpp @@ -1,6 +1,8 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ + #define __STDC_LIMIT_MACROS #include "ir_NEC.h" #include @@ -9,14 +11,6 @@ #include "IRsend.h" #include "IRutils.h" -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ - #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ SEND_PIONEER) // Send a raw NEC(Renesas) formatted message. @@ -103,13 +97,11 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { uint16_t offset = kStartOffset; // Header - if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks; + if (!matchMark(results->rawbuf[offset++], kNecHdrMark)) return false; // Check if it is a repeat code. if (results->rawlen == kNecRptLength && matchSpace(results->rawbuf[offset], kNecRptSpace) && - matchMark(results->rawbuf[offset + 1], kNecBitMarkTicks * mark_tick)) { + matchMark(results->rawbuf[offset + 1], kNecBitMark)) { results->value = kRepeat; results->decode_type = NEC; results->bits = 0; @@ -119,27 +111,13 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { return true; } - // Header (cont.) - if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kNecBitMarkTicks * mark_tick, - kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick, - kNecZeroSpaceTicks * space_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kNecMinGapTicks * space_tick)) - return false; - + // Match Header (cont.) + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, kNecHdrSpace, + kNecBitMark, kNecOneSpace, + kNecBitMark, kNecZeroSpace, + kNecBitMark, kNecMinGap, true)) return false; // Compliance // Calculate command and optionally enforce integrity checking. uint8_t command = (data & 0xFF00) >> 8; @@ -166,4 +144,4 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { results->address = reverseBits((data >> 16) & UINT16_MAX, 16); return true; } -#endif +#endif // DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_NEC.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_NEC.h similarity index 85% rename from lib/IRremoteESP8266-2.6.0/src/ir_NEC.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_NEC.h index c274c104e..fef1a65fd 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_NEC.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_NEC.h @@ -1,20 +1,14 @@ // Copyright 2009 Ken Shirriff // Copyright 2017, 2018 David Conran +// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ + #ifndef IR_NEC_H_ #define IR_NEC_H_ #include #include "IRremoteESP8266.h" -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ - // Constants // Ref: // http://www.sbprojects.com/knowledge/ir/nec.php diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.cpp new file mode 100644 index 000000000..bd2a3d1b5 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.cpp @@ -0,0 +1,554 @@ +// Copyright 2019 David Conran + +// Neoclima A/C support + +// Analysis by crankyoldgit & AndreyShpilevoy +// Code by crankyoldgit +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +// https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + + +// Supports: +// Brand: Neoclima, Model: NS-09AHTI A/C +// Brand: Neoclima, Model: ZH/TY-01 remote + +#include "ir_Neoclima.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants + +const uint16_t kNeoclimaHdrMark = 6112; +const uint16_t kNeoclimaHdrSpace = 7391; +const uint16_t kNeoclimaBitMark = 537; +const uint16_t kNeoclimaOneSpace = 1651; +const uint16_t kNeoclimaZeroSpace = 571; +const uint32_t kNeoclimaMinGap = kDefaultMessageGap; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_NEOCLIMA +// Send a Neoclima message. +// +// Args: +// data: message to be sent. +// nbytes: Nr. of bytes of the message to be sent. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: Beta / Known to be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +void IRsend::sendNeoclima(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + // Set IR carrier frequency + enableIROut(38); + + for (uint16_t i = 0; i <= repeat; i++) { + sendGeneric(kNeoclimaHdrMark, kNeoclimaHdrSpace, + kNeoclimaBitMark, kNeoclimaOneSpace, + kNeoclimaBitMark, kNeoclimaZeroSpace, + kNeoclimaBitMark, kNeoclimaHdrSpace, + data, nbytes, 38000, false, 0, // Repeats are already handled. + 50); + // Extra footer. + mark(kNeoclimaBitMark); + space(kNeoclimaMinGap); + } +} +#endif // SEND_NEOCLIMA + +IRNeoclimaAc::IRNeoclimaAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + this->stateReset(); +} + +void IRNeoclimaAc::stateReset(void) { + for (uint8_t i = 0; i < kNeoclimaStateLength; i++) + remote_state[i] = 0x0; + remote_state[7] = 0x6A; + remote_state[8] = 0x00; + remote_state[9] = 0x2A; + remote_state[10] = 0xA5; + // [11] is the checksum. +} + +void IRNeoclimaAc::begin(void) { _irsend.begin(); } + +uint8_t IRNeoclimaAc::calcChecksum(const uint8_t state[], + const uint16_t length) { + if (length == 0) return state[0]; + return sumBytes(state, length - 1); +} + +bool IRNeoclimaAc::validChecksum(const uint8_t state[], const uint16_t length) { + if (length < 2) + return true; // No checksum to compare with. Assume okay. + return (state[length - 1] == calcChecksum(state, length)); +} + +// Update the checksum for the internal state. +void IRNeoclimaAc::checksum(uint16_t length) { + if (length < 2) return; + remote_state[length - 1] = calcChecksum(remote_state, length); +} + +#if SEND_NEOCLIMA +void IRNeoclimaAc::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendNeoclima(remote_state, kNeoclimaStateLength, repeat); +} +#endif // SEND_NEOCLIMA + +uint8_t *IRNeoclimaAc::getRaw(void) { + this->checksum(); + return remote_state; +} + +void IRNeoclimaAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kNeoclimaStateLength; i++) + remote_state[i] = new_code[i]; +} + + +void IRNeoclimaAc::setButton(const uint8_t button) { + switch (button) { + case kNeoclimaButtonPower: + case kNeoclimaButtonMode: + case kNeoclimaButtonTempUp: + case kNeoclimaButtonTempDown: + case kNeoclimaButtonSwing: + case kNeoclimaButtonFanSpeed: + case kNeoclimaButtonAirFlow: + case kNeoclimaButtonHold: + case kNeoclimaButtonSleep: + case kNeoclimaButtonLight: + case kNeoclimaButtonEye: + case kNeoclimaButtonFollow: + case kNeoclimaButtonIon: + case kNeoclimaButtonFresh: + case kNeoclimaButton8CHeat: + case kNeoclimaButtonTurbo: + remote_state[5] &= ~kNeoclimaButtonMask; + remote_state[5] |= button; + break; + default: + this->setButton(kNeoclimaButtonPower); + } +} + +uint8_t IRNeoclimaAc::getButton(void) { + return remote_state[5] & kNeoclimaButtonMask; +} + +void IRNeoclimaAc::on(void) { this->setPower(true); } + +void IRNeoclimaAc::off(void) { this->setPower(false); } + +void IRNeoclimaAc::setPower(const bool on) { + this->setButton(kNeoclimaButtonPower); + if (on) + remote_state[7] |= kNeoclimaPowerMask; + else + remote_state[7] &= ~kNeoclimaPowerMask; +} + +bool IRNeoclimaAc::getPower(void) { + return remote_state[7] & kNeoclimaPowerMask; +} + +void IRNeoclimaAc::setMode(const uint8_t mode) { + switch (mode) { + case kNeoclimaDry: + // In this mode fan speed always LOW + this->setFan(kNeoclimaFanLow); + // FALL THRU + case kNeoclimaAuto: + case kNeoclimaCool: + case kNeoclimaFan: + case kNeoclimaHeat: + remote_state[9] &= ~kNeoclimaModeMask; + remote_state[9] |= (mode << 5); + this->setButton(kNeoclimaButtonMode); + break; + default: + // If we get an unexpected mode, default to AUTO. + this->setMode(kNeoclimaAuto); + } +} + +uint8_t IRNeoclimaAc::getMode(void) { + return (remote_state[9] & kNeoclimaModeMask) >> 5; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRNeoclimaAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kNeoclimaCool; + case stdAc::opmode_t::kHeat: + return kNeoclimaHeat; + case stdAc::opmode_t::kDry: + return kNeoclimaDry; + case stdAc::opmode_t::kFan: + return kNeoclimaFan; + default: + return kNeoclimaAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRNeoclimaAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kNeoclimaCool: return stdAc::opmode_t::kCool; + case kNeoclimaHeat: return stdAc::opmode_t::kHeat; + case kNeoclimaDry: return stdAc::opmode_t::kDry; + case kNeoclimaFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Set the temp. in deg C +void IRNeoclimaAc::setTemp(const uint8_t temp) { + uint8_t oldtemp = this->getTemp(); + uint8_t newtemp = std::max(kNeoclimaMinTemp, temp); + newtemp = std::min(kNeoclimaMaxTemp, newtemp); + if (oldtemp > newtemp) + this->setButton(kNeoclimaButtonTempDown); + else if (newtemp > oldtemp) + this->setButton(kNeoclimaButtonTempUp); + remote_state[9] = (remote_state[9] & ~kNeoclimaTempMask) | + (newtemp - kNeoclimaMinTemp); +} + +// Return the set temp. in deg C +uint8_t IRNeoclimaAc::getTemp(void) { + return (remote_state[9] & kNeoclimaTempMask) + kNeoclimaMinTemp; +} + +// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed +void IRNeoclimaAc::setFan(const uint8_t speed) { + switch (speed) { + case kNeoclimaFanAuto: + case kNeoclimaFanHigh: + case kNeoclimaFanMed: + if (this->getMode() == kNeoclimaDry) { // Dry mode only allows low speed. + this->setFan(kNeoclimaFanLow); + return; + } + // FALL-THRU + case kNeoclimaFanLow: + remote_state[7] &= ~kNeoclimaFanMask; + remote_state[7] |= (speed << 5); + this->setButton(kNeoclimaButtonFanSpeed); + break; + default: + // If we get an unexpected speed, default to Auto. + this->setFan(kNeoclimaFanAuto); + } +} + +uint8_t IRNeoclimaAc::getFan(void) { + return (remote_state[7] & kNeoclimaFanMask) >> 5; +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRNeoclimaAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kNeoclimaFanLow; + case stdAc::fanspeed_t::kMedium: + return kNeoclimaFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kNeoclimaFanHigh; + default: + return kNeoclimaFanAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRNeoclimaAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kNeoclimaFanHigh: return stdAc::fanspeed_t::kMax; + case kNeoclimaFanMed: return stdAc::fanspeed_t::kMedium; + case kNeoclimaFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +void IRNeoclimaAc::setSleep(const bool on) { + this->setButton(kNeoclimaButtonSleep); + if (on) + remote_state[7] |= kNeoclimaSleepMask; + else + remote_state[7] &= ~kNeoclimaSleepMask; +} + +bool IRNeoclimaAc::getSleep(void) { + return remote_state[7] & kNeoclimaSleepMask; +} + +// A.k.a. Swing +void IRNeoclimaAc::setSwingV(const bool on) { + this->setButton(kNeoclimaButtonSwing); + remote_state[7] &= ~kNeoclimaSwingVMask; + remote_state[7] |= on ? kNeoclimaSwingVOn : kNeoclimaSwingVOff; +} + +bool IRNeoclimaAc::getSwingV(void) { + return (remote_state[7] & kNeoclimaSwingVMask) == kNeoclimaSwingVOn; +} + +// A.k.a. Air Flow +void IRNeoclimaAc::setSwingH(const bool on) { + this->setButton(kNeoclimaButtonAirFlow); + if (on) + remote_state[7] &= ~kNeoclimaSwingHMask; + else + remote_state[7] |= kNeoclimaSwingHMask; +} + +bool IRNeoclimaAc::getSwingH(void) { + return !(remote_state[7] & kNeoclimaSwingHMask); +} + +void IRNeoclimaAc::setTurbo(const bool on) { + this->setButton(kNeoclimaButtonTurbo); + if (on) + remote_state[3] |= kNeoclimaTurboMask; + else + remote_state[3] &= ~kNeoclimaTurboMask; +} + +bool IRNeoclimaAc::getTurbo(void) { + return remote_state[3] & kNeoclimaTurboMask; +} + +void IRNeoclimaAc::setFresh(const bool on) { + this->setButton(kNeoclimaButtonFresh); + if (on) + remote_state[5] |= kNeoclimaFreshMask; + else + remote_state[5] &= ~kNeoclimaFreshMask; +} + +bool IRNeoclimaAc::getFresh(void) { + return remote_state[5] & kNeoclimaFreshMask; +} + +void IRNeoclimaAc::setHold(const bool on) { + this->setButton(kNeoclimaButtonHold); + if (on) + remote_state[3] |= kNeoclimaHoldMask; + else + remote_state[3] &= ~kNeoclimaHoldMask; +} + +bool IRNeoclimaAc::getHold(void) { + return remote_state[3] & kNeoclimaHoldMask; +} + +void IRNeoclimaAc::setIon(const bool on) { + this->setButton(kNeoclimaButtonIon); + if (on) + remote_state[1] |= kNeoclimaIonMask; + else + remote_state[1] &= ~kNeoclimaIonMask; +} + +bool IRNeoclimaAc::getIon(void) { + return remote_state[1] & kNeoclimaIonMask; +} + +void IRNeoclimaAc::setLight(const bool on) { + this->setButton(kNeoclimaButtonLight); + if (on) + remote_state[3] |= kNeoclimaLightMask; + else + remote_state[3] &= ~kNeoclimaLightMask; +} + +bool IRNeoclimaAc::getLight(void) { + return remote_state[3] & kNeoclimaLightMask; +} + +// This feature maintains the room temperature steadily at 8°C and prevents the +// room from freezing by activating the heating operation automatically when +// nobody is at home over a longer period during severe winter. +void IRNeoclimaAc::set8CHeat(const bool on) { + this->setButton(kNeoclimaButton8CHeat); + if (on) + remote_state[1] |= kNeoclima8CHeatMask; + else + remote_state[1] &= ~kNeoclima8CHeatMask; +} + +bool IRNeoclimaAc::get8CHeat(void) { + return remote_state[1] & kNeoclima8CHeatMask; +} + +void IRNeoclimaAc::setEye(const bool on) { + this->setButton(kNeoclimaButtonEye); + if (on) + remote_state[3] |= kNeoclimaEyeMask; + else + remote_state[3] &= ~kNeoclimaEyeMask; +} + +bool IRNeoclimaAc::getEye(void) { + return remote_state[3] & kNeoclimaEyeMask; +} + +/* DISABLED + TODO(someone): Work out why "on" is either 0x5D or 0x5F +void IRNeoclimaAc::setFollow(const bool on) { + this->setButton(kNeoclimaButtonFollow); + if (on) + remote_state[8] = kNeoclimaFollowMe; + else + remote_state[8] = 0; +} +*/ + +bool IRNeoclimaAc::getFollow(void) { + return (remote_state[8] & kNeoclimaFollowMe) == kNeoclimaFollowMe; +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRNeoclimaAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::NEOCLIMA; + 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->getSwingV() ? stdAc::swingv_t::kAuto + : stdAc::swingv_t::kOff; + result.swingh = this->getSwingH() ? stdAc::swingh_t::kAuto + : stdAc::swingh_t::kOff; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.filter = this->getIon(); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.quiet = false; + result.econo = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRNeoclimaAc::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kNeoclimaAuto, kNeoclimaCool, + kNeoclimaHeat, kNeoclimaDry, kNeoclimaFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kNeoclimaFanHigh, kNeoclimaFanLow, + kNeoclimaFanAuto, kNeoclimaFanAuto, kNeoclimaFanMed); + result += addBoolToString(getSwingV(), F("Swing(V)")); + result += addBoolToString(getSwingH(), F("Swing(H)")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getHold(), F("Hold")); + result += addBoolToString(getIon(), F("Ion")); + result += addBoolToString(getEye(), F("Eye")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getFollow(), F("Follow")); + result += addBoolToString(get8CHeat(), F("8C Heat")); + result += addBoolToString(getFresh(), F("Fresh")); + result += addIntToString(getButton(), F("Button")); + result += F(" ("); + switch (this->getButton()) { + case kNeoclimaButtonPower: result += F("Power"); break; + case kNeoclimaButtonMode: result += F("Mode"); break; + case kNeoclimaButtonTempUp: result += F("Temp Up"); break; + case kNeoclimaButtonTempDown: result += F("Temp Down"); break; + case kNeoclimaButtonSwing: result += F("Swing"); break; + case kNeoclimaButtonFanSpeed: result += F("Speed"); break; + case kNeoclimaButtonAirFlow: result += F("Air Flow"); break; + case kNeoclimaButtonHold: result += F("Hold"); break; + case kNeoclimaButtonSleep: result += F("Sleep"); break; + case kNeoclimaButtonLight: result += F("Light"); break; + case kNeoclimaButtonEye: result += F("Eye"); break; + case kNeoclimaButtonFollow: result += F("Follow"); break; + case kNeoclimaButtonIon: result += F("Ion"); break; + case kNeoclimaButtonFresh: result += F("Fresh"); break; + case kNeoclimaButton8CHeat: result += F("8C Heat"); break; + case kNeoclimaButtonTurbo: result += F("Turbo"); break; + default: + result += F("Unknown"); + } + result += ')'; + return result; +} + +#if DECODE_NEOCLIMA +// Decode the supplied Neoclima message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of data bits to expect. Typically kNeoclimaBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Known working +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +bool IRrecv::decodeNeoclima(decode_results *results, const uint16_t nbits, + const bool strict) { + // Compliance + if (strict && nbits != kNeoclimaBits) + return false; // Incorrect nr. of bits per spec. + + uint16_t offset = kStartOffset; + // Match Main Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kNeoclimaHdrMark, kNeoclimaHdrSpace, + kNeoclimaBitMark, kNeoclimaOneSpace, + kNeoclimaBitMark, kNeoclimaZeroSpace, + kNeoclimaBitMark, kNeoclimaHdrSpace, false, + kTolerance, 0, false); + if (!used) return false; + offset += used; + // Extra footer. + uint64_t unused; + if (!matchGeneric(results->rawbuf + offset, &unused, + results->rawlen - offset, 0, 0, 0, 0, 0, 0, 0, + kNeoclimaBitMark, kNeoclimaHdrSpace, true)) return false; + + // Compliance + if (strict) { + // Check we got a valid checksum. + if (!IRNeoclimaAc::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + results->decode_type = decode_type_t::NEOCLIMA; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_NEOCLIMA diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.h new file mode 100644 index 000000000..9e99c8a9e --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Neoclima.h @@ -0,0 +1,154 @@ +// Neoclima A/C +// +// Copyright 2019 David Conran + +// Analysis by crankyoldgit & AndreyShpilevoy + +#ifndef IR_NEOCLIMA_H_ +#define IR_NEOCLIMA_H_ + +#define __STDC_LIMIT_MACROS +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Supports: +// Brand: Neoclima, Model: NS-09AHTI A/C +// Brand: Neoclima, Model: ZH/TY-01 remote + +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764 +// https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + +// Constants +// state[1] +const uint8_t kNeoclima8CHeatMask = 0b00000010; +const uint8_t kNeoclimaIonMask = 0b00000100; +// state[3] +const uint8_t kNeoclimaLightMask = 0b00000001; +const uint8_t kNeoclimaHoldMask = 0b00000100; +const uint8_t kNeoclimaTurboMask = 0b00001000; +const uint8_t kNeoclimaEyeMask = 0b01000000; +// state[5] +const uint8_t kNeoclimaFreshMask = 0b10000000; +const uint8_t kNeoclimaButtonMask = 0b00011111; +const uint8_t kNeoclimaButtonPower = 0x00; +const uint8_t kNeoclimaButtonMode = 0x01; +const uint8_t kNeoclimaButtonTempUp = 0x02; +const uint8_t kNeoclimaButtonTempDown = 0x03; +const uint8_t kNeoclimaButtonSwing = 0x04; +const uint8_t kNeoclimaButtonFanSpeed = 0x05; +const uint8_t kNeoclimaButtonAirFlow = 0x07; +const uint8_t kNeoclimaButtonHold = 0x08; +const uint8_t kNeoclimaButtonSleep = 0x09; +const uint8_t kNeoclimaButtonTurbo = 0x0A; +const uint8_t kNeoclimaButtonLight = 0x0B; +const uint8_t kNeoclimaButtonEye = 0x0E; +const uint8_t kNeoclimaButtonFollow = 0x13; +const uint8_t kNeoclimaButtonIon = 0x14; +const uint8_t kNeoclimaButtonFresh = 0x15; +const uint8_t kNeoclimaButton8CHeat = 0x1D; +// state[7] +const uint8_t kNeoclimaSleepMask = 0b00000001; +const uint8_t kNeoclimaPowerMask = 0b00000010; +const uint8_t kNeoclimaSwingVMask = 0b00001100; +const uint8_t kNeoclimaSwingVOn = 0b00000100; +const uint8_t kNeoclimaSwingVOff = 0b00001000; +const uint8_t kNeoclimaSwingHMask = 0b00010000; +const uint8_t kNeoclimaFanMask = 0b01100000; +const uint8_t kNeoclimaFanAuto = 0b00; +const uint8_t kNeoclimaFanHigh = 0b01; +const uint8_t kNeoclimaFanMed = 0b10; +const uint8_t kNeoclimaFanLow = 0b11; +// state[8] +const uint8_t kNeoclimaFollowMe = 0x5D; // Also 0x5F +// state[9] +const uint8_t kNeoclimaTempMask = 0b00011111; +const uint8_t kNeoclimaMinTemp = 16; // 16C +const uint8_t kNeoclimaMaxTemp = 32; // 32C +const uint8_t kNeoclimaModeMask = 0b11100000; +const uint8_t kNeoclimaAuto = 0b000; +const uint8_t kNeoclimaCool = 0b001; +const uint8_t kNeoclimaDry = 0b010; +const uint8_t kNeoclimaFan = 0b011; +const uint8_t kNeoclimaHeat = 0b100; + +// Classes +class IRNeoclimaAc { + public: + explicit IRNeoclimaAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + void stateReset(void); +#if SEND_NEOCLIMA + void send(const uint16_t repeat = kNeoclimaMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_NEOCLIMA + void begin(void); + void setButton(const uint8_t button); + uint8_t getButton(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setSwingV(const bool on); + bool getSwingV(void); + void setSwingH(const bool on); + bool getSwingH(void); + void setSleep(const bool on); + bool getSleep(void); + void setTurbo(const bool on); + bool getTurbo(void); + void setFresh(const bool on); + bool getFresh(void); + void setHold(const bool on); + bool getHold(void); + void setIon(const bool on); + bool getIon(void); + void setLight(const bool on); + bool getLight(void); + void set8CHeat(const bool on); + bool get8CHeat(void); + void setEye(const bool on); + bool getEye(void); + // DISABLED: See TODO in ir_Neoclima.cpp + // void setFollow(const bool on); + bool getFollow(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kNeoclimaStateLength); + static bool validChecksum(const uint8_t state[], + const uint16_t length = kNeoclimaStateLength); + static uint8_t calcChecksum(const uint8_t state[], + const uint16_t length = kNeoclimaStateLength); + String toString(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); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + // The state of the IR remote in IR code form. + uint8_t remote_state[kNeoclimaStateLength]; + void checksum(const uint16_t length = kNeoclimaStateLength); +}; + +#endif // IR_NEOCLIMA_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Nikai.cpp similarity index 58% rename from lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Nikai.cpp index 9ac22a849..12e99c278 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Nikai.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Nikai.cpp @@ -1,20 +1,16 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// Nikai + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// NN NN IIIII KK KK AAA IIIII -// NNN NN III KK KK AAAAA III -// NN N NN III KKKK AA AA III -// NN NNN III KK KK AAAAAAA III -// NN NN IIIII KK KK AA AA IIIII - // Constants // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/309 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/309 const uint16_t kNikaiTick = 500; const uint16_t kNikaiHdrMarkTicks = 8; const uint16_t kNikaiHdrMark = kNikaiHdrMarkTicks * kNikaiTick; @@ -39,7 +35,7 @@ const uint16_t kNikaiMinGap = kNikaiMinGapTicks * kNikaiTick; // // Status: STABLE / Working. // -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/309 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/309 void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) { sendGeneric(kNikaiHdrMark, kNikaiHdrSpace, kNikaiBitMark, kNikaiOneSpace, kNikaiBitMark, kNikaiZeroSpace, kNikaiBitMark, kNikaiMinGap, data, @@ -61,38 +57,19 @@ void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) { // Status: STABLE / Working. // bool IRrecv::decodeNikai(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Nikai message. if (strict && nbits != kNikaiBits) return false; // We expect Nikai to be a certain sized message. uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kNikaiHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kNikaiHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kNikaiBitMarkTicks * m_tick, - kNikaiOneSpaceTicks * s_tick, kNikaiBitMarkTicks * m_tick, - kNikaiZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kNikaiBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kNikaiMinGapTicks * s_tick)) - return false; - - // Compliance - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kNikaiHdrMark, kNikaiHdrSpace, + kNikaiBitMark, kNikaiOneSpace, + kNikaiBitMark, kNikaiZeroSpace, + kNikaiBitMark, kNikaiMinGap, true)) return false; // Success results->bits = nbits; results->value = data; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Panasonic.cpp similarity index 69% rename from lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Panasonic.cpp index 47aa51c96..1a24ac41f 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Panasonic.cpp @@ -1,6 +1,8 @@ // Copyright 2015 Kristian Lauszus // Copyright 2017, 2018 David Conran +// Panasonic devices + #include "ir_Panasonic.h" #include #ifndef ARDUINO @@ -10,12 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC -// P P A A NN N A A S O O NN N I C -// PPPP AAAAA N N N AAAAA SSS O O N N N I C -// P A A N NN A A S O O N NN I C -// P A A N N A A SSSS OOO N N IIIII CCCC - // Panasonic protocol originally added by Kristian Lauszus from: // https://github.com/z3t0/Arduino-IRremote // (Thanks to zenwheel and other people at the original blog post) @@ -65,6 +61,14 @@ const uint16_t kPanasonicAcSectionGap = 10000; const uint16_t kPanasonicAcSection1Length = 8; const uint32_t kPanasonicAcMessageGap = kDefaultMessageGap; // Just a guess. +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if (SEND_PANASONIC || SEND_DENON) // Send a Panasonic formatted message. // @@ -77,7 +81,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 +101,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 +122,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,42 +148,21 @@ 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) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Not enough entries to be a Panasonic message. +bool IRrecv::decodePanasonic(decode_results *results, const uint16_t nbits, + const bool strict, const uint32_t manufacturer) { if (strict && nbits != kPanasonicBits) return false; // Request is out of spec. uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks; - - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!match(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kPanasonicEndGap)) - return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicEndGap, true)) return false; // Compliance uint32_t address = data >> 32; uint32_t command = data & 0xFFFFFFFF; @@ -217,7 +203,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 +223,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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +253,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 +291,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 +310,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 +324,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 +346,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 +380,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 +403,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 +417,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 +433,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 +453,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 +467,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 +478,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 +496,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 +507,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 +519,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 +534,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 +558,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,25 +590,12 @@ void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, remote_state[20] |= corrected >> 4; } -void IRPanasonicAc::cancelOffTimer() { setOffTimer(0, false); } +void IRPanasonicAc::cancelOffTimer(void) { this->setOffTimer(0, false); } -bool IRPanasonicAc::isOffTimerEnabled() { +bool IRPanasonicAc::isOffTimerEnabled(void) { return remote_state[13] & kPanasonicAcOffTimer; } -#ifdef ARDUINO -String IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { - String result = ""; -#else -std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { - std::string result = ""; -#endif // ARDUINO - result += uint64ToString(mins_since_midnight / 60) + ':'; - uint8_t mins = mins_since_midnight % 60; - if (mins < 10) result += '0'; // Zero pad the minutes. - return result + uint64ToString(mins); -} - // Convert a standard A/C mode into its native mode. uint8_t IRPanasonicAc::convertMode(const stdAc::opmode_t mode) { switch (mode) { @@ -685,14 +663,79 @@ 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 result = ""; -#endif // ARDUINO + result.reserve(180); // Reserve some heap for the string to reduce fragging. result += F("Model: "); result += uint64ToString(getModel()); switch (getModel()) { @@ -717,52 +760,14 @@ std::string IRPanasonicAc::toString() { default: result += F(" (UNKNOWN)"); } - result += F(", Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kPanasonicAcAuto: - result += F(" (AUTO)"); - break; - case kPanasonicAcCool: - result += F(" (COOL)"); - break; - case kPanasonicAcHeat: - result += F(" (HEAT)"); - break; - case kPanasonicAcDry: - result += F(" (DRY)"); - break; - case kPanasonicAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kPanasonicAcFanAuto: - result += F(" (AUTO)"); - break; - case kPanasonicAcFanMax: - result += F(" (MAX)"); - break; - case kPanasonicAcFanMin: - result += F(" (MIN)"); - break; - default: - result += F(" (UNKNOWN)"); - break; - } - result += F(", Swing (Vertical): "); - result += uint64ToString(getSwingVertical()); + result += addBoolToString(getPower(), F("Power")); + result += addModeToString(getMode(), kPanasonicAcAuto, kPanasonicAcCool, + kPanasonicAcHeat, kPanasonicAcDry, kPanasonicAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kPanasonicAcFanMax, kPanasonicAcFanMin, + kPanasonicAcFanAuto, kPanasonicAcFanAuto, + kPanasonicAcFanMed); + result += addIntToString(getSwingVertical(), F("Swing (Vertical)")); switch (getSwingVertical()) { case kPanasonicAcSwingVAuto: result += F(" (AUTO)"); @@ -786,8 +791,7 @@ std::string IRPanasonicAc::toString() { case kPanasonicCkp: break; // No Horizontal Swing support. default: - result += F(", Swing (Horizontal): "); - result += uint64ToString(getSwingHorizontal()); + result += addIntToString(getSwingHorizontal(), F("Swing (Horizontal)")); switch (getSwingHorizontal()) { case kPanasonicAcSwingHAuto: result += F(" (AUTO)"); @@ -812,28 +816,15 @@ std::string IRPanasonicAc::toString() { break; } } - result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); - result += F(", Powerful: "); - if (getPowerful()) - result += F("On"); - else - result += F("Off"); - result += F(", Clock: "); - result += timeToString(getClock()); - result += F(", On Timer: "); - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); - else - result += F("Off"); - result += F(", Off Timer: "); - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); - else - result += F("Off"); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); + result += addLabeledString(minsToString(getClock()), F("Clock")); + result += addLabeledString( + isOnTimerEnabled() ? minsToString(getOnTimer()) : F("Off"), + F("On Timer")); + result += addLabeledString( + isOffTimerEnabled() ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); return result; } @@ -856,11 +847,8 @@ std::string IRPanasonicAc::toString() { // A/C Remotes: // A75C3747 (Confirmed) // A75C3704 -bool IRrecv::decodePanasonicAC(decode_results *results, uint16_t nbits, - bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - +bool IRrecv::decodePanasonicAC(decode_results *results, const uint16_t nbits, + const bool strict) { uint8_t min_nr_of_messages = 1; if (strict) { if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits) @@ -871,79 +859,31 @@ bool IRrecv::decodePanasonicAC(decode_results *results, uint16_t nbits, min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1) return false; // Can't possibly be a valid PANASONIC_AC message. - uint16_t dataBitsSoFar = 0; uint16_t offset = kStartOffset; - match_result_t data_result; - // Header - if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks; + // Match Header + Data #1 + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcSectionGap, false, + kPanasonicAcTolerance, kPanasonicAcExcess, false); + if (!used) return false; + offset += used; - uint16_t i = 0; - // Data (Section #1) - // Keep reading bytes until we either run out of section or state to fill. - for (; offset <= results->rawlen - 16 && i < kPanasonicAcSection1Length; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance, - kPanasonicAcExcess, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - // Section footer. - if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) + // Match Header + Data #2 + Footer + if (!matchGeneric(results->rawbuf + offset, + results->state + kPanasonicAcSection1Length, + results->rawlen - offset, + nbits - kPanasonicAcSection1Length * 8, + kPanasonicHdrMark, kPanasonicHdrSpace, + kPanasonicBitMark, kPanasonicOneSpace, + kPanasonicBitMark, kPanasonicZeroSpace, + kPanasonicBitMark, kPanasonicAcMessageGap, true, + kPanasonicAcTolerance, kPanasonicAcExcess, false)) return false; - if (!matchSpace(results->rawbuf[offset++], kPanasonicAcSectionGap, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Header. - if (!matchMark(results->rawbuf[offset++], kPanasonicHdrMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - if (!matchSpace(results->rawbuf[offset++], kPanasonicHdrSpaceTicks * s_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Data (Section #2) - // Keep reading bytes until we either run out of data. - for (; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance, - kPanasonicAcExcess, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - // Message Footer. - if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kPanasonicAcMessageGap)) - return false; - // Compliance if (strict) { // Check the signatures of the section blocks. They start with 0x02& 0x20. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Panasonic.h similarity index 67% rename from lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Panasonic.h index 1a7b4e114..32899db9b 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Panasonic.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Panasonic.h @@ -1,5 +1,24 @@ // Copyright 2018 David Conran +// Supports: +// Brand: Panasonic, Model: TV +// Brand: Panasonic, Model: JKE series A/C +// Brand: Panasonic, Model: DKE series A/C +// Brand: Panasonic, Model: CKP series A/C +// Brand: Panasonic, Model: CS-ME10CKPG A/C +// Brand: Panasonic, Model: CS-ME12CKPG A/C +// Brand: Panasonic, Model: CS-ME14CKPG A/C +// Brand: Panasonic, Model: RKR series A/C +// Brand: Panasonic, Model: CS-Z9RKR A/C +// Brand: Panasonic, Model: NKE series A/C +// Brand: Panasonic, Model: CS-YW9MKD A/C +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3704 remote +// Brand: Panasonic, Model: A75C2311 remote (CKP) +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3747 remote +// Brand: Panasonic, Model: A75C3747 remote + #ifndef IR_PANASONIC_H_ #define IR_PANASONIC_H_ @@ -7,8 +26,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -16,12 +33,6 @@ #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 -// PPPP AAAAA N N N AAAAA SSS O O N N N I C -// P A A N NN A A S O O N NN I C -// P A A N N A A SSSS OOO N N IIIII CCCC - // Panasonic A/C support heavily influenced by: // https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp @@ -37,6 +48,7 @@ const uint8_t kPanasonicAcCool = 3; // 0b0011 const uint8_t kPanasonicAcHeat = 4; // 0b0010 const uint8_t kPanasonicAcFan = 6; // 0b0110 const uint8_t kPanasonicAcFanMin = 0; +const uint8_t kPanasonicAcFanMed = 2; const uint8_t kPanasonicAcFanMax = 4; const uint8_t kPanasonicAcFanAuto = 7; const uint8_t kPanasonicAcFanOffset = 3; @@ -81,62 +93,63 @@ enum panasonic_ac_remote_model_t { class IRPanasonicAc { public: - explicit IRPanasonicAc(uint16_t pin); + explicit IRPanasonicAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_PANASONIC void send(const uint16_t repeat = kPanasonicAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_PANASONIC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp, const bool remember = true); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t fan); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setRaw(const uint8_t state[]); - uint8_t *getRaw(); + uint8_t *getRaw(void); static bool validChecksum(uint8_t *state, const uint16_t length = kPanasonicAcStateLength); static uint8_t calcChecksum(uint8_t *state, const uint16_t length = kPanasonicAcStateLength); - void setQuiet(const bool state); - bool getQuiet(); - void setPowerful(const bool state); - bool getPowerful(); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); void setModel(const panasonic_ac_remote_model_t model); - panasonic_ac_remote_model_t getModel(); + panasonic_ac_remote_model_t getModel(void); void setSwingVertical(const uint8_t elevation); - uint8_t getSwingVertical(); + uint8_t getSwingVertical(void); void setSwingHorizontal(const uint8_t direction); - uint8_t getSwingHorizontal(); + uint8_t getSwingHorizontal(void); static uint16_t encodeTime(const uint8_t hours, const uint8_t mins); - uint16_t getClock(); + uint16_t getClock(void); void setClock(const uint16_t mins_since_midnight); - uint16_t getOnTimer(); + uint16_t getOnTimer(void); void setOnTimer(const uint16_t mins_since_midnight, const bool enable = true); - void cancelOnTimer(); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); + void cancelOnTimer(void); + bool isOnTimerEnabled(void); + uint16_t getOffTimer(void); void setOffTimer(const uint16_t mins_since_midnight, const bool enable = true); - void cancelOffTimer(); - bool isOffTimerEnabled(); + 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); -#ifdef ARDUINO - String toString(); - static String timeToString(const uint16_t mins_since_midnight); -#else - std::string toString(); - static std::string timeToString(const uint16_t mins_since_midnight); -#endif + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Pioneer.cpp similarity index 67% rename from lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Pioneer.cpp index 9134e3696..48197f2a5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Pioneer.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Pioneer.cpp @@ -2,6 +2,8 @@ // Copyright 2017, 2018 David Conran // Copyright 2018 Kamil Palczewski +// Pioneer remote emulation + #define __STDC_LIMIT_MACROS #include #include @@ -10,12 +12,6 @@ #include "IRutils.h" #include "ir_NEC.h" -// PPPP III OOO N N EEEE EEEE RRRR -// P P I O O NN N E E R R -// PPPP I O O N N N EEE EEE RRRR -// P I O O N NN E E R R -// P III OOO N N EEEE EEEE R RR - // Ref: // http://adrian-kingston.com/IRFormatPioneer.htm @@ -87,28 +83,22 @@ bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits, uint64_t data = 0; uint16_t offset = kStartOffset; - + results->value = 0; for (uint16_t section = 0; section < 2; section++) { - // Header - if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks; - // - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits / 2, kNecBitMarkTicks * mark_tick, - kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick, - kNecZeroSpaceTicks * space_tick); - if (data_result.success == false) return false; - uint8_t command = data_result.data >> 8; - uint8_t command_inverted = data_result.data; - uint8_t address = data_result.data >> 24; - uint8_t address_inverted = data_result.data >> 16; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits / 2, + kNecHdrMark, kNecHdrSpace, + kNecBitMark, kNecOneSpace, + kNecBitMark, kNecZeroSpace, + kNecBitMark, kNecMinGap, true); + if (!used) return false; + offset += used; + uint8_t command = data >> 8; + uint8_t command_inverted = data; + uint8_t address = data >> 24; + uint8_t address_inverted = data >> 16; // Compliance if (strict) { if (command != (command_inverted ^ 0xFF)) @@ -116,8 +106,7 @@ bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits, if (address != (address_inverted ^ 0xFF)) return false; // Address integrity failed. } - data = (data << (nbits / 2)) + data_result.data; - offset += data_result.used; + results->value = (results->value << (nbits / 2)) + data; // NEC-like commands and addresses are technically in LSB first order so the // final versions have to be reversed. uint16_t code = reverseBits((command << 8) + address, 16); @@ -125,18 +114,10 @@ bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits, results->command = code; else results->address = code; - - // Footer - if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kNecMinGapTicks * space_tick)) - return false; } // Success results->bits = nbits; - results->value = data; results->decode_type = PIONEER; return true; } diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Pronto.cpp similarity index 92% rename from lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Pronto.cpp index 9ab5c76d0..a408afed5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Pronto.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Pronto.cpp @@ -1,14 +1,10 @@ // Copyright 2017 David Conran +// Pronto code message generation + #include #include "IRsend.h" -// PPPPPP tt -// PP PP rr rr oooo nn nnn tt oooo -// PPPPPP rrr r oo oo nnn nn tttt oo oo -// PP rr oo oo nn nn tt oo oo -// PP rr oooo nn nn tttt oooo - // Constants const float kProntoFreqFactor = 0.241246; const uint16_t kProntoTypeOffset = 0; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_RC5_RC6.cpp similarity index 97% rename from lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_RC5_RC6.cpp index ef1500d60..b79416692 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_RC5_RC6.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_RC5_RC6.cpp @@ -1,21 +1,15 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote +// RC-5X support added by David Conran + #include #include "IRrecv.h" #include "IRsend.h" #include "IRtimer.h" #include "IRutils.h" -// RRRRRR CCCCC 555555 XX XX RRRRRR CCCCC 666 -// RR RR CC C 55 XX XX RR RR CC C 66 -// RRRRRR CC _____ 555555 XXXX RRRRRR CC _____ 666666 -// RR RR CC C 5555 XX XX RR RR CC C 66 66 -// RR RR CCCCC 555555 XX XX RR RR CCCCC 66666 - -// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote -// RC-5X support added by David Conran - // Constants // RC-5/RC-5X // Ref: diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_RCMM.cpp similarity index 94% rename from lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_RCMM.cpp index 1b03d2c07..cbc5e40b3 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_RCMM.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_RCMM.cpp @@ -1,19 +1,16 @@ // Copyright 2017 David Conran +// Send & decode support for Phillips RC-MM added by David Conran + +// Supports: +// Brand: Microsoft, Model: XBOX 360 + #include #include "IRrecv.h" #include "IRsend.h" #include "IRtimer.h" #include "IRutils.h" -// RRRRRR CCCCC MM MM MM MM -// RR RR CC C MMM MMM MMM MMM -// RRRRRR CC _____ MM MM MM MM MM MM -// RR RR CC C MM MM MM MM -// RR RR CCCCC MM MM MM MM - -// Send & decode support for RC-MM added by David Conran - // Constants // Ref: // http://www.sbprojects.com/knowledge/ir/rcmm.php diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Samsung.cpp similarity index 64% rename from lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Samsung.cpp index 7e54d17df..09ba3e199 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Samsung.cpp @@ -1,6 +1,8 @@ // Copyright 2009 Ken Shirriff // Copyright 2017, 2018, 2019 David Conran +// Samsung remote emulation + #include "ir_Samsung.h" #include #ifndef ARDUINO @@ -10,12 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// SSSS AAA MMM SSSS U U N N GGGG -// S A A M M M S U U NN N G -// SSS AAAAA M M M SSS U U N N N G GG -// S A A M M S U U N NN G G -// SSSS A A M M SSSS UUU N N GGG - // Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ // Constants @@ -54,6 +50,13 @@ const uint16_t kSamsungAcBitMark = 586; const uint16_t kSamsungAcOneSpace = 1432; const uint16_t kSamsungAcZeroSpace = 436; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_SAMSUNG // Send a Samsung formatted message. // Samsung has a separate message to indicate a repeat, like NEC does. @@ -68,7 +71,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 +89,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 +117,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) @@ -123,31 +127,14 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick, - kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick)) - return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kSamsungHdrMark, kSamsungHdrSpace, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungMinGap, true)) return false; // Compliance - // According to the spec, the customer (address) code is the first 8 // transmitted bits. It's then repeated. Check for that. uint8_t address = data >> 24; @@ -182,7 +169,7 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, // Protocol is used by Samsung Bluray Remote: ak59-00167a // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/621 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/621 void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, const uint16_t repeat) { if (nbits < 16) return; // To small to send. @@ -222,7 +209,7 @@ void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits, // Protocol is used by Samsung Bluray Remote: ak59-00167a // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/621 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/621 bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, const bool strict) { if (results->rawlen < 2 * nbits + kHeader + kFooter * 2 - 1) @@ -235,52 +222,29 @@ bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, uint64_t data = 0; uint16_t offset = kStartOffset; - // Header - if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks; - // Data (Block #1) - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 16, - kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick, - kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t bitsSoFar = data_result.used / 2; - // Footer (Block #1) - if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) - return false; - if (!matchSpace(results->rawbuf[offset++], kSamsungHdrSpaceTicks * s_tick)) - return false; + // Match Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, 16, + kSamsungHdrMark, kSamsungHdrSpace, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungHdrSpace, false); + if (!used) return false; + offset += used; // Data (Block #2) - data_result = matchData(&(results->rawbuf[offset]), - nbits - 16, - kSamsungBitMarkTicks * m_tick, - kSamsungOneSpaceTicks * s_tick, - kSamsungBitMarkTicks * m_tick, - kSamsungZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; + uint64_t data2 = 0; + if (!matchGeneric(results->rawbuf + offset, &data2, + results->rawlen - offset, nbits - 16, + 0, 0, + kSamsungBitMark, kSamsungOneSpace, + kSamsungBitMark, kSamsungZeroSpace, + kSamsungBitMark, kSamsungMinGap, true)) return false; data <<= (nbits - 16); - data += data_result.data; - offset += data_result.used; - bitsSoFar += data_result.used / 2; - // Footer (Block #2) - if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick)) - return false; - - // Compliance - if (nbits != bitsSoFar) return false; + data += data2; // Success - results->bits = bitsSoFar; + results->bits = nbits; results->value = data; results->decode_type = SAMSUNG36; results->command = data & ((1ULL << (nbits - 16)) - 1); @@ -297,10 +261,10 @@ bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits, // nbytes: Nr. of bytes of data in the array. (>=kSamsungAcStateLength) // repeat: Nr. of times the message is to be repeated. (Default = 0). // -// Status: ALPHA / Untested. +// Status: Stable / Known working. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505 void IRsend::sendSamsungAC(const uint8_t data[], const uint16_t nbytes, const uint16_t repeat) { if (nbytes < kSamsungAcStateLength && nbytes % kSamsungACSectionLength) @@ -326,9 +290,13 @@ 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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { + 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 +309,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 +334,35 @@ 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? + _sendpower = false; // It will now been sent. + if (this->getPower()) { + this->sendOn(); + } else { + this->sendOff(); + return; // No point sending anything else if we are turning the unit off. + } + } _irsend.sendSamsungAC(remote_state, kSamsungAcStateLength, repeat); } @@ -391,7 +370,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, @@ -409,7 +388,7 @@ void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { // 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 +// See: https://github.com/crankyoldgit/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, @@ -420,7 +399,7 @@ void IRSamsungAc::sendOn(const uint16_t 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 +// See: https://github.com/crankyoldgit/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, @@ -430,8 +409,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,26 +425,28 @@ void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { } } -void IRSamsungAc::on() { - remote_state[1] &= ~kSamsungAcPowerMask1; - remote_state[6] |= kSamsungAcPowerMask2; +void IRSamsungAc::on(void) { + remote_state[1] &= ~kSamsungAcPowerMask1; // Bit needs to be cleared. + remote_state[6] |= kSamsungAcPowerMask6; // Bit needs to be set. + _sendpower = true; // Flag that we need to send the special power message(s). } -void IRSamsungAc::off() { - remote_state[1] |= kSamsungAcPowerMask1; - remote_state[6] &= ~kSamsungAcPowerMask2; +void IRSamsungAc::off(void) { + remote_state[1] |= kSamsungAcPowerMask1; // Bit needs to be set. + remote_state[6] &= ~kSamsungAcPowerMask6; // Bit needs to be cleared. + _sendpower = true; // Flag that we need to send the special power message(s). } -void IRSamsungAc::setPower(const bool state) { - if (state) - on(); +void IRSamsungAc::setPower(const bool on) { + if (on) + this->on(); else - off(); + this->off(); } -bool IRSamsungAc::getPower() { - return ((remote_state[6] & kSamsungAcPowerMask2) != 0) && - ((remote_state[1] & kSamsungAcPowerMask1) == 0); +bool IRSamsungAc::getPower(void) { + return (remote_state[6] & kSamsungAcPowerMask6) && + !(remote_state[1] & kSamsungAcPowerMask1); } // Set the temp. in deg C @@ -477,7 +458,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 +470,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 +489,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 +500,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 { @@ -562,18 +546,42 @@ void IRSamsungAc::setClean(const bool state) { } } -// Very unsure this is correct. -bool IRSamsungAc::getQuiet() { - return remote_state[11] & kSamsungAcQuietMask11; +bool IRSamsungAc::getQuiet(void) { + return !(remote_state[1] & kSamsungAcQuietMask1) && + (remote_state[5] & kSamsungAcQuietMask5); } -// Very unsure this is correct. -void IRSamsungAc::setQuiet(const bool state) { - if (state) { - remote_state[11] |= kSamsungAcQuietMask11; - setFan(kSamsungAcFanAuto); // Quiet mode seems to set fan speed to auto. +void IRSamsungAc::setQuiet(const bool on) { + if (on) { + remote_state[1] &= ~kSamsungAcQuietMask1; // Bit needs to be cleared. + remote_state[5] |= kSamsungAcQuietMask5; // Bit needs to be set. + // Quiet mode seems to set fan speed to auto. + this->setFan(kSamsungAcFanAuto); + this->setPowerful(false); // Quiet 'on' is mutually exclusive to Powerful. } else { - remote_state[11] &= ~kSamsungAcQuietMask11; + remote_state[1] |= kSamsungAcQuietMask1; // Bit needs to be set. + remote_state[5] &= ~kSamsungAcQuietMask5; // Bit needs to be cleared. + } +} + +bool IRSamsungAc::getPowerful(void) { + return !(remote_state[8] & kSamsungAcPowerfulMask8) && + (remote_state[10] & kSamsungAcPowerfulMask10) && + this->getFan() == kSamsungAcFanTurbo; +} + +void IRSamsungAc::setPowerful(const bool on) { + if (on) { + remote_state[8] &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared. + remote_state[10] |= kSamsungAcPowerfulMask10; // Bit needs to be set. + // Powerful mode sets fan speed to Turbo. + this->setFan(kSamsungAcFanTurbo); + this->setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet. + } else { + remote_state[8] |= kSamsungAcPowerfulMask8; // Bit needs to be set. + remote_state[10] &= ~kSamsungAcPowerfulMask10; // Bit needs to be cleared. + // Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode + if (this->getFan() == kSamsungAcFanTurbo) this->setFan(kSamsungAcFanAuto); } } @@ -610,85 +618,90 @@ uint8_t IRSamsungAc::convertFan(const stdAc::fanspeed_t speed) { } } -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRSamsungAc::toString() { - String result = ""; -#else -std::string IRSamsungAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kSamsungAcAuto: - result += F(" (AUTO)"); - break; - case kSamsungAcCool: - result += F(" (COOL)"); - break; - case kSamsungAcHeat: - result += F(" (HEAT)"); - break; - case kSamsungAcDry: - result += F(" (DRY)"); - break; - case kSamsungAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); +// 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; } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRSamsungAc::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kSamsungAcFanTurbo: return stdAc::fanspeed_t::kMax; + case kSamsungAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSamsungAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSamsungAcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRSamsungAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::SAMSUNG_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwing() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.quiet = this->getQuiet(); + result.turbo = this->getPowerful(); + result.clean = this->getClean(); + result.beep = this->getBeep(); + // Not supported. + result.swingh = stdAc::swingh_t::kOff; + result.econo = false; + result.filter = false; + result.light = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRSamsungAc::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kSamsungAcAuto, kSamsungAcCool, + kSamsungAcHeat, kSamsungAcDry, + kSamsungAcFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); switch (getFan()) { case kSamsungAcFanAuto: case kSamsungAcFanAuto2: - result += F(" (AUTO)"); + result += F(" (Auto)"); break; case kSamsungAcFanLow: - result += F(" (LOW)"); + result += F(" (Low)"); break; case kSamsungAcFanMed: - result += F(" (MED)"); + result += F(" (Medium)"); break; case kSamsungAcFanHigh: - result += F(" (HIGH)"); + result += F(" (High)"); break; case kSamsungAcFanTurbo: - result += F(" (TURBO)"); + result += F(" (Turbo)"); break; default: result += F(" (UNKNOWN)"); break; } - result += F(", Swing: "); - if (getSwing()) - result += F("On"); - else - result += F("Off"); - result += F(", Beep: "); - if (getBeep()) - result += F("On"); - else - result += F("Off"); - result += F(", Clean: "); - if (getBeep()) - result += F("On"); - else - result += F("Off"); - result += F(", Quiet: "); - if (getQuiet()) - result += F("On"); - else - result += F("Off"); + result += addBoolToString(getSwing(), F("Swing")); + result += addBoolToString(getBeep(), F("Beep")); + result += addBoolToString(getClean(), F("Clean")); + result += addBoolToString(getQuiet(), F("Quiet")); + result += addBoolToString(getPowerful(), F("Powerful")); return result; } @@ -702,62 +715,38 @@ std::string IRSamsungAc::toString() { // Returns: // boolean: True if it can decode it, false if it can't. // -// Status: BETA / Appears to mostly work. +// Status: Stable / Known to be working. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 -bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, - bool strict) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505 +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; uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; // Message Header if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; if (!matchSpace(results->rawbuf[offset++], kSamsungAcHdrSpace)) return false; // Section(s) - for (uint16_t pos = kSamsungACSectionLength, i = 0; pos <= nbits / 8; + for (uint16_t pos = 0; pos <= (nbits / 8) - kSamsungACSectionLength; pos += kSamsungACSectionLength) { - uint64_t sectiondata = 0; - // Section Header - if (!matchMark(results->rawbuf[offset++], kSamsungAcSectionMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionSpace)) - return false; - // Section Data - // Keep reading bytes until we either run out of section or state to fill. - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kSamsungAcBitMark, - kSamsungAcOneSpace, kSamsungAcBitMark, - kSamsungAcZeroSpace, 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; - sectiondata = (sectiondata << 8) + data_result.data; - } - DPRINTLN("DEBUG: sectiondata = 0x" + uint64ToString(sectiondata, 16)); - // Section Footer - if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; - if (pos < nbits / 8) { // Inter-section gap. - if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionGap)) - return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kSamsungAcSectionGap)) - return false; - } + uint16_t used; + // Section Header + Section Data (7 bytes) + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, kSamsungACSectionLength * 8, + kSamsungAcSectionMark, kSamsungAcSectionSpace, + kSamsungAcBitMark, kSamsungAcOneSpace, + kSamsungAcBitMark, kSamsungAcZeroSpace, + kSamsungAcBitMark, kSamsungAcSectionGap, + pos + kSamsungACSectionLength >= nbits / 8, + kTolerance, 0, false); + if (used == 0) return false; + offset += used; } // Compliance - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != nbits) return false; // Is the signature correct? DPRINTLN("DEBUG: Checking signature."); if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; @@ -770,7 +759,7 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, } // Success results->decode_type = SAMSUNG_AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Samsung.h similarity index 53% rename from lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Samsung.h index 9df427c6f..1a2be79bb 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Samsung.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Samsung.h @@ -9,8 +9,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,14 +16,14 @@ #include "IRsend_test.h" #endif -// SSSS AAA MMM SSSS U U N N GGGG -// S A A M M M S U U NN N G -// SSS AAAAA M M M SSS U U N N N G GG -// S A A M M S U U N NN G G -// SSSS A A M M SSSS UUU N N GGG +// Supports: +// Brand: Samsung, Model: UA55H6300 TV +// Brand: Samsung, Model: IEC-R03 remote +// Brand: Samsung, Model: AR12KSFPEWQNET A/C +// Brand: Samsung, Model: AR12HSSDBWKNEU A/C // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505 // Constants const uint8_t kSamsungAcAuto = 0; @@ -33,7 +31,7 @@ const uint8_t kSamsungAcCool = 1; const uint8_t kSamsungAcDry = 2; const uint8_t kSamsungAcFan = 3; const uint8_t kSamsungAcHeat = 4; -const uint8_t kSamsungAcModeMask = 0x70; +const uint8_t kSamsungAcModeMask = 0x70; // 0b01110000 const uint8_t kSamsungAcFanAuto = 0; const uint8_t kSamsungAcFanLow = 2; const uint8_t kSamsungAcFanMed = 4; @@ -43,17 +41,20 @@ const uint8_t kSamsungAcFanTurbo = 7; const uint8_t kSamsungAcMinTemp = 16; // 16C const uint8_t kSamsungAcMaxTemp = 30; // 30C const uint8_t kSamsungAcAutoTemp = 25; // 25C -const uint8_t kSamsungAcTempMask = 0xF0; -const uint8_t kSamsungAcPowerMask1 = 0x20; -const uint8_t kSamsungAcPowerMask2 = 0x30; -const uint8_t kSamsungAcFanMask = 0x0E; -const uint8_t kSamsungAcSwingMask = 0x70; +const uint8_t kSamsungAcTempMask = 0xF0; // 0b11110000 +const uint8_t kSamsungAcPowerMask1 = 0x20; // 0b00100000 +const uint8_t kSamsungAcPowerMask6 = 0x30; // 0b00110000 +const uint8_t kSamsungAcFanMask = 0x0E; // 0b00001110 +const uint8_t kSamsungAcSwingMask = 0x70; // 0b01110000 const uint8_t kSamsungAcSwingMove = 0b010; const uint8_t kSamsungAcSwingStop = 0b111; -const uint8_t kSamsungAcBeepMask = 0x02; -const uint8_t kSamsungAcCleanMask10 = 0x80; -const uint8_t kSamsungAcCleanMask11 = 0x02; -const uint8_t kSamsungAcQuietMask11 = 0x01; +const uint8_t kSamsungAcBeepMask = 0x02; // 0b00000010 +const uint8_t kSamsungAcCleanMask10 = 0x80; // 0b10000000 +const uint8_t kSamsungAcCleanMask11 = 0x02; // 0b00000010 +const uint8_t kSamsungAcQuietMask1 = 0x10; // 0b00010000 +const uint8_t kSamsungAcQuietMask5 = 0x20; // 0b00100000 +const uint8_t kSamsungAcPowerfulMask8 = 0x50; // 0b01010000 +const uint8_t kSamsungAcPowerfulMask10 = 0x06; // 0b00000110 const uint16_t kSamsungACSectionLength = 7; const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; @@ -61,9 +62,10 @@ const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; // Classes class IRSamsungAc { public: - explicit IRSamsungAc(uint16_t pin); + explicit IRSamsungAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_SAMSUNG_AC void send(const uint16_t repeat = kSamsungAcDefaultRepeat, const bool calcchecksum = true); @@ -71,27 +73,30 @@ class IRSamsungAc { const bool calcchecksum = true); void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat); void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_SAMSUNG_AC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); - void setSwing(const bool state); - bool getSwing(); - void setBeep(const bool state); - bool getBeep(); - void setClean(const bool state); - bool getClean(); - void setQuiet(const bool state); - bool getQuiet(); - uint8_t* getRaw(); + uint8_t getMode(void); + void setSwing(const bool on); + bool getSwing(void); + void setBeep(const bool on); + bool getBeep(void); + void setClean(const bool on); + bool getClean(void); + void setQuiet(const bool on); + bool getQuiet(void); + void setPowerful(const bool on); + bool getPowerful(void); + uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kSamsungAcStateLength); static bool validChecksum(const uint8_t state[], @@ -100,11 +105,10 @@ class IRSamsungAc { 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 + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -114,6 +118,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-2.6.0/src/ir_Sanyo.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sanyo.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Sanyo.cpp index b2b4d7830..b05e76766 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sanyo.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sanyo.cpp @@ -6,12 +6,6 @@ #include "IRrecv.h" #include "IRsend.h" -// SSSS AAA N N Y Y OOO -// S A A NN N Y Y O O -// SSS AAAAA N N N Y O O -// S A A N NN Y O O -// SSSS A A N N Y OOO - // Sanyo SA 8650B originally added from: // https://github.com/shirriff/Arduino-IRremote/ // Sanyo LC7461 support originally by marcosamarinho diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.cpp new file mode 100644 index 000000000..b27c319e0 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.cpp @@ -0,0 +1,557 @@ +// Copyright 2009 Ken Shirriff +// Copyright 2017, 2019 David Conran + +// Sharp remote emulation + +#include "ir_Sharp.h" +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Equipment it seems compatible with: +// * Sharp LC-52D62U +// * +// + +// Constants +// period time = 1/38000Hz = 26.316 microseconds. +// Ref: +// GlobalCache's IR Control Tower data. +// http://www.sbprojects.com/knowledge/ir/sharp.php +const uint16_t kSharpTick = 26; +const uint16_t kSharpBitMarkTicks = 10; +const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; +const uint16_t kSharpOneSpaceTicks = 70; +const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; +const uint16_t kSharpZeroSpaceTicks = 30; +const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; +const uint16_t kSharpGapTicks = 1677; +const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; +// Address(5) + Command(8) + Expansion(1) + Check(1) +const uint64_t kSharpToggleMask = + ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; +const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; +const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if (SEND_SHARP || SEND_DENON) +// Send a (raw) Sharp message +// +// Args: +// data: Contents of the message to be sent. +// nbits: Nr. of bits of data to be sent. Typically kSharpBits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: BETA / Previously working fine. +// +// Notes: +// This procedure handles the inversion of bits required per protocol. +// The protocol spec says to send the LSB first, but legacy code & usage +// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() +// handles this for you, assuming you are using the correct/standard values. +// e.g. sendSharpRaw(encodeSharp(address, command)); +// +// Ref: +// http://www.sbprojects.com/knowledge/ir/sharp.htm +// 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(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. + // Note: Previously this used to be performed 3 times (normal, inverted, + // normal), however all data points to that being incorrect. + for (uint8_t n = 0; n < 2; n++) { + sendGeneric(0, 0, // No Header + kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, + 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. + tempdata ^= kSharpToggleMask; + } + } +} + +// Encode a (raw) Sharp message from it's components. +// +// Args: +// address: The value of the address to be sent. +// command: The value of the address to be sent. (8 bits) +// expansion: The value of the expansion bit to use. (0 or 1, typically 1) +// check: The value of the check bit to use. (0 or 1, typically 0) +// MSBfirst: Flag indicating MSB first or LSB first order. (Default: false) +// Returns: +// An uint32_t containing the raw Sharp message for sendSharpRaw(). +// +// Status: BETA / Should work okay. +// +// Notes: +// Assumes the standard Sharp bit sizes. +// Historically sendSharp() sends address & command in +// MSB first order. This is actually incorrect. It should be sent in LSB +// order. The behaviour of sendSharp() hasn't been changed to maintain +// backward compatibility. +// +// Ref: +// 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(const uint16_t address, const uint16_t command, + const uint16_t expansion, const uint16_t check, + const bool MSBfirst) { + // Mask any unexpected bits. + 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. + tempaddress = reverseBits(tempaddress, kSharpAddressBits); + tempcommand = reverseBits(tempcommand, kSharpCommandBits); + } + // Concatinate all the bits. + return (tempaddress << (kSharpCommandBits + 2)) | (tempcommand << 2) | + (tempexpansion << 1) | tempcheck; +} + +// Send a Sharp message +// +// Args: +// address: Address value to be sent. +// command: Command value to be sent. +// nbits: Nr. of bits of data to be sent. Typically kSharpBits. +// repeat: Nr. of additional times the message is to be sent. +// +// Status: DEPRICATED / Previously working fine. +// +// Notes: +// This procedure has a non-standard invocation style compared to similar +// sendProtocol() routines. This is due to legacy, compatibility, & historic +// reasons. Normally the calling syntax version is like sendSharpRaw(). +// This procedure transmits the address & command in MSB first order, which is +// incorrect. This behaviour is left as-is to maintain backward +// compatibility with legacy code. +// In short, you should use sendSharpRaw(), encodeSharp(), and the correct +// values of address & command instead of using this, & the wrong values. +// +// Ref: +// 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(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) + +#if (DECODE_SHARP || DECODE_DENON) +// Decode the supplied Sharp message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// nbits: Nr. of data bits to expect. Typically kSharpBits. +// strict: Flag indicating if we should perform strict matching. +// expansion: Should we expect the expansion bit to be set. Default is true. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: STABLE / Working fine. +// +// Note: +// This procedure returns a value suitable for use in sendSharpRaw(). +// TODO(crankyoldgit): Need to ensure capture of the inverted message as it can +// be missed due to the interrupt timeout used to detect an end of message. +// Several compliance checks are disabled until that is resolved. +// Ref: +// 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, 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 + if (strict) { + if (nbits != kSharpBits) return false; // Request is out of spec. + // DISABLED - See TODO +#ifdef UNIT_TEST + // An in spec message has the data sent normally, then inverted. So we + // expect twice as many entries than to just get the results. + if (results->rawlen < 2 * (2 * nbits + kFooter)) return false; +#endif + } + + uint64_t data = 0; + uint16_t offset = kStartOffset; + + // Match Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + 0, 0, // No Header + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35); + if (!used) return false; + offset += used; + // Compliance + if (strict) { + // Check the state of the expansion bit is what we expect. + if ((data & 0b10) >> 1 != expansion) return false; + // The check bit should be cleared in a normal message. + if (data & 0b1) return false; + // DISABLED - See TODO +#ifdef UNIT_TEST + // Grab the second copy of the data (i.e. inverted) + uint64_t second_data = 0; + // Match Data + Footer + if (!matchGeneric(results->rawbuf + offset, &second_data, + results->rawlen - offset, nbits, + 0, 0, + kSharpBitMark, kSharpOneSpace, + kSharpBitMark, kSharpZeroSpace, + kSharpBitMark, kSharpGap, true, 35)) return false; + // Check that second_data has been inverted correctly. + if (data != (second_data ^ kSharpToggleMask)) return false; +#endif // UNIT_TEST + } + + // Success + results->decode_type = SHARP; + results->bits = nbits; + results->value = data; + // Address & command are actually transmitted in LSB first order. + results->address = reverseBits(data, nbits) & kSharpAddressMask; + results->command = + reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); + 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/crankyoldgit/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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRSharpAc::begin(void) { _irsend.begin(); } + +#if SEND_SHARP_AC +void IRSharpAc::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendSharpAc(remote, kSharpAcStateLength, repeat); +} +#endif // SEND_SHARP_AC + +// Calculate the checksum for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// The 4 bit checksum. +uint8_t IRSharpAc::calcChecksum(uint8_t state[], const uint16_t length) { + uint8_t xorsum = xorBytes(state, length - 1); + xorsum ^= (state[length - 1] & 0xF); + xorsum ^= xorsum >> 4; + return xorsum & 0xF; +} + +// Verify the checksums are valid for a given state. +// Args: +// state: The array to verify the checksums of. +// length: The size of the state. +// Returns: +// A boolean. +bool IRSharpAc::validChecksum(uint8_t state[], const uint16_t length) { + return (state[length - 1] >> 4) == IRSharpAc::calcChecksum(state, length); +} + +// Calculate and set the checksum values for the internal state. +void IRSharpAc::checksum(void) { + remote[kSharpAcStateLength - 1] &= 0x0F; + remote[kSharpAcStateLength - 1] |= this->calcChecksum(remote) << 4; +} + +void IRSharpAc::stateReset(void) { + static const uint8_t reset[kSharpAcStateLength] = { + 0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x01, 0x00, 0x00, 0x08, 0x80, 0x00, 0xE0, + 0x01}; + for (uint8_t i = 0; i < kSharpAcStateLength; i++) remote[i] = reset[i]; +} + +uint8_t *IRSharpAc::getRaw(void) { + this->checksum(); // Ensure correct settings before sending. + return remote; +} + +void IRSharpAc::setRaw(const uint8_t new_code[], const uint16_t length) { + for (uint8_t i = 0; i < length && i < kSharpAcStateLength; i++) + remote[i] = new_code[i]; +} + +void IRSharpAc::on(void) { remote[kSharpAcBytePower] |= kSharpAcBitPower; } + +void IRSharpAc::off(void) { remote[kSharpAcBytePower] &= ~kSharpAcBitPower; } + +void IRSharpAc::setPower(const bool on) { + if (on) + this->on(); + else + this->off(); +} + +bool IRSharpAc::getPower(void) { + return remote[kSharpAcBytePower] & kSharpAcBitPower; +} + +// Set the temp in deg C +void IRSharpAc::setTemp(const uint8_t temp) { + switch (this->getMode()) { + // Auto & Dry don't allow temp changes and have a special temp. + case kSharpAcAuto: + case kSharpAcDry: + remote[kSharpAcByteTemp] = 0; + remote[kSharpAcByteManual] = 0; // When in Dry/Auto this byte is 0. + return; + default: + remote[kSharpAcByteTemp] = 0xC0; + remote[kSharpAcByteManual] |= kSharpAcBitTempManual; + } + uint8_t degrees = std::max(temp, kSharpAcMinTemp); + degrees = std::min(degrees, kSharpAcMaxTemp); + remote[kSharpAcByteTemp] &= ~kSharpAcMaskTemp; + remote[kSharpAcByteTemp] |= (degrees - kSharpAcMinTemp); +} + +uint8_t IRSharpAc::getTemp(void) { + return (remote[kSharpAcByteTemp] & kSharpAcMaskTemp) + kSharpAcMinTemp; +} + +uint8_t IRSharpAc::getMode(void) { + return remote[kSharpAcByteMode] & kSharpAcMaskMode; +} + +void IRSharpAc::setMode(const uint8_t mode) { + const uint8_t special = 0x20; // Non-auto modes have this bit set. + remote[kSharpAcBytePower] |= special; + switch (mode) { + case kSharpAcAuto: + remote[kSharpAcBytePower] &= ~special; // Auto has this bit cleared. + // FALLTHRU + case kSharpAcDry: + this->setTemp(0); // Dry/Auto have no temp setting. + // FALLTHRU + case kSharpAcCool: + case kSharpAcHeat: + remote[kSharpAcByteMode] &= ~kSharpAcMaskMode; + remote[kSharpAcByteMode] |= mode; + + break; + default: + this->setMode(kSharpAcAuto); + } +} + +// Set the speed of the fan +void IRSharpAc::setFan(const uint8_t speed) { + remote[kSharpAcByteManual] |= kSharpAcBitFanManual; // Manual fan mode. + switch (speed) { + case kSharpAcFanAuto: + // Clear the manual fan bit. + remote[kSharpAcByteManual] &= ~kSharpAcBitFanManual; + // FALLTHRU + case kSharpAcFanMin: + case kSharpAcFanMed: + case kSharpAcFanHigh: + case kSharpAcFanMax: + remote[kSharpAcByteFan] &= ~kSharpAcMaskFan; + remote[kSharpAcByteFan] |= (speed << 4); + break; + default: + this->setFan(kSharpAcFanAuto); + } +} + +uint8_t IRSharpAc::getFan(void) { + return (remote[kSharpAcByteFan] & kSharpAcMaskFan) >> 4; +} + +// Convert a standard A/C mode into its native mode. +uint8_t IRSharpAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kSharpAcCool; + case stdAc::opmode_t::kHeat: + return kSharpAcHeat; + case stdAc::opmode_t::kDry: + return kSharpAcDry; + // No Fan mode. + default: + return kSharpAcAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRSharpAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kSharpAcFanMin; + case stdAc::fanspeed_t::kMedium: + return kSharpAcFanMed; + case stdAc::fanspeed_t::kHigh: + return kSharpAcFanHigh; + case stdAc::fanspeed_t::kMax: + return kSharpAcFanMax; + default: + return kSharpAcFanAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRSharpAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kSharpAcCool: return stdAc::opmode_t::kCool; + case kSharpAcHeat: return stdAc::opmode_t::kHeat; + case kSharpAcDry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRSharpAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kSharpAcFanMax: return stdAc::fanspeed_t::kMax; + case kSharpAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kSharpAcFanMed: return stdAc::fanspeed_t::kMedium; + case kSharpAcFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRSharpAc::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::SHARP_AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.turbo = false; + result.clean = false; + result.beep = false; + result.econo = false; + result.filter = false; + result.light = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRSharpAc::toString(void) { + String result = ""; + result.reserve(60); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kSharpAcAuto, kSharpAcCool, kSharpAcHeat, + kSharpAcDry, kSharpAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kSharpAcFanMax, kSharpAcFanMin, + kSharpAcFanAuto, kSharpAcFanAuto, kSharpAcFanMed); + 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/crankyoldgit/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 Header + Data + Footer + uint16_t used; + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSharpAcHdrMark, kSharpAcHdrSpace, + kSharpAcBitMark, kSharpAcOneSpace, + kSharpAcBitMark, kSharpAcZeroSpace, + kSharpAcBitMark, kSharpAcGap, true, + kTolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; + // Compliance + if (strict) { + if (!IRSharpAc::validChecksum(results->state)) return false; + } + + // Success + results->decode_type = SHARP_AC; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_SHARP_AC diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.h new file mode 100644 index 000000000..e80c7cdde --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sharp.h @@ -0,0 +1,98 @@ +// Copyright 2019 crankyoldgit + +// Supports: +// Brand: Sharp, Model: LC-52D62U TV +// Brand: Sharp, Model: AY-ZP40KR A/C + +#ifndef IR_SHARP_H_ +#define IR_SHARP_H_ + +#ifndef UNIT_TEST +#include +#endif +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#ifdef UNIT_TEST +#include "IRsend_test.h" +#endif + +// Constants +const uint16_t kSharpAcHdrMark = 3800; +const uint16_t kSharpAcHdrSpace = 1900; +const uint16_t kSharpAcBitMark = 470; +const uint16_t kSharpAcZeroSpace = 500; +const uint16_t kSharpAcOneSpace = 1400; +const uint32_t kSharpAcGap = kDefaultMessageGap; + +const uint8_t kSharpAcAuto = 0b000; +const uint8_t kSharpAcDry = 0b011; +const uint8_t kSharpAcCool = 0b010; +const uint8_t kSharpAcHeat = 0b001; +const uint8_t kSharpAcMinTemp = 15; // Celsius +const uint8_t kSharpAcMaxTemp = 30; // Celsius +const uint8_t kSharpAcFanAuto = 0b010; // 2 +const uint8_t kSharpAcFanMin = 0b100; // 4 (FAN1) +const uint8_t kSharpAcFanMed = 0b011; // 3 (FAN2) +const uint8_t kSharpAcFanHigh = 0b101; // 5 (FAN3) +const uint8_t kSharpAcFanMax = 0b111; // 7 (FAN4) +const uint8_t kSharpAcByteTemp = 4; +const uint8_t kSharpAcMaskTemp = 0b00001111; +const uint8_t kSharpAcBytePower = 5; +const uint8_t kSharpAcBitPower = 0b00010000; +const uint8_t kSharpAcByteMode = 6; +const uint8_t kSharpAcMaskMode = 0b00000011; +const uint8_t kSharpAcByteFan = kSharpAcByteMode; +const uint8_t kSharpAcMaskFan = 0b01110000; +const uint8_t kSharpAcByteManual = 10; +const uint8_t kSharpAcBitFanManual = 0b00000001; +const uint8_t kSharpAcBitTempManual = 0b00000100; + + +class IRSharpAc { + public: + explicit IRSharpAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + +#if SEND_SHARP_AC + void send(const uint16_t repeat = kSharpAcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_SHARP_AC + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t temp); + uint8_t getTemp(void); + void setFan(const uint8_t fan); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kSharpAcStateLength); + static bool validChecksum(uint8_t state[], + const uint16_t length = kSharpAcStateLength); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); +#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-2.6.0/src/ir_Sherwood.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sherwood.cpp similarity index 66% rename from lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Sherwood.cpp index 8af7dfb34..47c6790de 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sherwood.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sherwood.cpp @@ -1,14 +1,14 @@ // Copyright 2017 David Conran +// Sherwood IR remote emulation + +// Supports: +// Brand: Sherwood, Model: RC-138 remote +// Brand: Sherwood, Model: RD6505(B) Receiver + #include #include "IRsend.h" -// SSSSS HH HH EEEEEEE RRRRRR WW WW OOOOO OOOOO DDDDD -// SS HH HH EE RR RR WW WW OO OO OO OO DD DD -// SSSSS HHHHHHH EEEEE RRRRRR WW W WW OO OO OO OO DD DD -// SS HH HH EE RR RR WW WWW WW OO OO OO OO DD DD -// SSSSS HH HH EEEEEEE RR RR WW WW OOOO0 OOOO0 DDDDDD - #if SEND_SHERWOOD // Send an IR command to a Sherwood device. // diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sony.cpp similarity index 88% rename from lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Sony.cpp index efa6e6a46..6fc39b7b5 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Sony.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Sony.cpp @@ -2,17 +2,13 @@ // Copyright 2016 marcosamarinho // Copyright 2017 David Conran +// Sony Remote Emulation + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// SSSS OOO N N Y Y -// S O O NN N Y Y -// SSS O O N N N Y -// S O O N NN Y -// SSSS OOO N N Y - // Sony originally added from https://github.com/shirriff/Arduino-IRremote/ // Updates from marcosamarinho @@ -123,25 +119,20 @@ bool IRrecv::decodeSony(decode_results *results, uint16_t nbits, bool strict) { uint64_t data = 0; uint16_t offset = kStartOffset; uint16_t actualBits; - uint32_t timeSoFar = 0; // Time in uSecs of the message length. // Header - timeSoFar += results->rawbuf[offset] * kRawTick; if (!matchMark(results->rawbuf[offset], kSonyHdrMark)) return false; // Calculate how long the common tick time is based on the header mark. uint32_t tick = results->rawbuf[offset++] * kRawTick / kSonyHdrMarkTicks; // Data for (actualBits = 0; offset < results->rawlen - 1; actualBits++, offset++) { - // The gap after a Sony packet for a repeat should be kSonyMinGap or - // (kSonyRptLength - timeSoFar) according to the spec. - if (matchSpace(results->rawbuf[offset], kSonyMinGapTicks * tick) || - matchAtLeast(results->rawbuf[offset], kSonyRptLength - timeSoFar)) + // The gap after a Sony packet for a repeat should be kSonyMinGap according + // to the spec. + if (matchAtLeast(results->rawbuf[offset], kSonyMinGapTicks * tick)) break; // Found a repeat space. - timeSoFar += results->rawbuf[offset] * kRawTick; if (!matchSpace(results->rawbuf[offset++], kSonySpaceTicks * tick)) return false; - timeSoFar += results->rawbuf[offset] * kRawTick; if (matchMark(results->rawbuf[offset], kSonyOneMarkTicks * tick)) data = (data << 1) | 1; else if (matchMark(results->rawbuf[offset], kSonyZeroMarkTicks * tick)) diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Tcl.cpp similarity index 69% rename from lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Tcl.cpp index 79fb23cf1..80cefdeb1 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Tcl.cpp @@ -10,6 +10,12 @@ // Constants +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; #if SEND_TCL112AC void IRsend::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes, @@ -22,9 +28,11 @@ 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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +72,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 +87,7 @@ void IRTcl112Ac::stateReset() { remote_state[13] = 0x03; } -uint8_t* IRTcl112Ac::getRaw() { +uint8_t* IRTcl112Ac::getRaw(void) { this->checksum(); return remote_state; } @@ -112,7 +120,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 +159,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 +182,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,69 +299,74 @@ uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) { } } -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRTcl112Ac::toString() { - String result = ""; -#else -std::string IRTcl112Ac::toString() { - std::string result = ""; -#endif // ARDUINO - result += F("Power: "); - result += (this->getPower() ? F("On") : F("Off")); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (this->getMode()) { - case kTcl112AcAuto: - result += F(" (AUTO)"); - break; - case kTcl112AcCool: - result += F(" (COOL)"); - break; - case kTcl112AcHeat: - result += F(" (HEAT)"); - break; - case kTcl112AcDry: - result += F(" (DRY)"); - break; - case kTcl112AcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTcl112Ac::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTcl112AcCool: return stdAc::opmode_t::kCool; + case kTcl112AcHeat: return stdAc::opmode_t::kHeat; + case kTcl112AcDry: return stdAc::opmode_t::kDry; + case kTcl112AcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax; + case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium; + case kTcl112AcFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTcl112Ac::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TCL112AC; + result.model = -1; // Not supported. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->getSwingVertical() ? stdAc::swingv_t::kAuto : + stdAc::swingv_t::kOff; + result.swingh = this->getSwingHorizontal() ? stdAc::swingh_t::kAuto : + stdAc::swingh_t::kOff; + result.turbo = this->getTurbo(); + result.light = this->getLight(); + result.filter = this->getHealth(); + result.econo = this->getEcono(); + // Not supported. + result.quiet = false; + result.clean = false; + result.beep = false; + result.sleep = -1; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRTcl112Ac::toString(void) { + String result = ""; + result.reserve(140); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kTcl112AcAuto, kTcl112AcCool, + kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan); uint16_t nrHalfDegrees = this->getTemp() * 2; result += F(", Temp: "); result += uint64ToString(nrHalfDegrees / 2); if (nrHalfDegrees & 1) result += F(".5"); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kTcl112AcFanAuto: - result += F(" (Auto)"); - break; - case kTcl112AcFanLow: - result += F(" (Low)"); - break; - case kTcl112AcFanMed: - result += F(" (Med)"); - break; - case kTcl112AcFanHigh: - result += F(" (High)"); - break; - } - result += F(", Econo: "); - result += (this->getEcono() ? F("On") : F("Off")); - result += ", Health: "; - result += (this->getHealth() ? F("On") : F("Off")); - result += F(", Light: "); - result += (this->getLight() ? F("On") : F("Off")); - result += F(", Turbo: "); - result += (this->getTurbo() ? F("On") : F("Off")); - result += ", Swing (H): "; - result += (this->getSwingHorizontal() ? F("On") : F("Off")); - result += F(", Swing (V): "); - result += (this->getSwingVertical() ? F("On") : F("Off")); + result += 'C'; + result += addFanToString(getFan(), kTcl112AcFanHigh, kTcl112AcFanLow, + kTcl112AcFanAuto, kTcl112AcFanAuto, kTcl112AcFanMed); + result += addBoolToString(getEcono(), F("Econo")); + result += addBoolToString(getHealth(), F("Health")); + result += addBoolToString(getLight(), F("Light")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getSwingHorizontal(), F("Swing (H)")); + result += addBoolToString(getSwingVertical(), F("Swing (V)")); return result; } @@ -370,48 +383,26 @@ std::string IRTcl112Ac::toString() { // Status: BETA / Appears to mostly work. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/619 -bool IRrecv::decodeTcl112Ac(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Samsung A/C message. +// https://github.com/crankyoldgit/IRremoteESP8266/issues/619 +bool IRrecv::decodeTcl112Ac(decode_results *results, const uint16_t nbits, + const bool strict) { if (strict && nbits != kTcl112AcBits) return false; uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; - - // Message Header - if (!matchMark(results->rawbuf[offset++], kTcl112AcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTcl112AcHdrSpace)) 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 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kTcl112AcBitMark, - kTcl112AcOneSpace, kTcl112AcBitMark, - kTcl112AcZeroSpace, 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++], kTcl112AcBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kTcl112AcGap)) return false; + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTcl112AcHdrMark, kTcl112AcHdrSpace, + kTcl112AcBitMark, kTcl112AcOneSpace, + kTcl112AcBitMark, kTcl112AcZeroSpace, + kTcl112AcBitMark, kTcl112AcGap, true, + kTcl112AcTolerance, 0, false)) 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 && !IRTcl112Ac::validChecksum(results->state)) return false; // Success results->decode_type = TCL112AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Tcl.h similarity index 84% rename from lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Tcl.h index a1595451d..dce81c4ec 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Tcl.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Tcl.h @@ -1,15 +1,17 @@ // Copyright 2019 David Conran +// Supports: +// Brand: Leberg, Model: LBS-TOR07 A/C + #ifndef IR_TCL_H_ #define IR_TCL_H_ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" +#include "IRrecv.h" #ifdef UNIT_TEST #include "IRsend_test.h" #endif @@ -21,6 +23,7 @@ const uint16_t kTcl112AcBitMark = 500; const uint16_t kTcl112AcOneSpace = 1050; const uint16_t kTcl112AcZeroSpace = 325; const uint32_t kTcl112AcGap = kDefaultMessageGap; // Just a guess. +const uint8_t kTcl112AcTolerance = kTolerance + 5; // Percent const uint8_t kTcl112AcHeat = 1; const uint8_t kTcl112AcDry = 2; @@ -48,10 +51,12 @@ const uint8_t kTcl112AcBitTurbo = 0b01000000; class IRTcl112Ac { public: - explicit IRTcl112Ac(uint16_t pin); + explicit IRTcl112Ac(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_TCL112AC void send(const uint16_t repeat = kTcl112AcDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TCL void begin(void); uint8_t* getRaw(void); @@ -85,11 +90,10 @@ class IRTcl112Ac { 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 + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -98,7 +102,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-2.6.0/src/ir_Teco.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Teco.cpp similarity index 61% rename from lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Teco.cpp index 779bf8f8f..9cc47a37d 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Teco.cpp @@ -20,6 +20,13 @@ const uint16_t kTecoOneSpace = 1650; const uint16_t kTecoZeroSpace = 580; const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess. +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_TECO // Send a Teco A/C message. // @@ -27,7 +34,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 +43,11 @@ 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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } -void IRTecoAc::begin() { _irsend.begin(); } +void IRTecoAc::begin(void) { _irsend.begin(); } #if SEND_TECO void IRTecoAc::send(const uint16_t repeat) { @@ -168,61 +178,65 @@ 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) { String result = ""; -#else -std::string IRTecoAc::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 kTecoAuto: - result += F(" (AUTO)"); - break; - case kTecoCool: - result += F(" (COOL)"); - break; - case kTecoHeat: - result += F(" (HEAT)"); - break; - case kTecoDry: - result += F(" (DRY)"); - break; - case kTecoFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (this->getFan()) { - case kTecoFanAuto: - result += F(" (Auto)"); - break; - case kTecoFanHigh: - result += F(" (High)"); - break; - case kTecoFanLow: - result += F(" (Low)"); - break; - case kTecoFanMed: - result += F(" (Med)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Sleep: "); - result += (this->getSleep() ? F("On") : F("Off")); - result += F(", Swing: "); - result += (this->getSwing() ? F("On") : F("Off")); + result.reserve(80); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kTecoAuto, kTecoCool, kTecoHeat, + kTecoDry, kTecoFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kTecoFanHigh, kTecoFanLow, + kTecoFanAuto, kTecoFanAuto, kTecoFanMed); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getSwing(), F("Swing")); return result; } @@ -237,39 +251,24 @@ 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) { - // Check if can possibly be a valid Teco message. - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; +bool IRrecv::decodeTeco(decode_results* results, + const uint16_t nbits, const bool strict) { if (strict && nbits != kTecoBits) return false; // Not what is expected uint64_t data = 0; uint16_t offset = kStartOffset; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset++], kTecoHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kTecoHdrSpace)) return false; - // Data (35 bits) - data_result = - matchData(&(results->rawbuf[offset]), 35, kTecoBitMark, kTecoOneSpace, - kTecoBitMark, kTecoZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t actualBits = data_result.used / 2; - - // Footer. - if (!matchMark(results->rawbuf[offset++], kTecoBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kTecoGap)) return false; - - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kTecoHdrMark, kTecoHdrSpace, + kTecoBitMark, kTecoOneSpace, + kTecoBitMark, kTecoZeroSpace, + kTecoBitMark, kTecoGap, true, + kTolerance, kMarkExcess, false)) return false; // Success results->decode_type = TECO; - results->bits = actualBits; + results->bits = nbits; results->value = data; results->address = 0; results->command = 0; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Teco.h similarity index 92% rename from lib/IRremoteESP8266-2.6.0/src/ir_Teco.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Teco.h index 65a0050ae..d594aca32 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Teco.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Teco.h @@ -5,8 +5,6 @@ #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -89,7 +87,8 @@ const uint64_t kTecoReset = 0b01001010000000000000010000000000000; // Classes class IRTecoAc { public: - explicit IRTecoAc(const uint16_t pin); + explicit IRTecoAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); void stateReset(void); #if SEND_TECO @@ -125,11 +124,10 @@ class IRTecoAc { uint8_t convertMode(const stdAc::opmode_t mode); uint8_t convertFan(const stdAc::fanspeed_t speed); -#ifdef ARDUINO + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); String toString(void); -#else - std::string toString(void); -#endif #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Toshiba.cpp similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Toshiba.cpp index a82a2fb24..00a0f4d5c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Toshiba.cpp @@ -1,5 +1,8 @@ // Copyright 2017 David Conran +// Toshiba A/C support added by David Conran + + #include "ir_Toshiba.h" #include #ifndef ARDUINO @@ -9,13 +12,6 @@ #include "IRsend.h" #include "IRutils.h" -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran // // Equipment it seems compatible with: // * Toshiba RAS-B13N3KV2 / Akita EVO II @@ -35,6 +31,13 @@ const uint16_t kToshibaAcOneSpace = 1623; const uint16_t kToshibaAcZeroSpace = 472; const uint16_t kToshibaAcMinGap = 7048; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + #if SEND_TOSHIBA_AC // Send a Toshiba A/C message. // @@ -46,8 +49,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 +67,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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +86,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 +138,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 +200,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 +211,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 +219,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,49 +272,64 @@ 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 result = ""; -#endif // ARDUINO - result += F("Power: "); - if (getPower()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kToshibaAcAuto: - result += F(" (AUTO)"); - break; - case kToshibaAcCool: - result += F(" (COOL)"); - break; - case kToshibaAcHeat: - result += F(" (HEAT)"); - break; - case kToshibaAcDry: - result += F(" (DRY)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kToshibaAcFanAuto: - result += F(" (AUTO)"); - break; - case kToshibaAcFanMax: - result += F(" (MAX)"); - break; - } + result.reserve(40); + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool, + kToshibaAcHeat, kToshibaAcDry, kToshibaAcAuto); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin, + kToshibaAcFanAuto, kToshibaAcFanAuto, + kToshibaAcFanMed); return result; } @@ -326,39 +347,22 @@ 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; - - // Have we got enough data to successfully decode? - if (results->rawlen < kToshibaACBits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid message. // Compliance if (strict && nbits != kToshibaACBits) return false; // Must be called with the correct nr. of bytes. - // Header - if (!matchMark(results->rawbuf[offset++], kToshibaAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kToshibaAcHdrSpace)) return false; - - // Data - for (uint8_t i = 0; i < kToshibaACStateLength; i++) { - // Read a byte's worth of data. - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 8, kToshibaAcBitMark, - kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace); - if (data_result.success == false) return false; // Fail - dataBitsSoFar += 8; - results->state[i] = (uint8_t)data_result.data; - offset += data_result.used; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kToshibaAcBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kToshibaAcMinGap)) return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kToshibaAcHdrMark, kToshibaAcHdrSpace, + kToshibaAcBitMark, kToshibaAcOneSpace, + kToshibaAcBitMark, kToshibaAcZeroSpace, + kToshibaAcBitMark, kToshibaAcMinGap, true, + kTolerance, kMarkExcess)) return false; // Compliance if (strict) { // Check that the checksum of the message is correct. @@ -367,7 +371,7 @@ bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t nbits, // Success results->decode_type = TOSHIBA_AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Toshiba.h similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Toshiba.h index 03b461add..6b06855bf 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Toshiba.h @@ -1,4 +1,15 @@ // Copyright 2017 David Conran + +// Toshiba A/C support added by David Conran + +// Supports: +// Brand: Toshiba, Model: RAS-B13N3KV2 +// Brand: Toshiba, Model: Akita EVO II +// Brand: Toshiba, Model: RAS-B13N3KVP-E +// Brand: Toshiba, Model: RAS 18SKP-ES +// Brand: Toshiba, Model: WH-TA04NE +// Brand: Toshiba, Model: WC-L03SE + #ifndef IR_TOSHIBA_H_ #define IR_TOSHIBA_H_ @@ -6,8 +17,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -15,14 +24,6 @@ #include "IRsend_test.h" #endif -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran - // Constants const uint8_t kToshibaAcAuto = 0; const uint8_t kToshibaAcCool = 1; @@ -30,6 +31,8 @@ const uint8_t kToshibaAcDry = 2; const uint8_t kToshibaAcHeat = 3; const uint8_t kToshibaAcPower = 4; const uint8_t kToshibaAcFanAuto = 0; +const uint8_t kToshibaAcFanMin = 1; +const uint8_t kToshibaAcFanMed = 3; const uint8_t kToshibaAcFanMax = 5; const uint8_t kToshibaAcMinTemp = 17; // 17C const uint8_t kToshibaAcMaxTemp = 30; // 30C @@ -47,34 +50,35 @@ const uint8_t kToshibaAcMaxTemp = 30; // 30C class IRToshibaAC { public: - explicit IRToshibaAC(uint16_t pin); + explicit IRToshibaAC(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_TOSHIBA_AC void send(const uint16_t repeat = kToshibaACMinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TOSHIBA_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(bool useRaw = false); - void setRaw(uint8_t newState[]); - uint8_t* getRaw(); + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(const bool useRaw = false); + void setRaw(const uint8_t newState[]); + uint8_t* getRaw(void); static bool validChecksum(const uint8_t state[], const uint16_t length = kToshibaACStateLength); 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 + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: diff --git a/lib/IRremoteESP8266-2.6.3.10/src/ir_Trotec.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Trotec.cpp new file mode 100644 index 000000000..b0ddda62d --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Trotec.cpp @@ -0,0 +1,296 @@ +// Copyright 2017 stufisher +// Copyright 2019 crankyoldgit + +#include "ir_Trotec.h" +#include +#ifndef UNIT_TEST +#include +#endif +#include "IRremoteESP8266.h" +#include "IRutils.h" + +// Constants +const uint16_t kTrotecHdrMark = 5952; +const uint16_t kTrotecHdrSpace = 7364; +const uint16_t kTrotecBitMark = 592; +const uint16_t kTrotecOneSpace = 1560; +const uint16_t kTrotecZeroSpace = 592; +const uint16_t kTrotecGap = 6184; +const uint16_t kTrotecGapEnd = 1500; // made up value + +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; + +#if SEND_TROTEC + +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, kTrotecBitMark, + kTrotecOneSpace, kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, data, nbytes, 36, false, + 0, // Repeats handled elsewhere + 50); + // More footer + enableIROut(36); + mark(kTrotecBitMark); + space(kTrotecGapEnd); + } +} +#endif // SEND_TROTEC + +IRTrotecESP::IRTrotecESP(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +void IRTrotecESP::begin(void) { _irsend.begin(); } + +#if SEND_TROTEC +void IRTrotecESP::send(const uint16_t repeat) { + this->checksum(); + _irsend.sendTrotec(remote_state, kTrotecStateLength, repeat); +} +#endif // SEND_TROTEC + +uint8_t IRTrotecESP::calcChecksum(const uint8_t state[], + const uint16_t length) { + return sumBytes(state + 2, length - 3); +} + +bool IRTrotecESP::validChecksum(const uint8_t state[], const uint16_t length) { + return state[length - 1] == calcChecksum(state, length); +} + +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; + + this->setPower(false); + this->setTemp(kTrotecDefTemp); + this->setSpeed(kTrotecFanMed); + this->setMode(kTrotecAuto); +} + +uint8_t* IRTrotecESP::getRaw(void) { + this->checksum(); + return remote_state; +} + +void IRTrotecESP::setRaw(const uint8_t state[]) { + for (uint16_t i = 0; i < kTrotecStateLength; i++) remote_state[i] = state[i]; +} + +void IRTrotecESP::setPower(const bool on) { + if (on) + remote_state[2] |= kTrotecPowerBit; + else + 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(void) { + 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(void) { return remote_state[2] & 0b00000011; } + +void IRTrotecESP::setTemp(const uint8_t celsius) { + uint8_t temp = std::max(celsius, kTrotecMinTemp); + temp = std::min(temp, kTrotecMaxTemp); + remote_state[3] = (remote_state[3] & 0x80) | (temp - kTrotecMinTemp); +} + +uint8_t IRTrotecESP::getTemp(void) { + return (remote_state[3] & 0b01111111) + kTrotecMinTemp; +} + +void IRTrotecESP::setSleep(const bool on) { + if (on) + remote_state[3] |= kTrotecSleepBit; + else + remote_state[3] &= ~kTrotecSleepBit; +} + +bool IRTrotecESP::getSleep(void) { return remote_state[3] & kTrotecSleepBit; } + +void IRTrotecESP::setTimer(const uint8_t timer) { + if (timer) + remote_state[5] |= kTrotecTimerBit; + else + remote_state[5] &= ~kTrotecTimerBit; + remote_state[6] = (timer > kTrotecMaxTimer) ? kTrotecMaxTimer : timer; +} + +uint8_t IRTrotecESP::getTimer(void) { return remote_state[6]; } + +// Convert a standard A/C mode into its native mode. +uint8_t IRTrotecESP::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: + return kTrotecCool; + case stdAc::opmode_t::kDry: + return kTrotecDry; + case stdAc::opmode_t::kFan: + return kTrotecFan; + // Note: No Heat mode. + default: + return kTrotecAuto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRTrotecESP::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kTrotecFanLow; + case stdAc::fanspeed_t::kMedium: + return kTrotecFanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kTrotecFanHigh; + default: + return kTrotecFanMed; + } +} + + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRTrotecESP::toCommonMode(const uint8_t mode) { + switch (mode) { + case kTrotecCool: return stdAc::opmode_t::kCool; + case kTrotecDry: return stdAc::opmode_t::kDry; + case kTrotecFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { + switch (spd) { + case kTrotecFanHigh: return stdAc::fanspeed_t::kMax; + case kTrotecFanMed: return stdAc::fanspeed_t::kMedium; + case kTrotecFanLow: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRTrotecESP::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::TROTEC; + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getSpeed()); + result.sleep = this->getSleep() ? 0 : -1; + // Not supported. + result.model = -1; // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + result.turbo = false; + result.light = false; + result.filter = false; + result.econo = false; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +// Convert the internal state into a human readable string. +String IRTrotecESP::toString(void) { + String result = ""; + result.reserve(100); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kTrotecAuto, kTrotecCool, kTrotecAuto, + kTrotecDry, kTrotecFan); + result += addTempToString(getTemp()); + result += addFanToString(getSpeed(), kTrotecFanHigh, kTrotecFanLow, + kTrotecFanHigh, kTrotecFanHigh, kTrotecFanMed); + result += addBoolToString(getSleep(), F("Sleep")); + 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 used; + // Header + Data + Footer #1 + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kTrotecHdrMark, kTrotecHdrSpace, + kTrotecBitMark, kTrotecOneSpace, + kTrotecBitMark, kTrotecZeroSpace, + kTrotecBitMark, kTrotecGap, true, + kTolerance, 0, false); + if (used == 0) return false; + offset += used; + + // Footer #2 + if (!matchMark(results->rawbuf[offset++], kTrotecBitMark)) return false; + if (offset <= results->rawlen && + !matchAtLeast(results->rawbuf[offset++], kTrotecGapEnd)) return false; + // Compliance + // Verify we got a valid checksum. + if (strict && !IRTrotecESP::validChecksum(results->state)) return false; + // Success + results->decode_type = TROTEC; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_TROTEC diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Trotec.h similarity index 63% rename from lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Trotec.h index dfbc26c07..98bf3795c 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Trotec.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Trotec.h @@ -1,8 +1,12 @@ // Copyright 2017 stufisher +// Copyright 2019 crankyoldgit #ifndef IR_TROTEC_H_ #define IR_TROTEC_H_ +#ifndef UNIT_TEST +#include +#endif #include "IRremoteESP8266.h" #include "IRsend.h" #ifdef UNIT_TEST @@ -55,35 +59,43 @@ const uint8_t kTrotecMaxTimer = 23; class IRTrotecESP { public: - explicit IRTrotecESP(uint16_t pin); + explicit IRTrotecESP(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); #if SEND_TROTEC void send(const uint16_t repeat = kTrotecDefaultRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_TROTEC - void begin(); + void begin(void); void setPower(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); + String toString(void); #ifndef UNIT_TEST private: @@ -92,8 +104,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-2.6.0/src/ir_Vestel.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Vestel.cpp similarity index 71% rename from lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Vestel.cpp index 1fbb822cf..1374c6b59 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Vestel.cpp @@ -1,12 +1,12 @@ // Copyright 2018 Erdem U. Altinyurt // Copyright 2019 David Conran +// Vestel added by Erdem U. Altinyurt + #include "ir_Vestel.h" #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" @@ -14,14 +14,6 @@ #include "IRutils.h" #include "ir_Haier.h" -// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL -// VV VV EE S TT EE LL -// VV VV EEEEE SSSS TT EEEEE LL -// VV VV EE S TT EE LL -// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL - -// Vestel added by Erdem U. Altinyurt - // Equipment it seems compatible with: // * Vestel AC Model BIOX CXP-9 (9K BTU) // * @@ -29,6 +21,13 @@ // Ref: // None. Totally reverse engineered. +using irutils::addBoolToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if SEND_VESTEL_AC // Send a Vestel message // @@ -53,10 +52,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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +65,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 +83,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 +110,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 +126,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 +166,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 +314,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 +387,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,89 +440,103 @@ 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 result = ""; -#endif // ARDUINO - if (isTimeCommand()) { - result += F("Time: "); - result += IRHaierAC::timeToString(getTime()); - - result += F(", Timer: "); - result += isTimerActive() ? IRHaierAC::timeToString(getTimer()) : F("Off"); - - result += F(", On Timer: "); - result += (isOnTimerActive() && !isTimerActive()) - ? IRHaierAC::timeToString(getOnTimer()) - : F("Off"); - - result += F(", Off Timer: "); - result += - isOffTimerActive() ? IRHaierAC::timeToString(getOffTimer()) : F("Off"); + result.reserve(100); // Reserve some heap for the string to reduce fragging. + if (this->isTimeCommand()) { + result += addLabeledString(minsToString(getTime()), F("Time"), false); + result += addLabeledString( + isTimerActive() ? minsToString(getTimer()) : F("Off"), + F("Timer")); + result += addLabeledString( + (isOnTimerActive() && !isTimerActive()) ? + minsToString(this->getOnTimer()) : F("Off"), + F("On Timer")); + result += addLabeledString( + isOffTimerActive() ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); return result; } // Not a time command, it's a normal command. - result += F("Power: "); - result += (getPower() ? F("On") : F("Off")); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kVestelAcAuto: - result += F(" (AUTO)"); - break; - case kVestelAcCool: - result += F(" (COOL)"); - break; - case kVestelAcHeat: - result += F(" (HEAT)"); - break; - case kVestelAcDry: - result += F(" (DRY)"); - break; - case kVestelAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kVestelAcAuto, kVestelAcCool, + kVestelAcHeat, kVestelAcDry, kVestelAcFan); + result += addTempToString(getTemp()); + result += addIntToString(getFan(), F("Fan")); + switch (this->getFan()) { case kVestelAcFanAuto: - result += F(" (AUTO)"); + result += F(" (Auto)"); break; case kVestelAcFanLow: - result += F(" (LOW)"); + result += F(" (Low)"); break; case kVestelAcFanMed: - result += F(" (MEDIUM)"); + result += F(" (Medium)"); break; case kVestelAcFanHigh: - result += F(" (HIGH)"); + result += F(" (High)"); break; case kVestelAcFanAutoCool: - result += F(" (AUTO COOL)"); + result += F(" (Auto Cool)"); break; case kVestelAcFanAutoHot: - result += F(" (AUTO HOT)"); + result += F(" (Auto Hot)"); break; default: result += F(" (UNKNOWN)"); } - result += F(", Sleep: "); - result += (getSleep() ? F("On") : F("Off")); - result += F(", Turbo: "); - result += (getTurbo() ? F("On") : F("Off")); - result += F(", Ion: "); - result += (getIon() ? F("On") : F("Off")); - result += F(", Swing: "); - result += (getSwing() ? F("On") : F("Off")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getTurbo(), F("Turbo")); + result += addBoolToString(getIon(), F("Ion")); + result += addBoolToString(getSwing(), F("Swing")); return result; } @@ -535,8 +552,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; @@ -550,26 +567,17 @@ bool IRrecv::decodeVestelAc(decode_results* results, uint16_t nbits, if (nbits > sizeof(data) * 8) return false; // We can't possibly capture a Vestel packet that big. - // Header - if (!matchMark(results->rawbuf[offset++], kVestelAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kVestelAcHdrSpace)) return false; - - // Data (Normal) - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kVestelAcBitMark, - kVestelAcOneSpace, kVestelAcBitMark, kVestelAcZeroSpace, - kVestelAcTolerance, kMarkExcess, false); - - if (data_result.success == false) return false; - offset += data_result.used; - data = data_result.data; - - // Footer - if (!matchMark(results->rawbuf[offset++], kVestelAcBitMark)) return false; - + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kVestelAcHdrMark, kVestelAcHdrSpace, + kVestelAcBitMark, kVestelAcOneSpace, + kVestelAcBitMark, kVestelAcZeroSpace, + kVestelAcBitMark, 0, false, + kVestelAcTolerance, kMarkExcess, false)) return false; // Compliance if (strict) - if (!IRVestelAc::validChecksum(data_result.data)) return false; + if (!IRVestelAc::validChecksum(data)) return false; // Success results->decode_type = VESTEL_AC; diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Vestel.h similarity index 83% rename from lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Vestel.h index ab04e8b35..f60c031aa 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Vestel.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Vestel.h @@ -1,6 +1,9 @@ // Copyright 2018 Erdem U. Altinyurt // Copyright 2019 David Conran +// Supports: +// Brand: Vestel, Model: BIOX CXP-9 A/C (9K BTU) + #ifndef IR_VESTEL_H_ #define IR_VESTEL_H_ @@ -8,8 +11,6 @@ #include #ifdef ARDUINO #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -17,12 +18,6 @@ #include "IRsend_test.h" #endif -// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL -// VV VV EE S TT EE LL -// VV VV EEEEE SSSS TT EEEEE LL -// VV VV EE S TT EE LL -// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL - // Vestel added by Erdem U. Altinyurt // Structure of a Command message (56 bits) @@ -31,7 +26,7 @@ // Swing: 4 bits. (auto 0xA, stop 0xF) // turbo_sleep_normal: 4bits. (normal 0x1, sleep 0x3, turbo 0x7) // Unused: 8 bits. (0x00) -// Temperature: 4 bits. (Celcius, but offset by -16 degrees. e.g. 0x0 = 16C) +// Temperature: 4 bits. (Celsius, but offset by -16 degrees. e.g. 0x0 = 16C) // Fan Speed: 4 bits (auto 0x1, low 0x5, mid 0x9, high 0xB, 0xD auto hot, // 0xC auto cool) // Mode: 3 bits. (auto 0x0, cold 0x1, dry 0x2, fan 0x3, hot 0x4) @@ -108,17 +103,19 @@ const uint64_t kVestelAcTimeStateDefault = 0x201ULL; class IRVestelAc { public: - explicit IRVestelAc(uint16_t pin); + explicit IRVestelAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_VESTEL_AC - void send(); + void send(void); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_VESTEL_AC void begin(void); void on(void); void off(void); - void setPower(const bool state); - bool getPower(); + void setPower(const bool on); + bool getPower(void); void setAuto(const int8_t autoLevel); void setTimer(const uint16_t minutes); uint16_t getTimer(void); @@ -134,17 +131,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,13 +151,12 @@ 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 + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -171,7 +167,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-2.6.0/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Whirlpool.cpp similarity index 62% rename from lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Whirlpool.cpp index 048c1a1eb..91fb58f91 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Whirlpool.cpp @@ -24,14 +24,8 @@ #include "IRsend.h" #include "IRutils.h" -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - // Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/509 const uint16_t kWhirlpoolAcHdrMark = 8950; const uint16_t kWhirlpoolAcHdrSpace = 4484; const uint16_t kWhirlpoolAcBitMark = 597; @@ -41,6 +35,14 @@ const uint16_t kWhirlpoolAcGap = 7920; const uint32_t kWhirlpoolAcMinGap = kDefaultMessageGap; // Just a guess. const uint8_t kWhirlpoolAcSections = 3; +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addTempToString; +using irutils::minsToString; + #if SEND_WHIRLPOOL_AC // Send a Whirlpool A/C message. // @@ -52,9 +54,9 @@ const uint8_t kWhirlpoolAcSections = 3; // Status: ALPHA / Untested. // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/509 +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 +87,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, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { 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 +132,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 +147,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 +164,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 +181,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 +191,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 +215,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 +240,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 +261,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 +273,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 +296,53 @@ bool IRWhirlpoolAc::isTimerEnabled(const uint16_t pos) { return remote_state[pos - 1] & kWhirlpoolAcTimerEnableMask; } -void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool state) { - if (state) +void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool on) { + if (on) remote_state[pos - 1] |= kWhirlpoolAcTimerEnableMask; else remote_state[pos - 1] &= ~kWhirlpoolAcTimerEnableMask; } void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcClockPos, minspastmidnight); + this->setTime(kWhirlpoolAcClockPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getClock() { return getTime(kWhirlpoolAcClockPos); } +uint16_t IRWhirlpoolAc::getClock(void) { + return this->getTime(kWhirlpoolAcClockPos); +} void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); + this->setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getOffTimer() { - return getTime(kWhirlpoolAcOffTimerPos); +uint16_t IRWhirlpoolAc::getOffTimer(void) { + return this->getTime(kWhirlpoolAcOffTimerPos); } -bool IRWhirlpoolAc::isOffTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOffTimerPos); +bool IRWhirlpoolAc::isOffTimerEnabled(void) { + return this->isTimerEnabled(kWhirlpoolAcOffTimerPos); } -void IRWhirlpoolAc::enableOffTimer(const bool state) { - enableTimer(kWhirlpoolAcOffTimerPos, state); - setCommand(kWhirlpoolAcCommandOffTimer); +void IRWhirlpoolAc::enableOffTimer(const bool on) { + this->enableTimer(kWhirlpoolAcOffTimerPos, on); + this->setCommand(kWhirlpoolAcCommandOffTimer); } void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); + this->setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); } -uint16_t IRWhirlpoolAc::getOnTimer() { return getTime(kWhirlpoolAcOnTimerPos); } - -bool IRWhirlpoolAc::isOnTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOnTimerPos); +uint16_t IRWhirlpoolAc::getOnTimer(void) { + return this->getTime(kWhirlpoolAcOnTimerPos); } -void IRWhirlpoolAc::enableOnTimer(const bool state) { - enableTimer(kWhirlpoolAcOnTimerPos, state); - setCommand(kWhirlpoolAcCommandOnTimer); +bool IRWhirlpoolAc::isOnTimerEnabled(void) { + return this->isTimerEnabled(kWhirlpoolAcOnTimerPos); +} + +void IRWhirlpoolAc::enableOnTimer(const bool on) { + this->enableTimer(kWhirlpoolAcOnTimerPos, on); + this->setCommand(kWhirlpoolAcCommandOnTimer); } void IRWhirlpoolAc::setPowerToggle(const bool on) { @@ -342,54 +350,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,34 +437,60 @@ uint8_t IRWhirlpoolAc::convertFan(const stdAc::fanspeed_t speed) { } } -#ifdef ARDUINO -String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { - String result = ""; -#else -std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { - std::string result = ""; -#endif // ARDUINO - uint8_t hours = minspastmidnight / 60; - if (hours < 10) result += '0'; - result += uint64ToString(hours); - result += ':'; - uint8_t mins = minspastmidnight % 60; - if (mins < 10) result += '0'; - result += uint64ToString(mins); +// 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; } // Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRWhirlpoolAc::toString() { +String IRWhirlpoolAc::toString(void) { String result = ""; -#else -std::string IRWhirlpoolAc::toString() { - std::string result = ""; -#endif // ARDUINO + result.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; @@ -466,88 +500,26 @@ std::string IRWhirlpoolAc::toString() { default: result += F(" (UNKNOWN)"); } - result += F(", Power toggle: "); - if (getPowerToggle()) - result += F("On"); - else - result += F("Off"); - result += F(", Mode: "); - result += uint64ToString(getMode()); - switch (getMode()) { - case kWhirlpoolAcHeat: - result += F(" (HEAT)"); - break; - case kWhirlpoolAcAuto: - result += F(" (AUTO)"); - break; - case kWhirlpoolAcCool: - result += F(" (COOL)"); - break; - case kWhirlpoolAcDry: - result += F(" (DRY)"); - break; - case kWhirlpoolAcFan: - result += F(" (FAN)"); - break; - default: - result += F(" (UNKNOWN)"); - } - result += F(", Temp: "); - result += uint64ToString(getTemp()); - result += F("C, Fan: "); - result += uint64ToString(getFan()); - switch (getFan()) { - case kWhirlpoolAcFanAuto: - result += F(" (AUTO)"); - break; - case kWhirlpoolAcFanHigh: - result += F(" (HIGH)"); - break; - case kWhirlpoolAcFanMedium: - result += F(" (MEDIUM)"); - break; - case kWhirlpoolAcFanLow: - result += F(" (LOW)"); - break; - default: - result += F(" (UNKNOWN)"); - break; - } - result += F(", Swing: "); - if (getSwing()) - result += F("On"); - else - result += F("Off"); - result += F(", Light: "); - if (getLight()) - result += F("On"); - else - result += F("Off"); - result += F(", Clock: "); - result += timeToString(getClock()); - result += F(", On Timer: "); - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); - else - result += F("Off"); - result += F(", Off Timer: "); - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); - else - result += F("Off"); - result += F(", Sleep: "); - if (getSleep()) - result += F("On"); - else - result += F("Off"); - result += F(", Super: "); - if (getSuper()) - result += F("On"); - else - result += F("Off"); - result += F(", Command: "); - result += uint64ToString(getCommand()); - switch (getCommand()) { + result += addBoolToString(getPowerToggle(), F("Power toggle")); + result += addModeToString(getMode(), kWhirlpoolAcAuto, kWhirlpoolAcCool, + kWhirlpoolAcHeat, kWhirlpoolAcDry, kWhirlpoolAcFan); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kWhirlpoolAcFanHigh, kWhirlpoolAcFanLow, + kWhirlpoolAcFanAuto, kWhirlpoolAcFanAuto, + kWhirlpoolAcFanMedium); + result += addBoolToString(getSwing(), F("Swing")); + result += addBoolToString(getLight(), F("Light")); + result += addLabeledString(minsToString(getClock()), F("Clock")); + result += addLabeledString( + isOnTimerEnabled() ? minsToString(getOnTimer()) : F("Off"), + F("On Timer")); + result += addLabeledString( + isOffTimerEnabled() ? minsToString(getOffTimer()) : F("Off"), + F("Off Timer")); + result += addBoolToString(getSleep(), F("Sleep")); + result += addBoolToString(getSuper(), F("Super")); + result += addIntToString(getCommand(), F("Command")); + switch (this->getCommand()) { case kWhirlpoolAcCommandLight: result += F(" (LIGHT)"); break; @@ -605,9 +577,9 @@ std::string IRWhirlpoolAc::toString() { // // // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, - bool strict) { +// https://github.com/crankyoldgit/IRremoteESP8266/issues/509 +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) { @@ -615,9 +587,6 @@ bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, } uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; // Header @@ -625,44 +594,36 @@ bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) return false; - // Data Section - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections; + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kWhirlpoolAcSections; section++) { + uint16_t used; + // Section Data + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, sectionSize[section] * 8, + 0, 0, + kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, + kWhirlpoolAcBitMark, kWhirlpoolAcGap, + section >= kWhirlpoolAcSections - 1, + kTolerance, kMarkExcess, false); + if (used == 0) return false; + offset += used; pos += sectionSize[section]; - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = - matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - // Data is in LSB order. We need to reverse it. - results->state[i] = (uint8_t)data_result.data; - } - // Section Footer - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark)) - return false; - if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap)) - return false; - } } // Compliance if (strict) { // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kWhirlpoolAcBits) return false; - if (!IRWhirlpoolAc::validChecksum(results->state, dataBitsSoFar / 8)) + if (pos * 8 != nbits) return false; + if (!IRWhirlpoolAc::validChecksum(results->state, nbits / 8)) return false; } // Success results->decode_type = WHIRLPOOL_AC; - results->bits = dataBitsSoFar; + results->bits = nbits; // No need to record the state as we stored it as we decoded it. // As we use result->state, we don't record value, address, or command as it // is a union data type. diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h b/lib/IRremoteESP8266-2.6.3.10/src/ir_Whirlpool.h similarity index 75% rename from lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Whirlpool.h index 9604d025c..156c4b631 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Whirlpool.h +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Whirlpool.h @@ -2,6 +2,16 @@ // // Copyright 2018 David Conran +// Supports: +// Brand: Whirlpool, Model: DG11J1-3A remote +// Brand: Whirlpool, Model: DG11J1-04 remote +// Brand: Whirlpool, Model: DG11J1-91 remote +// Brand: Whirlpool, Model: SPIS409L A/C +// Brand: Whirlpool, Model: SPIS412L A/C +// Brand: Whirlpool, Model: SPIW409L A/C +// Brand: Whirlpool, Model: SPIW412L A/C +// Brand: Whirlpool, Model: SPIW418L A/C + #ifndef IR_WHIRLPOOL_H_ #define IR_WHIRLPOOL_H_ @@ -9,8 +19,6 @@ #include #ifndef UNIT_TEST #include -#else -#include #endif #include "IRremoteESP8266.h" #include "IRsend.h" @@ -18,14 +26,8 @@ #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 -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - // Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/509 // Constants const uint8_t kWhirlpoolAcChecksumByte1 = 13; @@ -87,45 +89,47 @@ enum whirlpool_ac_remote_model_t { // Classes class IRWhirlpoolAc { public: - explicit IRWhirlpoolAc(uint16_t pin); + explicit IRWhirlpoolAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); - void stateReset(); + void stateReset(void); #if SEND_WHIRLPOOL_AC void send(const uint16_t repeat = kWhirlpoolAcDefaultRepeat, const bool calcchecksum = true); + uint8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_WHIRLPOOL_AC - void begin(); - void on(); - void off(); + void begin(void); + void on(void); + void off(void); void setPowerToggle(const bool on); - bool getPowerToggle(); + bool getPowerToggle(void); void setSleep(const bool on); - bool getSleep(); + bool getSleep(void); void setSuper(const bool on); - bool getSuper(); + bool getSuper(void); void setTemp(const uint8_t temp); - uint8_t getTemp(); + uint8_t getTemp(void); void setFan(const uint8_t speed); - uint8_t getFan(); + uint8_t getFan(void); void setMode(const uint8_t mode); - uint8_t getMode(); + uint8_t getMode(void); void setSwing(const bool on); - bool getSwing(); + bool getSwing(void); void setLight(const bool on); - bool getLight(); - uint16_t getClock(); + bool getLight(void); + uint16_t getClock(void); void setClock(const uint16_t minspastmidnight); - uint16_t getOnTimer(); + uint16_t getOnTimer(void); void setOnTimer(const uint16_t minspastmidnight); - void enableOnTimer(const bool state); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); + void enableOnTimer(const bool on); + bool isOnTimerEnabled(void); + uint16_t getOffTimer(void); void setOffTimer(const uint16_t minspastmidnight); - void enableOffTimer(const bool state); - bool isOffTimerEnabled(); + void enableOffTimer(const bool on); + bool isOffTimerEnabled(void); void setCommand(const uint8_t code); - uint8_t getCommand(); - whirlpool_ac_remote_model_t getModel(); + uint8_t getCommand(void); + whirlpool_ac_remote_model_t getModel(void); void setModel(const whirlpool_ac_remote_model_t model); uint8_t* getRaw(const bool calcchecksum = true); void setRaw(const uint8_t new_code[], @@ -134,11 +138,10 @@ class IRWhirlpoolAc { 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 + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + stdAc::state_t toCommon(void); + String toString(void); #ifndef UNIT_TEST private: @@ -156,12 +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(); -#ifdef ARDUINO - String timeToString(uint16_t minspastmidnight); -#else - std::string timeToString(uint16_t minspastmidnight); -#endif + int8_t getTempOffset(void); }; #endif // IR_WHIRLPOOL_H_ diff --git a/lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp b/lib/IRremoteESP8266-2.6.3.10/src/ir_Whynter.cpp similarity index 69% rename from lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp rename to lib/IRremoteESP8266-2.6.3.10/src/ir_Whynter.cpp index 555c50788..c5634f381 100644 --- a/lib/IRremoteESP8266-2.6.0/src/ir_Whynter.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/src/ir_Whynter.cpp @@ -1,20 +1,17 @@ // Copyright 2009 Ken Shirriff // Copyright 2017 David Conran +// Whynter A/C ARC-110WD added by Francesco Meschia +// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ + +// Supports: +// Brand: Whynter, Model: ARC-110WD A/C + #include #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" -// W W H H Y Y N N TTTTT EEEEE RRRRR -// W W H H Y Y NN N T E R R -// W W W HHHHH Y N N N T EEE RRRR -// W W W H H Y N NN T E R R -// WWW H H Y N N T EEEEE R R - -// Whynter A/C ARC-110WD added by Francesco Meschia -// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ - // Constants const uint16_t kWhynterTick = 50; @@ -91,39 +88,18 @@ bool IRrecv::decodeWhynter(decode_results *results, uint16_t nbits, return false; // Incorrect nr. of bits per spec. uint16_t offset = kStartOffset; - - // Header + uint64_t data = 0; + // Pre-Header // Sequence begins with a bit mark and a zero space. - // These are typically small, so we'll prefer to do the calibration - // on the much larger header mark & space that are next. if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false; if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false; - // Main header mark and space - if (!matchMark(results->rawbuf[offset], kWhynterHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kWhynterHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kWhynterHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kWhynterHdrSpaceTicks; - - // Data - uint64_t data = 0; - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kWhynterBitMarkTicks * m_tick, kWhynterOneSpaceTicks * s_tick, - kWhynterBitMarkTicks * m_tick, kWhynterZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kWhynterBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kWhynterMinGapTicks * s_tick)) - return false; - + // Match Main Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kWhynterHdrMark, kWhynterHdrSpace, + kWhynterBitMark, kWhynterOneSpace, + kWhynterBitMark, kWhynterZeroSpace, + kWhynterBitMark, kWhynterMinGap, true)) return false; // Success results->decode_type = WHYNTER; results->bits = nbits; diff --git a/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/IRac_test.cpp similarity index 62% rename from lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/IRac_test.cpp index 39c17a84b..f268f5ced 100644 --- a/lib/IRremoteESP8266-2.6.0/test/IRac_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/IRac_test.cpp @@ -1,8 +1,11 @@ // Copyright 2019 David Conran +#include #include "ir_Argo.h" #include "ir_Daikin.h" +#include "ir_Electra.h" #include "ir_Fujitsu.h" +#include "ir_Goodweather.h" #include "ir_Gree.h" #include "ir_Haier.h" #include "ir_Hitachi.h" @@ -10,8 +13,10 @@ #include "ir_Midea.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" +#include "ir_Neoclima.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 +47,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 @@ -74,25 +79,57 @@ TEST(TestIRac, Coolix) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + // Confirm we are sending with a repeat of 1. i.e. two messages. + EXPECT_EQ( + "f38000d50" // 38kHz Frequency and 50% duty-cycle. + // Start of message #1 (i.e. Repeat '0') + // Header + "m4480s4480" + // Data + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s560" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // Footer + "m560s5040" + // End of message #1 (i.e. Repeat '0') + // Start of message #2 (i.e. Repeat '1') + // Header + "m4480s4480" + // Data + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s560" + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // Footer + "m560s5040", + // End of message #2 (i.e. Repeat '1') + // Note: the two messages (#1 & #2) are identical. + ac._irsend.outputStr()); } TEST(TestIRac, Daikin) { IRDaikinESP ac(0); IRac irac(0); + IRrecv capture(0); char expected[] = - "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 2, Powerful: Off, " - "Quiet: Off, Sensor: Off, Eye: Off, Mold: On, Comfort: Off, " + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 5 (High), Powerful: Off, " + "Quiet: Off, Sensor: Off, Mold: On, Comfort: Off, " "Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 0:00, On Time: Off, Off Time: Off"; + "Current Time: 00:00, Current Day: (UNKNOWN), On Time: Off, " + "Off Time: Off, Weekly Timer: On"; ac.begin(); irac.daikin(&ac, true, // Power stdAc::opmode_t::kCool, // Mode 19, // Celsius - stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::fanspeed_t::kMax, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet @@ -100,6 +137,34 @@ TEST(TestIRac, Daikin) { true, // Filter true); // Clean ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikinBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Daikin160) { + IRDaikin160 ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (DRY), Temp: 23C, Fan: 1 (Low), " + "Vent Position (V): 3 (Middle)"; + + ac.begin(); + irac.daikin160(&ac, + true, // Power + stdAc::opmode_t::kDry, // Mode + 23, // Celsius + stdAc::fanspeed_t::kMin, // Fan speed + stdAc::swingv_t::kMiddle); // Veritcal swing + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(DAIKIN160, ac._irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Daikin2) { @@ -107,18 +172,18 @@ TEST(TestIRac, Daikin2) { 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"; + "Power: On, Mode: 3 (COOL), Temp: 19C, Fan: 1 (Low), " + "Swing (V): 14 (Auto), Swing (H): 0, Clock: 00: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::fanspeed_t::kLow, // Fan speed stdAc::swingv_t::kOff, // Veritcal swing stdAc::swingh_t::kOff, // Horizontal swing false, // Quiet @@ -134,8 +199,7 @@ TEST(TestIRac, Daikin2) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Daikin216) { @@ -143,8 +207,8 @@ TEST(TestIRac, Daikin216) { 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"; + "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, @@ -154,12 +218,37 @@ TEST(TestIRac, Daikin216) { stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kAuto, // Veritcal swing stdAc::swingh_t::kLeft, // Horizontal swing - true); // Quiet + 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); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Electra) { + IRElectraAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 6 (FAN), Temp: 26C, Fan: 1 (High), " + "Swing(V): On, Swing(H): On"; + + ac.begin(); + irac.electra(&ac, + true, // Power + stdAc::opmode_t::kFan, // Mode + 26, // Celsius + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kAuto, // Veritcal swing + stdAc::swingh_t::kLeft); // Horizontal swing + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(ELECTRA_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kElectraAcBits, ac._irsend.capture.bits); ac.setRaw(ac._irsend.capture.state); ASSERT_EQ(expected, ac.toString()); } @@ -168,9 +257,12 @@ 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"; + std::string ardb1_expected = + "Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 2 (Medium), Command: N/A"; + std::string arrah2e_expected = + "Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 19C, " + "Fan: 2 (Medium), Swing: Off, Command: N/A"; ac.begin(); irac.fujitsu(&ac, @@ -181,14 +273,15 @@ 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, IRAcUtils::resultAcToString(&ac._irsend.capture)); ac._irsend.reset(); irac.fujitsu(&ac, @@ -199,14 +292,41 @@ 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, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Goodweather) { + IRGoodweatherAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 2 (Medium), 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); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Gree) { @@ -214,9 +334,9 @@ TEST(TestIRac, Gree) { 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"; + "Power: On, Mode: 1 (COOL), Temp: 22C, Fan: 2 (Medium), Turbo: Off, " + "IFeel: Off, WiFi: Off, XFan: On, Light: On, Sleep: On, " + "Swing Vertical Mode: Manual, Swing Vertical Pos: 3"; ac.begin(); irac.gree(&ac, @@ -234,8 +354,7 @@ TEST(TestIRac, Gree) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Haier) { @@ -243,9 +362,9 @@ TEST(TestIRac, Haier) { 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"; + "Command: 1 (On), Mode: 1 (COOL), Temp: 24C, Fan: 2 (Medium), " + "Swing: 1 (Up), Sleep: On, Health: On, Current Time: 13:45, " + "On Timer: Off, Off Timer: Off"; ac.begin(); irac.haier(&ac, @@ -262,8 +381,7 @@ TEST(TestIRac, Haier) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } @@ -272,8 +390,8 @@ TEST(TestIRac, HaierYrwo2) { 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"; + "Power: On, Button: 5 (Power), Mode: 2 (COOL), Temp: 23C, " + "Fan: 4 (Medium), Turbo: 1 (High), Swing: 1 (Top), Sleep: On, Health: On"; ac.begin(); irac.haierYrwo2(&ac, @@ -290,8 +408,7 @@ TEST(TestIRac, HaierYrwo2) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Hitachi) { @@ -299,7 +416,7 @@ TEST(TestIRac, Hitachi) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 2 (AUTO), Temp: 22C, Fan: 3 (UNKNOWN), " + "Power: On, Mode: 2 (AUTO), Temp: 22C, Fan: 3 (Medium), " "Swing (Vertical): Off, Swing (Horizontal): On"; ac.begin(); @@ -316,8 +433,7 @@ TEST(TestIRac, Hitachi) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Kelvinator) { @@ -325,9 +441,9 @@ TEST(TestIRac, Kelvinator) { 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"; + "Power: On, Mode: 1 (COOL), Temp: 19C, Fan: 3 (Medium), Turbo: Off, " + "Quiet: Off, XFan: On, IonFilter: On, Light: On, " + "Swing (Horizontal): Off, Swing (Vertical): Off"; ac.begin(); irac.kelvinator(&ac, @@ -348,8 +464,7 @@ TEST(TestIRac, Kelvinator) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Midea) { @@ -357,7 +472,7 @@ TEST(TestIRac, Midea) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (DRY), Temp: 27C/81F, Fan: 2 (MED), Sleep: On"; + "Power: On, Mode: 1 (DRY), Temp: 27C/81F, Fan: 2 (Medium), Sleep: On"; ac.begin(); irac.midea(&ac, @@ -372,8 +487,7 @@ TEST(TestIRac, Midea) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Mitsubishi) { @@ -381,8 +495,8 @@ TEST(TestIRac, Mitsubishi) { 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: -"; + "Power: On, Mode: 24 (COOL), Temp: 20C, Fan: 2 (Medium), Vane: AUTO, " + "Time: 14:30, On timer: 00:00, Off timer: 00:00, Timer: -"; ac.begin(); irac.mitsubishi(&ac, @@ -398,8 +512,7 @@ TEST(TestIRac, Mitsubishi) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, MitsubishiHeavy88) { @@ -407,7 +520,7 @@ TEST(TestIRac, MitsubishiHeavy88) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 3 (Med), " + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 3 (Medium), " "Swing (V): 16 (Auto), Swing (H): 0 (Off), Turbo: Off, Econo: Off, " "3D: Off, Clean: On"; @@ -427,8 +540,7 @@ TEST(TestIRac, MitsubishiHeavy88) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, MitsubishiHeavy152) { @@ -436,7 +548,7 @@ TEST(TestIRac, MitsubishiHeavy152) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 6 (Econo), " + "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"; @@ -459,8 +571,37 @@ TEST(TestIRac, MitsubishiHeavy152) { 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, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Neoclima) { + IRNeoclimaAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): On, Sleep: On, Turbo: Off, Hold: Off, Ion: On, " + "Eye: Off, Light: On, Follow: Off, 8C Heat: Off, Fresh: Off, " + "Button: 0 (Power)"; + + ac.begin(); + irac.neoclima(&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 + false, // Turbo + true, // Light + true, // Filter + 8 * 60); // Sleep ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(decode_type_t::NEOCLIMA, ac._irsend.capture.decode_type); + ASSERT_EQ(kNeoclimaBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Panasonic) { @@ -468,7 +609,7 @@ TEST(TestIRac, Panasonic) { IRac irac(0); IRrecv capture(0); char expected_nke[] = - "Model: 2 (NKE), Power: On, Mode: 4 (HEAT), Temp: 28C, Fan: 2 (UNKNOWN), " + "Model: 2 (NKE), Power: On, Mode: 4 (HEAT), Temp: 28C, Fan: 2 (Medium), " "Swing (Vertical): 15 (AUTO), Swing (Horizontal): 6 (Middle), Quiet: On, " "Powerful: Off, Clock: 19:17, On Timer: Off, Off Timer: Off"; @@ -489,11 +630,10 @@ TEST(TestIRac, Panasonic) { 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()); + ASSERT_EQ(expected_nke, IRAcUtils::resultAcToString(&ac._irsend.capture)); char expected_dke[] = - "Model: 3 (DKE), Power: On, Mode: 3 (COOL), Temp: 18C, Fan: 4 (MAX), " + "Model: 3 (DKE), Power: On, Mode: 3 (COOL), Temp: 18C, Fan: 4 (High), " "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(); @@ -513,8 +653,7 @@ TEST(TestIRac, Panasonic) { 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()); + ASSERT_EQ(expected_dke, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Samsung) { @@ -522,8 +661,8 @@ TEST(TestIRac, Samsung) { 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"; + "Power: On, Mode: 0 (AUTO), Temp: 28C, Fan: 6 (Auto), Swing: On, " + "Beep: On, Clean: On, Quiet: On, Powerful: Off"; ac.begin(); irac.samsung(&ac, @@ -536,14 +675,13 @@ 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)); 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); ac._irsend.reset(); irac.samsung(&ac, @@ -556,19 +694,39 @@ 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)); 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()); + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off"; + ASSERT_EQ(expected_on, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, Sharp) { + IRSharpAc ac(0); + IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 2 (COOL), Temp: 28C, Fan: 3 (Medium)"; + + 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); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Tcl112) { @@ -576,7 +734,7 @@ TEST(TestIRac, Tcl112) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 3 (Med), Econo: On, " + "Power: On, Mode: 3 (COOL), Temp: 20C, Fan: 3 (Medium), Econo: On, " "Health: On, Light: On, Turbo: Off, Swing (H): On, Swing (V): Off"; ac.begin(); @@ -596,8 +754,7 @@ TEST(TestIRac, Tcl112) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Teco) { @@ -605,7 +762,7 @@ TEST(TestIRac, Teco) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 0 (AUTO), Temp: 21C, Fan: 2 (Med), Sleep: On, " + "Power: On, Mode: 0 (AUTO), Temp: 21C, Fan: 2 (Medium), Sleep: On, " "Swing: On"; ac.begin(); @@ -621,15 +778,14 @@ TEST(TestIRac, Teco) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Toshiba) { IRToshibaAC ac(0); IRac irac(0); IRrecv capture(0); - char expected[] = "Power: On, Mode: 2 (DRY), Temp: 29C, Fan: 2"; + char expected[] = "Power: On, Mode: 2 (DRY), Temp: 29C, Fan: 2 (UNKNOWN)"; ac.begin(); irac.toshiba(&ac, @@ -642,13 +798,15 @@ TEST(TestIRac, Toshiba) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Trotec) { IRTrotecESP ac(0); IRac irac(0); + IRrecv capture(0); + char expected[] = + "Power: On, Mode: 1 (COOL), Temp: 18C, Fan: 3 (High), Sleep: On"; ac.begin(); irac.trotec(&ac, @@ -662,6 +820,12 @@ 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); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); } TEST(TestIRac, Vestel) { @@ -669,7 +833,7 @@ TEST(TestIRac, Vestel) { IRac irac(0); IRrecv capture(0); char expected[] = - "Power: On, Mode: 0 (AUTO), Temp: 22C, Fan: 5 (LOW), Sleep: On, " + "Power: On, Mode: 0 (AUTO), Temp: 22C, Fan: 5 (Low), Sleep: On, " "Turbo: Off, Ion: On, Swing: On"; ac.begin(); @@ -688,8 +852,7 @@ TEST(TestIRac, Vestel) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); ac._irsend.reset(); char expected_clocks[] = @@ -713,8 +876,7 @@ TEST(TestIRac, Vestel) { 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()); + ASSERT_EQ(expected_clocks, IRAcUtils::resultAcToString(&ac._irsend.capture)); // Now check it sends both messages during normal operation when the // clock is set. @@ -760,7 +922,7 @@ TEST(TestIRac, Whirlpool) { 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, " + "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(); @@ -780,8 +942,114 @@ TEST(TestIRac, Whirlpool) { 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()); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + +TEST(TestIRac, cmpStates) { + stdAc::state_t a, b; + a.protocol = decode_type_t::COOLIX; + a.model = -1; + a.power = true; + a.celsius = true; + a.degrees = 25; + a.mode = stdAc::opmode_t::kAuto; + a.fanspeed = stdAc::fanspeed_t::kAuto; + a.swingh = stdAc::swingh_t::kOff; + a.swingv = stdAc::swingv_t::kOff; + a.quiet = false; + a.turbo = false; + a.light = false; + a.econo = false; + a.beep = false; + a.filter = false; + a.clean = false; + a.quiet = false; + a.sleep = -1; + a.clock = -1; + + ASSERT_FALSE(IRac::cmpStates(a, a)); + ASSERT_TRUE(IRac::cmpStates(a, b)); + + b = a; + ASSERT_FALSE(IRac::cmpStates(a, b)); + + // Check we don't compare the clock. + b.clock = 1234; + ASSERT_FALSE(IRac::cmpStates(a, b)); + + // Now make them different. + b.power = false; + ASSERT_TRUE(IRac::cmpStates(a, b)); +} + +TEST(TestIRac, handleToggles) { + stdAc::state_t desired, prev, result; + desired.protocol = decode_type_t::COOLIX; + desired.model = -1; + desired.power = true; + desired.celsius = true; + desired.degrees = 25; + desired.mode = stdAc::opmode_t::kAuto; + desired.fanspeed = stdAc::fanspeed_t::kAuto; + desired.swingh = stdAc::swingh_t::kOff; + desired.swingv = stdAc::swingv_t::kOff; + desired.quiet = false; + desired.turbo = false; + desired.light = false; + desired.econo = false; + desired.beep = false; + desired.filter = false; + desired.clean = false; + desired.quiet = false; + desired.sleep = -1; + desired.clock = -1; + + // The states should be the same as we gave no previous state. + EXPECT_FALSE(IRac::cmpStates(desired, IRac::handleToggles(desired))); + // The states should be the same as we gave no settings that changed. + prev = desired; + EXPECT_FALSE(IRac::cmpStates(desired, IRac::handleToggles(desired, &prev))); + // Change something that isn't a toggle. + desired.degrees = 26; + ASSERT_TRUE(IRac::cmpStates(desired, prev)); + // Still shouldn't change. + EXPECT_FALSE(IRac::cmpStates(desired, IRac::handleToggles(desired, &prev))); + prev.turbo = true; // This requires a toggle. + result = IRac::handleToggles(desired, &prev); + EXPECT_TRUE(IRac::cmpStates(desired, result)); + EXPECT_TRUE(result.turbo); + desired.turbo = true; // As the desired setting hasn't changed from previous + // the result should not have turbo set, as it is + // a toggle setting. + result = IRac::handleToggles(desired, &prev); + EXPECT_TRUE(IRac::cmpStates(desired, result)); + EXPECT_FALSE(result.turbo); + + // Go back to the same states. + prev = desired; + ASSERT_FALSE(IRac::cmpStates(desired, prev)); + // Test swing, as it is more complicated. + result = IRac::handleToggles(desired, &prev); + EXPECT_EQ(stdAc::swingv_t::kOff, result.swingv); + desired.swingv = stdAc::swingv_t::kAuto; + result = IRac::handleToggles(desired, &prev); + EXPECT_NE(stdAc::swingv_t::kOff, result.swingv); + + prev = desired; // Pretend it was sent and time has passed. + ASSERT_FALSE(IRac::cmpStates(desired, prev)); + ASSERT_NE(stdAc::swingv_t::kOff, desired.swingv); + + // User changes setting but it's still an "on" setting, as this device + // only has a binary on/off for swingv. Nothing should change. + desired.swingv = stdAc::swingv_t::kHigh; + result = IRac::handleToggles(desired, &prev); + ASSERT_EQ(stdAc::swingv_t::kOff, result.swingv); // i.e No toggle. + + prev = desired; // Pretend it was sent and time has passed. + // User changes setting to off. i.e. It is no longer on, so it should toggle. + desired.swingv = stdAc::swingv_t::kOff; + result = IRac::handleToggles(desired, &prev); + ASSERT_NE(stdAc::swingv_t::kOff, result.swingv); // i.e A toggle. } TEST(TestIRac, strToBool) { @@ -863,3 +1131,61 @@ TEST(TestIRac, strToModel) { EXPECT_EQ(-1, IRac::strToModel("FOOBAR")); EXPECT_EQ(0, IRac::strToModel("FOOBAR", 0)); } + +TEST(TestIRac, boolToString) { + EXPECT_EQ("on", IRac::boolToString(true)); + EXPECT_EQ("off", IRac::boolToString(false)); +} + +TEST(TestIRac, opmodeToString) { + EXPECT_EQ("off", IRac::opmodeToString(stdAc::opmode_t::kOff)); + EXPECT_EQ("auto", IRac::opmodeToString(stdAc::opmode_t::kAuto)); + EXPECT_EQ("cool", IRac::opmodeToString(stdAc::opmode_t::kCool)); + EXPECT_EQ("unknown", IRac::opmodeToString((stdAc::opmode_t)500)); +} + +TEST(TestIRac, fanspeedToString) { + EXPECT_EQ("low", IRac::fanspeedToString(stdAc::fanspeed_t::kLow)); + EXPECT_EQ("auto", IRac::fanspeedToString(stdAc::fanspeed_t::kAuto)); + EXPECT_EQ("unknown", IRac::fanspeedToString((stdAc::fanspeed_t)500)); +} + +TEST(TestIRac, swingvToString) { + EXPECT_EQ("off", IRac::swingvToString(stdAc::swingv_t::kOff)); + EXPECT_EQ("low", IRac::swingvToString(stdAc::swingv_t::kLow)); + EXPECT_EQ("auto", IRac::swingvToString(stdAc::swingv_t::kAuto)); + EXPECT_EQ("unknown", IRac::swingvToString((stdAc::swingv_t)500)); +} + +TEST(TestIRac, swinghToString) { + EXPECT_EQ("off", IRac::swinghToString(stdAc::swingh_t::kOff)); + EXPECT_EQ("left", IRac::swinghToString(stdAc::swingh_t::kLeft)); + EXPECT_EQ("auto", IRac::swinghToString(stdAc::swingh_t::kAuto)); + EXPECT_EQ("unknown", IRac::swinghToString((stdAc::swingh_t)500)); +} + +// Check that we keep the previous state info if the message is a special +// state-less command. +TEST(TestIRac, CoolixDecodeToState) { + stdAc::state_t prev; + prev.mode = stdAc::opmode_t::kHeat; + prev.power = true; + prev.celsius = true; + prev.degrees = 20; + prev.fanspeed = stdAc::fanspeed_t::kLow; + + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + irsend.sendCOOLIX(kCoolixOff); // Special state-less "off" message. + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + stdAc::state_t result; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); + ASSERT_EQ(decode_type_t::COOLIX, result.protocol); + ASSERT_FALSE(result.power); + ASSERT_EQ(stdAc::opmode_t::kHeat, result.mode); + ASSERT_TRUE(result.celsius); + ASSERT_EQ(20, result.degrees); + ASSERT_EQ(stdAc::fanspeed_t::kLow, result.fanspeed); +} diff --git a/lib/IRremoteESP8266-2.6.3.10/test/IRrecv_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/IRrecv_test.cpp new file mode 100644 index 000000000..899841de5 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/test/IRrecv_test.cpp @@ -0,0 +1,1209 @@ +// Copyright 2017 David Conran + +#include "IRrecv_test.h" +#include "IRrecv.h" +#include "IRremoteESP8266.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for the IRrecv object. +TEST(TestIRrecv, DefaultBufferSize) { + IRrecv irrecv_default(1); + EXPECT_EQ(kRawBuf, irrecv_default.getBufSize()); +} + +TEST(TestIRrecv, LargeBufferSize) { + IRrecv irrecv_large(3, 1024); + EXPECT_EQ(1024, irrecv_large.getBufSize()); +} + +TEST(TestIRrecv, SmallBufferSize) { + IRrecv irrecv_small(4, 80); + EXPECT_EQ(80, irrecv_small.getBufSize()); +} + +TEST(TestIRrecv, MediumBufferSize) { + IRrecv irrecv_medium(4, 512); + EXPECT_EQ(512, irrecv_medium.getBufSize()); +} + +TEST(TestIRrecv, IRrecvDestructor) { + IRrecv *irrecv_ptr = new IRrecv(1); + EXPECT_EQ(kRawBuf, irrecv_ptr->getBufSize()); + + delete irrecv_ptr; + irrecv_ptr = new IRrecv(1, 1234); + EXPECT_EQ(1234, irrecv_ptr->getBufSize()); + delete irrecv_ptr; + + irrecv_ptr = new IRrecv(1, 123); + EXPECT_EQ(123, irrecv_ptr->getBufSize()); + delete irrecv_ptr; +} + +// Tests for copyIrParams() + +TEST(TestCopyIrParams, CopyEmpty) { + irparams_t src; + irparams_t dst; + uint16_t test_size = 1234; + src.bufsize = test_size; + src.rawlen = 0; + src.rawbuf = new uint16_t[test_size]; + src.overflow = false; + dst.bufsize = 4567; + dst.rawlen = 123; + dst.rawbuf = new uint16_t[test_size]; + dst.overflow = true; + // Confirm we are looking at different memory for the buffers. + ASSERT_NE(src.rawbuf, dst.rawbuf); + + IRrecv irrecv(4); + irrecv.copyIrParams(&src, &dst); + + ASSERT_EQ(src.bufsize, dst.bufsize); + ASSERT_EQ(src.rawlen, dst.rawlen); + ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. + ASSERT_EQ(src.overflow, dst.overflow); + // Contents of the buffers needs to match. + EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); +} + +TEST(TestCopyIrParams, CopyNonEmpty) { + irparams_t src; + irparams_t dst; + uint16_t test_size = 1234; + src.bufsize = test_size; + src.rawlen = 67; + src.rawbuf = new uint16_t[test_size]; + src.rawbuf[0] = 0xF00D; + src.rawbuf[1] = 0xBEEF; + src.rawbuf[test_size - 1] = 0xDEAD; + src.overflow = true; + dst.bufsize = 0; + dst.rawlen = 0; + dst.rawbuf = new uint16_t[test_size]; + dst.overflow = false; + // Confirm we are looking at different memory for the buffers. + ASSERT_NE(src.rawbuf, dst.rawbuf); + // and that they differ before we test. + EXPECT_NE(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); + + IRrecv irrecv(4); + irrecv.copyIrParams(&src, &dst); + + ASSERT_EQ(src.bufsize, dst.bufsize); + EXPECT_EQ(test_size, dst.bufsize); + ASSERT_EQ(src.rawlen, dst.rawlen); + EXPECT_EQ(67, dst.rawlen); + ASSERT_EQ(src.overflow, dst.overflow); + EXPECT_TRUE(dst.overflow); + ASSERT_NE(src.rawbuf, dst.rawbuf); // Pointers, not content. + // Contents of the buffers needs to match. + EXPECT_EQ(0, memcmp(src.rawbuf, dst.rawbuf, src.bufsize * sizeof(uint16_t))); + // Check the canary values. + EXPECT_EQ(0xF00D, dst.rawbuf[0]); + EXPECT_EQ(0xBEEF, dst.rawbuf[1]); + EXPECT_EQ(0xDEAD, dst.rawbuf[test_size - 1]); +} + +// Tests for decode(). + +// Test decode of a NEC message. +TEST(TestDecode, DecodeNEC) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendNEC(0x807F40BF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x807F40BF, irsend.capture.value); +} + +// Test decode of a JVC message. +TEST(TestDecode, DecodeJVC) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendJVC(0xC2B8); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(JVC, irsend.capture.decode_type); + EXPECT_EQ(kJvcBits, irsend.capture.bits); + EXPECT_EQ(0xC2B8, irsend.capture.value); +} + +// Test decode of a LG message. +TEST(TestDecode, DecodeLG) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendLG(0x4B4AE51); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLgBits, irsend.capture.bits); + EXPECT_EQ(0x4B4AE51, irsend.capture.value); + + irsend.reset(); + irsend.sendLG(0xB4B4AE51, kLg32Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(LG, irsend.capture.decode_type); + EXPECT_EQ(kLg32Bits, irsend.capture.bits); + EXPECT_EQ(0xB4B4AE51, irsend.capture.value); +} + +// Test decode of a Panasonic message. +TEST(TestDecode, DecodePanasonic) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendPanasonic64(0x40040190ED7C); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodePanasonic(&irsend.capture, kPanasonicBits, true)); + EXPECT_EQ(PANASONIC, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicBits, irsend.capture.bits); + EXPECT_EQ(0x40040190ED7C, irsend.capture.value); +} + +// Test decode of a Samsun message. +TEST(TestDecode, DecodeSamsung) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSAMSUNG(0xE0E09966); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SAMSUNG, irsend.capture.decode_type); + EXPECT_EQ(kSamsungBits, irsend.capture.bits); + EXPECT_EQ(0xE0E09966, irsend.capture.value); +} + +// Test decode of a Sherwood message. +TEST(TestDecode, DecodeSherwood) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSherwood(0x807F40BF); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + // Sherwood codes are really NEC codes. + EXPECT_EQ(NEC, irsend.capture.decode_type); + EXPECT_EQ(kNECBits, irsend.capture.bits); + EXPECT_EQ(0x807F40BF, irsend.capture.value); +} + +// Test decode of a Whynter message. +TEST(TestDecode, DecodeWhynter) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendWhynter(0x87654321); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(WHYNTER, irsend.capture.decode_type); + EXPECT_EQ(kWhynterBits, irsend.capture.bits); + EXPECT_EQ(0x87654321, irsend.capture.value); +} + +// Test decode of Sony messages. +TEST(TestDecode, DecodeSony) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Synthesised Normal Sony 20-bit message. + irsend.reset(); + irsend.sendSony(irsend.encodeSony(kSony20Bits, 0x1, 0x1, 0x1)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony20Bits, irsend.capture.bits); + EXPECT_EQ(0x81080, irsend.capture.value); + + // Synthesised Normal Sony 15-bit message. + irsend.reset(); + irsend.sendSony(irsend.encodeSony(kSony15Bits, 21, 1), kSony15Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony15Bits, irsend.capture.bits); + EXPECT_EQ(0x5480, irsend.capture.value); + + // Synthesised Normal Sony 12-bit message. + irsend.reset(); + irsend.sendSony(irsend.encodeSony(kSony12Bits, 21, 1), kSony12Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SONY, irsend.capture.decode_type); + EXPECT_EQ(kSony12Bits, irsend.capture.bits); + EXPECT_EQ(0xA90, irsend.capture.value); +} + +// Test decode of Sharp messages. +TEST(TestDecode, DecodeSharp) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSharpRaw(0x454A); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SHARP, irsend.capture.decode_type); + EXPECT_EQ(kSharpBits, irsend.capture.bits); + EXPECT_EQ(0x454A, irsend.capture.value); +} + +// Test decode of Sanyo messages. +TEST(TestDecode, DecodeSanyo) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendSanyoLC7461(0x2468DCB56A9); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type); + EXPECT_EQ(kSanyoLC7461Bits, irsend.capture.bits); + EXPECT_EQ(0x2468DCB56A9, irsend.capture.value); +} + +// Test decode of RC-MM messages. +TEST(TestDecode, DecodeRCMM) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Normal RCMM 24-bit message. + irsend.reset(); + irsend.sendRCMM(0xe0a600); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(kRCMMBits, irsend.capture.bits); + EXPECT_EQ(0xe0a600, irsend.capture.value); + + // Normal RCMM 12-bit message. + irsend.reset(); + irsend.sendRCMM(0x600, 12); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(12, irsend.capture.bits); + EXPECT_EQ(0x600, irsend.capture.value); + + // Normal RCMM 32-bit message. + irsend.reset(); + irsend.sendRCMM(0x28e0a600, 32); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RCMM, irsend.capture.decode_type); + EXPECT_EQ(32, irsend.capture.bits); + EXPECT_EQ(0x28e0a600, irsend.capture.value); +} + +// Test decode of Mitsubishi messages. +TEST(TestDecode, DecodeMitsubishi) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendMitsubishi(0xC2B8); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishiBits, irsend.capture.bits); + EXPECT_EQ(0xC2B8, irsend.capture.value); +} + +// Test decode of RC-5/RC-5X messages. +TEST(TestDecode, DecodeRC5) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + // Normal RC-5 12-bit message. + irsend.reset(); + irsend.sendRC5(0x175); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC5, irsend.capture.decode_type); + EXPECT_EQ(kRC5Bits, irsend.capture.bits); + EXPECT_EQ(0x175, irsend.capture.value); + // Synthesised Normal RC-5X 13-bit message. + irsend.reset(); + irsend.sendRC5(irsend.encodeRC5X(0x02, 0x41, true), kRC5XBits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC5X, irsend.capture.decode_type); + EXPECT_EQ(kRC5XBits, irsend.capture.bits); + EXPECT_EQ(0x1881, irsend.capture.value); +} + +// Test decode of RC-6 messages. +TEST(TestDecode, DecodeRC6) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + // Normal RC-6 Mode 0 (20-bit) message. + irsend.reset(); + irsend.sendRC6(0x175); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC6, irsend.capture.decode_type); + EXPECT_EQ(kRC6Mode0Bits, irsend.capture.bits); + EXPECT_EQ(0x175, irsend.capture.value); + + // Normal RC-6 36-bit message. + irsend.reset(); + irsend.sendRC6(0xC800F742A, kRC6_36Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(RC6, irsend.capture.decode_type); + EXPECT_EQ(kRC6_36Bits, irsend.capture.bits); + EXPECT_EQ(0xC800F742A, irsend.capture.value); +} + +// Test decode of Dish messages. +TEST(TestDecode, DecodeDish) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendDISH(0x9C00); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DISH, irsend.capture.decode_type); + EXPECT_EQ(kDishBits, irsend.capture.bits); + EXPECT_EQ(0x9C00, irsend.capture.value); +} + +// Test decode of Denon messages. +TEST(TestDecode, DecodeDenon) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + // Normal Denon 15-bit message. (Sharp) + irsend.reset(); + irsend.sendDenon(0x2278); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenonBits, irsend.capture.bits); + EXPECT_EQ(0x2278, irsend.capture.value); + // Legacy Denon 14-bit message. + irsend.reset(); + irsend.sendDenon(0x1278, kDenonLegacyBits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenonBits, irsend.capture.bits); + EXPECT_EQ(0x1278, irsend.capture.value); + // Normal Denon 48-bit message. (Panasonic/Kaseikyo) + irsend.reset(); + irsend.sendDenon(0x2A4C028D6CE3, kDenon48Bits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(DENON, irsend.capture.decode_type); + EXPECT_EQ(kDenon48Bits, irsend.capture.bits); + EXPECT_EQ(0x2A4C028D6CE3, irsend.capture.value); +} + +// Test decode of Coolix messages. +TEST(TestDecode, DecodeCoolix) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendCOOLIX(0x123456); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, irsend.capture.bits); + EXPECT_EQ(0x123456, irsend.capture.value); +} + +// Test decode of Aiwa messages. +TEST(TestDecode, DecodeAiwa) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + irsend.reset(); + irsend.sendAiwaRCT501(0x7F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type); + EXPECT_EQ(kAiwaRcT501Bits, irsend.capture.bits); + EXPECT_EQ(0x7F, irsend.capture.value); +} + +// Test matchData() on space encoded data. +TEST(TestMatchData, SpaceEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t space_encoded_raw[11] = {500, 500, 500, 1500, 499, 499, + 501, 1501, 499, 1490, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(space_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1500, 500, 500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(space_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 500, 1000, 500, 500); + ASSERT_FALSE(result.success); +} + +// Test matchData() on mark encoded data. +TEST(TestMatchData, MarkEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t mark_encoded_raw[11] = {500, 500, 1500, 500, 499, 499, + 1501, 501, 1499, 490, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(mark_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + // MSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + // LSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500, + kTolerance, kMarkExcess, false); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b11010, result.data); // Bits reversed of the previous test. + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(mark_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + // MSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500); + ASSERT_FALSE(result.success); + // LSBF order. + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500, + kTolerance, kMarkExcess, false); + ASSERT_FALSE(result.success); +} + +// Test matchData() on "equal total bit time" encoded data. +TEST(TestMatchData, EqualTotalBitTimeEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t equal_encoded_raw[11] = {500, 1500, 1500, 500, 499, 1499, + 1501, 501, 1499, 490, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 1500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(equal_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); + ASSERT_FALSE(result.success); +} + +// Test matchData() on arbitrary encoded data. +TEST(TestMatchData, ArbitraryEncoded) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t arbitrary_encoded_raw[11] = {500, 1500, 3000, 1000, 499, 1499, + 3001, 1001, 2999, 990, 500}; + match_result_t result; + + irsend.reset(); + irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = + irrecv.matchData(irsend.capture.rawbuf + 1, 5, 3000, 1000, 500, 1500); + ASSERT_TRUE(result.success); + EXPECT_EQ(0b01011, result.data); + EXPECT_EQ(10, result.used); + + irsend.reset(); + irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); + irsend.makeDecodeResult(); + result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 1000); + ASSERT_FALSE(result.success); +} + +TEST(TestMatchGeneric, NormalWithNoAtleast) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t good_entries_trailing_space = 12; + uint16_t good_trailing_space_data[good_entries_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000, // Footer mark + 15000}; // Footer space + + uint16_t good_entries_no_trailing_space = 11; + uint16_t good_no_trailing_space_data[good_entries_no_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(good_trailing_space_data, good_entries_trailing_space, 38000); + irsend.makeDecodeResult(); + uint64_t result_data = 0; + uint16_t entries_used = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + false, // No atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_trailing_space, entries_used); + + // Same again but with a footer space mis-match, which should fail. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 30000, // Footer + false, // no atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Same again as first part but with no footer space data as the last entry. + irsend.reset(); + result_data = 0; + irsend.sendRaw(good_no_trailing_space_data, good_entries_no_trailing_space, + 38000); + irsend.makeDecodeResult(); + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + false, // No atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_no_trailing_space, entries_used); +} + + +TEST(TestMatchGeneric, NormalWithAtleast) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t good_entries_trailing_space = 12; + uint16_t good_trailing_space_data[good_entries_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000, // Footer mark + 15000}; // Footer space + + uint16_t good_entries_no_trailing_space = 11; + uint16_t good_no_trailing_space_data[good_entries_no_trailing_space] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(good_trailing_space_data, good_entries_trailing_space, 38000); + irsend.makeDecodeResult(); + uint64_t result_data = 0; + uint16_t entries_used = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_trailing_space, entries_used); + + // Same again but with a footer space under-match. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 1500, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_trailing_space, entries_used); + + // Same again but with a footer space under-match using less bits so the + // atleast footer isn't the last entry in the buffer. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 3, // nbits (1 less than normal) + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 500, 500, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b101, result_data); + // -2 because we reduced nbits by 1. + EXPECT_EQ(irsend.capture.rawlen - kStartOffset - 2, entries_used); + EXPECT_EQ(good_entries_trailing_space - 2, entries_used); + + // Same again but with a footer space over-match, which should fail. + result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 3, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 500, 10000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Same as first part but with no footer space data as the last entry. + irsend.reset(); + result_data = 0; + irsend.sendRaw(good_no_trailing_space_data, good_entries_no_trailing_space, + 38000); + irsend.makeDecodeResult(); + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(good_entries_no_trailing_space, entries_used); +} + +TEST(TestMatchGeneric, FailureCases) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 11; + uint16_t data[entries] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + // Wanting too many bits should fail. + uint64_t result_data = 0; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 5, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad header mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 2000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad header space. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 2000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad one mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 600, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad one space. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2500, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad zero space. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1500, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad zero mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 900, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Bad Footer mark. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 1000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Confirm it really does match as expected.. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); +} + +TEST(TestMatchGeneric, MissingHeaderFooter) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 11; + uint16_t data[entries] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + uint64_t result_data = 0; + + // No footer match + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // NO Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset - 1, entries_used); + EXPECT_EQ(entries - 1, entries_used); + + // No header match (should fail) + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 0, 0, // NO Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // NO Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // No header match but starting after header + offset += 2; + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 0, 0, // NO Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - offset, entries_used); + EXPECT_EQ(entries - 2, entries_used); +} + +TEST(TestMatchGeneric, BitOrdering) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 11; + uint16_t data[entries] = { + 8000, // Header mark + 4000, // Header space + 500, 2000, // Bit #0 (1) + 500, 1000, // Bit #1 (0) + 500, 2000, // Bit #2 (1) + 500, 1000, // Bit #3 (0) + 3000}; // Footer mark (No Footer space.) + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + uint64_t result_data = 0; + + // MSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b1010, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); + + // LSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, &result_data, + irsend.capture.rawlen - offset, + 4, // nbits + 8000, 4000, // Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 3000, 15000, // Footer + true, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + false); // LSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b0101, result_data); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); +} + +TEST(TestMatchGeneric, UsingBytes) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + uint16_t entries = 32; + uint16_t data[entries] = { + // No header + 500, 2000, // Byte #0 Bit #0 (1) + 500, 1000, // Byte #0 Bit #1 (0) + 500, 2000, // Byte #0 Bit #2 (1) + 500, 1000, // Byte #0 Bit #3 (0) + 500, 2000, // Byte #0 Bit #4 (1) + 500, 1000, // Byte #0 Bit #5 (0) + 500, 2000, // Byte #0 Bit #6 (1) + 500, 1000, // Byte #0 Bit #7 (0) + 500, 2000, // Byte #1 Bit #0 (1) + 500, 2000, // Byte #1 Bit #1 (1) + 500, 2000, // Byte #1 Bit #2 (1) + 500, 2000, // Byte #1 Bit #3 (1) + 500, 1000, // Byte #1 Bit #4 (0) + 500, 1000, // Byte #1 Bit #5 (0) + 500, 1000, // Byte #1 Bit #6 (0) + 500, 1000}; // Byte #1 Bit #7 (0) & No footer + + uint16_t offset = kStartOffset; + irsend.reset(); + irsend.sendRaw(data, entries, 38000); + irsend.makeDecodeResult(); + uint16_t entries_used = 0; + + uint8_t result_data[4] = {}; // Bigger than we need. + + // MSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 2 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b10101010, result_data[0]); + EXPECT_EQ(0b11110000, result_data[1]); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); + + // LSB order + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 2 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + false); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b01010101, result_data[0]); + EXPECT_EQ(0b00001111, result_data[1]); + EXPECT_EQ(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(entries, entries_used); + + // Asking for too much. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 3 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Asking for less than what is there. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 1 * 8, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_NE(0, entries_used); + EXPECT_EQ(0b10101010, result_data[0]); + EXPECT_GT(irsend.capture.rawlen - kStartOffset, entries_used); + EXPECT_EQ(16, entries_used); + + // Asking for non mod-8 size should fail. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 9, // nbits + 0, 0, // No Header + 500, 2000, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); + + // Expecting different timings should fail. + entries_used = irrecv.matchGeneric( + irsend.capture.rawbuf + offset, result_data, + irsend.capture.rawlen - offset, + 8, // nbits + 0, 0, // No Header + 500, 900, // one mark & space + 500, 1000, // zero mark & space + 0, 0, // No Footer + false, // atleast on the footer space. + 1, // 1% Tolerance + 0, // No excess margin + true); // MSB first. + ASSERT_EQ(0, entries_used); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h b/lib/IRremoteESP8266-2.6.3.10/test/IRrecv_test.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/IRrecv_test.h rename to lib/IRremoteESP8266-2.6.3.10/test/IRrecv_test.h diff --git a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/IRsend_test.cpp similarity index 84% rename from lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/IRsend_test.cpp index ffd69cf71..fbf8ba229 100644 --- a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/IRsend_test.cpp @@ -1,6 +1,7 @@ // Copyright 2017,2019 David Conran #include "IRsend_test.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRutils.h" #include "gtest/gtest.h" @@ -378,7 +379,7 @@ TEST(TestLowLevelSend, SpaceNoModulation) { EXPECT_EQ("[Off]1000usecs", irsend.low_level_sequence); } -// Test expected to work/produce a message for irsend:send() +// Test expected to work/produce a message for simple irsend:send() TEST(TestSend, GenericSimpleSendMethod) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -657,7 +658,7 @@ TEST(TestSend, GenericSimpleSendMethod) { EXPECT_EQ(0x1234, irsend.capture.value); } -// Test some expected types to NOT work/produce a message via irsend:send() +// Test some expected types to NOT work/produce a message via irsend:send() TEST(TestSend, GenericSimpleSendMethodFailure) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -670,17 +671,118 @@ TEST(TestSend, GenericSimpleSendMethodFailure) { ASSERT_FALSE(irrecv.decode(&irsend.capture)); // For every A/C protocol which decodes to having a state[]. - for (int i = 0; i < 200; i++) { + for (int i = 0; i <= kLastDecodeType; i++) { if (hasACState((decode_type_t)i) && i != GREE) { // Gree is an exception. // Check it fails. - ASSERT_FALSE(irsend.send((decode_type_t)i, 0, 0)); + ASSERT_FALSE(irsend.send((decode_type_t)i, (uint64_t)0, 0)); } } // Test some other special cases. - ASSERT_FALSE(irsend.send(UNKNOWN, 0, 0)); - ASSERT_FALSE(irsend.send(UNUSED, 0, 0)); - ASSERT_FALSE(irsend.send(RAW, 0, 0)); - ASSERT_FALSE(irsend.send(PRONTO, 0, 0)); - ASSERT_FALSE(irsend.send(GLOBALCACHE, 0, 0)); + ASSERT_FALSE(irsend.send(UNKNOWN, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(UNUSED, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(RAW, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(PRONTO, (uint64_t)0, 0)); + ASSERT_FALSE(irsend.send(GLOBALCACHE, (uint64_t)0, 0)); +} + +// Test expected to work/produce a message for complex irsend:send() +TEST(TestSend, GenericComplexSendMethod) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint8_t kelvinator[kKelvinatorStateLength] = { + 0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, + 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0}; + ASSERT_TRUE(irsend.send(KELVINATOR, kelvinator, kKelvinatorStateLength)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(KELVINATOR, irsend.capture.decode_type); + EXPECT_EQ(kKelvinatorStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(kelvinator, irsend.capture.state, irsend.capture.bits / 8); + + irsend.reset(); + uint8_t panasonic[kPanasonicAcStateLength] = { + 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, + 0x20, 0xE0, 0x04, 0x00, 0x4E, 0x2E, 0x80, 0xAF, 0x00, + 0x00, 0x0E, 0xE0, 0x11, 0x00, 0x01, 0x00, 0x06, 0xB7}; + ASSERT_TRUE(irsend.send(PANASONIC_AC, panasonic, kPanasonicAcStateLength)); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(PANASONIC_AC, irsend.capture.decode_type); + EXPECT_EQ(kPanasonicAcStateLength * 8, irsend.capture.bits); + EXPECT_STATE_EQ(panasonic, irsend.capture.state, irsend.capture.bits / 8); +} + +// Test some expected types to NOT work/produce a complex message via +// irsend:send() +TEST(TestSend, GenericComplexSendMethodFailure) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Check nothing is sent for unexpected protocols + uint8_t state[kStateSizeMax] = {}; + irsend.reset(); + ASSERT_FALSE(irsend.send(NEC, state, kNECBits)); + irsend.makeDecodeResult(); + ASSERT_FALSE(irrecv.decode(&irsend.capture)); + + // For every A/C protocol which DOESN'T decode to having a state[]. + for (int i = -1; i <= kLastDecodeType; i++) { + if (!hasACState((decode_type_t)i)) + // Check it fails. + ASSERT_FALSE(irsend.send((decode_type_t)i, state, 0)); + else // Or if it is okay. + ASSERT_TRUE(irsend.send((decode_type_t)i, state, 0)); + } +} + +TEST(TestSend, GenericSendExistsForEveryRealProtocol) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t state[kStateSizeMax] = {}; + uint64_t value = 0; + for (int i = 1; i <= kLastDecodeType; i++) { + switch (i) { + // Protocols that don't have a generic send equiv. + case PRONTO: + case RAW: + case GLOBALCACHE: + // Protocols that are disabled because they don't work. + case SANYO: + break; + default: + EXPECT_TRUE(irsend.send((decode_type_t)i, state, 0) || + irsend.send((decode_type_t)i, value, 0)) << + "Protocol " << typeToString((decode_type_t)i) << "(" << i << + ") doesn't have a generic send option for it."; + } + } +} + +TEST(TestSend, defaultBits) { + for (int i = 1; i <= kLastDecodeType; i++) { + switch (i) { + // Protocols that don't have have a default bit size. + case PRONTO: + case RAW: + case GLOBALCACHE: + case SANYO: // Not implemented / disabled. + // Deliberate no default size. + case FUJITSU_AC: + case MWM: + EXPECT_EQ(IRsend::defaultBits((decode_type_t)i), 0) << + "Protocol " << typeToString((decode_type_t)i) << "(" << i << + ") doesn't have a correct value for it."; + break; + default: + EXPECT_GT(IRsend::defaultBits((decode_type_t)i), 0) << + "Protocol " << typeToString((decode_type_t)i) << "(" << i << + ") doesn't have a correct value for it."; + } + } } diff --git a/lib/IRremoteESP8266-2.6.0/test/IRsend_test.h b/lib/IRremoteESP8266-2.6.3.10/test/IRsend_test.h similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/IRsend_test.h rename to lib/IRremoteESP8266-2.6.3.10/test/IRsend_test.h diff --git a/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/IRutils_test.cpp similarity index 77% rename from lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/IRutils_test.cpp index 4a4907649..f8d1394cb 100644 --- a/lib/IRremoteESP8266-2.6.0/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/IRutils_test.cpp @@ -1,8 +1,9 @@ -// Copyright 2017 David Conran +// Copyright 2017-2019 David Conran #include "IRutils.h" #include #include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -403,15 +404,115 @@ TEST(TestStrToDecodeType, strToDecodeType) { } 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("", irutils::htmlEscape("")); + EXPECT_EQ("No Changes", irutils::htmlEscape("No Changes")); + EXPECT_EQ("No\tChanges+_%^$@~`\n:\\", + irutils::htmlEscape("No\tChanges+_%^$@~`\n:\\")); + EXPECT_EQ(""With Changes"", + irutils::htmlEscape("\"With Changes\"")); EXPECT_EQ( "';!‐"<>&#equals;&#{}" - "()", htmlEscape("';!-\"<>=&#{}()")); - EXPECT_EQ("""", htmlEscape("\"\"")); + "()", irutils::htmlEscape("';!-\"<>=&#{}()")); + EXPECT_EQ("""", irutils::htmlEscape("\"\"")); EXPECT_EQ( "&quot;&lt;&apos;&gt;&amp;", - htmlEscape(""<'>&")); + irutils::htmlEscape(""<'>&")); +} + +TEST(TestUtils, TemperatureConversion) { + // Freezing point of water. + ASSERT_EQ(32.0, celsiusToFahrenheit(0.0)); + ASSERT_EQ(0.0, fahrenheitToCelsius(32.0)); + // Boiling point of water. + ASSERT_EQ(212.0, celsiusToFahrenheit(100.0)); + ASSERT_EQ(100.0, fahrenheitToCelsius(212.0)); + // Room Temp. (RTP) + ASSERT_EQ(77.0, celsiusToFahrenheit(25.0)); + ASSERT_EQ(25.0, fahrenheitToCelsius(77.0)); + // Misc + ASSERT_EQ(-40.0, fahrenheitToCelsius(-40.0)); +} + +TEST(TestResultToRawArray, TypicalCase) { + IRsendTest irsend(0); + IRrecv irrecv(1); + irsend.begin(); + + // Generate a known message. + irsend.reset(); + irsend.sendNikai(0xD0F2F); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(NIKAI, irsend.capture.decode_type); + ASSERT_EQ(kNikaiBits, irsend.capture.bits); + EXPECT_EQ( + "uint16_t rawData[52] = {4000, 4000, 500, 2000, 500, 2000, " + "500, 2000, 500, 2000, 500, 1000, 500, 1000, 500, 2000, 500, 1000, " + "500, 2000, 500, 2000, 500, 2000, 500, 2000, 500, 1000, 500, 1000, " + "500, 1000, 500, 1000, 500, 2000, 500, 2000, 500, 1000, 500, 2000, " + "500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 8500 };" + " // NIKAI D0F2F\n" + "uint64_t data = 0xD0F2F;\n", + resultToSourceCode(&irsend.capture)); + uint16_t rawData[52] = { // Data taken from above. + 4000, 4000, 500, 2000, 500, 2000, 500, 2000, 500, 2000, 500, 1000, 500, + 1000, 500, 2000, 500, 1000, 500, 2000, 500, 2000, 500, 2000, 500, 2000, + 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 2000, 500, 2000, 500, + 1000, 500, 2000, 500, 1000, 500, 1000, 500, 1000, 500, 1000, 500, 8500}; + uint16_t * result = resultToRawArray(&irsend.capture); + ASSERT_EQ(52, getCorrectedRawLength(&irsend.capture)); + EXPECT_STATE_EQ(rawData, result, getCorrectedRawLength(&irsend.capture)); + if (result != NULL) delete[] result; +} + +TEST(TestResultToRawArray, LargeValues) { + IRsendTest irsend(0); + IRrecv irrecv(1); + uint16_t test_data[9] = {10, 20, 30, 40, 50, 60, 70, 80, 90}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(test_data, 9, 38000); + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + uint16_t * result = resultToRawArray(&irsend.capture); + ASSERT_EQ(9, getCorrectedRawLength(&irsend.capture)); + EXPECT_STATE_EQ(test_data, result, 9); + if (result != NULL) delete[] result; + // Stick in some large values. + irsend.capture.rawbuf[3] = 60000; + EXPECT_EQ( + "uint16_t rawData[11] = {10, 20, 65535, 0, 54465, 40, 50, 60, 70, " + "80, 90}; // UNKNOWN 54051FFD\n", + resultToSourceCode(&irsend.capture)); + uint16_t large_test_data[11] = { + 10, 20, 65535, 0, 54465, 40, 50, 60, 70, 80, 90}; + ASSERT_EQ(11, getCorrectedRawLength(&irsend.capture)); + result = resultToRawArray(&irsend.capture); + EXPECT_STATE_EQ(large_test_data, result, 11); + if (result != NULL) delete[] result; +} + +TEST(TestUtils, TypeStringConversionRangeTests) { + ASSERT_EQ("UNKNOWN", typeToString((decode_type_t)(kLastDecodeType + 1))); + ASSERT_EQ("UNKNOWN", typeToString(decode_type_t::UNKNOWN)); + for (int i = 0; i <= kLastDecodeType; i++) { + EXPECT_NE("UNKNOWN", typeToString((decode_type_t)i)) << "Protocol " << i << + " doesn't have a valid string for it."; + EXPECT_EQ(i, strToDecodeType(typeToString((decode_type_t)i).c_str())) << + "Protocol " << typeToString((decode_type_t)i) << + " doesn't decode from a string correctly"; + } +} + +TEST(TestUtils, MinsToString) { + EXPECT_EQ("00:00", irutils::minsToString(0)); + EXPECT_EQ("00:01", irutils::minsToString(1)); + EXPECT_EQ("00:10", irutils::minsToString(10)); + EXPECT_EQ("00:59", irutils::minsToString(59)); + + EXPECT_EQ("01:00", irutils::minsToString(60)); + EXPECT_EQ("01:01", irutils::minsToString(61)); + EXPECT_EQ("01:59", irutils::minsToString(60 + 59)); + EXPECT_EQ("18:59", irutils::minsToString(18 * 60 + 59)); + EXPECT_EQ("23:59", irutils::minsToString(23 * 60 + 59)); } diff --git a/lib/IRremoteESP8266-2.6.0/test/Makefile b/lib/IRremoteESP8266-2.6.3.10/test/Makefile similarity index 91% rename from lib/IRremoteESP8266-2.6.0/test/Makefile rename to lib/IRremoteESP8266-2.6.3.10/test/Makefile index 9a64aaaaa..1de3aa220 100644 --- a/lib/IRremoteESP8266-2.6.0/test/Makefile +++ b/lib/IRremoteESP8266-2.6.3.10/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 ir_Neoclima_test # All Google Test headers. Usually you shouldn't change this # definition. @@ -83,21 +84,25 @@ 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 ir_Neoclima.o # All the IR Protocol header files. PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \ $(USER_DIR)/ir_Gree.h \ $(USER_DIR)/ir_Magiquest.h \ $(USER_DIR)/ir_Coolix.h \ + $(USER_DIR)/ir_Electra.h \ $(USER_DIR)/ir_Haier.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_Neoclima.h \ + $(USER_DIR)/ir_Sharp.h \ $(USER_DIR)/ir_Samsung.h \ $(USER_DIR)/ir_Trotec.h \ $(USER_DIR)/ir_Fujitsu.h \ @@ -106,7 +111,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 +294,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 @@ -494,8 +500,8 @@ ir_Lutron_test.o : ir_Lutron_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) ir_Lutron_test : $(COMMON_OBJ) ir_Lutron_test.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ -ir_Electra.o : $(USER_DIR)/ir_Electra.cpp $(COMMON_DEPS) $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Electra.cpp +ir_Electra.o : $(USER_DIR)/ir_Electra.h $(USER_DIR)/ir_Electra.cpp $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Electra.cpp ir_Electra_test.o : ir_Electra_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Electra_test.cpp @@ -560,5 +566,44 @@ 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 $@ + +ir_Neoclima.o : $(USER_DIR)/ir_Neoclima.h $(USER_DIR)/ir_Neoclima.cpp $(COMMON_DEPS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Neoclima.cpp + +ir_Neoclima_test.o : ir_Neoclima_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_Neoclima_test.cpp + +ir_Neoclima_test : $(COMMON_OBJ) ir_Neoclima_test.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Aiwa_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Aiwa_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Aiwa_test.cpp diff --git a/lib/IRremoteESP8266-2.6.3.10/test/ir_Argo_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Argo_test.cpp new file mode 100644 index 000000000..7932416e0 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/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-2.6.0/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Carrier_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Carrier_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Carrier_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Coolix_test.cpp similarity index 80% rename from lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Coolix_test.cpp index 0f97c5ead..4ab61b9a8 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Coolix_test.cpp @@ -395,6 +395,8 @@ TEST(TestCoolixACClass, SetAndGetMode) { TEST(TestCoolixACClass, SetAndGetFan) { IRCoolixAC ircoolix(0); + // This mode allows pretty much everything except Auto0 speed. + ircoolix.setMode(kCoolixCool); ircoolix.setFan(kCoolixFanMax); EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); ircoolix.setFan(kCoolixFanMin); @@ -403,12 +405,29 @@ TEST(TestCoolixACClass, SetAndGetFan) { EXPECT_EQ(kCoolixFanZoneFollow, ircoolix.getFan()); ircoolix.setFan(kCoolixFanAuto); EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto0); + EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); ircoolix.setFan(kCoolixFanMax); EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); ASSERT_NE(3, kCoolixFanAuto); // Now try some unexpected value. ircoolix.setFan(3); EXPECT_EQ(kCoolixFanAuto, ircoolix.getFan()); + + // These modes allows pretty much everything except Auto speed. + ircoolix.setMode(kCoolixDry); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanMax); + EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + + ircoolix.setMode(kCoolixAuto); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanMax); + EXPECT_EQ(kCoolixFanMax, ircoolix.getFan()); + ircoolix.setFan(kCoolixFanAuto0); + EXPECT_EQ(kCoolixFanAuto0, ircoolix.getFan()); } TEST(TestCoolixACClass, SetGetClearSensorTempAndZoneFollow) { @@ -547,7 +566,7 @@ TEST(TestCoolixACClass, RealCaptureExample) { // Tests to debug/fix: -// https://github.com/markszabo/IRremoteESP8266/issues/624 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/624 TEST(TestCoolixACClass, Issue624HandleSpecialStatesBetter) { IRCoolixAC ac(0); ac.begin(); @@ -597,3 +616,132 @@ TEST(TestCoolixACClass, Issue624HandleSpecialStatesBetter) { ac.toString()); EXPECT_EQ(0xB2BF40, ac.getRaw()); } + +TEST(TestCoolixACClass, toCommon) { + IRCoolixAC ac(0); + ac.setPower(true); + ac.setMode(kCoolixCool); + ac.setTemp(20); + ac.setFan(kCoolixFanMax); + + // Now test it. + ASSERT_EQ(decode_type_t::COOLIX, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestCoolixACClass, Issue722) { + IRrecv irrecv(0); + IRCoolixAC ac(0); + + // Auto 17C ON pressed + uint32_t on_auto_17c_fan_auto0 = 0xB21F08; + ac.begin(); + ac.setPower(true); + ac.setMode(kCoolixAuto); + ac.setFan(kCoolixFanAuto); + ac.setTemp(17); + EXPECT_EQ(on_auto_17c_fan_auto0, ac.getRaw()); + + // Off + uint32_t off = 0xB27BE0; + ac.off(); + EXPECT_EQ(off, ac.getRaw()); + + // ON Auto Temp 18C + uint32_t on_auto_18c_fan_auto0 = 0xB21F18; + ac.setTemp(18); + EXPECT_EQ(on_auto_18c_fan_auto0, ac.getRaw()); + + // Set Mode Cool 18C + uint32_t on_cool_18c_fan_auto = 0xB2BF10; + ac.setMode(kCoolixCool); + EXPECT_EQ(on_cool_18c_fan_auto, ac.getRaw()); + + // Set Mode DRY 18C + uint32_t on_dry_18c_fan_auto0 = 0xB21F14; + ac.setMode(kCoolixDry); + EXPECT_EQ(on_dry_18c_fan_auto0, ac.getRaw()); + + // Set Mode HEAT 18C + uint32_t on_heat_18c_fan_auto = 0xB2BF1C; + ac.setMode(kCoolixHeat); + EXPECT_EQ(on_heat_18c_fan_auto, ac.getRaw()); + + // Set mode FAN + uint32_t on_fan_18c_fan_auto = 0xB2BFE4; + ac.setMode(kCoolixFan); + EXPECT_EQ(on_fan_18c_fan_auto, ac.getRaw()); + + // Fan level 2 (initial was auto) + uint32_t on_fan_18c_fan_min = 0xB29FE4; + ac.setFan(kCoolixFanMin); + EXPECT_EQ(on_fan_18c_fan_min, ac.getRaw()); + + // Fan level 3 + uint32_t on_fan_18c_fan_med = 0xB25FE4; + ac.setFan(kCoolixFanMed); + EXPECT_EQ(on_fan_18c_fan_med, ac.getRaw()); + + // Fan level 4 + uint32_t on_fan_18c_fan_max = 0xB23FE4; + ac.setFan(kCoolixFanMax); + EXPECT_EQ(on_fan_18c_fan_max, ac.getRaw()); + + // Test sending the last message to verify the class send() method works. + ac.send(); + ac._irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(COOLIX, ac._irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, ac._irsend.capture.bits); + EXPECT_EQ(on_fan_18c_fan_max, ac._irsend.capture.value); + EXPECT_EQ(0x0, ac._irsend.capture.address); + EXPECT_EQ(0x0, ac._irsend.capture.command); + EXPECT_EQ( + // Raw data supplied by @mariusmotea + "f38000d50" + // 4434,4376, + "m4480s4480" + // 566,1614,592,504,566,1618,566,1616,568,528,564,532,564,1616,568,532, + "m560s1680m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560" + // 566,530,566,1620,568,528,566,530,566,1618,564,1618,566,530,564,1624, + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680" + // 538,560,566,530,564,1620,566,1618,566,1618,566,1616,566,1616,566,1620, + "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680" + // 568,1620,566,1616,566,530,566,530,564,530,562,532,564,530,566,530, + "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560" + // 566,1622,566,1616,540,1642,566,528,566,530,566,1616,566,530,566,532, + "m560s1680m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s560" + // 564,532,564,530,566,530,566,1614,566,1616,562,532,564,1620,566,1618, + "m560s560m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s1680" + // 538,5254,4432,4364,566,1616,568,530,564,1620,568,1616,564,532,564,530, + "m560s5040m4480s4480m560s1680m560s560m560s1680m560s1680m560s560m560s560" + // 566,1616,566,532,564,532,566,1620,568,528,566,530,566,1616,564,1618, + "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s1680m560s1680" + // 566,530,566,1622,566,532,566,528,566,1620,568,1614,566,1618,566,1618, + "m560s560m560s1680m560s560m560s560m560s1680m560s1680m560s1680m560s1680" + // 566,1614,568,1618,566,1622,568,1616,566,530,564,530,566,530,566,528, + "m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560" + // 564,530,566,532,566,1622,564,1616,566,1616,564,532,564,530,564,1616, + "m560s560m560s560m560s1680m560s1680m560s1680m560s560m560s560m560s1680" + // 564,530,564,532,566,530,564,530,566,528,564,1618,564,1618,564,532, + "m560s560m560s560m560s560m560s560m560s560m560s1680m560s1680m560s560" + // 564,1620,566,1618,562 // Raw data matches what is expected. + "m560s1680m560s1680m560s5040", ac._irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Daikin_test.cpp similarity index 79% rename from lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Daikin_test.cpp index 67d144d54..6ed27fff8 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Daikin_test.cpp @@ -1,5 +1,6 @@ -// Copyright 2017 David Conran +// Copyright 2017-2019 David Conran #include "ir_Daikin.h" +#include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" #include "IRsend.h" @@ -540,33 +541,47 @@ TEST(TestDaikinClass, OnOffTimers) { ASSERT_FALSE(ac.getPowerful()); } -// Test Eye mode. -TEST(TestDaikinClass, EyeSetting) { +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. ac.setEcono(false); - ac.setEye(false); - ASSERT_FALSE(ac.getEye()); + ac.setWeeklyTimerEnable(false); + ASSERT_FALSE(ac.getWeeklyTimerEnable()); EXPECT_FALSE(ac.getEcono()); - ac.setEye(true); - ASSERT_TRUE(ac.getEye()); + ac.setWeeklyTimerEnable(true); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); EXPECT_FALSE(ac.getEcono()); ac.setEcono(false); - ASSERT_TRUE(ac.getEye()); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); EXPECT_FALSE(ac.getEcono()); ac.setEcono(true); - ASSERT_TRUE(ac.getEye()); + ASSERT_TRUE(ac.getWeeklyTimerEnable()); EXPECT_TRUE(ac.getEcono()); - ac.setEye(false); - ASSERT_FALSE(ac.getEye()); + ac.setWeeklyTimerEnable(false); + ASSERT_FALSE(ac.getWeeklyTimerEnable()); EXPECT_TRUE(ac.getEcono()); + + // Tests with real data from: + // https://github.com/crankyoldgit/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. @@ -614,13 +629,6 @@ TEST(TestDaikinClass, SensorSetting) { ASSERT_FALSE(ac.getSensor()); } -TEST(TestDaikinClass, RenderTime) { - EXPECT_EQ("0:00", IRDaikinESP::renderTime(0)); - EXPECT_EQ("0:10", IRDaikinESP::renderTime(10)); - EXPECT_EQ("1:00", IRDaikinESP::renderTime(1 * 60 + 0)); - EXPECT_EQ("23:59", IRDaikinESP::renderTime(23 * 60 + 59)); -} - TEST(TestDaikinClass, SetAndGetRaw) { IRDaikinESP ac(0); uint8_t shortState[kDaikinStateLengthShort] = { @@ -683,30 +691,33 @@ TEST(TestDaikinClass, HumanReadable) { 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, " + "Power: On, Mode: 4 (HEAT), Temp: 15C, Fan: 11 (Quiet), " + "Powerful: Off, Quiet: Off, Sensor: Off, Mold: Off, " "Comfort: Off, Swing (Horizontal): Off, Swing (Vertical): Off, " - "Current Time: 0:00, On Time: Off, Off Time: Off", + "Current Time: 00: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.setEye(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, Comfort: On, " + "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 10 (Auto), " + "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", + "Current Time: 09:15, Current Day: WED, On Time: 08:00, Off Time: 17:30, " + "Weekly Timer: Off", ac.toString()); } @@ -855,10 +866,11 @@ TEST(TestDecodeDaikin, RealExample) { 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, Eye: Off, Mold: Off, Comfort: Off, " + "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, On Time: 21:30, Off Time: 6:10", ac.toString()); + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 06:10, Weekly Timer: On", ac.toString()); } // Decoding a message we entirely constructed based solely on a given state. @@ -887,10 +899,11 @@ TEST(TestDecodeDaikin, ShortSyntheticExample) { 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, Eye: Off, Mold: Off, Comfort: Off, " + "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, On Time: 21:30, Off Time: 6:10", ac.toString()); + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 06:10, Weekly Timer: On", ac.toString()); } // Decoding a message we entirely constructed based solely on a given state. @@ -915,10 +928,11 @@ TEST(TestDecodeDaikin, LongSyntheticExample) { 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, Eye: Off, Mold: Off, Comfort: Off, " + "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, On Time: 21:30, Off Time: 6:10", ac.toString()); + "Current Time: 22:18, Current Day: (UNKNOWN), " + "On Time: 21:30, Off Time: 06:10, Weekly Timer: On", ac.toString()); } // Test decoding a message captured from a real IR remote. @@ -932,7 +946,7 @@ TEST(TestDecodeDaikin2, RealExample) { 0x80, 0x04, 0xB0, 0x16, 0x24, 0x00, 0x00, 0xBE, 0xD5, 0xF5, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x08, 0x26, 0x00, 0xA0, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC1, 0x80, 0x60, 0xE7}; - // "Off" Data from https://github.com/markszabo/IRremoteESP8266/issues/582 + // "Off" Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/582 uint16_t rawData[633] = { // Data supplied by @sheppy99 10024, 25180, 3494, 1732, 436, 1300, 436, 436, 432, 438, 430, 438, 426, 1306, 430, 442, 430, 438, 428, 440, 430, 440, 430, 1304, @@ -1428,9 +1442,9 @@ TEST(TestDaikin2Class, HumanReadable) { ac.setPurify(true); ac.setEcono(false); EXPECT_EQ( - "Power: On, Mode: 3 (COOL), Temp: 21C, Fan: 5 (Max), " + "Power: On, Mode: 3 (COOL), Temp: 21C, Fan: 5 (High), " "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 12:34, " - "On Time: Off, Off Time: 20:00, Sleep Time: 4:00, Beep: 2 (Loud), " + "On Time: Off, Off Time: 20:00, Sleep Time: 04:00, Beep: 2 (Loud), " "Light: 2 (Dim), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " "Eye Auto: On, Quiet: Off, Powerful: On, Purify: On, Econo: Off", ac.toString()); @@ -1443,9 +1457,9 @@ TEST(TestDaikin2Class, HumanReadable) { ac.setCurrentTime(23 * 60 + 45); // 23:45 ac.enableOnTimer(9 * 60 + 11); // 9:11 EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 32C, Fan: 1 (Min), " + "Power: On, Mode: 4 (HEAT), Temp: 32C, Fan: 1 (Low), " "Swing (V): 14 (Auto), Swing (H): 191 (Swing), Clock: 23:45, " - "On Time: 9:11, Off Time: 20:00, Sleep Time: Off, Beep: 1 (Quiet), " + "On Time: 09:11, Off Time: 20:00, Sleep Time: Off, Beep: 1 (Quiet), " "Light: 1 (Bright), Mold: On, Clean: Off, Fresh Air: On, Eye: On, " "Eye Auto: On, Quiet: On, Powerful: Off, Purify: On, Econo: Off", ac.toString()); @@ -1498,17 +1512,25 @@ 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_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN)); + + ASSERT_EQ("DAIKIN160", typeToString(decode_type_t::DAIKIN160)); + ASSERT_EQ(decode_type_t::DAIKIN160, strToDecodeType("DAIKIN160")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN160)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN160)); ASSERT_EQ("DAIKIN2", typeToString(decode_type_t::DAIKIN2)); ASSERT_EQ(decode_type_t::DAIKIN2, strToDecodeType("DAIKIN2")); ASSERT_TRUE(hasACState(decode_type_t::DAIKIN2)); + ASSERT_TRUE(IRac::isProtocolSupported(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)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN216)); } -// https://github.com/markszabo/IRremoteESP8266/issues/582#issuecomment-453863879 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/582#issuecomment-453863879 TEST(TestDecodeDaikin2, Issue582DeepDecodeExample) { IRDaikin2 ac(0); @@ -1524,10 +1546,10 @@ TEST(TestDecodeDaikin2, Issue582DeepDecodeExample) { ASSERT_TRUE(ac.getPurify()); EXPECT_EQ( "Power: On, Mode: 0 (AUTO), Temp: 19C, Fan: 10 (Auto), " - "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 9:20, On Time: Off, " - "Off Time: Off, Sleep Time: Off, Beep: 3 (Off), Light: 3 (Off), " - "Mold: On, Clean: On, Fresh Air: Off, Eye: On, Eye Auto: Off, " - "Quiet: Off, Powerful: Off, Purify: On, Econo: Off", + "Swing (V): 14 (Auto), Swing (H): 190 (Auto), Clock: 09:20, " + "On Time: Off, Off Time: Off, Sleep Time: Off, Beep: 3 (Off), " + "Light: 3 (Off), Mold: On, Clean: On, Fresh Air: Off, Eye: On, " + "Eye Auto: Off, Quiet: Off, Powerful: Off, Purify: On, Econo: Off", ac.toString()); } @@ -1677,7 +1699,6 @@ TEST(TestDaikin216Class, OperatingMode) { EXPECT_EQ(kDaikinAuto, ac.getMode()); } - TEST(TestDaikin216Class, VaneSwing) { IRDaikin216 ac(0); ac.begin(); @@ -1744,15 +1765,34 @@ TEST(TestDaikin216Class, FanSpeed) { EXPECT_EQ(kDaikinFanQuiet, ac.getFan()); } -TEST(TestDaikin216Class, Quiet) { +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()); @@ -1761,22 +1801,23 @@ TEST(TestDaikin216Class, Quiet) { TEST(TestDaikin216Class, ExampleStates) { IRDaikin216 ac(0); ac.begin(); - // https://github.com/markszabo/IRremoteESP8266/pull/690#issuecomment-487770194 + // https://github.com/crankyoldgit/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", + "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 + // https://github.com/crankyoldgit/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, @@ -1789,18 +1830,19 @@ TEST(TestDaikin216Class, ReconstructKnownState) { 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", + "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 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 TEST(TestDecodeDaikin216, RealExample) { IRsendTest irsend(0); IRrecv irrecv(0); - // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + // https://github.com/crankyoldgit/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, @@ -1852,16 +1894,17 @@ TEST(TestDecodeDaikin216, RealExample) { 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", + "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 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/689 TEST(TestDecodeDaikin216, SyntheticExample) { IRsendTest irsend(0); IRrecv irrecv(0); - // https://github.com/markszabo/IRremoteESP8266/issues/689#issue-438086949 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/689#issue-438086949 uint8_t expectedState[kDaikin216StateLength] = { // 8 bytes 0x11, 0xDA, 0x27, 0xF0, 0x00, 0x00, 0x00, 0x02, @@ -1878,3 +1921,370 @@ TEST(TestDecodeDaikin216, SyntheticExample) { ASSERT_EQ(kDaikin216Bits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); } + +TEST(TestDaikinClass, toCommon) { + IRDaikinESP ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setEcono(false); + ac.setMold(true); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDaikin2Class, toCommon) { + IRDaikin2 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(kDaikin2SwingVHigh + 3); + ac.setSwingHorizontal(kDaikin2SwingHAuto); + ac.setQuiet(false); + ac.setPowerful(true); + ac.setEcono(false); + ac.setMold(true); + ac.setLight(true); + ac.setPurify(true); + ac.setBeep(true); + ac.enableSleepTimer(6 * 60); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN2, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().light); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_TRUE(ac.toCommon().beep); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kMiddle, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_EQ(6 * 60, ac.toCommon().sleep); + // Unsupported. + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDaikin216Class, toCommon) { + IRDaikin216 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(true); + ac.setSwingHorizontal(true); + ac.setQuiet(false); + ac.setPowerful(true); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN216, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_TRUE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + // Unsupported. + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/731 +TEST(TestDecodeDaikin160, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t rawData[327] = { + 5024, 2144, 342, 1786, 344, 706, 342, 706, 344, 706, 342, 1786, 342, 706, + 342, 708, 342, 708, 342, 708, 342, 1786, 342, 708, 342, 1786, 342, 1788, + 342, 708, 342, 1786, 344, 1786, 342, 1786, 342, 1786, 342, 1786, 342, 708, + 342, 708, 340, 1786, 344, 706, 342, 708, 342, 706, 342, 708, 342, 708, + 342, 706, 342, 1786, 342, 1788, 342, 1786, 342, 1786, 342, 1788, 342, 706, + 342, 1788, 342, 1786, 342, 706, 342, 706, 342, 708, 342, 708, 342, 708, + 342, 708, 342, 706, 342, 708, 342, 708, 342, 706, 342, 706, 342, 708, 342, + 1786, 342, 1786, 342, 1786, 342, 1788, 342, 706, 342, 706, 342, 706, 342, + 708, 342, 29442, 5022, 2146, 342, 1786, 342, 706, 342, 708, 342, 708, 342, + 1788, 342, 706, 342, 706, 342, 706, 342, 706, 342, 1788, 342, 708, 342, + 1786, 342, 1786, 342, 708, 342, 1786, 342, 1788, 342, 1786, 342, 1786, + 342, 1786, 342, 706, 342, 706, 342, 1786, 344, 706, 342, 706, 344, 706, + 342, 706, 342, 706, 342, 708, 342, 706, 342, 706, 342, 706, 342, 706, 342, + 1786, 342, 1786, 342, 706, 342, 706, 342, 1788, 342, 708, 342, 1786, 342, + 1786, 342, 706, 342, 706, 342, 706, 342, 708, 342, 1786, 342, 1786, 342, + 708, 342, 708, 342, 1786, 342, 706, 342, 706, 344, 706, 342, 1786, 342, + 708, 342, 706, 342, 706, 342, 708, 342, 706, 342, 708, 342, 706, 342, 708, + 342, 706, 342, 704, 344, 706, 342, 706, 344, 706, 342, 706, 342, 708, 342, + 706, 342, 706, 344, 706, 342, 706, 342, 706, 344, 1786, 342, 1786, 342, + 1786, 342, 1786, 342, 706, 342, 706, 344, 706, 342, 706, 342, 1786, 344, + 706, 342, 1786, 342, 706, 342, 708, 342, 706, 342, 706, 344, 706, 342, + 706, 342, 706, 342, 1786, 342, 706, 344, 706, 342, 706, 342, 708, 342, + 706, 342, 1788, 342, 1786, 342, 706, 344, 1786, 344, 706, 344, 1786, 342, + 708, 342}; // UNKNOWN 99CC993 + + uint8_t expectedState[kDaikin160StateLength] = { + // 7 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x0D, 0x00, 0x0F, + // 13 bytes + 0x11, 0xDA, 0x27, 0x00, 0xD3, 0x30, 0x11, + 0x00, 0x00, 0x1E, 0x0A, 0x08, 0x56}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 327, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN160, irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRDaikin160 ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ("Power: Off, Mode: 3 (COOL), Temp: 25C, Fan: 10 (Auto), " + "Vent Position (V): 1 (Lowest)", ac.toString()); +} + +TEST(TestDecodeDaikin160, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + uint8_t expectedState[kDaikin160StateLength] = { + // 7 bytes + 0x11, 0xDA, 0x27, 0xF0, 0x0D, 0x00, 0x0F, + // 13 bytes + 0x11, 0xDA, 0x27, 0x00, 0xD3, 0x30, 0x11, + 0x00, 0x00, 0x1E, 0x0A, 0x08, 0x56}; + + irsend.begin(); + irsend.reset(); + irsend.sendDaikin160(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN160, irsend.capture.decode_type); + ASSERT_EQ(kDaikin160Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDaikin160Class, toCommon) { + IRDaikin160 ac(0); + ac.setPower(true); + ac.setMode(kDaikinCool); + ac.setTemp(20); + ac.setFan(kDaikinFanMax); + ac.setSwingVertical(kDaikin160SwingVAuto); + // Now test it. + ASSERT_EQ(decode_type_t::DAIKIN160, 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); + // Unsupported. + 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().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); +} + +TEST(TestDaikin160Class, FanSpeed) { + IRDaikin160 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(TestDaikin160Class, VaneSwing) { + IRDaikin160 ac(0); + ac.begin(); + + ac.setSwingVertical(kDaikin160SwingVAuto); + EXPECT_EQ(kDaikin160SwingVAuto, ac.getSwingVertical()); + + ac.setSwingVertical(kDaikin160SwingVHigh); + EXPECT_EQ(kDaikin160SwingVHigh, ac.getSwingVertical()); + + ac.setSwingVertical(255); + EXPECT_EQ(kDaikin160SwingVAuto, ac.getSwingVertical()); + + EXPECT_EQ(kDaikin160SwingVHighest, + IRDaikin160::convertSwingV(stdAc::swingv_t::kHighest)); + EXPECT_EQ(kDaikin160SwingVLowest, + IRDaikin160::convertSwingV(stdAc::swingv_t::kLowest)); + EXPECT_EQ(kDaikin160SwingVMiddle, + IRDaikin160::convertSwingV(stdAc::swingv_t::kMiddle)); +} + +TEST(TestDaikin160Class, Power) { + IRDaikin160 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(TestDaikin160Class, Temperature) { + IRDaikin160 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(TestDaikin160Class, OperatingMode) { + IRDaikin160 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(TestDaikin160Class, HumanReadable) { + IRDaikin160 ac(0); + + EXPECT_EQ( + "Power: Off, Mode: 3 (COOL), Temp: 25C, Fan: 10 (Auto), " + "Vent Position (V): 1 (Lowest)", + ac.toString()); + ac.setMode(kDaikinAuto); + ac.setTemp(19); + ac.setFan(kDaikinFanMin); + ac.setSwingVertical(kDaikin160SwingVAuto); + ac.setPower(true); + EXPECT_EQ( + "Power: On, Mode: 0 (AUTO), Temp: 19C, Fan: 1 (Low), " + "Vent Position (V): 15 (Auto)", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Denon_test.cpp similarity index 91% rename from lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Denon_test.cpp index 6b80eae02..bfb554bb5 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Denon_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Denon_test.cpp @@ -25,7 +25,7 @@ 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" @@ -45,7 +45,7 @@ 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" @@ -61,7 +61,7 @@ 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" @@ -90,7 +90,7 @@ 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" @@ -110,7 +110,7 @@ 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" @@ -185,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); @@ -208,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); @@ -235,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); @@ -256,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); @@ -281,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-2.6.0/test/ir_Dish_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Dish_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Dish_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Dish_test.cpp diff --git a/lib/IRremoteESP8266-2.6.3.10/test/ir_Electra_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Electra_test.cpp new file mode 100644 index 000000000..581d40561 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Electra_test.cpp @@ -0,0 +1,250 @@ +// Copyright 2018, 2019 David Conran + +#include "ir_Electra.h" +#include +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for sendElectraAC(). + +// Test sending typical data only. +TEST(TestSendElectraAC, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + uint8_t data[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; + + irsend.sendElectraAC(data); + EXPECT_EQ( + "f38000d50" + "m9166s4470" + "m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647m646s1647" + "m646s1647m646s1647m646s1647m646s547m646s547m646s547m646s547m646s1647" + "m646s547m646s1647m646s1647m646s547m646s1647m646s1647m646s1647m646s1647" + "m646s547m646s547m646s547m646s1647m646s547m646s1647m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s1647m646s1647m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s1647m646s547m646s547" + "m646s547m646s547m646s547m646s547m646s547m646s547m646s547m646s547" + "m646s1647m646s547m646s1647m646s547m646s547m646s547m646s547m646s547" + "m646s1647m646s547m646s1647m646s1647m646s547m646s547m646s547m646s547" + "m646s100000", + irsend.outputStr()); +} + +// Tests for decodeElectraAC(). +// Decode normal ElectraAC messages. + +TEST(TestDecodeElectraAC, SyntheticDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Synthesised Normal ElectraAC message. + irsend.reset(); + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; + irsend.sendElectraAC(expectedState); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(ELECTRA_AC, irsend.capture.decode_type); + EXPECT_EQ(kElectraAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +// Decode a recorded example +TEST(TestDecodeElectraAC, RealExampleDecode) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + // Real ElectraAC message. + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/527 + uint16_t rawData[211] = { + 9166, 4470, 642, 1632, 642, 1632, 668, 534, 666, 534, 668, 534, + 614, 536, 640, 1636, 640, 1646, 694, 1662, 612, 1628, 642, 1666, + 664, 532, 668, 534, 666, 534, 666, 532, 666, 1644, 642, 532, + 640, 1634, 668, 1632, 642, 538, 666, 1660, 610, 1666, 664, 1632, + 642, 1672, 610, 536, 666, 534, 694, 532, 666, 1636, 614, 538, + 666, 1632, 642, 536, 666, 544, 692, 534, 640, 558, 640, 534, + 640, 540, 666, 534, 638, 1666, 638, 1636, 640, 550, 666, 534, + 640, 540, 666, 534, 640, 540, 666, 536, 638, 540, 666, 536, + 638, 550, 664, 536, 638, 540, 664, 536, 638, 540, 666, 534, + 638, 1640, 664, 536, 692, 546, 664, 536, 664, 536, 664, 536, + 664, 546, 612, 532, 636, 538, 664, 536, 664, 546, 612, 538, + 638, 538, 638, 538, 664, 536, 690, 538, 662, 538, 664, 538, + 662, 548, 664, 536, 662, 538, 662, 562, 638, 564, 636, 564, + 636, 1668, 582, 556, 652, 572, 612, 568, 636, 564, 610, 570, + 636, 556, 616, 550, 656, 566, 610, 570, 632, 578, 608, 1640, + 662, 562, 642, 1686, 582, 570, 634, 566, 604, 576, 636, 566, + 610, 578, 634, 1664, 584, 590, 660, 1636, 610, 1642, 664, 590, + 610, 590, 636, 566, 634, 568, 686}; // UNKNOWN 9AD8CDB5 + uint8_t expectedState[kElectraAcStateLength] = {0xC3, 0x87, 0xF6, 0x28, 0x60, + 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x05, 0x0D}; + + irsend.reset(); + irsend.sendRaw(rawData, 211, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(ELECTRA_AC, irsend.capture.decode_type); + ASSERT_EQ(kElectraAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): Off", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +TEST(TestIRElectraAcClass, Power) { + IRElectraAc 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(TestIRElectraAcClass, OperatingMode) { + IRElectraAc ac(0); + ac.begin(); + + ac.setMode(kElectraAcAuto); + EXPECT_EQ(kElectraAcAuto, ac.getMode()); + + ac.setMode(kElectraAcCool); + EXPECT_EQ(kElectraAcCool, ac.getMode()); + + ac.setMode(kElectraAcHeat); + EXPECT_EQ(kElectraAcHeat, ac.getMode()); + + ac.setMode(kElectraAcDry); + EXPECT_EQ(kElectraAcDry, ac.getMode()); + + ac.setMode(kElectraAcFan); + EXPECT_EQ(kElectraAcFan, ac.getMode()); + + ac.setMode(kElectraAcHeat + 1); + EXPECT_EQ(kElectraAcAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kElectraAcAuto, ac.getMode()); +} + +TEST(TestIRElectraAcClass, SetAndGetTemp) { + IRElectraAc ac(0); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kElectraAcMinTemp); + EXPECT_EQ(kElectraAcMinTemp, ac.getTemp()); + ac.setTemp(kElectraAcMinTemp - 1); + EXPECT_EQ(kElectraAcMinTemp, ac.getTemp()); + ac.setTemp(kElectraAcMaxTemp); + EXPECT_EQ(kElectraAcMaxTemp, ac.getTemp()); + ac.setTemp(kElectraAcMaxTemp + 1); + EXPECT_EQ(kElectraAcMaxTemp, ac.getTemp()); +} + +TEST(TestIRElectraAcClass, FanSpeed) { + IRElectraAc ac(0); + ac.begin(); + + ac.setFan(0); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(kElectraAcFanHigh); + EXPECT_EQ(kElectraAcFanHigh, ac.getFan()); + + ac.setFan(std::max(kElectraAcFanHigh, kElectraAcFanLow) + 1); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(kElectraAcFanHigh - 1); + EXPECT_EQ(kElectraAcFanAuto, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); +} + +TEST(TestIRElectraAcClass, toCommon) { + IRElectraAc ac(0); + ac.setPower(true); + ac.setMode(kElectraAcCool); + ac.setTemp(20); + ac.setFan(kElectraAcFanHigh); + ac.setSwingV(true); + ac.setSwingH(true); + // Now test it. + ASSERT_EQ(decode_type_t::ELECTRA_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().filter); + 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().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestIRElectraAcClass, HumanReadable) { + IRElectraAc ac(0); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/778#issue-460052080 + uint8_t on_cool_32C_auto_voff[13] = { + 0xC3, 0xC7, 0xE0, 0x00, 0xA0, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x40, 0x8A}; + ac.setRaw(on_cool_32C_auto_voff); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 32C, Fan: 5 (Auto), " + "Swing(V): Off, Swing(H): Off", ac.toString()); + uint8_t on_cool_16C_auto_voff[13] = { + 0xC3, 0x47, 0xE0, 0x00, 0xA0, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x41, 0x0B}; + ac.setRaw(on_cool_16C_auto_voff); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 5 (Auto), " + "Swing(V): Off, Swing(H): Off", ac.toString()); + uint8_t on_cool_16C_low_voff[13] = { + 0xC3, 0x47, 0xE0, 0x00, 0x60, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x41, 0xCB}; + ac.setRaw(on_cool_16C_low_voff); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): Off", ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.3.10/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Fujitsu_test.cpp new file mode 100644 index 000000000..b6613906f --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Fujitsu_test.cpp @@ -0,0 +1,798 @@ +// Copyright 2017 Jonny Graham, David Conran + +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "ir_Fujitsu.h" +#include "gtest/gtest.h" + +// Tests for Fujitsu A/C methods. + +// Test sending typical data only. +TEST(TestIRFujitsuACClass, GetRawDefault) { + IRFujitsuAC ac(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_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, 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 ac(0); + ac.setModel(ARRAH2E); + ac.off(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: Off, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Swing: Vert + Horiz, Command: N/A", + ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: Off, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Command: N/A", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepHoriz) { + IRFujitsuAC ac(0); + ac.stepHoriz(); + uint8_t expected[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x79, 0x86}; + EXPECT_STATE_EQ(expected, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ( + "Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 24C, " + "Fan: 1 (High), Swing: Vert + Horiz, Command: Step vane horizontally", + ac.toString()); +} + +TEST(TestIRFujitsuACClass, GetRawStepVert) { + IRFujitsuAC ac(0); + ac.setModel(ARRAH2E); + ac.stepVert(); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x6C, 0x93}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), 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_STATE_EQ(expected_ardb1, ac.getRaw(), 6 * 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, + 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 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_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 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 + ac.setModel(ARRAH2E); + uint8_t expected_arrah2e[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x40, 0x03, 0x22, 0x00, 0x00, 0x00, 0x20, 0x4B}; + EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 3 (FAN), Temp: 20C, " + "Fan: 2 (Medium), Swing: Horiz, Command: N/A", ac.toString()); + + ac.setModel(ARDB1); + uint8_t expected_ardb1[15] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x40, 0x03, 0x02, 0x00, 0x00, 0x00, 0x8B}; + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_STATE_EQ(expected_ardb1, ac.getRaw(), ac.getStateLength() * 8); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 3 (FAN), Temp: 20C, " + "Fan: 2 (Medium), Command: N/A", ac.toString()); +} + +TEST(TestIRFujitsuACClass, SetRaw) { + 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_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}; + 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 ac(0); + IRsendTest irsend(0); + ac.begin(); + irsend.begin(); + + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingBoth); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); + + EXPECT_EQ(kFujitsuAcFanHigh, ac.getFanSpeed()); + EXPECT_EQ(kFujitsuAcModeCool, ac.getMode()); + EXPECT_EQ(24, ac.getTemp()); + EXPECT_EQ(kFujitsuAcSwingBoth, ac.getSwing()); + EXPECT_EQ(kFujitsuAcCmdStayOn, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLength); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s8100", + irsend.outputStr()); +} + +TEST(TestSendFujitsuAC, GenerateShortMessage) { + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); + irsend.begin(); + + ac.off(); + + EXPECT_EQ(kFujitsuAcCmdTurnOff, ac.getCmd()); + + irsend.reset(); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + "m3324s1574m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448" + "s390m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s1182m448" + "s1182m448s1182m448s1182m448s1182m448s1182m448s8100", + irsend.outputStr()); +} + +// Issue #275 +TEST(TestSendFujitsuAC, Issue275) { + IRFujitsuAC ac(0); + IRsendTest irsend(0); + ac.begin(); + irsend.begin(); + irsend.reset(); + + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), kFujitsuAcStateLengthShort); + EXPECT_EQ( + "f38000d50" + // Header + "m3324s1574" + // 0 0 1 0 1 0 0 0 (0x28) + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + // 1 1 0 0 0 1 1 0 (0xC6) + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + // 0 0 0 0 0 0 0 0 (0x00) + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 0 0 0 1 0 0 0 (0x08) + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + // 0 1 0 0 0 0 0 0 (0x40) + "m448s390m448s1182m448s390m448s390m448s390m448s390m448s390m448s390" + // 1 0 1 1 1 1 1 1 (0xBF) + "m448s1182m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + // Footer + "m448s8100", irsend.outputStr()); + + irsend.reset(); + // Per report in Issue #275 + uint16_t off[115] = { + 3350, 1650, + 450, 400, 450, 450, 450, 1250, 450, 400, 450, 1250, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, + 450, 400, 450, 1250, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 1250, + 450, 400, 450, 1250, 450, 1250, 450, 1250, 450, 1250, 450, 1250, + 450, 1250, 450}; + irsend.sendRaw(off, 115, 38); + EXPECT_EQ( + "f38000d50" + // Header + "m3350s1650" + // 0 0 1 0 1 0 0 0 (0x28) + "m450s400m450s450m450s1250m450s400m450s1250m450s400m450s400m450s400" + // 1 1 0 0 0 1 1 0 (0xC6) + "m450s1250m450s1250m450s400m450s400m450s400m450s1250m450s1250m450s400" + // 0 0 0 0 0 0 0 0 (0x00) + "m450s400m450s400m450s400m450s400m450s400m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 0 0 0 1 0 0 0 (0x08) + "m450s400m450s400m450s400m450s400m450s1250m450s400m450s400m450s400" + // 0 1 0 0 0 0 0 0 (0x40) + "m450s400m450s1250m450s400m450s400m450s400m450s400m450s400m450s400" + // 1 0 1 1 1 1 1 1 (0xBF) + "m450s1250m450s400m450s1250m450s1250m450s1250m450s1250m450s1250m450s1250" + // Footer + "m450", + irsend.outputStr()); +} + +TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { + IRsendTest irsend(0); + IRFujitsuAC ac(0); + IRrecv irrecv(0); + + irsend.begin(); + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits + 8, irsend.capture.bits); + uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + + irsend.reset(); + + ac.setModel(ARDB1); + ac.setCmd(kFujitsuAcCmdTurnOff); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected_ardb1[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { + IRsendTest irsend(0); + IRFujitsuAC ac(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + + ac.setModel(ARRAH2E); + ac.setCmd(kFujitsuAcCmdStayOn); + ac.setSwing(kFujitsuAcSwingVert); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setTemp(18); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + ASSERT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decodeFujitsuAC(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + uint8_t expected_arrah2e[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x20, 0x01, 0x14, 0x00, 0x00, 0x00, 0x20, 0x7B}; + EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (Quiet), Swing: Vert, Command: N/A", ac.toString()); + + irsend.reset(); + + ac.setModel(ARDB1); + irsend.sendFujitsuAC(ac.getRaw(), ac.getStateLength()); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected_ardb1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x20, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAB}; + EXPECT_STATE_EQ(expected_ardb1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), 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 ac(0); + + irsend.begin(); + + irsend.reset(); + // "Off" Message recorded from an AR-DB1 remote. + uint16_t rawData[99] = { + 3310, 1636, 440, 386, 440, 394, 442, 1210, 442, 390, 414, 1220, + 444, 390, 446, 380, 446, 380, 436, 1216, 438, 1214, 438, 388, + 438, 386, 438, 396, 410, 1222, 440, 1220, 442, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 442, 382, 444, 380, 446, 380, + 446, 380, 444, 380, 436, 390, 436, 388, 436, 388, 438, 1214, + 438, 386, 438, 388, 438, 386, 440, 386, 440, 384, 442, 384, + 442, 384, 442, 1210, 444, 382, 444, 382, 444, 382, 444, 380, + 446, 1206, 436, 390, 436, 388, 436, 388, 438, 388, 438, 396, + 420, 388, 436}; + irsend.sendRaw(rawData, 99, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcMinBits, irsend.capture.bits); + uint8_t expected[6] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02}; + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLengthShort - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), 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 ac(0); + + irsend.begin(); + irsend.reset(); + uint16_t rawData1[243] = { + 3316, 1632, 444, 390, 438, 388, 436, 1216, 438, 388, 438, 1214, + 438, 388, 438, 386, 440, 386, 440, 1212, 440, 1210, 442, 392, + 412, 396, 442, 392, 444, 1208, 444, 1208, 444, 380, 444, 380, + 446, 380, 436, 390, 436, 390, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 438, 386, 440, 384, 440, 1210, + 442, 384, 442, 382, 442, 384, 442, 384, 442, 382, 442, 382, + 444, 382, 444, 1208, 444, 382, 444, 380, 446, 380, 436, 390, + 436, 390, 436, 1214, 438, 1214, 438, 1212, 440, 1212, 440, 1220, + 412, 1222, 440, 394, 442, 382, 442, 382, 444, 1208, 444, 382, + 444, 380, 446, 380, 446, 380, 434, 390, 436, 388, 438, 388, + 438, 388, 438, 1214, 438, 1212, 440, 386, 440, 394, 412, 1222, + 440, 394, 442, 384, 442, 384, 442, 382, 442, 1208, 444, 390, + 414, 394, 442, 1216, 446, 380, 436, 390, 436, 390, 436, 388, + 436, 390, 436, 388, 438, 386, 440, 386, 440, 386, 438, 1212, + 440, 386, 440, 384, 440, 384, 442, 392, 412, 396, 440, 394, + 442, 382, 444, 382, 444, 382, 444, 380, 444, 380, 444, 382, + 444, 380, 446, 380, 436, 388, 436, 390, 436, 388, 438, 388, + 438, 388, 438, 388, 438, 386, 440, 386, 440, 386, 442, 384, + 440, 386, 442, 384, 440, 384, 442, 384, 442, 382, 442, 382, + 444, 1208, 444, 382, 444, 1208, 444, 380, 446, 1206, 436, 390, + 436, 1216, 436}; + irsend.sendRaw(rawData1, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected1[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x21, 0x01, 0x04, 0x00, 0x00, 0x00, 0xAA}; + EXPECT_STATE_EQ(expected1, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), Power: On, Mode: 1 (COOL), Temp: 18C, " + "Fan: 4 (Quiet), Command: N/A", ac.toString()); + + irsend.reset(); + uint16_t rawData2[243] = { + 3316, 1630, 436, 398, 438, 386, 438, 1212, 440, 384, 440, 1212, + 442, 384, 442, 392, 414, 394, 442, 1218, 446, 1206, 436, 390, + 436, 388, 438, 388, 438, 1214, 440, 1212, 440, 384, 442, 384, + 442, 384, 442, 382, 444, 382, 444, 382, 444, 380, 446, 380, + 444, 380, 436, 390, 436, 388, 438, 396, 418, 388, 438, 1232, + 410, 396, 440, 394, 442, 384, 442, 384, 442, 382, 442, 392, + 414, 392, 444, 1216, 446, 380, 436, 390, 436, 396, 418, 390, + 436, 398, 438, 1214, 440, 1212, 440, 1210, 442, 1208, 444, 1216, + 416, 1218, 444, 388, 436, 390, 436, 388, 438, 1214, 440, 386, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 382, + 444, 382, 444, 1206, 446, 1206, 436, 390, 436, 388, 438, 388, + 438, 386, 440, 394, 410, 396, 440, 1220, 442, 1210, 442, 392, + 414, 394, 442, 1218, 446, 406, 410, 388, 436, 390, 436, 390, + 436, 388, 438, 386, 440, 386, 440, 386, 440, 386, 440, 384, + 442, 384, 442, 384, 442, 382, 444, 382, 444, 380, 446, 380, + 446, 380, 436, 390, 436, 390, 436, 388, 438, 386, 438, 388, + 438, 386, 440, 386, 440, 384, 442, 384, 442, 384, 442, 384, + 442, 382, 444, 382, 444, 380, 446, 380, 446, 380, 436, 390, + 436, 388, 436, 388, 438, 386, 438, 386, 440, 386, 440, 1212, + 440, 1210, 442, 1210, 442, 1208, 444, 1208, 436, 390, 436, 388, + 436, 1214, 440}; + irsend.sendRaw(rawData2, 243, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits - 8, irsend.capture.bits); + uint8_t expected2[kFujitsuAcStateLength - 1] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFC, 0x08, 0x30, + 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x9F}; + EXPECT_STATE_EQ(expected2, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength - 1, ac.getStateLength()); + EXPECT_EQ("Model: 2 (ARDB1), 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 ac(0); + + // Capture as supplied by arpmota + uint16_t rawData[259] = {3352, 1574, 480, 350, 480, 346, 480, 1190, 458, 346, + 508, 1140, 480, 346, 506, 346, 458, 346, 480, 1168, 480, 1192, 452, 374, + 458, 346, 480, 346, 508, 1168, 480, 1140, 480, 346, 506, 346, 458, 346, + 480, 346, 480, 346, 480, 346, 484, 372, 454, 374, 456, 346, 508, 318, + 480, 374, 458, 374, 480, 318, 480, 1196, 452, 346, 480, 346, 484, 342, + 484, 346, 480, 374, 458, 346, 506, 318, 508, 1170, 452, 346, 480, 374, + 458, 346, 506, 318, 480, 1196, 452, 1190, 458, 1162, 480, 1196, 452, + 1170, 480, 1190, 458, 1164, 480, 1196, 480, 318, 508, 346, 456, 1192, + 480, 346, 456, 374, 452, 346, 480, 374, 458, 342, 484, 346, 508, 346, + 456, 342, 512, 1164, 458, 1164, 508, 346, 456, 346, 480, 1190, 456, 342, + 484, 346, 506, 346, 456, 374, 452, 346, 508, 346, 458, 1164, 508, 346, + 458, 374, 452, 1168, 480, 374, 480, 318, 480, 374, 456, 346, 508, 318, + 480, 346, 484, 374, 480, 318, 484, 342, 484, 374, 480, 318, 484, 342, + 484, 346, 508, 318, 508, 346, 458, 346, 506, 318, 480, 374, 458, 346, + 506, 318, 480, 346, 484, 374, 480, 318, 482, 372, 456, 346, 508, 318, + 506, 348, 456, 342, 484, 346, 508, 318, 484, 374, 480, 318, 508, 318, + 484, 346, 508, 318, 480, 374, 456, 346, 508, 346, 480, 318, 480, 346, + 484, 374, 480, 320, 484, 1164, 508, 346, 458, 342, 512, 1164, 458, 1190, + 454, 346, 484, 1164, 508, 346, 458, 1164, 480, 350, 480, 374, 480}; + uint8_t state[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x81, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x2B}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 259, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_EQ("Model: 1 (ARRAH2E), 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(); + irsend.sendFujitsuAC(state, 16); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(FUJITSU_AC, irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s390m448s390m448s1182m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s1182m448s390m448s390" + "m448s1182m448s1182m448s390m448s1182m448s390m448s1182m448s390m448s390" + "m448s8100", irsend.outputStr()); +} + +TEST(TestIRFujitsuACClass, toCommon) { + IRFujitsuAC ac(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/crankyoldgit/IRremoteESP8266/issues/716#issuecomment-495852309 + uint8_t off[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x2F}; + uint8_t on[kFujitsuAcStateLength] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xAF}; + // Make sure we can't accidentally inherit the correct model. + ac.setModel(fujitsu_ac_remote_model_t::ARDB1); + ac.setRaw(off, kFujitsuAcStateLength); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); + EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); + EXPECT_FALSE(ac.getOutsideQuiet()); + // We can really only tell the difference between ARRAH2E & ARREB1E if + // the option is set. Otheriwse they appear the same. + EXPECT_EQ( + "Model: 1 (ARRAH2E), 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-2.6.0/test/ir_GICable_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_GICable_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_GICable_test.cpp index bad9bbded..234a748b5 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_GICable_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_GICable_test.cpp @@ -102,7 +102,7 @@ TEST(TestDecodeGICable, RealExampleDecodeOK) { irsend.begin(); // Real GICable "OK/Select" message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/447 uint16_t rawData[39] = {9064, 4408, 580, 4408, 580, 2152, 578, 2150, 580, 2150, 580, 4408, 580, 2150, 580, 2150, 580, 2150, 580, 2150, 580, 2150, 580, 2150, @@ -125,7 +125,7 @@ TEST(TestDecodeGICable, RealExampleDecodeLEFT) { irsend.begin(); // Real GICable "LEFT" message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/447 uint16_t rawData[39] = {9040, 4434, 554, 2176, 580, 4408, 554, 4434, 582, 2148, 554, 4434, 580, 4408, 556, 2174, 580, 2150, 580, 2150, 582, 2148, 556, 2176, @@ -149,7 +149,7 @@ TEST(TestDecodeGICable, RealExampleDecodeZEROKey) { // Real GICable "Zero Key" message. // Note: Zero key looks similar to a JVC message, hence this test. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/447 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/447 uint16_t rawData[39] = {9036, 4434, 552, 2178, 552, 2178, 552, 2180, 550, 2178, 552, 2178, 550, 2180, 552, 2178, 552, 2178, 550, 2180, 552, 2178, 526, 2204, diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_GlobalCache_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_GlobalCache_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_GlobalCache_test.cpp diff --git a/lib/IRremoteESP8266-2.6.3.10/test/ir_Goodweather_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Goodweather_test.cpp new file mode 100644 index 000000000..58f42f0fe --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/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/crankyoldgit/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-2.6.0/test/ir_Gree_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Gree_test.cpp similarity index 85% rename from lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Gree_test.cpp index a05b06391..def4949c7 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Gree_test.cpp @@ -338,6 +338,50 @@ TEST(TestGreeClass, Turbo) { EXPECT_TRUE(irgree.getTurbo()); } +TEST(TestGreeClass, IFeel) { + IRGreeAC ac(0); + ac.begin(); + + ac.setIFeel(true); + EXPECT_TRUE(ac.getIFeel()); + + ac.setIFeel(false); + EXPECT_FALSE(ac.getIFeel()); + + ac.setIFeel(true); + EXPECT_TRUE(ac.getIFeel()); + + // https://github.com/crankyoldgit/IRremoteESP8266/pull/770#issuecomment-504992209 + uint8_t on[8] = {0x08, 0x09, 0x60, 0x50, 0x00, 0x44, 0x00, 0xF0}; + uint8_t off[8] = {0x08, 0x09, 0x60, 0x50, 0x00, 0x40, 0x00, 0xF0}; + ac.setRaw(off); + EXPECT_FALSE(ac.getIFeel()); + ac.setRaw(on); + EXPECT_TRUE(ac.getIFeel()); +} + +TEST(TestGreeClass, WiFi) { + IRGreeAC ac(0); + ac.begin(); + + ac.setWiFi(true); + EXPECT_TRUE(ac.getWiFi()); + + ac.setWiFi(false); + EXPECT_FALSE(ac.getWiFi()); + + ac.setWiFi(true); + EXPECT_TRUE(ac.getWiFi()); + + // https://github.com/crankyoldgit/IRremoteESP8266/pull/770#issuecomment-504992209 + uint8_t on[8] = {0x09, 0x09, 0x60, 0x50, 0x00, 0x40, 0x00, 0x00}; + uint8_t off[8] = {0x09, 0x09, 0x60, 0x50, 0x00, 0x00, 0x00, 0xC0}; + ac.setRaw(off); + EXPECT_FALSE(ac.getWiFi()); + ac.setRaw(on); + EXPECT_TRUE(ac.getWiFi()); +} + TEST(TestGreeClass, Sleep) { IRGreeAC irgree(0); irgree.begin(); @@ -451,9 +495,9 @@ TEST(TestGreeClass, HumanReadable) { IRGreeAC irgree(0); EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), Turbo: Off, " - "XFan: Off, Light: On, Sleep: Off, Swing Vertical Mode: Manual, " - "Swing Vertical Pos: 0 (Last Pos)", + "Power: Off, Mode: 0 (AUTO), Temp: 25C, Fan: 0 (Auto), Turbo: Off, " + "IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " + "Swing Vertical Mode: Manual, Swing Vertical Pos: 0 (Last Pos)", irgree.toString()); irgree.on(); irgree.setMode(kGreeCool); @@ -463,11 +507,13 @@ TEST(TestGreeClass, HumanReadable) { irgree.setSleep(true); irgree.setLight(false); irgree.setTurbo(true); + irgree.setIFeel(true); + irgree.setWiFi(true); irgree.setSwingVertical(true, kGreeSwingAuto); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (MAX), Turbo: On, " - "XFan: On, Light: Off, Sleep: On, Swing Vertical Mode: Auto, " - "Swing Vertical Pos: 1 (Auto)", + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 3 (High), Turbo: On, " + "IFeel: On, WiFi: On, XFan: On, Light: Off, Sleep: On, " + "Swing Vertical Mode: Auto, Swing Vertical Pos: 1 (Auto)", irgree.toString()); } @@ -501,7 +547,7 @@ TEST(TestDecodeGree, NormalRealExample) { uint8_t gree_code[kGreeStateLength] = {0x19, 0x0A, 0x60, 0x50, 0x02, 0x23, 0x00, 0xF0}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/386 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/386 uint16_t rawData[139] = { 9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, 556, 650, 584, 626, 560, 644, 580, 628, 1680, 624, 560, @@ -525,8 +571,41 @@ TEST(TestDecodeGree, NormalRealExample) { EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits); irgree.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 1, Turbo: Off, " - "XFan: Off, Light: On, Sleep: Off, Swing Vertical Mode: Manual, " - "Swing Vertical Pos: 2", + "Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 1 (Low), Turbo: Off, " + "IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " + "Swing Vertical Mode: Manual, 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-2.6.0/test/ir_Haier_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Haier_test.cpp similarity index 90% rename from lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Haier_test.cpp index 8d306cb0b..03cc63f93 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Haier_test.cpp @@ -343,24 +343,11 @@ TEST(TestHaierACClass, Timers) { EXPECT_EQ(kHaierAcCmdTimerCancel, haier.getCommand()); } -TEST(TestHaierACClass, TimeToString) { - EXPECT_EQ("00:00", IRHaierAC::timeToString(0)); - EXPECT_EQ("00:01", IRHaierAC::timeToString(1)); - EXPECT_EQ("00:10", IRHaierAC::timeToString(10)); - EXPECT_EQ("00:59", IRHaierAC::timeToString(59)); - - EXPECT_EQ("01:00", IRHaierAC::timeToString(60)); - EXPECT_EQ("01:01", IRHaierAC::timeToString(61)); - EXPECT_EQ("01:59", IRHaierAC::timeToString(60 + 59)); - EXPECT_EQ("18:59", IRHaierAC::timeToString(18 * 60 + 59)); - EXPECT_EQ("23:59", IRHaierAC::timeToString(23 * 60 + 59)); -} - TEST(TestHaierACClass, MessageConstuction) { IRHaierAC haier(0); EXPECT_EQ( - "Command: 1 (On), Mode: 0 (AUTO), Temp: 25C, Fan: 0 (AUTO), " + "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", haier.toString()); @@ -368,7 +355,7 @@ TEST(TestHaierACClass, MessageConstuction) { haier.setTemp(21); haier.setFan(kHaierAcFanHigh); EXPECT_EQ( - "Command: 3 (Fan), Mode: 1 (COOL), Temp: 21C, Fan: 3 (MAX), " + "Command: 3 (Fan), Mode: 1 (COOL), Temp: 21C, Fan: 3 (High), " "Swing: 0 (Off), Sleep: Off, Health: Off, " "Current Time: 00:00, On Timer: Off, Off Timer: Off", haier.toString()); @@ -377,7 +364,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 (High), " "Swing: 3 (Chg), Sleep: On, Health: On, " "Current Time: 10:15, On Timer: Off, Off Timer: Off", haier.toString()); @@ -386,7 +373,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 (Medium), " "Swing: 3 (Chg), Sleep: On, Health: On, " "Current Time: 10:15, On Timer: 13:20, Off Timer: 18:45", haier.toString()); @@ -394,20 +381,20 @@ TEST(TestHaierACClass, MessageConstuction) { // Now change a few already set things. haier.setMode(kHaierAcHeat); EXPECT_EQ( - "Command: 2 (Mode), Mode: 3 (HEAT), Temp: 21C, Fan: 2, " + "Command: 2 (Mode), Mode: 3 (HEAT), Temp: 21C, Fan: 2 (Medium), " "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, " + "Command: 6 (Temp Up), Mode: 3 (HEAT), Temp: 25C, Fan: 2 (Medium), " "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. @@ -419,7 +406,7 @@ TEST(TestHaierACClass, MessageConstuction) { EXPECT_FALSE(IRHaierAC::validChecksum(randomState)); haier.setRaw(randomState); EXPECT_EQ( - "Command: 9 (Timer Set), Mode: 3 (HEAT), Temp: 20C, Fan: 2, " + "Command: 9 (Timer Set), Mode: 3 (HEAT), Temp: 20C, Fan: 2 (Medium), " "Swing: 1 (Up), Sleep: On, Health: Off, " "Current Time: 16:32, On Timer: Off, Off Timer: Off", haier.toString()); @@ -683,7 +670,7 @@ TEST(TestHaierACYRW02Class, MessageConstuction) { IRHaierACYRW02 haier(0); EXPECT_EQ( - "Power: On, Button: 5 (Power), Mode: 0 (Auto), Temp: 25C," + "Power: On, Button: 5 (Power), Mode: 0 (AUTO), Temp: 25C," " Fan: 10 (Auto), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off," " Health: On", haier.toString()); @@ -691,7 +678,7 @@ TEST(TestHaierACYRW02Class, MessageConstuction) { haier.setTemp(21); haier.setFan(kHaierAcYrw02FanHigh); EXPECT_EQ( - "Power: On, Button: 4 (Fan), Mode: 2 (Cool), Temp: 21C," + "Power: On, Button: 4 (Fan), Mode: 2 (COOL), Temp: 21C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off," " Health: On", haier.toString()); @@ -701,7 +688,7 @@ TEST(TestHaierACYRW02Class, MessageConstuction) { haier.setSleep(true); haier.setTurbo(kHaierAcYrw02TurboHigh); EXPECT_EQ( - "Power: On, Button: 8 (Turbo), Mode: 2 (Cool), Temp: 21C," + "Power: On, Button: 8 (Turbo), Mode: 2 (COOL), Temp: 21C," " Fan: 2 (High), Turbo: 1 (High), Swing: 2 (Middle)," " Sleep: On, Health: Off", haier.toString()); @@ -716,7 +703,7 @@ TEST(TestHaierACYRW02Class, RealStates) { IRHaierACYRW02 haier(0); haier.setRaw(expectedState1); EXPECT_EQ( - "Power: On, Button: 7 (Health), Mode: 8 (Heat), Temp: 30C," + "Power: On, Button: 7 (Health), Mode: 8 (HEAT), Temp: 30C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 1 (Top), Sleep: Off," " Health: Off", haier.toString()); @@ -726,7 +713,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75}; haier.setRaw(expectedState2); EXPECT_EQ( - "Power: Off, Button: 5 (Power), Mode: 8 (Heat), Temp: 30C," + "Power: Off, Button: 5 (Power), Mode: 8 (HEAT), Temp: 30C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off," " Health: Off", haier.toString()); @@ -736,7 +723,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2B}; haier.setRaw(expectedState3); EXPECT_EQ( - "Power: On, Button: 1 (Temp Down), Mode: 2 (Cool), Temp: 16C," + "Power: On, Button: 1 (Temp Down), Mode: 2 (COOL), Temp: 16C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off," " Health: On", haier.toString()); @@ -747,7 +734,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x20, 0x80, 0x00, 0x00, 0x00, 0x0B, 0xD7}; haier.setRaw(expectedState4); EXPECT_EQ( - "Power: On, Button: 11 (Sleep), Mode: 2 (Cool), Temp: 25C," + "Power: On, Button: 11 (Sleep), Mode: 2 (COOL), Temp: 25C," " Fan: 10 (Auto), Turbo: 0 (Off), Swing: 12 (Auto), Sleep: On," " Health: On", haier.toString()); @@ -758,7 +745,7 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x20, 0x80, 0x00, 0x00, 0x00, 0x04, 0x85}; haier.setRaw(expectedState5); EXPECT_EQ( - "Power: On, Button: 4 (Fan), Mode: 2 (Cool), Temp: 25C," + "Power: On, Button: 4 (Fan), Mode: 2 (COOL), Temp: 25C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 12 (Auto), Sleep: On," " Health: On", haier.toString()); @@ -831,7 +818,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()); @@ -873,7 +860,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()); @@ -915,7 +902,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()); @@ -985,14 +972,14 @@ TEST(TestDecodeHaierAC_YRW02, RealExample) { IRHaierACYRW02 haier(0); haier.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Button: 5 (Power), Mode: 2 (Cool), Temp: 17C," + "Power: On, Button: 5 (Power), Mode: 2 (COOL), Temp: 17C," " Fan: 2 (High), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off," " Health: On", haier.toString()); } // Default state of the remote needed to include hidden data. -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/668 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/668 TEST(TestHaierAcIssues, Issue668) { IRHaierAC ac(0); IRHaierAC acText(1); @@ -1002,13 +989,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 + // https://github.com/crankyoldgit/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(); @@ -1039,11 +1027,11 @@ 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: - // https://github.com/markszabo/IRremoteESP8266/issues/668#issuecomment-483531895 + // https://github.com/crankyoldgit/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()); @@ -1063,7 +1051,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()); @@ -1078,3 +1066,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-2.6.0/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Hitachi_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Hitachi_test.cpp index a2471c4aa..7e7935638 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Hitachi_test.cpp @@ -300,7 +300,7 @@ TEST(TestIRHitachiAcClass, HumanReadable) { ac.setFan(kHitachiAcFanHigh); ac.setSwingVertical(true); EXPECT_EQ( - "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (HIGH), " + "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (High), " "Swing (Vertical): On, Swing (Horizontal): Off", ac.toString()); ac.setMode(kHitachiAcCool); @@ -309,7 +309,7 @@ TEST(TestIRHitachiAcClass, HumanReadable) { ac.setSwingVertical(false); ac.setSwingHorizontal(true); EXPECT_EQ( - "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 2 (LOW), " + "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 2 (Low), " "Swing (Vertical): Off, Swing (Horizontal): On", ac.toString()); } @@ -376,7 +376,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample1) { 0x20, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xAC}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 // 'On' '16c' 'auto fan' 'cooling mode' uint16_t rawData[451] = { 3318, 1720, 400, 1276, 400, 432, 398, 434, 398, 434, 400, 432, @@ -428,7 +428,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample1) { IRHitachiAc ac(0); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 1 (AUTO), " + "Power: On, Mode: 4 (COOL), Temp: 16C, Fan: 1 (Auto), " "Swing (Vertical): Off, Swing (Horizontal): Off", ac.toString()); } @@ -444,7 +444,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample2) { 0xC0, 0x02, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xD0}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 // 'On' '32c' 'auto fan' 'heating mode' uint16_t rawData[451] = { 3322, 1718, 400, 1278, 398, 432, 402, 430, 400, 430, 402, 430, @@ -496,7 +496,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample2) { IRHitachiAc ac(0); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (HIGH), " + "Power: On, Mode: 3 (HEAT), Temp: 32C, Fan: 5 (High), " "Swing (Vertical): Off, Swing (Horizontal): Off", ac.toString()); } @@ -543,7 +543,7 @@ TEST(TestDecodeHitachiAC1, NormalRealExample) { 0x61, 0x84, 0x00, 0x00, 0x00, 0x00, 0x10, 0x98}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/453 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/453 uint16_t rawData[211] = { 3400, 3350, 450, 1250, 450, 400, 400, 1300, 400, 1300, 400, 400, 450, 400, 400, 1300, 400, 400, 400, 1300, 400, 400, 450, 1250, @@ -685,7 +685,7 @@ TEST(TestDecodeHitachiAC2, NormalRealExample) { 0x01, 0xFE, 0xC0, 0x3F, 0x80, 0x7F, 0x11, 0xEE, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/417 uint16_t rawData[851] = { // ON - 32c cool (fan auto) 3432, 1654, 492, 1180, 488, 360, 486, 360, 486, 360, 486, 362, @@ -768,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-2.6.3.10/test/ir_Inax_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Inax_test.cpp new file mode 100644 index 000000000..b182cce32 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/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-2.6.0/test/ir_JVC_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_JVC_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_JVC_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_JVC_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Kelvinator_test.cpp similarity index 93% rename from lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Kelvinator_test.cpp index 38a298e58..436336c04 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Kelvinator_test.cpp @@ -422,7 +422,7 @@ TEST(TestKelvinatorClass, HumanReadable) { IRKelvinatorAC irkelv(0); EXPECT_EQ( - "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (AUTO), Turbo: Off, " + "Power: Off, Mode: 0 (AUTO), Temp: 16C, Fan: 0 (Auto), Turbo: Off, " "Quiet: Off, XFan: Off, IonFilter: Off, Light: Off, " "Swing (Horizontal): Off, Swing (Vertical): Off", irkelv.toString()); @@ -435,7 +435,7 @@ TEST(TestKelvinatorClass, HumanReadable) { irkelv.setLight(true); irkelv.setSwingHorizontal(true); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 5 (MAX), Turbo: Off, " + "Power: On, Mode: 1 (COOL), Temp: 25C, Fan: 5 (High), Turbo: Off, " "Quiet: Off, XFan: On, IonFilter: On, Light: On, " "Swing (Horizontal): On, Swing (Vertical): Off", irkelv.toString()); @@ -520,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-2.6.0/test/ir_LG_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_LG_test.cpp similarity index 99% rename from lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_LG_test.cpp index 2925494b9..d1e1b8659 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_LG_test.cpp @@ -429,7 +429,7 @@ TEST(TestDecodeLG2, RealLG2Example) { } // Tests for issue reported in -// https://github.com/markszabo/IRremoteESP8266/issues/620 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/620 TEST(TestDecodeLG, Issue620) { IRsendTest irsend(0); IRrecv irrecv(0); diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Lasertag_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Lasertag_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Lasertag_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Lego_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Lego_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Lego_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Lutron_test.cpp similarity index 98% rename from lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Lutron_test.cpp index d682967ca..81cf0df9c 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Lutron_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Lutron_test.cpp @@ -120,7 +120,7 @@ TEST(TestDecodeLutron, DocumentedExampleFullOff) { irsend.begin(); // Full Off code. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/515 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/515 uint16_t rawData[14] = {20518, 6839, 2280, 6839, 2280, 2280, 9119, 2280, 2280, 6839, 2280, 4560, 2280, 11399}; irsend.reset(); diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_MWM_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_MWM_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_MWM_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Magiquest_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Magiquest_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Magiquest_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Midea_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Midea_test.cpp index ced3ea10c..b5e133713 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Midea_test.cpp @@ -445,7 +445,7 @@ TEST(TestMideaACClass, HumanReadableOutput) { midea.setRaw(0xA1826FFFFF62); EXPECT_EQ( - "Power: On, Mode: 2 (AUTO), Temp: 25C/77F, Fan: 0 (AUTO), " + "Power: On, Mode: 2 (AUTO), Temp: 25C/77F, Fan: 0 (Auto), " "Sleep: Off", midea.toString()); midea.off(); @@ -453,12 +453,14 @@ TEST(TestMideaACClass, HumanReadableOutput) { midea.setFan(kMideaACFanHigh); midea.setMode(kMideaACDry); midea.setSleep(true); - EXPECT_EQ("Power: Off, Mode: 1 (DRY), Temp: 16C/62F, Fan: 3 (HI), Sleep: On", - midea.toString()); + EXPECT_EQ( + "Power: Off, Mode: 1 (DRY), Temp: 16C/62F, Fan: 3 (High), Sleep: On", + midea.toString()); midea.setRaw(0xA19867FFFF7E); - EXPECT_EQ("Power: On, Mode: 0 (COOL), Temp: 20C/69F, Fan: 3 (HI), Sleep: Off", - midea.toString()); + EXPECT_EQ( + "Power: On, Mode: 0 (COOL), Temp: 20C/69F, Fan: 3 (High), Sleep: Off", + midea.toString()); } // Tests for decodeMidea(). @@ -656,3 +658,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-2.6.0/test/ir_MitsubishiHeavy_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_MitsubishiHeavy_test.cpp similarity index 87% rename from lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_MitsubishiHeavy_test.cpp index 340a04078..0af6b5d07 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_MitsubishiHeavy_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_MitsubishiHeavy_test.cpp @@ -293,7 +293,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { IRMitsubishiHeavy152Ac ac(0); EXPECT_EQ( - "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "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()); @@ -310,7 +310,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (Max), " + "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()); @@ -327,7 +327,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftMax); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 8 (Turbo), " + "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()); @@ -338,7 +338,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setSwingVertical(kMitsubishiHeavy152SwingVOff); EXPECT_EQ( - "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 6 (Econo), " + "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()); @@ -349,7 +349,7 @@ TEST(TestMitsubishiHeavy152AcClass, HumanReadable) { ac.setMode(kMitsubishiHeavyDry); ac.setSwingHorizontal(kMitsubishiHeavy152SwingHLeftRight); EXPECT_EQ( - "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "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()); @@ -359,7 +359,7 @@ TEST(TestMitsubishiHeavy152AcClass, ReconstructKnownExample) { IRMitsubishiHeavy152Ac ac(0); EXPECT_EQ( - "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "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()); @@ -377,7 +377,7 @@ TEST(TestMitsubishiHeavy152AcClass, ReconstructKnownExample) { ac.setSwingVertical(kMitsubishiHeavy152SwingVAuto); ac.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 24C, Fan: 4 (Max), " + "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()); @@ -635,7 +635,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { IRMitsubishiHeavy88Ac ac(0); EXPECT_EQ( - "Power: Off, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "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()); @@ -648,7 +648,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.set3D(true); ac.setSwingVertical(kMitsubishiHeavy88SwingVAuto); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 4 (High), " + "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()); @@ -662,7 +662,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftMax); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 6 (Turbo), " + "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()); @@ -673,7 +673,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.setSwingVertical(kMitsubishiHeavy88SwingVOff); EXPECT_EQ( - "Power: On, Mode: 0 (Auto), Temp: 31C, Fan: 7 (Econo), " + "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()); @@ -684,7 +684,7 @@ TEST(TestMitsubishiHeavy88AcClass, HumanReadable) { ac.setMode(kMitsubishiHeavyDry); ac.setSwingHorizontal(kMitsubishiHeavy88SwingHLeftRight); EXPECT_EQ( - "Power: On, Mode: 2 (Dry), Temp: 25C, Fan: 0 (Auto), " + "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()); @@ -704,7 +704,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + // Ref: https://github.com/crankyoldgit/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, @@ -739,7 +739,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample) { 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), " + "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()); @@ -766,7 +766,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsSyntheticExample) { 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), " + "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()); @@ -784,7 +784,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample2) { 0xF8, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x80, 0x7F}; - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/660#issuecomment-480571466 + // Ref: https://github.com/crankyoldgit/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, @@ -819,7 +819,7 @@ TEST(TestDecodeMitsubishiHeavy, ZmsRealExample2) { 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), " + "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()); @@ -844,8 +844,77 @@ TEST(TestDecodeMitsubishiHeavy, ZjsSyntheticExample) { 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), " + "Power: On, Mode: 2 (DRY), Temp: 25C, Fan: 0 (Auto), " "Swing (V): 0 (Off), Swing (H): 72 (Left Right), Turbo: Off, Econo: Off, " "3D: Off, Clean: Off", ac.toString()); } + +TEST(TestMitsubishiHeavy152AcClass, toCommon) { + IRMitsubishiHeavy152Ac ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(20); + ac.setFan(kMitsubishiHeavy152FanLow); + ac.setSwingVertical(kMitsubishiHeavy152SwingVHighest); + ac.setSwingHorizontal(kMitsubishiHeavy152SwingHRightMax); + ac.setTurbo(false); + ac.setEcono(true); + ac.setClean(true); + ac.setFilter(true); + ac.setSilent(true); + ac.setNight(true); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_HEAVY_152, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kRightMax, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().filter); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestMitsubishiHeavy88AcClass, toCommon) { + IRMitsubishiHeavy88Ac ac(0); + ac.setPower(true); + ac.setMode(kMitsubishiHeavyCool); + ac.setTemp(20); + ac.setFan(kMitsubishiHeavy88FanLow); + ac.setSwingVertical(kMitsubishiHeavy88SwingVHighest); + ac.setSwingHorizontal(kMitsubishiHeavy88SwingHRightMax); + ac.setTurbo(false); + ac.setEcono(true); + ac.setClean(true); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI_HEAVY_88, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kRightMax, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().econo); + ASSERT_TRUE(ac.toCommon().clean); + // Unsupported. + ASSERT_FALSE(ac.toCommon().quiet); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Mitsubishi_test.cpp similarity index 97% rename from lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Mitsubishi_test.cpp index 6c9480b31..a5045cf49 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Mitsubishi_test.cpp @@ -984,7 +984,7 @@ TEST(TestDecodeMitsubishiAC, DecodeRealExampleRepeatNeededButError) { TEST(TestMitsubishiACClass, HumanReadable) { IRMitsubishiAC irMitsu(0); EXPECT_EQ( - "Power: On (HEAT), Temp: 22C, FAN: SILENT, VANE: AUTO, " + "Power: On, Mode: 8 (HEAT), Temp: 22C, Fan: 6 (Quiet), Vane: AUTO, " "Time: 17:10, On timer: 00:00, Off timer: 00:00, Timer: -", irMitsu.toString()); } @@ -1133,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-2.6.0/test/ir_NEC_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_NEC_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_NEC_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_NEC_test.cpp diff --git a/lib/IRremoteESP8266-2.6.3.10/test/ir_Neoclima_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Neoclima_test.cpp new file mode 100644 index 000000000..38747f276 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Neoclima_test.cpp @@ -0,0 +1,434 @@ +// Copyright 2019 David Conran (crankyoldgit) + +#include "ir_Neoclima.h" +#include +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "gtest/gtest.h" + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("NEOCLIMA", typeToString(decode_type_t::NEOCLIMA)); + ASSERT_EQ(decode_type_t::NEOCLIMA, strToDecodeType("NEOCLIMA")); + ASSERT_TRUE(hasACState(decode_type_t::NEOCLIMA)); +} + +// Test sending typical data only. +TEST(TestSendNeoclima, SendDataOnly) { + IRsendTest irsend(0); + irsend.begin(); + + uint8_t state[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + irsend.reset(); + irsend.sendNeoclima(state); + EXPECT_EQ( + "f38000d50" + "m6112s7391" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s1651m537s571m537s1651m537s571m537s1651m537s1651m537s571" + "m537s571m537s571m537s571m537s571m537s571m537s571m537s571m537s571" + "m537s571m537s1651m537s571m537s1651m537s571m537s1651m537s571m537s571" + "m537s1651m537s571m537s1651m537s571m537s571m537s1651m537s571m537s1651" + "m537s1651m537s571m537s571m537s1651m537s1651m537s1651m537s571m537s571" + "m537s7391" + "m537s100000", + irsend.outputStr()); +} + +// https://github.com/crankyoldgit/IRremoteESP8266/issues/764#issuecomment-503755096 +TEST(TestDecodeNeoclima, RealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + uint16_t rawData[197] = { + 6112, 7392, 540, 602, 516, 578, 522, 604, 540, 554, 540, 554, 540, 576, + 518, 576, 516, 554, 540, 608, 542, 554, 540, 554, 540, 576, 518, 604, 516, + 556, 540, 576, 546, 580, 542, 578, 542, 602, 518, 554, 542, 554, 568, 582, + 540, 554, 540, 582, 540, 578, 518, 582, 542, 576, 544, 530, 566, 534, 562, + 534, 562, 552, 542, 582, 540, 604, 518, 608, 542, 554, 540, 582, 540, 604, + 518, 580, 540, 606, 544, 554, 542, 554, 542, 580, 542, 576, 520, 554, 540, + 578, 518, 578, 518, 582, 544, 552, 570, 580, 544, 580, 542, 554, 542, 604, + 520, 576, 520, 580, 540, 556, 540, 556, 542, 584, 566, 580, 542, 1622, + 542, 554, 542, 1620, 544, 604, 520, 1642, 518, 1674, 548, 560, 564, 580, + 544, 554, 544, 552, 544, 554, 542, 556, 542, 576, 522, 554, 542, 556, 542, + 580, 542, 1670, 520, 578, 520, 1622, 542, 580, 518, 1646, 520, 558, 568, + 552, 546, 1628, 566, 580, 544, 1668, 522, 576, 520, 578, 520, 1670, 522, + 576, 522, 1670, 496, 1676, 570, 560, 566, 532, 564, 1648, 544, 1670, 522, + 1650, 544, 552, 544, 576, 520, 7390, 544}; // UNKNOWN EE182D95 + + uint8_t expectedState[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 197, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::NEOCLIMA, irsend.capture.decode_type); + ASSERT_EQ(kNeoclimaBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + IRNeoclimaAc ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 26C, Fan: 3 (Low), " + "Swing(V): Off, Swing(H): On, Sleep: Off, Turbo: Off, Hold: Off, " + "Ion: Off, Eye: Off, Light: Off, Follow: Off, 8C Heat: Off, Fresh: Off, " + "Button: 0 (Power)", + ac.toString()); +} + +// Self decode. +TEST(TestDecodeNeoclima, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + + uint8_t expectedState[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + + irsend.begin(); + irsend.reset(); + irsend.sendNeoclima(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::NEOCLIMA, irsend.capture.decode_type); + ASSERT_EQ(kNeoclimaBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); +} + +TEST(TestIRNeoclimaAcClass, Power) { + IRNeoclimaAc 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()); + + EXPECT_EQ(kNeoclimaButtonPower, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, OperatingMode) { + IRNeoclimaAc ac(0); + ac.begin(); + + ac.setMode(kNeoclimaAuto); + EXPECT_EQ(kNeoclimaAuto, ac.getMode()); + EXPECT_EQ(kNeoclimaButtonMode, ac.getButton()); + + + ac.setMode(kNeoclimaCool); + EXPECT_EQ(kNeoclimaCool, ac.getMode()); + + ac.setMode(kNeoclimaHeat); + EXPECT_EQ(kNeoclimaHeat, ac.getMode()); + + ASSERT_NE(kNeoclimaFanHigh, kNeoclimaFanLow); + ac.setFan(kNeoclimaFanHigh); + ac.setMode(kNeoclimaDry); // Dry should lock the fan to speed LOW. + EXPECT_EQ(kNeoclimaDry, ac.getMode()); + EXPECT_EQ(kNeoclimaFanLow, ac.getFan()); + ac.setFan(kNeoclimaFanHigh); + EXPECT_EQ(kNeoclimaFanLow, ac.getFan()); + + ac.setMode(kNeoclimaFan); + EXPECT_EQ(kNeoclimaFan, ac.getMode()); + + ac.setMode(kNeoclimaHeat + 1); + EXPECT_EQ(kNeoclimaAuto, ac.getMode()); + + ac.setMode(255); + EXPECT_EQ(kNeoclimaAuto, ac.getMode()); +} + +TEST(TestIRNeoclimaAcClass, SetAndGetTemp) { + IRNeoclimaAc ac(0); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kNeoclimaMinTemp); + EXPECT_EQ(kNeoclimaMinTemp, ac.getTemp()); + EXPECT_EQ(kNeoclimaButtonTempDown, ac.getButton()); + ac.setTemp(kNeoclimaMinTemp - 1); + EXPECT_EQ(kNeoclimaMinTemp, ac.getTemp()); + ac.setTemp(kNeoclimaMaxTemp); + EXPECT_EQ(kNeoclimaMaxTemp, ac.getTemp()); + EXPECT_EQ(kNeoclimaButtonTempUp, ac.getButton()); + ac.setTemp(kNeoclimaMaxTemp + 1); + EXPECT_EQ(kNeoclimaMaxTemp, ac.getTemp()); +} + +TEST(TestIRNeoclimaAcClass, FanSpeed) { + IRNeoclimaAc ac(0); + ac.begin(); + + ac.setFan(0); + EXPECT_EQ(0, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kNeoclimaFanAuto, ac.getFan()); + + ac.setFan(kNeoclimaFanHigh); + EXPECT_EQ(kNeoclimaFanHigh, ac.getFan()); + + ac.setFan(std::max(kNeoclimaFanHigh, kNeoclimaFanLow) + 1); + EXPECT_EQ(kNeoclimaFanAuto, ac.getFan()); + + ac.setFan(kNeoclimaFanHigh - 1); + EXPECT_EQ(kNeoclimaFanHigh - 1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(1); + EXPECT_EQ(1, ac.getFan()); + + ac.setFan(3); + EXPECT_EQ(3, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + + // Data from: + // https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + uint8_t fan_low[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x6A, 0x00, 0x29, 0xA5, 0x3D}; + uint8_t fan_medium[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x29, 0xA5, 0x1D}; + uint8_t fan_high[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x29, 0xA5, 0xFD}; + uint8_t fan_auto[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x29, 0xA5, 0xDD}; + ac.setRaw(fan_low); + EXPECT_EQ(kNeoclimaFanLow, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + ac.setRaw(fan_medium); + EXPECT_EQ(kNeoclimaFanMed, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + ac.setRaw(fan_high); + EXPECT_EQ(kNeoclimaFanHigh, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); + ac.setRaw(fan_auto); + EXPECT_EQ(kNeoclimaFanAuto, ac.getFan()); + EXPECT_EQ(kNeoclimaButtonFanSpeed, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Sleep) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + EXPECT_EQ(kNeoclimaButtonSleep, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Turbo) { + IRNeoclimaAc 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()); + EXPECT_EQ(kNeoclimaButtonTurbo, ac.getButton()); + // Data from: + // https://drive.google.com/file/d/1tA09Gu_ZqDcHucscnqzv0V3cIUWOE0d1/view + uint8_t turbo_on[12] = { + 0x00, 0x00, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x6A, 0x00, 0x88, 0xA5, 0xA9}; + uint8_t turbo_off[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x6A, 0x00, 0x88, 0xA5, 0xA1}; + ac.setRaw(turbo_on); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_EQ(kNeoclimaButtonTurbo, ac.getButton()); + ac.setRaw(turbo_off); + EXPECT_EQ(kNeoclimaButtonTurbo, ac.getButton()); + EXPECT_FALSE(ac.getTurbo()); +} + +TEST(TestIRNeoclimaAcClass, Fresh) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setFresh(true); + EXPECT_TRUE(ac.getFresh()); + ac.setFresh(false); + EXPECT_FALSE(ac.getFresh()); + ac.setFresh(true); + EXPECT_TRUE(ac.getFresh()); + EXPECT_EQ(kNeoclimaButtonFresh, ac.getButton()); + // Data from: + // https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view + uint8_t on[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x6A, 0x00, 0x29, 0xA5, 0xCD}; + uint8_t off[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x6A, 0x00, 0x29, 0xA5, 0x4D}; + ac.setRaw(on); + EXPECT_TRUE(ac.getFresh()); + EXPECT_EQ(kNeoclimaButtonFresh, ac.getButton()); + ac.setRaw(off); + EXPECT_EQ(kNeoclimaButtonFresh, ac.getButton()); + EXPECT_FALSE(ac.getFresh()); +} + +TEST(TestIRNeoclimaAcClass, Hold) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setHold(true); + EXPECT_TRUE(ac.getHold()); + ac.setHold(false); + EXPECT_FALSE(ac.getHold()); + ac.setHold(true); + EXPECT_TRUE(ac.getHold()); + EXPECT_EQ(kNeoclimaButtonHold, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, 8CHeat) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.set8CHeat(true); + EXPECT_TRUE(ac.get8CHeat()); + ac.set8CHeat(false); + EXPECT_FALSE(ac.get8CHeat()); + ac.set8CHeat(true); + EXPECT_TRUE(ac.get8CHeat()); + EXPECT_EQ(kNeoclimaButton8CHeat, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Light) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + EXPECT_EQ(kNeoclimaButtonLight, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Ion) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + ac.setIon(false); + EXPECT_FALSE(ac.getIon()); + ac.setIon(true); + EXPECT_TRUE(ac.getIon()); + EXPECT_EQ(kNeoclimaButtonIon, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Eye) { + IRNeoclimaAc ac(0); + ac.begin(); + ac.setEye(true); + EXPECT_TRUE(ac.getEye()); + ac.setEye(false); + EXPECT_FALSE(ac.getEye()); + ac.setEye(true); + EXPECT_TRUE(ac.getEye()); + EXPECT_EQ(kNeoclimaButtonEye, ac.getButton()); +} + +TEST(TestIRNeoclimaAcClass, Follow) { + IRNeoclimaAc ac(0); + ac.begin(); + /* DISABLED: See TODO in ir_Neoclima.cpp + ac.setFollow(true); + EXPECT_TRUE(ac.getFollow()); + ac.setFollow(false); + EXPECT_FALSE(ac.getFollow()); + ac.setFollow(true); + EXPECT_TRUE(ac.getFollow()); + EXPECT_EQ(kNeoclimaButtonFollow, ac.getButton()); + */ + uint8_t on_5F[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x0A, 0x5F, 0x89, 0xA5, 0xAA}; + uint8_t on_5D[12] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x6A, 0x5D, 0x29, 0xA5, 0xA8}; + uint8_t off[12] = { + 0x00, 0x04, 0x00, 0x40, 0x00, 0x13, 0x00, 0x6B, 0x00, 0x29, 0xA5, 0x90}; + ac.setRaw(on_5F); + EXPECT_TRUE(ac.getFollow()); + ac.setRaw(off); + EXPECT_FALSE(ac.getFollow()); + ac.setRaw(on_5D); + EXPECT_TRUE(ac.getFollow()); +} + +TEST(TestIRNeoclimaAcClass, ChecksumCalculation) { + uint8_t examplestate[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + const uint8_t originalstate[kNeoclimaStateLength] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6A, 0x00, 0x2A, 0xA5, 0x39}; + + EXPECT_TRUE(IRNeoclimaAc::validChecksum(examplestate)); + EXPECT_EQ(0x39, IRNeoclimaAc::calcChecksum(examplestate)); + + examplestate[11] = 0x12; // Set an incorrect checksum. + EXPECT_FALSE(IRNeoclimaAc::validChecksum(examplestate)); + EXPECT_EQ(0x39, IRNeoclimaAc::calcChecksum(examplestate)); + IRNeoclimaAc ac(0); + ac.setRaw(examplestate); + // Extracting the state from the object should have a correct checksum. + EXPECT_TRUE(IRNeoclimaAc::validChecksum(ac.getRaw())); + EXPECT_STATE_EQ(originalstate, ac.getRaw(), kNeoclimaBits); + examplestate[11] = 0x39; // Restore old checksum value. + + // Change the state to force a different checksum. + examplestate[8] = 0x01; + EXPECT_FALSE(IRNeoclimaAc::validChecksum(examplestate)); + EXPECT_EQ(0x3A, IRNeoclimaAc::calcChecksum(examplestate)); +} + +TEST(TestIRNeoclimaAcClass, toCommon) { + IRNeoclimaAc ac(0); + ac.setPower(true); + ac.setMode(kNeoclimaCool); + ac.setTemp(20); + ac.setFan(kNeoclimaFanHigh); + ac.setSwingV(true); + ac.setSwingH(true); + ac.setTurbo(false); + ac.setIon(true); + ac.setLight(true); + ac.setSleep(true); + // Now test it. + ASSERT_EQ(decode_type_t::NEOCLIMA, 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_TRUE(ac.toCommon().filter); + 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::kAuto, ac.toCommon().swingh); + ASSERT_EQ(0, ac.toCommon().sleep); + // Unsupported. + 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-2.6.0/test/ir_Nikai_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Nikai_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Nikai_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Nikai_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Panasonic_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Panasonic_test.cpp index 4d10f8fb1..7f84c2a8a 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Panasonic_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Panasonic_test.cpp @@ -543,7 +543,7 @@ TEST(TestIRPanasonicAcClass, ChecksumCalculation) { EXPECT_TRUE(IRPanasonicAc::validChecksum(examplestate)); EXPECT_EQ(0x83, IRPanasonicAc::calcChecksum(examplestate)); - examplestate[kPanasonicAcStateLength - 1] = 0x0; // Set incoorect checksum. + examplestate[kPanasonicAcStateLength - 1] = 0x0; // Set incorrect checksum. EXPECT_FALSE(IRPanasonicAc::validChecksum(examplestate)); EXPECT_EQ(0x83, IRPanasonicAc::calcChecksum(examplestate)); pana.setRaw(examplestate); @@ -739,7 +739,7 @@ TEST(TestIRPanasonicAcClass, HumanReadable) { EXPECT_EQ( "Model: 4 (JKE), Power: Off, Mode: 0 (AUTO), Temp: 0C, " "Fan: 253 (UNKNOWN), Swing (Vertical): 0 (UNKNOWN), Quiet: Off, " - "Powerful: Off, Clock: 0:00, On Timer: Off, Off Timer: Off", + "Powerful: Off, Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); pana.setPower(true); pana.setTemp(kPanasonicAcMaxTemp); @@ -749,24 +749,24 @@ TEST(TestIRPanasonicAcClass, HumanReadable) { pana.setPowerful(true); EXPECT_EQ( "Model: 4 (JKE), Power: On, Mode: 4 (HEAT), Temp: 30C, " - "Fan: 4 (MAX), Swing (Vertical): 15 (AUTO), Quiet: Off, " - "Powerful: On, Clock: 0:00, On Timer: Off, Off Timer: Off", + "Fan: 4 (High), Swing (Vertical): 15 (AUTO), Quiet: Off, " + "Powerful: On, Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); pana.setQuiet(true); pana.setModel(kPanasonicLke); EXPECT_EQ( "Model: 1 (LKE), Power: Off, Mode: 4 (HEAT), Temp: 30C, " - "Fan: 4 (MAX), Swing (Vertical): 15 (AUTO), " + "Fan: 4 (High), Swing (Vertical): 15 (AUTO), " "Swing (Horizontal): 6 (Middle), Quiet: On, Powerful: Off, " - "Clock: 0:00, On Timer: 0:00, Off Timer: Off", + "Clock: 00:00, On Timer: 00:00, Off Timer: Off", pana.toString()); pana.setModel(kPanasonicDke); pana.setSwingHorizontal(kPanasonicAcSwingHRight); EXPECT_EQ( "Model: 3 (DKE), Power: Off, Mode: 4 (HEAT), Temp: 30C, " - "Fan: 4 (MAX), Swing (Vertical): 15 (AUTO), " + "Fan: 4 (High), Swing (Vertical): 15 (AUTO), " "Swing (Horizontal): 11 (Right), Quiet: On, Powerful: Off, " - "Clock: 0:00, On Timer: Off, Off Timer: Off", + "Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); } @@ -857,8 +857,8 @@ TEST(TestDecodePanasonicAC, SyntheticExample) { pana.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 4 (JKE), Power: Off, Mode: 3 (COOL), Temp: 25C, " - "Fan: 7 (AUTO), Swing (Vertical): 15 (AUTO), Quiet: Off, " - "Powerful: Off, Clock: 0:00, On Timer: Off, Off Timer: Off", + "Fan: 7 (Auto), Swing (Vertical): 15 (AUTO), Quiet: Off, " + "Powerful: Off, Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); } @@ -936,9 +936,9 @@ TEST(TestDecodePanasonicAC, Issue540) { // TODO(crankyoldgit): Try to figure out what model this should be. EXPECT_EQ( "Model: 0 (UNKNOWN), Power: On, Mode: 3 (COOL), Temp: 26C, " - "Fan: 7 (AUTO), Swing (Vertical): 15 (AUTO), " + "Fan: 7 (Auto), Swing (Vertical): 15 (AUTO), " "Swing (Horizontal): 13 (AUTO), Quiet: Off, Powerful: Off, " - "Clock: 0:00, On Timer: Off, Off Timer: Off", + "Clock: 00:00, On Timer: Off, Off Timer: Off", pana.toString()); } @@ -948,20 +948,10 @@ TEST(TestIRPanasonicAcClass, TimeBasics) { EXPECT_EQ(0x448, IRPanasonicAc::encodeTime(18, 16)); EXPECT_EQ(0, IRPanasonicAc::encodeTime(0, 0)); EXPECT_EQ(kPanasonicAcTimeMax, IRPanasonicAc::encodeTime(23, 59)); - EXPECT_EQ("16:10", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(16, 10))); - EXPECT_EQ("6:30", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(6, 30))); - EXPECT_EQ("18:16", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(18, 16))); - EXPECT_EQ("1:01", - IRPanasonicAc::timeToString(IRPanasonicAc::encodeTime(1, 1))); EXPECT_EQ(kPanasonicAcTimeMax, IRPanasonicAc::encodeTime(23, 59)); EXPECT_EQ(kPanasonicAcTimeMax, IRPanasonicAc::encodeTime(25, 72)); EXPECT_EQ(59, IRPanasonicAc::encodeTime(0, 72)); EXPECT_EQ(23 * 60, IRPanasonicAc::encodeTime(27, 0)); - EXPECT_EQ("0:00", IRPanasonicAc::timeToString(0)); - EXPECT_EQ("23:59", IRPanasonicAc::timeToString(kPanasonicAcTimeMax)); } TEST(TestIRPanasonicAcClass, TimersAndClock) { @@ -1126,8 +1116,8 @@ TEST(TestDecodePanasonicAC, CkpModelSpecifics) { pana.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 5 (CKP), Power: Off, Mode: 4 (HEAT), Temp: 23C, " - "Fan: 7 (AUTO), Swing (Vertical): 15 (AUTO), Quiet: Off, " - "Powerful: On, Clock: 0:00, On Timer: 0:00, Off Timer: 0:00", + "Fan: 7 (Auto), Swing (Vertical): 15 (AUTO), Quiet: Off, " + "Powerful: On, Clock: 00:00, On Timer: 00:00, Off Timer: 00:00", pana.toString()); pana.setQuiet(true); @@ -1142,3 +1132,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-2.6.0/test/ir_Pioneer_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Pioneer_test.cpp similarity index 95% rename from lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Pioneer_test.cpp index 36d61c706..5f96b3f2e 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Pioneer_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Pioneer_test.cpp @@ -61,7 +61,7 @@ TEST(TestEncodePioneer, SimpleEncoding) { EXPECT_EQ(0xA55A6A95F50A04FB, irsend.encodePioneer(0xA556, 0xAF20)); // "Source" from - // https://github.com/markszabo/IRremoteESP8266/pull/547#issuecomment-429616582 + // https://github.com/crankyoldgit/IRremoteESP8266/pull/547#issuecomment-429616582 EXPECT_EQ(0x659A05FAF50AC53A, irsend.encodePioneer(0xA6A0, 0xAFA3)); } @@ -92,7 +92,7 @@ TEST(TestDecodePioneer, RealExampleLongDecodeSourceButton) { irsend.reset(); // "Source" button. - // https://github.com/markszabo/IRremoteESP8266/pull/547#issuecomment-429616582 + // https://github.com/crankyoldgit/IRremoteESP8266/pull/547#issuecomment-429616582 uint16_t rawData[135] = { 8552, 4184, 596, 472, 592, 1524, 594, 1524, 594, 472, 592, 472, 598, 1520, 596, 472, 594, 1524, 592, 1524, 592, 472, 592, 472, @@ -119,7 +119,7 @@ TEST(TestDecodePioneer, RealExampleLongDecodeSourceButton) { // Synthetic Pioneer message. // For: -// https://github.com/markszabo/IRremoteESP8266/pull/547#issuecomment-430800734 +// https://github.com/crankyoldgit/IRremoteESP8266/pull/547#issuecomment-430800734 TEST(TestDecodePioneer, SyntheticPioneerMessage) { IRsendTest irsend(0); IRrecv irrecv(0); diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Pronto_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Pronto_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Pronto_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_RC5_RC6_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_RC5_RC6_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_RC5_RC6_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_RCMM_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_RCMM_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_RCMM_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Samsung_test.cpp similarity index 75% rename from lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Samsung_test.cpp index 8670ac4ab..b928350b6 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/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" @@ -414,7 +415,7 @@ TEST(TestIRSamsungAcClass, SetAndGetSwing) { EXPECT_TRUE(samsung.getSwing()); // Real examples from: - // https://github.com/markszabo/IRremoteESP8266/issues/505#issuecomment-424036602 + // https://github.com/crankyoldgit/IRremoteESP8266/issues/505#issuecomment-424036602 // TODO(Hollako): Explain why state[9] lowest bit changes between on and off. const uint8_t expected_off[kSamsungAcStateLength] = { 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, @@ -499,15 +500,87 @@ TEST(TestIRSamsungAcClass, SetAndGetFan) { } TEST(TestIRSamsungAcClass, SetAndGetQuiet) { - IRSamsungAc samsung(0); - samsung.setQuiet(false); - EXPECT_FALSE(samsung.getQuiet()); - samsung.setFan(kSamsungAcFanHigh); - samsung.setQuiet(true); - EXPECT_TRUE(samsung.getQuiet()); - EXPECT_EQ(kSamsungAcFanAuto, samsung.getFan()); - samsung.setQuiet(false); - EXPECT_FALSE(samsung.getQuiet()); + IRSamsungAc ac(0); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + ac.setFan(kSamsungAcFanHigh); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + + // Actual quiet on & off states from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500071419 + uint8_t on[14] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x20, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(on, 14); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + uint8_t off[14] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(off, 14); + EXPECT_FALSE(ac.getQuiet()); +} + + +TEST(TestIRSamsungAcClass, SetAndGetPowerful) { + IRSamsungAc ac(0); + ac.setFan(kSamsungAcFanMed); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanMed, ac.getFan()); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + ac.setPowerful(false); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + + // Actual powerful on & off states from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500120270 + uint8_t on[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xA2, 0xFE, 0x77, 0x00, 0x1F, 0xF0}; + ac.setRaw(on, kSamsungAcStateLength); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 7 (Turbo), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: On", ac.toString()); + + uint8_t off[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + ac.setRaw(off, kSamsungAcStateLength); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", ac.toString()); +} + +TEST(TestIRSamsungAcClass, QuietAndPowerfulAreMutuallyExclusive) { + IRSamsungAc ac(0); + ac.setQuiet(false); + ac.setPowerful(false); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + ac.setPowerful(true); + EXPECT_FALSE(ac.getQuiet()); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); } TEST(TestIRSamsungAcClass, ChecksumCalculation) { @@ -529,7 +602,7 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { EXPECT_TRUE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0, IRSamsungAc::calcChecksum(examplestate)); - examplestate[8] = 0x12; // Set an incoorect checksum. + examplestate[8] = 0x12; // Set an incorrect checksum. EXPECT_FALSE(IRSamsungAc::validChecksum(examplestate)); EXPECT_EQ(0, IRSamsungAc::calcChecksum(examplestate)); samsung.setRaw(examplestate); @@ -550,8 +623,8 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { TEST(TestIRSamsungAcClass, HumanReadable) { IRSamsungAc samsung(0); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (LOW), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (Low), Swing: On, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); samsung.setTemp(kSamsungAcMaxTemp); samsung.setMode(kSamsungAcHeat); @@ -561,13 +634,19 @@ TEST(TestIRSamsungAcClass, HumanReadable) { samsung.setBeep(true); samsung.setClean(true); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 5 (HIGH), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off", + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 5 (High), Swing: Off, " + "Beep: On, Clean: On, Quiet: Off, Powerful: Off", samsung.toString()); samsung.setQuiet(true); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " - "Beep: On, Clean: On, Quiet: On", + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (Auto), Swing: Off, " + "Beep: On, Clean: On, Quiet: On, Powerful: Off", + samsung.toString()); + samsung.setQuiet(false); + samsung.setPowerful(true); + EXPECT_EQ( + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 7 (Turbo), Swing: Off, " + "Beep: On, Clean: On, Quiet: Off, Powerful: On", samsung.toString()); } @@ -670,8 +749,8 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (LOW), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 2 (Low), Swing: On, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -719,13 +798,13 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } // Decode a real Samsung A/C example from: -// https://github.com/markszabo/IRremoteESP8266/issues/505#issuecomment-424036602 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505#issuecomment-424036602 TEST(TestDecodeSamsungAC, DecodePowerOnSample) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -778,13 +857,13 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } // Decode a real Samsung A/C example from: -// https://github.com/markszabo/IRremoteESP8266/issues/505#issuecomment-424036602 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/505#issuecomment-424036602 TEST(TestDecodeSamsungAC, DecodePowerOffSample) { IRsendTest irsend(0); IRrecv irrecv(0); @@ -838,8 +917,8 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength); EXPECT_EQ( - "Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: Off, Mode: 1 (COOL), Temp: 24C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -885,8 +964,8 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 17C, Fan: 0 (AUTO), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 4 (HEAT), Temp: 17C, Fan: 0 (Auto), Swing: On, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -932,8 +1011,8 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: On, Mode: 1 (COOL), Temp: 20C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -990,8 +1069,8 @@ TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { IRSamsungAc samsung(0); samsung.setRaw(irsend.capture.state, irsend.capture.bits / 8); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (AUTO), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off", + "Power: Off, Mode: 4 (HEAT), Temp: 30C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", samsung.toString()); } @@ -1128,3 +1207,239 @@ TEST(TestDecodeSamsung36, SyntheticExample) { EXPECT_EQ(0xE00FF, irsend.capture.command); EXPECT_EQ(0x400, irsend.capture.address); } + +// https://github.com/crankyoldgit/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 (Medium), " + "Swing: On, Beep: Off, Clean: Off, Quiet: Off, " + "Powerful: Off"; + // Don't do a setPower()/on()/off() as that will trigger the special message. + // So it should only be the normal "settings" message. + ac.setTemp(23); + ac.setMode(kSamsungAcCool); + ac.setFan(kSamsungAcFanMed); + ac.send(); + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Now trigger a special power message by using a power method. + ac.on(); + ac.send(); // This should result in two messages. 1 x extended + 1 x normal. + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + poweron + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Subsequent sending should be just the "settings" message. + ac.send(); + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + settings, ac._irsend.outputStr()); + ac._irsend.reset(); + // Now trigger a special power message by using a power method (again). + ac.setPower(true); + ac.send(); // This should result in two messages. 1 x extended + 1 x normal. + EXPECT_EQ(text, ac.toString()); + EXPECT_EQ(freqduty + poweron + settings, ac._irsend.outputStr()); +} + +TEST(TestIRSamsungAcClass, toCommon) { + IRSamsungAc ac(0); + ac.setPower(true); + ac.setMode(kSamsungAcCool); + ac.setTemp(20); + ac.setFan(kSamsungAcFanAuto); + ac.setSwing(true); + ac.setBeep(true); + ac.setClean(true); + ac.setQuiet(true); + // Now test it. + ASSERT_EQ(decode_type_t::SAMSUNG_AC, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(20, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_TRUE(ac.toCommon().quiet); + ASSERT_TRUE(ac.toCommon().clean); + ASSERT_TRUE(ac.toCommon().beep); + // Unsupported. + ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestDecodeSamsungAC, Issue734QuietSetting) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // QUIET MODE ON data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-499791618 + uint16_t quietOn[233] = { + 624, 17360, 3076, 8902, 520, 476, 520, 1472, 520, 476, 520, 474, 520, 476, + 520, 476, 520, 474, 522, 476, 520, 478, 518, 1476, 516, 500, 494, 502, + 548, 448, 546, 450, 544, 452, 522, 1468, 520, 1474, 520, 1472, 520, 1472, + 520, 1472, 520, 476, 520, 476, 518, 478, 516, 480, 516, 500, 496, 500, + 494, 502, 550, 446, 546, 450, 544, 452, 524, 472, 522, 474, 518, 476, 520, + 476, 520, 474, 522, 474, 520, 474, 520, 476, 520, 474, 520, 476, 518, 478, + 518, 480, 516, 480, 516, 502, 494, 502, 548, 1444, 524, 472, 522, 472, + 520, 474, 518, 478, 518, 476, 520, 476, 520, 1472, 520, 1470, 520, 1472, + 520, 1474, 516, 2980, 2998, 8980, 498, 1498, 548, 448, 526, 470, 544, 452, + 524, 472, 520, 474, 520, 476, 520, 476, 520, 476, 520, 1472, 520, 474, + 520, 476, 520, 1474, 518, 1476, 516, 1496, 496, 1498, 548, 446, 546, 1446, + 524, 1468, 518, 1474, 520, 1472, 520, 1472, 520, 1472, 520, 1474, 518, + 1476, 518, 480, 516, 500, 496, 528, 520, 1446, 544, 1446, 524, 1470, 518, + 476, 520, 476, 520, 474, 520, 476, 520, 474, 520, 476, 520, 474, 520, 476, + 520, 476, 518, 1476, 516, 482, 514, 502, 548, 448, 548, 1442, 544, 452, + 522, 474, 518, 476, 518, 476, 520, 476, 520, 474, 520, 476, 520, 1472, + 520, 1470, 522, 1474, 518, 1476, 536}; + + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x20, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + + irsend.sendRaw(quietOn, 233, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc ac(0); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off", + ac.toString()); + + // Make sure the ac class state is in something wildly different first. + ac.stateReset(); + ac.setPower(false); + ac.setMode(kSamsungAcAuto); + ac.setTemp(30); + ac.setSwing(true); + ac.setBeep(true); + ac.setClean(true); + ac.setQuiet(false); + // See if we can build the state from scratch. + ac.setPower(true); + ac.setMode(kSamsungAcCool); + ac.setTemp(16); + ac.setSwing(false); + ac.setBeep(false); + ac.setClean(false); + ac.setQuiet(true); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: On, Powerful: Off", + ac.toString()); + // Check it matches the known good/expected state. + EXPECT_STATE_EQ(expectedState, ac.getRaw(), kSamsungAcBits); +} + +TEST(TestDecodeSamsungAC, Issue734PowerfulOff) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + // 1st Powerful off data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500114580 + uint16_t powerfulOff[233] = { + 652, 17336, 3078, 8910, 562, 456, 546, 1448, 550, 446, 552, 444, 552, 444, + 550, 446, 550, 446, 552, 446, 550, 446, 552, 1440, 550, 446, 550, 446, + 550, 1470, 478, 518, 502, 492, 536, 1458, 542, 1450, 552, 1440, 552, 1442, + 552, 1442, 550, 446, 550, 446, 550, 446, 552, 444, 550, 446, 550, 446, + 550, 472, 524, 472, 480, 516, 510, 488, 538, 458, 542, 452, 548, 448, 550, + 446, 550, 446, 550, 444, 552, 444, 552, 444, 552, 444, 552, 444, 552, 444, + 552, 444, 550, 446, 550, 446, 550, 472, 524, 472, 482, 514, 510, 486, 536, + 460, 542, 454, 546, 450, 550, 446, 552, 1442, 552, 1442, 550, 1442, 552, + 1440, 508, 2994, 3030, 8932, 552, 1638, 450, // <= (was 356) + // Above hack due to poor data. + 470, 526, 470, 506, 492, 510, + 486, 542, 454, 544, 450, 550, 446, 554, 444, 550, 1442, 550, 444, 550, + 446, 550, 1442, 552, 1440, 550, 1442, 550, 1470, 524, 470, 480, 1512, 512, + 1480, 546, 1448, 550, 1442, 552, 1442, 552, 1442, 550, 1440, 552, 1440, + 552, 446, 550, 444, 552, 444, 550, 1468, 484, 1510, 512, 1482, 544, 452, + 550, 446, 552, 442, 554, 444, 552, 444, 554, 442, 554, 442, 552, 444, 554, + 442, 554, 1440, 552, 444, 554, 442, 554, 468, 528, 1466, 508, 488, 512, + 484, 544, 450, 550, 446, 554, 442, 556, 442, 554, 442, 554, 1438, 554, + 1438, 554, 1438, 554, 1438, 562}; // UNKNOWN 7B551B62}; + + uint8_t expectedState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xF2, 0xFE, 0x71, 0x00, 0x11, 0xF0}; + + irsend.sendRaw(powerfulOff, 233, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decodeSamsungAC(&irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRSamsungAc ac(0); + ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); + EXPECT_EQ( + "Power: On, Mode: 1 (COOL), Temp: 16C, Fan: 0 (Auto), Swing: Off, " + "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Sanyo_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sanyo_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Sanyo_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Sharp_test.cpp similarity index 52% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Sharp_test.cpp index c9d3e851b..2949580ed 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Sharp_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/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" @@ -360,3 +363,345 @@ TEST(TestDecodeSharp, FailToDecodeNonSharpExample) { ASSERT_FALSE(irrecv.decodeSharp(&irsend.capture)); ASSERT_FALSE(irrecv.decodeSharp(&irsend.capture, kSharpBits, false)); } + +// https://github.com/crankyoldgit/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/crankyoldgit/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/crankyoldgit/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 (Low)", + 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 (Medium)", + 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 (UNKNOWN)", + 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 (High)", + 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-2.6.0/test/ir_Sherwood_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Sherwood_test.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sherwood_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Sherwood_test.cpp diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Sony_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Sony_test.cpp index 35c3287b0..51bacbd6d 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Sony_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Sony_test.cpp @@ -153,7 +153,7 @@ TEST(TestDecodeSony, NormalSonyDecodeWithStrict) { irsend.reset(); irsend.sendSony(irsend.encodeSony(kSony20Bits, 0x1, 0x1, 0x1)); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(kSony20Bits, irsend.capture.bits); EXPECT_EQ(0x81080, irsend.capture.value); @@ -164,7 +164,7 @@ TEST(TestDecodeSony, NormalSonyDecodeWithStrict) { irsend.reset(); irsend.sendSony(irsend.encodeSony(kSony15Bits, 21, 1), kSony15Bits); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(kSony15Bits, irsend.capture.bits); EXPECT_EQ(0x5480, irsend.capture.value); @@ -175,7 +175,7 @@ TEST(TestDecodeSony, NormalSonyDecodeWithStrict) { irsend.reset(); irsend.sendSony(irsend.encodeSony(kSony12Bits, 21, 1), kSony12Bits); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture, kSony12Bits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(kSony12Bits, irsend.capture.bits); EXPECT_EQ(0xA90, irsend.capture.value); @@ -248,7 +248,7 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(8, irsend.capture.bits); EXPECT_EQ(0xFF, irsend.capture.value); @@ -264,7 +264,7 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(13, irsend.capture.bits); EXPECT_EQ(0x1FFF, irsend.capture.value); @@ -280,7 +280,7 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(17, irsend.capture.bits); EXPECT_EQ(0x1FFFF, irsend.capture.value); @@ -296,19 +296,18 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony15Bits, true)); EXPECT_FALSE(irrecv.decodeSony(&irsend.capture, kSony20Bits, true)); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(21, irsend.capture.bits); EXPECT_EQ(0x1FFFFF, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); EXPECT_EQ(0x0, irsend.capture.command); - irsend.reset(); // Illegal 64-bit (max) Sony-like message. irsend.sendSony(0xFFFFFFFFFFFFFFFF, 64, 0); irsend.makeDecodeResult(); // Should work with a 'normal' match (not strict) - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(64, irsend.capture.bits); EXPECT_EQ(0xFFFFFFFFFFFFFFFF, irsend.capture.value); @@ -316,7 +315,6 @@ TEST(TestDecodeSony, SonyDecodeWithIllegalSize) { EXPECT_EQ(0x0, irsend.capture.command); } -// Decode unsupported Sony messages. i.e non-standard sizes. TEST(TestDecodeSony, DecodeGlobalCacheExample) { IRsendTest irsend(4); IRrecv irrecv(4); @@ -331,7 +329,7 @@ TEST(TestDecodeSony, DecodeGlobalCacheExample) { irsend.makeDecodeResult(); // Without strict. - ASSERT_TRUE(irrecv.decodeSony(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(12, irsend.capture.bits); EXPECT_EQ(0x750, irsend.capture.value); diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Tcl_test.cpp similarity index 78% rename from lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Tcl_test.cpp index 249dcc637..8432cf9ac 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/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,82 @@ 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); +} + +TEST(TestDecodeTcl112Ac, Issue744) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[227] = { + 3164, 1532, 584, 1082, 472, 1068, 580, 244, 602, 264, 542, 328, 530, 1034, + 586, 262, 540, 326, 508, 1064, 582, 1082, 490, 328, 532, 1032, 586, 262, + 544, 352, 478, 1060, 584, 1082, 486, 328, 502, 1058, 588, 1084, 472, 344, + 530, 250, 600, 1086, 492, 322, 530, 258, 594, 1082, 494, 318, 510, 344, + 530, 248, 600, 262, 544, 326, 504, 296, 578, 252, 598, 260, 550, 318, 506, + 344, 530, 250, 600, 258, 546, 318, 508, 342, 532, 254, 596, 236, 606, 266, + 524, 1066, 580, 242, 602, 266, 542, 1054, 574, 246, 604, 262, 550, 1088, + 530, 1034, 588, 262, 542, 328, 504, 296, 582, 238, 606, 262, 546, 322, + 508, 342, 530, 250, 602, 260, 544, 1052, 572, 252, 600, 260, 546, 320, + 506, 344, 530, 254, 596, 264, 578, 268, 552, 316, 528, 256, 598, 260, 578, + 272, 520, 372, 476, 294, 582, 240, 604, 266, 542, 328, 502, 294, 582, 238, + 604, 268, 540, 322, 506, 346, 530, 244, 604, 260, 542, 354, 478, 298, 580, + 240, 604, 262, 542, 326, 506, 342, 530, 250, 600, 260, 548, 318, 506, 344, + 530, 250, 600, 260, 546, 320, 528, 322, 530, 254, 598, 262, 548, 316, 468, + 380, 532, 250, 600, 260, 546, 1092, 500, 300, 578, 246, 602, 1082, 474, + 346, 530, 248, 602, 260, 542, 1054, 570, 1090, 524}; // UNKNOWN 3338FACE + + uint8_t expectedState[kTcl112AcStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC4}; + + irsend.sendRaw(rawData, 227, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL112AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl112AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + + IRTcl112Ac ac(0); + ac.setRaw(irsend.capture.state); + EXPECT_EQ( + "Power: On, Mode: 3 (COOL), Temp: 23C, Fan: 0 (Auto), Econo: Off, " + "Health: Off, Light: On, Turbo: Off, Swing (H): Off, Swing (V): Off", + ac.toString()); +} diff --git a/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Teco_test.cpp similarity index 90% rename from lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Teco_test.cpp index 6b03a671d..aeacfcc68 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Teco_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Teco_test.cpp @@ -352,7 +352,38 @@ TEST(TestDecodeTeco, RealNormalExample) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Power: On, Mode: 2 (DRY), Temp: 21C, Fan: 2 (Med), Sleep: Off, " + "Power: On, Mode: 2 (DRY), Temp: 21C, Fan: 2 (Medium), Sleep: Off, " "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-2.6.0/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Toshiba_test.cpp similarity index 96% rename from lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Toshiba_test.cpp index d74866f92..15f8e6b90 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Toshiba_test.cpp @@ -352,16 +352,17 @@ TEST(TestToshibaACClass, HumanReadableOutput) { 0x00, 0xC1, 0x00, 0xC0}; toshiba.setRaw(initial_state); - EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (AUTO)", + EXPECT_EQ("Power: On, Mode: 0 (AUTO), Temp: 17C, Fan: 0 (Auto)", toshiba.toString()); toshiba.setRaw(modified_state); - EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 17C, Fan: 5 (MAX)", + EXPECT_EQ("Power: On, Mode: 1 (COOL), Temp: 17C, Fan: 5 (High)", toshiba.toString()); toshiba.off(); toshiba.setTemp(25); toshiba.setFan(3); toshiba.setMode(kToshibaAcDry); - EXPECT_EQ("Power: Off, Mode: 2 (DRY), Temp: 25C, Fan: 3", toshiba.toString()); + EXPECT_EQ("Power: Off, Mode: 2 (DRY), Temp: 25C, Fan: 3 (Medium)", + toshiba.toString()); } TEST(TestToshibaACClass, MessageConstuction) { @@ -669,3 +670,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-2.6.3.10/test/ir_Trotec_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Trotec_test.cpp new file mode 100644 index 000000000..f9272f384 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/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: 2 (Medium), 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: 2 (Medium), 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-2.6.0/test/ir_Vestel_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Vestel_test.cpp similarity index 88% rename from lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Vestel_test.cpp index 077a0a25e..da95b57c4 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Vestel_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Vestel_test.cpp @@ -321,7 +321,7 @@ TEST(TestVestelAcClass, MessageConstuction) { IRVestelAc ac(0); EXPECT_EQ( - "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (Auto Hot), Sleep: Off, " "Turbo: Off, Ion: Off, Swing: Off", ac.toString()); ac.setMode(kVestelAcCool); @@ -329,7 +329,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setFan(kVestelAcFanHigh); EXPECT_FALSE(ac.isTimeCommand()); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (High), Sleep: Off, " "Turbo: Off, Ion: Off, Swing: Off", ac.toString()); ac.setSwing(true); @@ -337,7 +337,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setTurbo(true); EXPECT_FALSE(ac.isTimeCommand()); EXPECT_EQ( - "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (HIGH), Sleep: Off, " + "Power: On, Mode: 1 (COOL), Temp: 21C, Fan: 11 (High), Sleep: Off, " "Turbo: On, Ion: On, Swing: On", ac.toString()); @@ -345,7 +345,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setSleep(true); ac.setMode(kVestelAcHeat); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 11 (HIGH), Sleep: On, " + "Power: On, Mode: 4 (HEAT), Temp: 21C, Fan: 11 (High), Sleep: On, " "Turbo: Off, Ion: On, Swing: On", ac.toString()); EXPECT_FALSE(ac.isTimeCommand()); @@ -353,7 +353,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setTemp(25); ac.setPower(false); EXPECT_EQ( - "Power: Off, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Power: Off, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (High), Sleep: On, " "Turbo: Off, Ion: On, Swing: On", ac.toString()); EXPECT_FALSE(ac.isTimeCommand()); @@ -368,19 +368,19 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.setTimer(8 * 60 + 0); EXPECT_TRUE(ac.isTimeCommand()); EXPECT_EQ( - "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + "Time: 23:59, Timer: 08:00, On Timer: Off, Off Timer: Off", ac.toString()); ac.setOnTimer(7 * 60 + 40); EXPECT_EQ( - "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: Off", + "Time: 23:59, Timer: Off, On Timer: 07:40, Off Timer: Off", ac.toString()); ac.setOffTimer(17 * 60 + 10); EXPECT_EQ( - "Time: 23:59, Timer: Off, On Timer: 7:40, Off Timer: 17:10", + "Time: 23:59, Timer: Off, On Timer: 07:40, Off Timer: 17:10", ac.toString()); ac.setTimer(8 * 60 + 0); EXPECT_EQ( - "Time: 23:59, Timer: 8:00, On Timer: Off, Off Timer: Off", + "Time: 23:59, Timer: 08:00, On Timer: Off, Off Timer: Off", ac.toString()); ac.setTimer(0); EXPECT_EQ( @@ -389,7 +389,7 @@ TEST(TestVestelAcClass, MessageConstuction) { ac.on(); EXPECT_FALSE(ac.isTimeCommand()); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (HIGH), Sleep: On, " + "Power: On, Mode: 4 (HEAT), Temp: 25C, Fan: 11 (High), Sleep: On, " "Turbo: Off, Ion: On, Swing: On", ac.toString()); } @@ -431,7 +431,7 @@ TEST(TestDecodeVestelAc, NormalDecodeWithStrict) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (AUTO HOT), Sleep: Off, " + "Power: On, Mode: 0 (AUTO), Temp: 25C, Fan: 13 (Auto Hot), Sleep: Off, " "Turbo: Off, Ion: Off, Swing: Off", ac.toString()); } @@ -467,7 +467,7 @@ TEST(TestDecodeVestelAc, RealNormalExample) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Power: On, Mode: 4 (HEAT), Temp: 16C, Fan: 1 (AUTO), Sleep: Off, " + "Power: On, Mode: 4 (HEAT), Temp: 16C, Fan: 1 (Auto), Sleep: Off, " "Turbo: Off, Ion: On, Swing: Off", ac.toString()); } @@ -502,7 +502,7 @@ TEST(TestDecodeVestelAc, RealTimerExample) { ac.begin(); ac.setRaw(irsend.capture.value); EXPECT_EQ( - "Time: 5:45, Timer: Off, On Timer: 14:00, Off Timer: 23:00", + "Time: 05:45, Timer: Off, On Timer: 14:00, Off Timer: 23:00", ac.toString()); } @@ -511,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-2.6.0/test/ir_Whirlpool_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Whirlpool_test.cpp similarity index 93% rename from lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Whirlpool_test.cpp index e282989f0..6349a8154 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Whirlpool_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Whirlpool_test.cpp @@ -70,7 +70,7 @@ TEST(TestDecodeWhirlpoolAC, SyntheticDecode) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", ac.toString()); } @@ -94,7 +94,7 @@ TEST(TestDecodeWhirlpoolAC, Real26CFanAutoCoolingSwingOnClock1918) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 2 (COOL), Temp: 26C, " - "Fan: 0 (AUTO), Swing: On, Light: On, Clock: 19:18, On Timer: Off, " + "Fan: 0 (Auto), Swing: On, Light: On, Clock: 19:18, On Timer: Off, " "Off Timer: Off, Sleep: Off, Super: Off, Command: 7 (SWING)", ac.toString()); } @@ -149,7 +149,7 @@ TEST(TestDecodeWhirlpoolAC, RealTimerExample) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", ac.toString()); } @@ -161,7 +161,7 @@ TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { irsend.begin(); // Real WhirlpoolAC message. - // Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/509 uint16_t rawData[343] = { 8950, 4484, 598, 1642, 598, 1646, 594, 534, 594, 538, 602, 532, 598, 540, 600, 542, 598, 1650, 600, 522, 598, 1644, 596, 1650, @@ -207,7 +207,7 @@ TEST(TestDecodeWhirlpoolAC, RealExampleDecode) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 1 (AUTO), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 17:31, On Timer: Off, " "Off Timer: Off, Sleep: Off, Super: Off, Command: 2 (TEMP)", ac.toString()); } @@ -324,25 +324,18 @@ TEST(TestIRWhirlpoolAcClass, SetAndGetClock) { IRWhirlpoolAc ac(0); ac.setClock(0); EXPECT_EQ(0, ac.getClock()); - EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); ac.setClock(1); EXPECT_EQ(1, ac.getClock()); - EXPECT_EQ("00:01", ac.timeToString(ac.getClock())); ac.setClock(12 * 60 + 34); EXPECT_EQ(12 * 60 + 34, ac.getClock()); - EXPECT_EQ("12:34", ac.timeToString(ac.getClock())); ac.setClock(7 * 60 + 5); EXPECT_EQ(7 * 60 + 5, ac.getClock()); - EXPECT_EQ("07:05", ac.timeToString(ac.getClock())); ac.setClock(23 * 60 + 59); EXPECT_EQ(23 * 60 + 59, ac.getClock()); - EXPECT_EQ("23:59", ac.timeToString(ac.getClock())); ac.setClock(24 * 60 + 0); EXPECT_EQ(0, ac.getClock()); - EXPECT_EQ("00:00", ac.timeToString(ac.getClock())); ac.setClock(25 * 60 + 23); EXPECT_EQ(1 * 60 + 23, ac.getClock()); - EXPECT_EQ("01:23", ac.timeToString(ac.getClock())); } TEST(TestIRWhirlpoolAcClass, OnOffTimers) { @@ -353,56 +346,42 @@ TEST(TestIRWhirlpoolAcClass, OnOffTimers) { ac.enableOnTimer(false); ac.setOnTimer(0); EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); EXPECT_FALSE(ac.isOnTimerEnabled()); EXPECT_EQ(kWhirlpoolAcCommandOnTimer, ac.getCommand()); ac.setOnTimer(1); EXPECT_EQ(1, ac.getOnTimer()); - EXPECT_EQ("00:01", ac.timeToString(ac.getOnTimer())); ac.enableOnTimer(true); ac.setOnTimer(12 * 60 + 34); EXPECT_EQ(12 * 60 + 34, ac.getOnTimer()); - EXPECT_EQ("12:34", ac.timeToString(ac.getOnTimer())); EXPECT_TRUE(ac.isOnTimerEnabled()); ac.setOnTimer(7 * 60 + 5); EXPECT_EQ(7 * 60 + 5, ac.getOnTimer()); - EXPECT_EQ("07:05", ac.timeToString(ac.getOnTimer())); ac.setOnTimer(23 * 60 + 59); EXPECT_EQ(23 * 60 + 59, ac.getOnTimer()); - EXPECT_EQ("23:59", ac.timeToString(ac.getOnTimer())); ac.setOnTimer(24 * 60 + 0); EXPECT_EQ(0, ac.getOnTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOnTimer())); ac.setOnTimer(25 * 60 + 23); EXPECT_EQ(1 * 60 + 23, ac.getOnTimer()); - EXPECT_EQ("01:23", ac.timeToString(ac.getOnTimer())); // Off Timer ac.enableOffTimer(false); ac.setOffTimer(0); EXPECT_EQ(0, ac.getOffTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); EXPECT_FALSE(ac.isOffTimerEnabled()); EXPECT_EQ(kWhirlpoolAcCommandOffTimer, ac.getCommand()); ac.setOffTimer(1); EXPECT_EQ(1, ac.getOffTimer()); - EXPECT_EQ("00:01", ac.timeToString(ac.getOffTimer())); ac.enableOffTimer(true); ac.setOffTimer(12 * 60 + 34); EXPECT_EQ(12 * 60 + 34, ac.getOffTimer()); - EXPECT_EQ("12:34", ac.timeToString(ac.getOffTimer())); EXPECT_TRUE(ac.isOffTimerEnabled()); ac.setOffTimer(7 * 60 + 5); EXPECT_EQ(7 * 60 + 5, ac.getOffTimer()); - EXPECT_EQ("07:05", ac.timeToString(ac.getOffTimer())); ac.setOffTimer(23 * 60 + 59); EXPECT_EQ(23 * 60 + 59, ac.getOffTimer()); - EXPECT_EQ("23:59", ac.timeToString(ac.getOffTimer())); ac.setOffTimer(24 * 60 + 0); EXPECT_EQ(0, ac.getOffTimer()); - EXPECT_EQ("00:00", ac.timeToString(ac.getOffTimer())); ac.setOffTimer(25 * 60 + 23); EXPECT_EQ(1 * 60 + 23, ac.getOffTimer()); - EXPECT_EQ("01:23", ac.timeToString(ac.getOffTimer())); } TEST(TestIRWhirlpoolAcClass, SetAndGetCommand) { @@ -578,8 +557,41 @@ TEST(TestIRWhirlpoolAcClass, MessageConstruction) { EXPECT_EQ( "Model: 1 (DG11J13A), Power toggle: Off, Mode: 3 (DRY), Temp: 25C, " - "Fan: 0 (AUTO), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " + "Fan: 0 (Auto), Swing: Off, Light: On, Clock: 07:35, On Timer: 07:40, " "Off Timer: 08:05, Sleep: Off, Super: Off, Command: 5 (ONTIMER)", 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-2.6.0/test/ir_Whynter_test.cpp b/lib/IRremoteESP8266-2.6.3.10/test/ir_Whynter_test.cpp similarity index 97% rename from lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp rename to lib/IRremoteESP8266-2.6.3.10/test/ir_Whynter_test.cpp index 92ced5cf6..0c45f2654 100644 --- a/lib/IRremoteESP8266-2.6.0/test/ir_Whynter_test.cpp +++ b/lib/IRremoteESP8266-2.6.3.10/test/ir_Whynter_test.cpp @@ -151,7 +151,7 @@ TEST(TestDecodeWhynter, NormalDecodeWithStrict) { irsend.reset(); irsend.sendWhynter(0x87654321); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); @@ -170,20 +170,20 @@ TEST(TestDecodeWhynter, NormalDecodeWithRepeatAndStrict) { irsend.reset(); irsend.sendWhynter(0x87654321, kWhynterBits, 2); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); EXPECT_FALSE(irsend.capture.repeat); irsend.makeDecodeResult(2 * kWhynterBits + 6); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); irsend.makeDecodeResult(2 * (2 * kWhynterBits + 6)); - ASSERT_TRUE(irrecv.decodeWhynter(&irsend.capture, kWhynterBits, true)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(kWhynterBits, irsend.capture.bits); EXPECT_EQ(0x87654321, irsend.capture.value); diff --git a/lib/IRremoteESP8266-2.6.0/tools/Makefile b/lib/IRremoteESP8266-2.6.3.10/tools/Makefile similarity index 87% rename from lib/IRremoteESP8266-2.6.0/tools/Makefile rename to lib/IRremoteESP8266-2.6.3.10/tools/Makefile index 08488949c..7ae48d392 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/Makefile +++ b/lib/IRremoteESP8266-2.6.3.10/tools/Makefile @@ -29,7 +29,7 @@ run_tests : all failed=""; \ for py_unittest in *_test.py; do \ echo "RUNNING: $${py_unittest}"; \ - python ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ + python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ done; \ if [ -n "$${failed}" ]; then \ echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \ @@ -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 ir_Neoclima.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 @@ -192,7 +196,7 @@ ir_Lutron.o : $(USER_DIR)/ir_Lutron.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Lutron.cpp ir_Electra.o : $(USER_DIR)/ir_Electra.cpp $(GTEST_HEADERS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Electra.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Electra.cpp ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(GTEST_HEADERS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pioneer.cpp @@ -209,5 +213,17 @@ 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 + +ir_Neoclima.o : $(USER_DIR)/ir_Neoclima.cpp $(USER_DIR)/ir_Neoclima.h $(COMMON_DEPS) $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_Neoclima.cpp diff --git a/lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh b/lib/IRremoteESP8266-2.6.3.10/tools/RawToGlobalCache.sh similarity index 100% rename from lib/IRremoteESP8266-2.6.0/tools/RawToGlobalCache.sh rename to lib/IRremoteESP8266-2.6.3.10/tools/RawToGlobalCache.sh diff --git a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266-2.6.3.10/tools/auto_analyse_raw_data.py similarity index 98% rename from lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py rename to lib/IRremoteESP8266-2.6.3.10/tools/auto_analyse_raw_data.py index b23cdb46f..8a2e45794 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266-2.6.3.10/tools/auto_analyse_raw_data.py @@ -8,7 +8,7 @@ import argparse import sys -class RawIRMessage(object): +class RawIRMessage(): """Basic analyse functions & structure for raw IR messages.""" # pylint: disable=too-many-instance-attributes @@ -62,7 +62,7 @@ class RawIRMessage(object): def _usec_compare(self, seen, expected): """Compare two usec values and see if they match within a subtractive margin.""" - return seen <= expected and seen > expected - self.margin + return expected - self.margin < seen <= expected def _usec_compares(self, usecs, expecteds): """Compare a usec value to a list of values and return True @@ -160,7 +160,7 @@ class RawIRMessage(object): def avg_list(items): """Return the average of a list of numbers.""" if items: - return sum(items) / len(items) + return int(sum(items) / len(items)) return 0 @@ -293,7 +293,7 @@ def decode_data(message, defines, function_code, output=sys.stdout): output.write("kHdrSpace+") function_code.append(" space(kHdrSpace);") elif message.is_bit_mark(usec) and count % 2: - if state != "HS" and state != "BS": + if state not in ("HS", "BS"): output.write("kBitMark(UNEXPECTED)") state = "BM" elif message.is_zero_space(usec): diff --git a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py b/lib/IRremoteESP8266-2.6.3.10/tools/auto_analyse_raw_data_test.py similarity index 97% rename from lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py rename to lib/IRremoteESP8266-2.6.3.10/tools/auto_analyse_raw_data_test.py index 681ff5520..5d5504ffc 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/auto_analyse_raw_data_test.py +++ b/lib/IRremoteESP8266-2.6.3.10/tools/auto_analyse_raw_data_test.py @@ -1,6 +1,6 @@ -#!/usr/bin/python +#!/usr/bin/python3 """Unit tests for auto_analyse_raw_data.py""" -import StringIO +from io import StringIO import unittest import auto_analyse_raw_data as analyse @@ -12,7 +12,7 @@ class TestRawIRMessage(unittest.TestCase): def test_display_binary(self): """Test the display_binary() method.""" - output = StringIO.StringIO() + output = StringIO() message = analyse.RawIRMessage(100, [8000, 4000, 500, 500, 500], output, False) self.assertEqual(output.getvalue(), '') @@ -52,8 +52,8 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_dump_constants_simple(self): """Simple tests for the dump_constants() function.""" - ignore = StringIO.StringIO() - output = StringIO.StringIO() + ignore = StringIO() + output = StringIO() defs = [] message = analyse.RawIRMessage(200, [ 7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, @@ -75,8 +75,8 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_dump_constants_aircon(self): """More complex tests for the dump_constants() function.""" - ignore = StringIO.StringIO() - output = StringIO.StringIO() + ignore = StringIO() + output = StringIO() defs = [] message = analyse.RawIRMessage(200, [ 9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, @@ -111,7 +111,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): self.assertEqual(analyse.convert_rawdata("0"), [0]) with self.assertRaises(ValueError) as context: analyse.convert_rawdata("") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data contains a non-numeric value of ''.") # Single parenthesis @@ -132,13 +132,13 @@ class TestAutoAnalyseRawData(unittest.TestCase): # Bad parentheses with self.assertRaises(ValueError) as context: analyse.convert_rawdata("}10{") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data not parsible due to parentheses placement.") # Non base-10 values with self.assertRaises(ValueError) as context: analyse.convert_rawdata("10, 20, foo, bar, 30") - self.assertEqual(context.exception.message, + self.assertEqual(str(context.exception), "Raw Data contains a non-numeric value of 'foo'.") # A messy usual "good" case. @@ -155,7 +155,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): """Tests for the parse_and_report() function.""" # Without code generation. - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[139] = {9008, 4496, 644, 1660, 676, 530, 648, 558, 672, 1636, 646, 1660, 644, 556, 650, 584, 626, 560, 644, 580, 628, 1680, @@ -210,7 +210,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): 'Total Nr. of suspected bits: 67\n') # With code generation. - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[37] = {7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494, @@ -294,7 +294,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): """Tests for unusual Space Gaps in parse_and_report() function.""" # Tests for unusual Gaps. (Issue #482) - output = StringIO.StringIO() + output = StringIO() input_str = """ uint16_t rawbuf[272] = {3485, 3512, 864, 864, 864, 2620, 864, 864, 864, 2620, 864, 2620, 864, 2620, 864, 2620, 864, 2620, 864, 864, @@ -466,7 +466,7 @@ class TestAutoAnalyseRawData(unittest.TestCase): def test_reduce_list(self): """Tests for the reduce_list method.""" - ignore = StringIO.StringIO() + ignore = StringIO() message = analyse.RawIRMessage(200, [ 7930, 3952, 494, 1482, 520, 1482, 494, 1508, 494, 520, 494, 1482, 494, 520, 494, 1482, 494, 1482, 494, 3978, 494, 520, 494, 520, 494, 520, 494, diff --git a/lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp b/lib/IRremoteESP8266-2.6.3.10/tools/gc_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/tools/gc_decode.cpp rename to lib/IRremoteESP8266-2.6.3.10/tools/gc_decode.cpp diff --git a/lib/IRremoteESP8266-2.6.0/tools/mkkeywords b/lib/IRremoteESP8266-2.6.3.10/tools/mkkeywords similarity index 82% rename from lib/IRremoteESP8266-2.6.0/tools/mkkeywords rename to lib/IRremoteESP8266-2.6.3.10/tools/mkkeywords index a2cdccd9b..3172383ad 100644 --- a/lib/IRremoteESP8266-2.6.0/tools/mkkeywords +++ b/lib/IRremoteESP8266-2.6.3.10/tools/mkkeywords @@ -30,9 +30,8 @@ cat << EndOfTextEndOfTextEndOfText EndOfTextEndOfTextEndOfText -CLASSES=$(grep "^class " src/*.h | cut -d' ' -f2 | sort -u) -# Manually add typedefs as they are hard to parse out. -CLASSES="${CLASSES} ir_params_t match_result_t" +CLASSES=$(egrep -h "^ *((enum|class) |} [a-zA-Z0-9_]+_t;$)" src/*.h | + sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sort -u) for i in ${CLASSES}; do echo -e "${i}\tKEYWORD1" done | sort -du @@ -44,10 +43,11 @@ cat << EndOfTextEndOfTextEndOfText ####################################### EndOfTextEndOfTextEndOfText - -METHODS=$(egrep "^(u?int|void|bool|String|static|match_result_t).*\(" src/*.cpp| - cut -d' ' -f2- | sed 's/^.*:://' | cut -d'(' -f1 | sort -u | - grep -v ICACHE_RAM_ATTR) +CTYPES="u?int(8|16|32|64)?(_t)?|void|bool|char|float|long|double|String|static" +OURTYPES="match_result_t|state_t|decode_type_t" +METHODS=$(egrep -h "^[ ]{0,2}(${CTYPES}|${OURTYPES})\*? [^ ]*\(" src/*.cpp | + sed 's/^ //' | cut -d' ' -f2 | sed 's/^\([^:]*::\| *\* *\)//' | + cut -d'(' -f1 | sort -u | grep -v RAM_ATTR) for i in ${METHODS}; do echo -e "${i}\tKEYWORD2" done | sort -u diff --git a/lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp b/lib/IRremoteESP8266-2.6.3.10/tools/mode2_decode.cpp similarity index 100% rename from lib/IRremoteESP8266-2.6.0/tools/mode2_decode.cpp rename to lib/IRremoteESP8266-2.6.3.10/tools/mode2_decode.cpp diff --git a/lib/IRremoteESP8266-2.6.3.10/tools/scrape_supported_devices.py b/lib/IRremoteESP8266-2.6.3.10/tools/scrape_supported_devices.py new file mode 100644 index 000000000..574eac351 --- /dev/null +++ b/lib/IRremoteESP8266-2.6.3.10/tools/scrape_supported_devices.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +"""Generate SupportedProtocols.md by scraping source code files""" +import pathlib +import argparse +import sys +import re +import time + +CODE_URL = "https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_" + +BRAND_MODEL = re.compile(r"Brand: *(?P.+), *Model: *(?P.+)") +ENUMS = re.compile(r"enum \w+ {(.+?)};", re.DOTALL) +ENUM_ENTRY = re.compile(r"^\s+(\w+)", re.MULTILINE) +DECODED_PROTOCOLS = re.compile(r".*results->decode_type *=.*?(\w+);") +AC_FN = re.compile(r"ir_(.+).h") + +ALL_FN = re.compile(r"ir_(.+).(h|cpp)") + +EXCLUDED_PROTOCOLS = ["UNKNOWN", "UNUSED", "kLastDecodeType"] + +MARKDOWN_HEADER = """""".format(time.asctime()) + + +def getallprotocols(): + """Return all protocls configured in IRremoteESP8266.h + """ + irremote = ARGS.directory / "IRremoteESP8266.h" + enums = getenums(irremote) + if not enums: + errorexit("Error getting ENUMS from IRremoteESP8266.h") + return enums + + +def getdecodedprotocols(): + """All protocols that include decoding support""" + ret = set() + for path in ARGS.directory.iterdir(): + if path.suffix != ".cpp": + continue + matches = DECODED_PROTOCOLS.finditer(path.open().read()) + for match in matches: + protocol = match.group(1) + if protocol not in EXCLUDED_PROTOCOLS: + ret.add(protocol) + return ret + + +def getallacs(): + """All supported A/C codes""" + ret = {} + for path in ARGS.directory.iterdir(): + match = AC_FN.match(path.name) + if not match: + continue + acprotocol = match.group(1) + rawmodels = getenums(path) + models = set() + for model in rawmodels: + model = model.upper() + model = model.replace("K{}".format(acprotocol.upper()), "") + if model and model not in EXCLUDED_PROTOCOLS: + models.add(model) + ret[acprotocol] = models + return ret + + +def getalldevices(): + """All devices and associated branding and model information (if available) + """ + allcodes = {} + fnnomatch = set() + fnmatch = set() + for path in ARGS.directory.iterdir(): + match = ALL_FN.match(path.name) + if not match: + continue + supports = extractsupports(path) + if supports: + fnmatch.add(path.stem) + else: + fnnomatch.add(path.stem) + protocol = match.group(1) + for brand, model in supports: + protocolbrand = (protocol, brand) + allcodes[protocolbrand] = allcodes.get(protocolbrand, list()) + [model] + nosupports = fnnomatch - fnmatch + for fnprotocol in nosupports: + allcodes[(fnprotocol[3:], "Unknown")] = [] + return allcodes, nosupports + + +def getenums(path): + """Returns the keys for the first enum type in path + """ + enums = ENUMS.search(path.open().read()) + ret = set() + if not enums: + return ret + for enum in ENUM_ENTRY.finditer(enums.group(1)): + enum = enum.group(1) + if enum in EXCLUDED_PROTOCOLS: + continue + ret.add(enum) + return ret + + +ARGS = None + + +def initargs(): + """Init the command line arguments""" + global ARGS # pylint: disable=global-statement + parser = argparse.ArgumentParser() + parser.add_argument( + "-s", + "--stdout", + help="output to stdout rather than SupportedProtocols.md", + action="store_true", + ) + parser.add_argument("-v", + "--verbose", + help="increase output verbosity", + action="store_true") + parser.add_argument( + "-a", + "--alert", + help="alert if a file does not have a supports section", + action="store_true", + ) + parser.add_argument( + "directory", + nargs="?", + help="directory of the source git checkout", + default=None, + ) + ARGS = parser.parse_args() + if ARGS.directory is None: + src = pathlib.Path("../src") + if not src.is_dir(): + src = pathlib.Path("./src") + else: + src = pathlib.Path(ARGS.directory) / "src" + if not src.is_dir(): + errorexit("Directory not valid: {}".format(str(src))) + ARGS.directory = src + return ARGS + + +def errorexit(msg): + """Print an error and exit on critical error""" + sys.stderr.write("{}\n".format(msg)) + sys.exit(1) + + +def extractsupports(path): + """Extract all of the Supports: sections and associated brands and models + """ + supports = [] + insupports = False + for line in path.open(): + if not line.startswith("//"): + continue + line = line[2:].strip() + if line == "Supports:": + insupports = True + continue + if insupports: + match = BRAND_MODEL.match(line) + if match: + supports.append((match.group("brand"), match.group("model"))) + else: + insupports = False + continue + return supports + + +def makeurl(txt, path): + """Make a Markup URL from given filename""" + return "[{}]({})".format(txt, CODE_URL + path) + + +def outputprotocols(fout, protocols): + """For a given protocol set, sort and output the markdown""" + protocols = list(protocols) + protocols.sort() + for protocol in protocols: + fout.write("- {}\n".format(protocol)) + + +def main(): + """Standard boiler plate""" + initargs() + if ARGS.verbose: + print("Looking for files in: {}".format(str(ARGS.directory.resolve()))) + if ARGS.stdout: + fout = sys.stdout + else: + foutpath = ARGS.directory / "../SupportedProtocols.md" + foutpath = foutpath.resolve() + if ARGS.verbose: + print("Output path: {}".format(str(foutpath))) + fout = foutpath.open("w") + decodedprotocols = getdecodedprotocols() + sendonly = getallprotocols() - decodedprotocols + allacs = getallacs() + + allcodes, nosupports = getalldevices() + allbrands = list(allcodes.keys()) + allbrands.sort() + + fout.write(MARKDOWN_HEADER) + fout.write("\n# IR Protocols supported by this library\n\n") + fout.write( + "| Protocol | Brand | Model | A/C Model | Detailed A/C Support |\n") + fout.write("| --- | --- | --- | --- | --- |\n") + + for protocolbrand in allbrands: + protocol, brand = protocolbrand + codes = allcodes[protocolbrand] + codes.sort() + if protocol in allacs: + acmodels = list(allacs[protocol]) + acmodels.sort() + acsupport = "Yes" + brand = makeurl(brand, protocol + ".h") + else: + acmodels = [] + acsupport = "-" + + fout.write("| {} | **{}** | {} | {} | {} |\n".format( + makeurl(protocol, protocol + ".cpp"), + brand, + "
".join(codes), + "
".join(acmodels), + acsupport, + )) + + fout.write("\n\n## Send only protocols:\n\n") + outputprotocols(fout, sendonly) + + fout.write("\n\n## Send & decodable protocols:\n\n") + outputprotocols(fout, decodedprotocols) + + if ARGS.alert: + nosupports = list(nosupports) + nosupports.sort() + print("The following files had no supports section:") + for path in nosupports: + print("\t{}".format(path)) + + +if __name__ == "__main__": + main()