diff --git a/lib/IRremoteESP8266/.gitattributes b/lib/IRremoteESP8266/.gitattributes index 380830a9d..05945c4fb 100644 --- a/lib/IRremoteESP8266/.gitattributes +++ b/lib/IRremoteESP8266/.gitattributes @@ -1,4 +1,3 @@ # Directories & files to ignore from release archives docs export-ignore assets export-ignore -SECURITY.md export-ignore diff --git a/lib/IRremoteESP8266/.gitignore b/lib/IRremoteESP8266/.gitignore index a73bc8b00..b58efd41d 100644 --- a/lib/IRremoteESP8266/.gitignore +++ b/lib/IRremoteESP8266/.gitignore @@ -40,6 +40,7 @@ tools/*.o tools/*.a tools/gc_decode tools/mode2_decode +tools/code_to_raw .pioenvs .piolibdeps @@ -51,3 +52,4 @@ tools/mode2_decode # Mac extended attributes .DS_Store +/.vs diff --git a/lib/IRremoteESP8266/README.md b/lib/IRremoteESP8266/README.md index 5dae9cfd9..f392c5529 100644 --- a/lib/IRremoteESP8266/README.md +++ b/lib/IRremoteESP8266/README.md @@ -10,8 +10,8 @@ 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.7.20 Now Available -Version 2.7.20 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. +## v2.8.1 Now Available +Version 2.8.1 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/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266/README_de.md b/lib/IRremoteESP8266/README_de.md index 51ded0420..884628149 100644 --- a/lib/IRremoteESP8266/README_de.md +++ b/lib/IRremoteESP8266/README_de.md @@ -11,8 +11,8 @@ Diese Programmbibliothek ermöglicht das **Senden _und_ Empfangen** von Infrarotsignalen mit [ESP8266](https://github.com/esp8266/Arduino)- und [ESP32](https://github.com/espressif/arduino-esp32)-Mikrocontrollern mithilfe des [Arduino-Frameworks](https://www.arduino.cc/) und handelsüblichen 940nm Infrarot-LEDs undIR-Empfängermodulen, wie zum Beispiel TSOP{17,22,24,36,38,44,48}*-Demodulatoren. -## v2.7.20 jetzt verfügbar -Version 2.7.20 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen. +## v2.8.1 jetzt verfügbar +Version 2.8.1 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen. #### Hinweis für Nutzer von Versionen vor v2.0 Die Benutzung der Bibliothek hat sich mit Version 2.0 leicht geändert. Einige Anpassungen im aufrufenden Code werden nötig sein, um mit Version ab 2.0 korrekt zu funktionieren. Mehr zu den Anpassungen finden sich auf unserer [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0)-Seite. diff --git a/lib/IRremoteESP8266/README_fr.md b/lib/IRremoteESP8266/README_fr.md index fe314c459..3f1a93048 100644 --- a/lib/IRremoteESP8266/README_fr.md +++ b/lib/IRremoteESP8266/README_fr.md @@ -10,8 +10,8 @@ Cette librairie vous permetra de **recevoir et d'envoyer des signaux** infrarouge sur le protocole [ESP8266](https://github.com/esp8266/Arduino) ou sur le protocole [ESP32](https://github.com/espressif/arduino-esp32) en utilisant le [Arduino framework](https://www.arduino.cc/) qui utilise la norme 940nm IR LEDs et le module basique de reception d'onde IR. Exemple : TSOP{17,22,24,36,38,44,48}* modules etc. -## v2.7.20 disponible -Version 2.7.20 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants. +## v2.8.1 disponible +Version 2.8.1 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants. #### mise à jour depuis pre-v2.0 L'utilisation de la librairie à un peu changer depuis la version in v2.0. Si vous voulez l'utiliser vous devrez changer votre utilisation aussi. Vous pouvez vous renseigner sur les précondition d'utilisation ici : [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266/ReleaseNotes.md b/lib/IRremoteESP8266/ReleaseNotes.md index f5bc0f66a..9c8d7df98 100644 --- a/lib/IRremoteESP8266/ReleaseNotes.md +++ b/lib/IRremoteESP8266/ReleaseNotes.md @@ -1,5 +1,97 @@ # Release Notes +## _v2.8.1 (20220101)_ + +**[Bug Fixes]** +- Arduino ESP32 Core v2.0.2+ crashes due to our timer hack. (#1715 #1713) +- SONY: Fix old Sony CD-Player Remote (12 Bit) (#1714) + +**[Features]** +- Add tool to convert protocol & code to raw timing info. (#1708 #1707 #1703) +- Add basic support for COOLIX48 protocol. (#1697 #1694) +- MITSUBISHI_AC: Added support for i-SAVE mode. (#1666) +- TOSHIBA_AC: Add Filter setting support. aka. Pure. (#1693 #1692) +- Airton: Add detailed A/C support. (#1688 #1670) + +**[Misc]** +- Add a structured library version number. (#1717) +- Workflows Split UnitTests (#1712) +- Reduce time for workflow/Build (#1709) +- Fix some compiler & linter warnings (#1699 #1700) +- Fujitsu: Update supported A/C models (#1690 #1689 #1702 #1701) +- Remove extra `const` qualifier for char pointer (#1704) +- TCL: Update supported devices. (#1698) +- ESP32-C3: Work around for some C3 specific compiler issues. (#1696 #1695) + + +## _v2.8.0 (20211119)_ + +**[Bug Fixes]** +- Fix compilation issue when using old 8266 Arduino Frameworks. (#1639 #1640) +- Fix potential security issue with `scrape_supported_devices.py` (#1616 #1619) + +**[Features]** +- SAMSUNG_AC + - Change `clean` setting to a toggle. (#1676 #1677) + - Highest fan speed is available without Powerful setting. (#1675 #1678) + - Change `beep` setting to a toggle. (#1669 #1671) + - Fix Beep for AR12TXEAAWKNEU (#1668 #1669) + - Add support for Horizontal Swing & Econo (#1277 #1667) + - Add support for On, Off, & Sleep Timers (#1277 #1662) + - Fix power control. Clean-up code & bitmaps from Checksum changes. (#1277 #1648 #1650) +- HAIER_AC176/HAIER_AC_YRW02 + - Add support A/B unit setting (#1672) + - Add support degree Fahrenheit (#1659) + - Add support `Lock` function (#1652) + - Implement horizontal swing feature (#1641) + - Implement Quiet setting. (#1634 #1635) +- Basic support for Airton Protocol (#1670 #1681) +- HAIER_AC176: Add Turbo and Quiet settings (#1634) +- Gree: Add `SwingH` & `Econo` control. (#1587 #1653) +- MIRAGE + - Add experimental detailed support. (#1573 #1615) + - Experimental detailed support for KKG29A-C1 remote. (#1573 #1660) +- ELECTRA_AC: Add support for "IFeel" & Sensor settings. (#1644 #1645) +- Add Russian translation (#1649) +- Add Swedish translation (#1627) +- Reduce flash space used. (#1633) +- Strings finally in Flash! (#1493 #1614 #1623) +- Add support for Rhoss Idrowall MPCV 20-30-35-40 A/C protocol (#1630) +- Make `IRAc::opmodeToString()` output nicer for humans. (#1613) +- TCL112AC/TEKNOPOINT: Add support for `GZ055BE1` model (#1486 #1602) +- Support for Arris protocol. (#1598) +- SharpAc: Allow position control of SwingV (#1590 #1594) + +**[Misc]** +- HAIER_AC176/HAIER_AC_YRW02 + - Replace some magic numbers with constants (#1679) + - Small fix `Quiet` and `Turbo` test (#1674) + - Fix `IRHaierAC176::getTemp()` return value description (#1663) +- Security Policy creation and changes. (#1616 #1617 #1618 #1621 #1680) +- IRrecvDumpV2/3: Update PlatformIO envs for missing languages (#1661) +- IRMQTTServer + - Use the correct string for Fan mode in Home Assistant. (#1610 #1657) + - Move a lot of the strings/text to flash. (#1638) +- Minor code style improvements. (#1656) +- Update Supported Devices + - HAIER_AC176 (#1673) + - LG A/C (#1651 #1655) + - Symphony (#1603 #1605) + - Epson (#1574 #1601) + - GREE (#1587 #1588) + - SharpAc (#1590 #1591) +- Add extra tests for LG2 protocol (#1654) +- Fix parameter expansion in several macros. +- Move some strings to `IRtext.cpp` & `locale/default.h` (#1637) +- RHOSS: Move include and defines to their correct places (#1636) +- Make makefile only build required files when running `run-%` target (#1632) +- Update Portuguese translation (#1628) +- Add possibility to run specific test case (#1625) +- Change `googletest` library ignore (#1626) +- Re-work "Fan Only" strings & matching. (#1610) +- Address `C0209` pylint warnings. (#1608) + + ## _v2.7.20 (20210828)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266/SECURITY.md b/lib/IRremoteESP8266/SECURITY.md new file mode 100644 index 000000000..c98300789 --- /dev/null +++ b/lib/IRremoteESP8266/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +You can find this library's Security Policy and contact procedure at https://crankyoldgit.github.io/IRremoteESP8266/SECURITY diff --git a/lib/IRremoteESP8266/SupportedProtocols.md b/lib/IRremoteESP8266/SupportedProtocols.md index 348a6d641..0b6fb06c1 100644 --- a/lib/IRremoteESP8266/SupportedProtocols.md +++ b/lib/IRremoteESP8266/SupportedProtocols.md @@ -1,18 +1,21 @@ + Last generated: Fri 31 Dec 2021 21:49:00 +0000 ---> # IR Protocols supported by this library | Protocol | Brand | Model | A/C Model | Detailed A/C Support | | --- | --- | --- | --- | --- | +| [Airton](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airton.cpp) | **[Airton](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airton.h)** | RD1A1 remote
SMVH09B-2A2A3NH ref. 409730 A/C | | Yes | | [Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airwell.cpp) | **[Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Airwell.h)** | DLS 21 DCI R410 AW A/C
RC04 remote
RC08W remote | | Yes | | [Aiwa](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Aiwa.cpp) | **Aiwa** | RC-T501 RCU | | - | | [Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.cpp) | **[Amcor](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Amcor.h)** | ADR-853H A/C
TAC-444 remote
TAC-495 remote | | Yes | | [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 | +| [Arris](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Arris.cpp) | **Arris** | 120A V1.0 A18 remote
VIP1113M Set-top box | | - | | [Bose](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Bose.cpp) | **Bose** | Bose TV Speaker | | - | | [Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.cpp) | **[Carrier/Surrey](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Carrier.h)** | 42QG5A55970 remote
53NGK009/012 Inverter
619EGX0090E0 A/C
619EGX0120E0 A/C
619EGX0180E0 A/C
619EGX0220E0 A/C | | Yes | | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Airwell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | RC08B remote | | Yes | | [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
RG57K7(B)/BGEF Remote | | Yes | +| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Bosch](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | B1ZAI2441W/B1ZAO2441W A/C
RG36B4/BGE remote | | Yes | | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Kastron](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | RG57A7/BGEF Inverter remote | | Yes | | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Kaysun](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | Casual CF A/C | | 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
MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
RG52D/BGE Remote | | Yes | @@ -26,12 +29,14 @@ | [Doshisha](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Doshisha.cpp) | **Doshisha** | CZ-S32D LED Light
CZ-S38D LED Light
CZ-S50D LED Light
RCZ01 remote | | - | | [Ecoclim](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Ecoclim.cpp) | **[EcoClim](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Ecoclim.h)** | HYSFR-P348 remote
ZC200DPO A/C | | Yes | | [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 | +| [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[Centek](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | SCT-65Q09 A/C
YKR-P/002E remote | | Yes | | [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | Classic INV 17 / AXW12DCS A/C
YKR-M/003E remote | | Yes | | [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[Frigidaire](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | FGPC102AB1 A/C | | Yes | +| [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[Subtropic](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | SUB-07HN1_18Y A/C
YKR-H/102E remote | | Yes | | [EliteScreens](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_EliteScreens.cpp) | **Elite Screens** | CineTension2 / CineTension3 series
Home2 / Home3 series
Spectrum series
VMAX Plus4 series
VMAX2 / VMAX2 Plus series
ZSP-IR-B / ZSP-IR-W remote | | - | | [EliteScreens](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_EliteScreens.cpp) | **Lumene Screens** | Embassy | | - | -| [Epson](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Epson.cpp) | **Epson** | EN-TW9100W Projector | | - | -| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AGTV14LAC A/C (ARRAH2E)
AR-DB1 remote (ARDB1)
AR-DL10 remote (ARDB1)
AR-RAC1E remote (ARRAH2E)
AR-RAE1E remote (ARRAH2E)
AR-RAH1U remote (ARREB1E)
AR-RAH2E remote (ARRAH2E)
AR-REB1E remote (ARREB1E)
AR-REW4E remote (ARREW4E)
AR-RY4 remote (ARRY4)
AST9RSGCW A/C (ARDB1)
ASTB09LBC A/C (ARRY4)
ASU12RLF A/C (ARREB1E)
ASU30C1 A/C (ARDB1)
ASYG09KETA-B A/C (ARREW4E)
ASYG30LFCA A/C (ARRAH2E)
ASYG7LMCA A/C (ARREB1E) | ARDB1
ARJW2
ARRAH2E
ARREB1E
ARREW4E
ARRY4 | Yes | +| [Epson](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Epson.cpp) | **Epson** | EN-TW9100W Projector
EX3220 Projector
EX5220 Projector
EX5230 Projector
EX6220 Projector
EX7220 Projector
VS230 Projector
VS330 Projector | | - | +| [Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.cpp) | **[Fujitsu](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Fujitsu.h)** | AGTV14LAC A/C (ARRAH2E)
AR-DB1 remote (ARDB1)
AR-DL10 remote (ARDB1)
AR-RAC1E remote (ARRAH2E)
AR-RAE1E remote (ARRAH2E)
AR-RAH1U remote (ARREB1E)
AR-RAH2E remote (ARRAH2E)
AR-REB1E remote (ARREB1E)
AR-REB4E remote (ARREB1E)
AR-REW1E remote (ARREW4E)
AR-REW4E remote (ARREW4E)
AR-RY4 remote (ARRY4)
AST9RSGCW A/C (ARDB1)
ASTB09LBC A/C (ARRY4)
ASTG09K A/C (ARREW4E)
ASTG18K A/C (ARREW4E)
ASU12RLF A/C (ARREB1E)
ASU30C1 A/C (ARDB1)
ASYG09KETA-B A/C (ARREW4E)
ASYG30LFCA A/C (ARRAH2E)
ASYG7LMCA A/C (ARREB1E) | ARDB1
ARJW2
ARRAH2E
ARREB1E
ARREW4E
ARRY4 | 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)** | AOHG09LLC A/C (ARRAH2E)
AR-JW2 remote (ARJW2)
AR-RCE1E remote (ARRAH2E)
ASHG09LLCA A/C (ARRAH2E) | ARDB1
ARJW2
ARRAH2E
ARREB1E
ARREW4E
ARRY4 | Yes | | [GICable](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **G.I. Cable** | XRC-200 remote | | - | | [GlobalCache](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GlobalCache.cpp) | **Global Cache** | Control Tower IR DB | | - | @@ -39,11 +44,13 @@ | [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Amana](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | PBC093G00CC A/C
YX1FF remote | YAW1F
YBOFB | Yes | | [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Cooper & Hunter](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | CH-S09FTXG A/C
YB1F2 remote | YAW1F
YBOFB | 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 | YAW1F
YBOFB | Yes | -| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | YAA1FBF remote
YB1F2F remote | YAW1F
YBOFB | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | VIR09HP115V1AH A/C
VIR12HP230V1AH A/C
YAA1FBF remote
YAN1F1 remote
YB1F2F remote | YAW1F
YBOFB | Yes | | [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | YBOFB remote
YBOFB2 remote | YAW1F
YBOFB | 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 | YAW1F
YBOFB | 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 | YAW1F
YBOFB | 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 (HAIER_AC_YRW02)
HSU07-HEA03 remote (HAIER_AC)
YR-W02 remote (HAIER_AC_YRW02) | | Yes | +| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Vailland](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | VAI5-035WNI A/C
YACIFB remote | YAW1F
YBOFB | Yes | +| [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Daichi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | D-H A/C (HAIER_AC176) | | 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 (HAIER_AC_YRW02)
HSU07-HEA03 remote (HAIER_AC)
V9014557 M47 8D remote (HAIER_AC176)
YR-W02 remote (HAIER_AC_YRW02) | | Yes | | [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Mabe](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | MMI18HDBWCA6MI8 A/C (HAIER_AC176)
V12843 HJ200223 remote (HAIER_AC176) | | 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)** | KAZE-312KSDP A/C (HITACHI_AC1)
LT0541-HTA remote (HITACHI_AC1)
PC-LH3B (HITACHI_AC3)
R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1)
RAR-8P2 remote (HITACHI_AC424)
RAS-22NK A/C (HITACHI_AC344)
RAS-35THA6 remote
RAS-AJ25H A/C (HITACHI_AC424)
RF11T1 remote (HITACHI_AC344)
Series VI A/C (Circa 2007) (HITACHI_AC1) | | Yes | | [Inax](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - | @@ -53,7 +60,7 @@ | [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 | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | A5VEY A/C
YB1FA remote | | Yes | | [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[General Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711AR2853M A/C Remote (LG)
AG1BH09AW101 Split A/C (LG) | | 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 (LG)
A4UW30GFA2 A/C (LG2 - AKB74955603 & AKB73757604)
AKB73757604 remote (LG2 - AKB73757604)
AKB74395308 remote (LG2)
AKB74955603 remote (LG2 - AKB74955603)
AKB75215403 remote (LG2)
AMNW09GSJA0 A/C (LG2 - AKB74955603)
AMNW24GTPA1 A/C (LG2 - AKB73757604)
S4-W12JA3AA A/C (LG2) | | 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 (LG)
A4UW30GFA2 A/C (LG2 - AKB74955603 & AKB73757604)
AKB73315611 remote (LG2 - AKB74955603)
AKB73757604 remote (LG2 - AKB73757604)
AKB74395308 remote (LG2)
AKB74955603 remote (LG2 - AKB74955603)
AKB75215403 remote (LG2)
AMNW09GSJA0 A/C (LG2 - AKB74955603)
AMNW24GTPA1 A/C (LG2 - AKB73757604)
MS05SQ NW0 A/C (LG2 - AKB74955603)
S4-W12JA3AA A/C (LG2) | | Yes | | [Lasertag](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Lasertag** | Phaser emitters | | - | | [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) | **Lutron** | MIR-ITFS remote
MIR-ITFS-F remote
MIR-ITFS-LF remote
SP-HT remote | | - | @@ -69,9 +76,11 @@ | [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)** | RG66B6(B)/BGEFU1 remote (MIDEA)
RUBO18GMFILCAD A/C (18K BTU) (MIDEA)
RYBO12GMFILCAD A/C (12K BTU) (MIDEA)
UB018GMFILCFHD A/C (12K BTU) (MIDEA)
WS012GMFI22HLD A/C (12K BTU) (MIDEA)
WS018GMFI22HLD A/C (12K BTU) (MIDEA) | | Yes | | [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RG57H(B)/BGE remote (MIDEA)
TROTEC PAC 3900 X (MIDEA) | | Yes | | [MilesTag2](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MilesTag2.cpp) | **Milestag2** | Various | | - | -| [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **Mirage** | VLU series A/C | | - | +| [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Maxell](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | KKG9A-C1 remote
MX-CH18CF A/C | KKG29AC1
KKG9AC1 | Yes | +| [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | VLU series A/C | KKG29AC1
KKG9AC1 | Yes | +| [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Tronitechnik](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | KKG29A-C1 remote
Reykir 9000 A/C | KKG29AC1
KKG9AC1 | 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 (MITSUBISHI2)
KM14A 0179213 remote
MS-GK24VA A/C
TV (MITSUBISHI) | | Yes | -| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote (MITSUBISHI136)
KPOA remote (MITSUBISHI112)
MLZ-RX5017AS A/C (MITSUBISHI_AC)
MSH-A24WV A/C (MITSUBISHI112)
MSZ-GV2519 A/C (MITSUBISHI_AC)
MSZ-SF25VE3 A/C (MITSUBISHI_AC)
MSZ-ZW4017S A/C (MITSUBISHI_AC)
MUH-A24WV A/C (MITSUBISHI112)
PEAD-RP71JAA Ducted A/C (MITSUBISHI136)
RH151/M21ED6426 remote (MITSUBISHI_AC)
SG153/M21EDF426 remote (MITSUBISHI_AC)
SG15D remote (MITSUBISHI_AC) | | Yes | +| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote (MITSUBISHI136)
KPOA remote (MITSUBISHI112)
MLZ-RX5017AS A/C (MITSUBISHI_AC)
MSH-A24WV A/C (MITSUBISHI112)
MSZ-FHnnVE A/C (MITSUBISHI_AC)
MSZ-GV2519 A/C (MITSUBISHI_AC)
MSZ-SF25VE3 A/C (MITSUBISHI_AC)
MSZ-ZW4017S A/C (MITSUBISHI_AC)
MUH-A24WV A/C (MITSUBISHI112)
PEAD-RP71JAA Ducted A/C (MITSUBISHI136)
RH151/M21ED6426 remote (MITSUBISHI_AC)
SG153/M21EDF426 remote (MITSUBISHI_AC)
SG15D remote (MITSUBISHI_AC) | | 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 (88 bit)
RLA502A700B remote (152 bit)
SRKxxZJ-S A/C (88 bit)
SRKxxZM-S A/C (152 bit)
SRKxxZMXA-S A/C (152 bit) | | Yes | | [Multibrackets](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Multibrackets.cpp) | **Multibrackets** | Motorized Swing mount large - 4500 | | - | | [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Aloka](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | SleepyLights LED Lamp | | - | @@ -87,23 +96,27 @@ | [Pronto](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Pronto.cpp) | **Pronto** | Pronto Hex | | - | | [RC5_RC6](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_RC5_RC6.cpp) | **Philips** | RC-5X (RC5X)
Standard RC-5 (RC5)
Standard RC-6 (RC6) | | - | | [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)** | AH59-02692E Soundbar remote (SAMSUNG36)
AK59-00167A Bluray remote (SAMSUNG36)
AR09FSSDAWKNFA A/C (SAMSUNG_AC)
AR09HSFSBWKN A/C (SAMSUNG_AC)
AR12HSSDBWKNEU A/C (SAMSUNG_AC)
AR12KSFPEWQNET A/C (SAMSUNG_AC)
AR12NXCXAWKXEU A/C (SAMSUNG_AC)
BN59-01178B TV remote (SAMSUNG)
DB63-03556X003 remote
DB93-14195A remote (SAMSUNG_AC)
DB93-16761C remote
HW-J551 Soundbar (SAMSUNG36)
IEC-R03 remote
UA55H6300 TV (SAMSUNG) | | Yes | +| [Rhoss](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Rhoss.cpp) | **[Rhoss](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Rhoss.h)** | Idrowall MPCV 20-30-35-40 | | Yes | +| [Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.cpp) | **[Samsung](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Samsung.h)** | AH59-02692E Soundbar remote (SAMSUNG36)
AK59-00167A Bluray remote (SAMSUNG36)
AR09FSSDAWKNFA A/C (SAMSUNG_AC)
AR09HSFSBWKN A/C (SAMSUNG_AC)
AR12HSSDBWKNEU A/C (SAMSUNG_AC)
AR12KSFPEWQNET A/C (SAMSUNG_AC)
AR12NXCXAWKXEU A/C (SAMSUNG_AC)
AR12TXEAAWKNEU A/C (SAMSUNG_AC)
BN59-01178B TV remote (SAMSUNG)
DB63-03556X003 remote
DB93-14195A remote (SAMSUNG_AC)
DB93-16761C remote
DB96-24901C remote (SAMSUNG_AC)
HW-J551 Soundbar (SAMSUNG36)
IEC-R03 remote
UA55H6300 TV (SAMSUNG) | | Yes | | [Sanyo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sanyo.cpp) | **[Sanyo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sanyo.h)** | LC7461 transmitter IC (SANYO_LC7461)
RCS-2HS4E remote (SANYO_AC)
RCS-2S4E remote (SANYO_AC)
SA 8650B - disabled
SAP-K121AHA A/C (SANYO_AC)
SAP-K242AH A/C (SANYO_AC) | | Yes | -| [Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AH-A12REVP-1 A/C (A903)
AH-AxSAY A/C (A907)
AH-PR13-GL A/C (A903)
AH-XP10NRY A/C (A903)
AY-ZP40KR A/C (A907)
CRMC-820 JBEZ remote (A903)
CRMC-A705 JBEZ remote (A705)
CRMC-A863 JBEZ remote (A903)
CRMC-A903JBEZ remote (A903)
CRMC-A907 JBEZ remote (A907)
LC-52D62U TV | A705
A903
A907 | Yes | +| [Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sharp.h)** | AH-A12REVP-1 A/C (A903)
AH-AxSAY A/C (A907)
AH-PR13-GL A/C (A903)
AH-XP10NRY A/C (A903)
AY-ZP40KR A/C (A907)
CRMC-820 JBEZ remote (A903)
CRMC-A705 JBEZ remote (A705)
CRMC-A863 JBEZ remote (A903)
CRMC-A903JBEZ remote (A903)
CRMC-A907 JBEZ remote (A907)
CRMC-A950 JBEZ (A907)
LC-52D62U TV | A705
A903
A907 | 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) | **Sony** | HT-CT380 Soundbar (Uses 38kHz & 3 repeats) | | - | | [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Blyss** | Owen-SW-5 3 Fan
WP-YK8 090218 remote | | - | | [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **SamHop** | SM3015 Fan Remote Control
SM5021 Encoder chip
SM5032 Decoder chip | | - | | [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Satellite Electronic** | ID6 Remote
JY199I Fan driver
JY199I-L Fan driver | | - | +| [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **SilverCrest** | SSVS 85 A1 Fan | | - | | [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Symphony** | Air Cooler 3Di | | - | | [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Westinghouse** | 78095 Remote
Ceiling fan | | - | -| [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 | -| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[TCL](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | TAC-09CHSD/XA31I A/C | | Yes | +| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Daewoo](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | DSB-F0934ELH-V A/C
GYKQ-52E remote | GZ055BE1
TAC09CHSD | Yes | +| [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 (TAC09CHSD) | GZ055BE1
TAC09CHSD | Yes | +| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[TCL](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | TAC-09CHSD/XA31I A/C (TAC09CHSD) | GZ055BE1
TAC09CHSD | Yes | +| [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | Allegro SSA-09H A/C (GZ055BE1)
GZ-055B-E1 remote (GZ055BE1) | GZ055BE1
TAC09CHSD | Yes | | [Technibel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Technibel.cpp) | **[Technibel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Technibel.h)** | IRO PLUS | | Yes | | [Teco](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.cpp) | **[Alaska](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teco.h)** | SAC9010QC A/C
SAC9010QC remote | | Yes | | [Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teknopoint.cpp) | **Teknopoint** | Allegro SSA-09H A/C
GZ-055B-E1 remote | | - | | [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | 42NQV025M2 / 38NYV025M2 A/C
42NQV035M2 / 38NYV035M2 A/C
42NQV050M2 / 38NYV050M2 A/C
42NQV060M2 / 38NYV060M2 A/C | | 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-2558V A/C
RAS-B13N3KV2
RAS-B13N3KVP-E
WC-L03SE
WH-TA04NE
WH-UB03NJ remote | | 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-2558V A/C
RAS-25SKVP2-ND A/C
RAS-B13N3KV2
RAS-B13N3KVP-E
WC-L03SE
WH-TA01JE remote
WH-TA04NE
WH-UB03NJ remote | | Yes | | [Transcold](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Transcold.cpp) | **[Transcold](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Transcold.h)** | M1-F-NO-6 A/C | | Yes | | [Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Duux](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | Blizzard Smart 10K / DXMA04 A/C (TROTEC) | | Yes | | [Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.cpp) | **[Trotec](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Trotec.h)** | PAC 3200 A/C (TROTEC)
PAC 3550 Pro A/C (TROTEC_3550) | | Yes | @@ -127,15 +140,18 @@ ## Send & decodable protocols: +- AIRTON - AIRWELL - AIWA_RC_T501 - AMCOR - ARGO +- ARRIS - BOSE - CARRIER_AC - CARRIER_AC40 - CARRIER_AC64 - COOLIX +- COOLIX48 - CORONA_AC - DAIKIN - DAIKIN128 @@ -202,6 +218,7 @@ - RC5X - RC6 - RCMM +- RHOSS - SAMSUNG - SAMSUNG36 - SAMSUNG_AC diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino index 180c4214c..c2e427166 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -1249,7 +1249,7 @@ void handleInfo(void) { "Built: " __DATE__ " " __TIME__ "
" "Period Offset: ") + String(offset) + F("us
" - "IR Lib Version: " _IRREMOTEESP8266_VERSION_ "
" + "IR Lib Version: " _IRREMOTEESP8266_VERSION_STR "
" #if defined(ESP8266) "ESP8266 Core Version: ") + ESP.getCoreVersion() + F("
" "Free Sketch Space: ") + String(maxSketchSpace() >> 10) + F("k
" diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino index ae7e21dab..70dbdba42 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -154,7 +154,7 @@ void loop() { if (results.overflow) Serial.printf(D_WARN_BUFFERFULL "\n", kCaptureBufferSize); // Display the library version the message was captured with. - Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_ "\n"); + Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_STR "\n"); // Display the tolerance percentage if it has been change from the default. if (kTolerancePercentage != kTolerance) Serial.printf(D_STR_TOLERANCE " : %d%%\n", kTolerancePercentage); diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini index 907ee7cb3..b56304f66 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini @@ -52,12 +52,15 @@ build_flags = -D_IR_LOCALE_=it-IT ; Italian [env:pt-BR] build_flags = -D_IR_LOCALE_=pt-BR ; Portuguese (Brazilian) -[env:zh-CN] -build_flags = -D_IR_LOCALE_=zh-CN ; Chinese (Simplified) +[env:ru-RU] +build_flags = -D_IR_LOCALE_=ru-RU ; Russian [env:sv-SE] build_flags = -D_IR_LOCALE_=sv-SE ; Swedish +[env:zh-CN] +build_flags = -D_IR_LOCALE_=zh-CN ; Chinese (Simplified) + ; Build the library with all protocols disabled to flush out #if/#ifdef issues & ; any compiler warnings, by turning them into errors. [env:shakedown_no_protocols] diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino index fc2037368..0948ae5e9 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino @@ -163,7 +163,7 @@ void loop() { if (results.overflow) Serial.printf(D_WARN_BUFFERFULL "\n", kCaptureBufferSize); // Display the library version the message was captured with. - Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_ "\n"); + Serial.println(D_STR_LIBRARY " : v" _IRREMOTEESP8266_VERSION_STR "\n"); // Display the tolerance percentage if it has been change from the default. if (kTolerancePercentage != kTolerance) Serial.printf(D_STR_TOLERANCE " : %d%%\n", kTolerancePercentage); diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini b/lib/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini index 95a90ac86..e5df92876 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini @@ -49,5 +49,14 @@ build_flags = -D_IR_LOCALE_=fr-FR ; French [env:it-IT] build_flags = -D_IR_LOCALE_=it-IT ; Italian +[env:pt-BR] +build_flags = -D_IR_LOCALE_=pt-BR ; Portuguese (Brazilian) + +[env:ru-RU] +build_flags = -D_IR_LOCALE_=ru-RU ; Russian + +[env:sv-SE] +build_flags = -D_IR_LOCALE_=sv-SE ; Swedish + [env:zh-CN] build_flags = -D_IR_LOCALE_=zh-CN ; Chinese (Simplified) diff --git a/lib/IRremoteESP8266/keywords.txt b/lib/IRremoteESP8266/keywords.txt index 60e714c5d..8e0379173 100644 --- a/lib/IRremoteESP8266/keywords.txt +++ b/lib/IRremoteESP8266/keywords.txt @@ -20,6 +20,8 @@ # Datatypes & Classes (KEYWORD1) ####################################### +CoronaSection KEYWORD1 +IRAirtonAc KEYWORD1 IRAirwellAc KEYWORD1 IRAmcorAc KEYWORD1 IRArgoAC KEYWORD1 @@ -52,6 +54,7 @@ IRKelonAc KEYWORD1 IRKelvinatorAC KEYWORD1 IRLgAc KEYWORD1 IRMideaAC KEYWORD1 +IRMirageAc KEYWORD1 IRMitsubishi112 KEYWORD1 IRMitsubishi136 KEYWORD1 IRMitsubishiAC KEYWORD1 @@ -60,6 +63,7 @@ IRMitsubishiHeavy88Ac KEYWORD1 IRNeoclimaAc KEYWORD1 IRPanasonicAc KEYWORD1 IRPanasonicAc32 KEYWORD1 +IRRhossAc KEYWORD1 IRSamsungAc KEYWORD1 IRSanyoAc KEYWORD1 IRSanyoAc88 KEYWORD1 @@ -85,10 +89,12 @@ decode_type_t KEYWORD1 fanspeed_t KEYWORD1 fujitsu_ac_remote_model_t KEYWORD1 gree_ac_remote_model_t KEYWORD1 +haier_ac176_remote_model_t KEYWORD1 hitachi_ac1_remote_model_t KEYWORD1 irparams_t KEYWORD1 lg_ac_remote_model_t KEYWORD1 match_result_t KEYWORD1 +mirage_ac_remote_model_t KEYWORD1 opmode_t KEYWORD1 panasonic_ac_remote_model_t KEYWORD1 sharp_ac_remote_model_t KEYWORD1 @@ -108,6 +114,8 @@ _cancelOffTimer KEYWORD2 _cancelOnTimer KEYWORD2 _delayMicroseconds KEYWORD2 _getEconoToggle KEYWORD2 +_getOffTimer KEYWORD2 +_getOnTimer KEYWORD2 _getTime KEYWORD2 _getTimer KEYWORD2 _isAKB73757604 KEYWORD2 @@ -118,6 +126,9 @@ _restoreState KEYWORD2 _sendSony KEYWORD2 _setEconoToggle KEYWORD2 _setMode KEYWORD2 +_setOffTimer KEYWORD2 +_setOnTimer KEYWORD2 +_setSleepTimer KEYWORD2 _setTemp KEYWORD2 _setTime KEYWORD2 _setTimer KEYWORD2 @@ -136,7 +147,9 @@ addSwingHToString KEYWORD2 addSwingVToString KEYWORD2 addTempFloatToString KEYWORD2 addTempToString KEYWORD2 +addToggleToString KEYWORD2 adjustRepeat KEYWORD2 +airton KEYWORD2 airwell KEYWORD2 amcor KEYWORD2 argo KEYWORD2 @@ -164,6 +177,7 @@ checkSum KEYWORD2 checkZjsSig KEYWORD2 checkZmsSig KEYWORD2 checksum KEYWORD2 +cleanState KEYWORD2 clearOnTimerFlag KEYWORD2 clearPowerSpecial KEYWORD2 clearSensorTemp KEYWORD2 @@ -190,6 +204,7 @@ daikin2 KEYWORD2 daikin216 KEYWORD2 daikin64 KEYWORD2 decode KEYWORD2 +decodeAirton KEYWORD2 decodeAirwell KEYWORD2 decodeAiwaRCT501 KEYWORD2 decodeAmcor KEYWORD2 @@ -200,6 +215,7 @@ decodeCOOLIX KEYWORD2 decodeCarrierAC KEYWORD2 decodeCarrierAC40 KEYWORD2 decodeCarrierAC64 KEYWORD2 +decodeCoolix48 KEYWORD2 decodeCoronaAc KEYWORD2 decodeDISH KEYWORD2 decodeDaikin KEYWORD2 @@ -260,6 +276,7 @@ decodePioneer KEYWORD2 decodeRC5 KEYWORD2 decodeRC6 KEYWORD2 decodeRCMM KEYWORD2 +decodeRhoss KEYWORD2 decodeSAMSUNG KEYWORD2 decodeSamsung36 KEYWORD2 decodeSamsungAC KEYWORD2 @@ -322,10 +339,12 @@ fahrenheitToCelsius KEYWORD2 fanspeedToString KEYWORD2 fixChecksum KEYWORD2 fixup KEYWORD2 +fromCommon KEYWORD2 fujitsu KEYWORD2 get10CHeat KEYWORD2 get3D KEYWORD2 get8CHeat KEYWORD2 +getAbsenseDetect KEYWORD2 getBeep KEYWORD2 getBit KEYWORD2 getBoost KEYWORD2 @@ -334,6 +353,7 @@ getBufSize KEYWORD2 getButton KEYWORD2 getCelsius KEYWORD2 getClean KEYWORD2 +getCleanToggle KEYWORD2 getClock KEYWORD2 getCmd KEYWORD2 getComfort KEYWORD2 @@ -342,9 +362,11 @@ getCorrectedRawLength KEYWORD2 getCurrTime KEYWORD2 getCurrentDay KEYWORD2 getCurrentTime KEYWORD2 +getDirectIndirect KEYWORD2 getDisplay KEYWORD2 getDisplayTempSource KEYWORD2 getDryGrade KEYWORD2 +getEcocool KEYWORD2 getEcono KEYWORD2 getEconoToggle KEYWORD2 getEnableSensorTemp KEYWORD2 @@ -363,6 +385,8 @@ getHold KEYWORD2 getHumid KEYWORD2 getHumidity KEYWORD2 getIFeel KEYWORD2 +getISave10C KEYWORD2 +getISee KEYWORD2 getId KEYWORD2 getInternalStateLength KEYWORD2 getIon KEYWORD2 @@ -370,9 +394,11 @@ getIonFilter KEYWORD2 getLed KEYWORD2 getLight KEYWORD2 getLightToggle KEYWORD2 +getLock KEYWORD2 getMax KEYWORD2 getMode KEYWORD2 getMold KEYWORD2 +getNaturalFlow KEYWORD2 getNight KEYWORD2 getOffSleepTimer KEYWORD2 getOffTime KEYWORD2 @@ -399,14 +425,18 @@ getSectionByte KEYWORD2 getSectionChecksum KEYWORD2 getSensor KEYWORD2 getSensorTemp KEYWORD2 +getSensorUpdate KEYWORD2 getSilent KEYWORD2 getSleep KEYWORD2 getSleepTime KEYWORD2 +getSleepTimer KEYWORD2 getSleepTimerEnabled KEYWORD2 getSpecial KEYWORD2 getSpeed KEYWORD2 getStartClock KEYWORD2 +getState KEYWORD2 getStateLength KEYWORD2 +getStatePrev KEYWORD2 getStopClock KEYWORD2 getSupercool KEYWORD2 getSwing KEYWORD2 @@ -457,6 +487,7 @@ haier KEYWORD2 haier176 KEYWORD2 haierYrwo2 KEYWORD2 handleSpecialState KEYWORD2 +handleToggles KEYWORD2 hasACState KEYWORD2 hasInvertedStates KEYWORD2 hasStateChanged KEYWORD2 @@ -513,6 +544,7 @@ matchSpaceRange KEYWORD2 midea KEYWORD2 minRepeats KEYWORD2 minsToString KEYWORD2 +mirage KEYWORD2 mitsubishi KEYWORD2 mitsubishi112 KEYWORD2 mitsubishi136 KEYWORD2 @@ -536,11 +568,13 @@ resultToSourceCode KEYWORD2 resultToTimingInfo KEYWORD2 resume KEYWORD2 reverseBits KEYWORD2 +rhoss KEYWORD2 samsung KEYWORD2 sanyo KEYWORD2 sanyo88 KEYWORD2 send KEYWORD2 sendAc KEYWORD2 +sendAirton KEYWORD2 sendAirwell KEYWORD2 sendAiwaRCT501 KEYWORD2 sendAmcor KEYWORD2 @@ -551,6 +585,7 @@ sendCOOLIX KEYWORD2 sendCarrierAC KEYWORD2 sendCarrierAC40 KEYWORD2 sendCarrierAC64 KEYWORD2 +sendCoolix48 KEYWORD2 sendCoronaAc KEYWORD2 sendDISH KEYWORD2 sendDaikin KEYWORD2 @@ -626,6 +661,7 @@ sendRC5 KEYWORD2 sendRC6 KEYWORD2 sendRCMM KEYWORD2 sendRaw KEYWORD2 +sendRhoss KEYWORD2 sendSAMSUNG KEYWORD2 sendSamsung36 KEYWORD2 sendSamsungAC KEYWORD2 @@ -658,6 +694,7 @@ serialPrintUint64 KEYWORD2 set10CHeat KEYWORD2 set3D KEYWORD2 set8CHeat KEYWORD2 +setAbsenseDetect KEYWORD2 setAuto KEYWORD2 setBeep KEYWORD2 setBit KEYWORD2 @@ -667,6 +704,7 @@ setBreeze KEYWORD2 setButton KEYWORD2 setCelsius KEYWORD2 setClean KEYWORD2 +setCleanToggle KEYWORD2 setClock KEYWORD2 setCmd KEYWORD2 setComfort KEYWORD2 @@ -674,9 +712,11 @@ setCommand KEYWORD2 setCurrTime KEYWORD2 setCurrentDay KEYWORD2 setCurrentTime KEYWORD2 +setDirectIndirect KEYWORD2 setDisplay KEYWORD2 setDisplayTempSource KEYWORD2 setDryGrade KEYWORD2 +setEcocool KEYWORD2 setEcono KEYWORD2 setEconoToggle KEYWORD2 setEnableSensorTemp KEYWORD2 @@ -695,6 +735,8 @@ setHold KEYWORD2 setHumid KEYWORD2 setHumidity KEYWORD2 setIFeel KEYWORD2 +setISave10C KEYWORD2 +setISee KEYWORD2 setId KEYWORD2 setInvertedStates KEYWORD2 setIon KEYWORD2 @@ -702,10 +744,12 @@ setIonFilter KEYWORD2 setLed KEYWORD2 setLight KEYWORD2 setLightToggle KEYWORD2 +setLock KEYWORD2 setMax KEYWORD2 setMode KEYWORD2 setModel KEYWORD2 setMold KEYWORD2 +setNaturalFlow KEYWORD2 setNight KEYWORD2 setOffTime KEYWORD2 setOffTimeEnabled KEYWORD2 @@ -730,6 +774,7 @@ setSave KEYWORD2 setSensor KEYWORD2 setSensorTemp KEYWORD2 setSensorTempRaw KEYWORD2 +setSensorUpdate KEYWORD2 setSilent KEYWORD2 setSleep KEYWORD2 setSleepTimer KEYWORD2 @@ -795,6 +840,7 @@ technibel KEYWORD2 teco KEYWORD2 ticksHigh KEYWORD2 ticksLow KEYWORD2 +toCommon KEYWORD2 toString KEYWORD2 toggleArrisRelease KEYWORD2 toggleRC5 KEYWORD2 @@ -827,6 +873,7 @@ xorBytes KEYWORD2 A705 LITERAL1 A903 LITERAL1 A907 LITERAL1 +AIRTON LITERAL1 AIRWELL LITERAL1 AIWA_RC_T501 LITERAL1 AIWA_RC_T501_BITS LITERAL1 @@ -871,6 +918,7 @@ CARRIER_AC40 LITERAL1 CARRIER_AC64 LITERAL1 CARRIER_AC_BITS LITERAL1 COOLIX LITERAL1 +COOLIX48 LITERAL1 COOLIX_BITS LITERAL1 CORONA_AC LITERAL1 DAIKIN LITERAL1 @@ -894,6 +942,7 @@ DAIKIN_HEAT LITERAL1 DAIKIN_MAX_TEMP LITERAL1 DAIKIN_MIN_TEMP LITERAL1 DECODE_AC LITERAL1 +DECODE_AIRTON LITERAL1 DECODE_AIRWELL LITERAL1 DECODE_AIWA_RC_T501 LITERAL1 DECODE_AMCOR LITERAL1 @@ -904,6 +953,7 @@ DECODE_CARRIER_AC LITERAL1 DECODE_CARRIER_AC40 LITERAL1 DECODE_CARRIER_AC64 LITERAL1 DECODE_COOLIX LITERAL1 +DECODE_COOLIX48 LITERAL1 DECODE_CORONA_AC LITERAL1 DECODE_DAIKIN LITERAL1 DECODE_DAIKIN128 LITERAL1 @@ -969,6 +1019,7 @@ DECODE_PRONTO LITERAL1 DECODE_RC5 LITERAL1 DECODE_RC6 LITERAL1 DECODE_RCMM LITERAL1 +DECODE_RHOSS LITERAL1 DECODE_SAMSUNG LITERAL1 DECODE_SAMSUNG36 LITERAL1 DECODE_SAMSUNG_AC LITERAL1 @@ -1072,7 +1123,7 @@ HAIER_AC_CMD_MODE LITERAL1 HAIER_AC_CMD_OFF LITERAL1 HAIER_AC_CMD_ON LITERAL1 HAIER_AC_CMD_SLEEP LITERAL1 -HAIER_AC_CMD_SWING LITERAL1 +HAIER_AC_CMD_SWINGV LITERAL1 HAIER_AC_CMD_TEMP_DOWN LITERAL1 HAIER_AC_CMD_TEMP_UP LITERAL1 HAIER_AC_CMD_TIMER_CANCEL LITERAL1 @@ -1089,10 +1140,10 @@ HAIER_AC_HEAT LITERAL1 HAIER_AC_MAX_TEMP LITERAL1 HAIER_AC_MIN_TEMP LITERAL1 HAIER_AC_STATE_LENGTH LITERAL1 -HAIER_AC_SWING_CHG LITERAL1 -HAIER_AC_SWING_DOWN LITERAL1 -HAIER_AC_SWING_OFF LITERAL1 -HAIER_AC_SWING_UP LITERAL1 +HAIER_AC_SWINGV_CHG LITERAL1 +HAIER_AC_SWINGV_DOWN LITERAL1 +HAIER_AC_SWINGV_OFF LITERAL1 +HAIER_AC_SWINGV_UP LITERAL1 HAIER_AC_YRW02 LITERAL1 HAIER_AC_YRW02_AUTO LITERAL1 HAIER_AC_YRW02_BUTTON_FAN LITERAL1 @@ -1119,8 +1170,6 @@ HAIER_AC_YRW02_SWING_DOWN LITERAL1 HAIER_AC_YRW02_SWING_MIDDLE LITERAL1 HAIER_AC_YRW02_SWING_OFF LITERAL1 HAIER_AC_YRW02_SWING_TOP LITERAL1 -HAIER_AC_YRW02_TURBO_HIGH LITERAL1 -HAIER_AC_YRW02_TURBO_LOW LITERAL1 HAIER_AC_YRW02_TURBO_OFF LITERAL1 HIGH LITERAL1 HITACHI_AC LITERAL1 @@ -1153,6 +1202,8 @@ KELVINATOR_HEAT LITERAL1 KELVINATOR_MAX_TEMP LITERAL1 KELVINATOR_MIN_TEMP LITERAL1 KELVINATOR_STATE_LENGTH LITERAL1 +KKG29AC1 LITERAL1 +KKG9AC1 LITERAL1 LASERTAG LITERAL1 LASERTAG_BITS LITERAL1 LEGOPF LITERAL1 @@ -1222,7 +1273,6 @@ PANASONIC_BITS LITERAL1 PIONEER LITERAL1 PROGMEM LITERAL1 PRONTO LITERAL1 -PSTR LITERAL1 RAW LITERAL1 RAWTICK LITERAL1 RC5 LITERAL1 @@ -1234,6 +1284,7 @@ RC6_36_BITS LITERAL1 RC6_MODE0_BITS LITERAL1 RCMM LITERAL1 RCMM_BITS LITERAL1 +RHOSS LITERAL1 R_LT0541_HTA_A LITERAL1 R_LT0541_HTA_B LITERAL1 SAMSUNG LITERAL1 @@ -1246,6 +1297,7 @@ SANYO_AC88 LITERAL1 SANYO_LC7461 LITERAL1 SANYO_LC7461_BITS LITERAL1 SANYO_SA8650B_BITS LITERAL1 +SEND_AIRTON LITERAL1 SEND_AIRWELL LITERAL1 SEND_AIWA_RC_T501 LITERAL1 SEND_AMCOR LITERAL1 @@ -1256,6 +1308,7 @@ SEND_CARRIER_AC LITERAL1 SEND_CARRIER_AC40 LITERAL1 SEND_CARRIER_AC64 LITERAL1 SEND_COOLIX LITERAL1 +SEND_COOLIX48 LITERAL1 SEND_CORONA_AC LITERAL1 SEND_DAIKIN LITERAL1 SEND_DAIKIN128 LITERAL1 @@ -1321,6 +1374,7 @@ SEND_RAW LITERAL1 SEND_RC5 LITERAL1 SEND_RC6 LITERAL1 SEND_RCMM LITERAL1 +SEND_RHOSS LITERAL1 SEND_SAMSUNG LITERAL1 SEND_SAMSUNG36 LITERAL1 SEND_SAMSUNG_AC LITERAL1 @@ -1393,6 +1447,8 @@ TRUMA LITERAL1 UNKNOWN LITERAL1 UNUSED LITERAL1 USE_IRAM_ATTR LITERAL1 +V9014557_A LITERAL1 +V9014557_B LITERAL1 VESTEL_AC LITERAL1 VOLTAS LITERAL1 WHIRLPOOL_AC LITERAL1 @@ -1402,11 +1458,39 @@ XMP LITERAL1 YAW1F LITERAL1 YBOFB LITERAL1 ZEPEAL LITERAL1 +k0Str LITERAL1 k10CHeatStr LITERAL1 +k122lzfStr LITERAL1 +k1Str LITERAL1 k3DStr LITERAL1 k6thSenseStr LITERAL1 k8CHeatStr LITERAL1 +kA705Str LITERAL1 +kA903Str LITERAL1 +kA907Str LITERAL1 +kAbsenseDetectStr LITERAL1 kAirFlowStr LITERAL1 +kAirtonAuto LITERAL1 +kAirtonBitMark LITERAL1 +kAirtonBits LITERAL1 +kAirtonCool LITERAL1 +kAirtonDefaultRepeat LITERAL1 +kAirtonDry LITERAL1 +kAirtonFan LITERAL1 +kAirtonFanAuto LITERAL1 +kAirtonFanHigh LITERAL1 +kAirtonFanLow LITERAL1 +kAirtonFanMax LITERAL1 +kAirtonFanMed LITERAL1 +kAirtonFanMin LITERAL1 +kAirtonFreq LITERAL1 +kAirtonHdrMark LITERAL1 +kAirtonHdrSpace LITERAL1 +kAirtonHeat LITERAL1 +kAirtonMaxTemp LITERAL1 +kAirtonMinTemp LITERAL1 +kAirtonOneSpace LITERAL1 +kAirtonZeroSpace LITERAL1 kAirwellAuto LITERAL1 kAirwellBits LITERAL1 kAirwellCool LITERAL1 @@ -1432,6 +1516,9 @@ kAiwaRcT501PostBits LITERAL1 kAiwaRcT501PostData LITERAL1 kAiwaRcT501PreBits LITERAL1 kAiwaRcT501PreData LITERAL1 +kAkb73757604Str LITERAL1 +kAkb74955603Str LITERAL1 +kAkb75215403Str LITERAL1 kAllProtocolNamesStr LITERAL1 kAlokaBits LITERAL1 kAlokaLedBlue LITERAL1 @@ -1476,6 +1563,7 @@ kAmcorTolerance LITERAL1 kAmcorVentOn LITERAL1 kAmcorZeroMark LITERAL1 kAmcorZeroSpace LITERAL1 +kArdb1Str LITERAL1 kArgoAuto LITERAL1 kArgoBitMark LITERAL1 kArgoBits LITERAL1 @@ -1509,6 +1597,10 @@ kArgoOneSpace LITERAL1 kArgoStateLength LITERAL1 kArgoTempDelta LITERAL1 kArgoZeroSpace LITERAL1 +kArjw2Str LITERAL1 +kArrah2eStr LITERAL1 +kArreb1eStr LITERAL1 +kArrew4eStr LITERAL1 kArrisBits LITERAL1 kArrisChecksumSize LITERAL1 kArrisCommandSize LITERAL1 @@ -1519,6 +1611,7 @@ kArrisHdrSpace LITERAL1 kArrisOverhead LITERAL1 kArrisReleaseBit LITERAL1 kArrisReleaseToggle LITERAL1 +kArry4Str LITERAL1 kAuto LITERAL1 kAutoStr LITERAL1 kAutomaticStr LITERAL1 @@ -1580,6 +1673,7 @@ kCelsiusStr LITERAL1 kCentreStr LITERAL1 kChangeStr LITERAL1 kCirculateStr LITERAL1 +kCkpStr LITERAL1 kCleanStr LITERAL1 kClockStr LITERAL1 kCodeStr LITERAL1 @@ -1589,6 +1683,9 @@ kCommaSpaceStr LITERAL1 kCommandStr LITERAL1 kCool LITERAL1 kCoolStr LITERAL1 +kCoolingStr LITERAL1 +kCoolix48Bits LITERAL1 +kCoolix48ExtraTolerance LITERAL1 kCoolixAuto LITERAL1 kCoolixBitMark LITERAL1 kCoolixBitMarkTicks LITERAL1 @@ -1888,10 +1985,12 @@ kDaikinSwingOn LITERAL1 kDaikinTolerance LITERAL1 kDaikinUnusedTime LITERAL1 kDaikinZeroSpace LITERAL1 +kDashStr LITERAL1 kDayStr LITERAL1 kDaysStr LITERAL1 kDefaultESP32Timer LITERAL1 kDefaultMessageGap LITERAL1 +kDehumidifyStr LITERAL1 kDelonghiAcAuto LITERAL1 kDelonghiAcBitMark LITERAL1 kDelonghiAcBits LITERAL1 @@ -1936,6 +2035,11 @@ kDenonOneSpaceTicks LITERAL1 kDenonTick LITERAL1 kDenonZeroSpace LITERAL1 kDenonZeroSpaceTicks LITERAL1 +kDg11j104Str LITERAL1 +kDg11j13aStr LITERAL1 +kDg11j191Str LITERAL1 +kDirectIndirectModeStr LITERAL1 +kDirectStr LITERAL1 kDishBitMark LITERAL1 kDishBitMarkTicks LITERAL1 kDishBits LITERAL1 @@ -1952,6 +2056,7 @@ kDishTick LITERAL1 kDishZeroSpace LITERAL1 kDishZeroSpaceTicks LITERAL1 kDisplayTempStr LITERAL1 +kDkeStr LITERAL1 kDoshishaBitMark LITERAL1 kDoshishaBits LITERAL1 kDoshishaHdrMark LITERAL1 @@ -1961,6 +2066,7 @@ kDoshishaZeroSpace LITERAL1 kDownStr LITERAL1 kDry LITERAL1 kDryStr LITERAL1 +kDryingStr LITERAL1 kDutyDefault LITERAL1 kDutyMax LITERAL1 kEcoclimAuto LITERAL1 @@ -2014,6 +2120,9 @@ kElectraAcMessageGap LITERAL1 kElectraAcMinRepeat LITERAL1 kElectraAcMinTemp LITERAL1 kElectraAcOneSpace LITERAL1 +kElectraAcSensorMaxTemp LITERAL1 +kElectraAcSensorMinTemp LITERAL1 +kElectraAcSensorTempDelta LITERAL1 kElectraAcStateLength LITERAL1 kElectraAcSwingOff LITERAL1 kElectraAcSwingOn LITERAL1 @@ -2089,6 +2198,7 @@ kFujitsuAcTempOffsetC LITERAL1 kFujitsuAcTempOffsetF LITERAL1 kFujitsuAcTimerMax LITERAL1 kFujitsuAcZeroSpace LITERAL1 +kGe6711ar2853mStr LITERAL1 kGicableBitMark LITERAL1 kGicableBits LITERAL1 kGicableHdrMark LITERAL1 @@ -2171,6 +2281,13 @@ kGreeStateLength LITERAL1 kGreeSwingAuto LITERAL1 kGreeSwingDown LITERAL1 kGreeSwingDownAuto LITERAL1 +kGreeSwingHAuto LITERAL1 +kGreeSwingHLeft LITERAL1 +kGreeSwingHMaxLeft LITERAL1 +kGreeSwingHMaxRight LITERAL1 +kGreeSwingHMiddle LITERAL1 +kGreeSwingHOff LITERAL1 +kGreeSwingHRight LITERAL1 kGreeSwingLastPos LITERAL1 kGreeSwingMiddle LITERAL1 kGreeSwingMiddleAuto LITERAL1 @@ -2180,6 +2297,7 @@ kGreeSwingUp LITERAL1 kGreeSwingUpAuto LITERAL1 kGreeTimerMax LITERAL1 kGreeZeroSpace LITERAL1 +kGz055be1Str LITERAL1 kHaierAC176Bits LITERAL1 kHaierAC176StateLength LITERAL1 kHaierACBits LITERAL1 @@ -2220,21 +2338,26 @@ kHaierAcMinTemp LITERAL1 kHaierAcOneSpace LITERAL1 kHaierAcPrefix LITERAL1 kHaierAcSleepBit LITERAL1 -kHaierAcSwingChg LITERAL1 -kHaierAcSwingDown LITERAL1 -kHaierAcSwingOff LITERAL1 -kHaierAcSwingUp LITERAL1 +kHaierAcSwingVChg LITERAL1 +kHaierAcSwingVDown LITERAL1 +kHaierAcSwingVOff LITERAL1 +kHaierAcSwingVUp LITERAL1 kHaierAcYrw02Auto LITERAL1 +kHaierAcYrw02ButtonCFAB LITERAL1 kHaierAcYrw02ButtonFan LITERAL1 kHaierAcYrw02ButtonHealth LITERAL1 +kHaierAcYrw02ButtonLock LITERAL1 kHaierAcYrw02ButtonMode LITERAL1 kHaierAcYrw02ButtonPower LITERAL1 kHaierAcYrw02ButtonSleep LITERAL1 -kHaierAcYrw02ButtonSwing LITERAL1 +kHaierAcYrw02ButtonSwingH LITERAL1 +kHaierAcYrw02ButtonSwingV LITERAL1 kHaierAcYrw02ButtonTempDown LITERAL1 kHaierAcYrw02ButtonTempUp LITERAL1 +kHaierAcYrw02ButtonTimer LITERAL1 kHaierAcYrw02ButtonTurbo LITERAL1 kHaierAcYrw02Cool LITERAL1 +kHaierAcYrw02DefTempC LITERAL1 kHaierAcYrw02DefaultRepeat LITERAL1 kHaierAcYrw02Dry LITERAL1 kHaierAcYrw02Fan LITERAL1 @@ -2243,26 +2366,35 @@ kHaierAcYrw02FanHigh LITERAL1 kHaierAcYrw02FanLow LITERAL1 kHaierAcYrw02FanMed LITERAL1 kHaierAcYrw02Heat LITERAL1 +kHaierAcYrw02MaxTempC LITERAL1 +kHaierAcYrw02MaxTempF LITERAL1 +kHaierAcYrw02MinTempC LITERAL1 +kHaierAcYrw02MinTempF LITERAL1 +kHaierAcYrw02ModelA LITERAL1 +kHaierAcYrw02ModelB LITERAL1 kHaierAcYrw02NoTimers LITERAL1 kHaierAcYrw02OffThenOnTimer LITERAL1 kHaierAcYrw02OffTimer LITERAL1 kHaierAcYrw02OnThenOffTimer LITERAL1 kHaierAcYrw02OnTimer LITERAL1 -kHaierAcYrw02Prefix LITERAL1 -kHaierAcYrw02SwingAuto LITERAL1 -kHaierAcYrw02SwingBottom LITERAL1 -kHaierAcYrw02SwingDown LITERAL1 -kHaierAcYrw02SwingMiddle LITERAL1 -kHaierAcYrw02SwingOff LITERAL1 -kHaierAcYrw02SwingTop LITERAL1 -kHaierAcYrw02TurboHigh LITERAL1 -kHaierAcYrw02TurboLow LITERAL1 -kHaierAcYrw02TurboOff LITERAL1 +kHaierAcYrw02SwingHAuto LITERAL1 +kHaierAcYrw02SwingHLeft LITERAL1 +kHaierAcYrw02SwingHLeftMax LITERAL1 +kHaierAcYrw02SwingHMiddle LITERAL1 +kHaierAcYrw02SwingHRight LITERAL1 +kHaierAcYrw02SwingHRightMax LITERAL1 +kHaierAcYrw02SwingVAuto LITERAL1 +kHaierAcYrw02SwingVBottom LITERAL1 +kHaierAcYrw02SwingVDown LITERAL1 +kHaierAcYrw02SwingVMiddle LITERAL1 +kHaierAcYrw02SwingVOff LITERAL1 +kHaierAcYrw02SwingVTop LITERAL1 kHaierAcZeroSpace LITERAL1 kHeader LITERAL1 kHealthStr LITERAL1 kHeat LITERAL1 kHeatStr LITERAL1 +kHeatingStr LITERAL1 kHiStr LITERAL1 kHigh LITERAL1 kHighNibble LITERAL1 @@ -2389,6 +2521,7 @@ kHourStr LITERAL1 kHoursStr LITERAL1 kHumidStr LITERAL1 kIFeelStr LITERAL1 +kISeeStr LITERAL1 kIdStr LITERAL1 kIdleState LITERAL1 kInaxBitMark LITERAL1 @@ -2400,8 +2533,10 @@ kInaxMinRepeat LITERAL1 kInaxOneSpace LITERAL1 kInaxTick LITERAL1 kInaxZeroSpace LITERAL1 +kIndirectStr LITERAL1 kInsideStr LITERAL1 kIonStr LITERAL1 +kJkeStr LITERAL1 kJvcBitMark LITERAL1 kJvcBitMarkTicks LITERAL1 kJvcBits LITERAL1 @@ -2470,6 +2605,8 @@ kKelvinatorStateLength LITERAL1 kKelvinatorTick LITERAL1 kKelvinatorZeroSpace LITERAL1 kKelvinatorZeroSpaceTicks LITERAL1 +kKkg29ac1Str LITERAL1 +kKkg9ac1Str LITERAL1 kLasertagBits LITERAL1 kLasertagDelta LITERAL1 kLasertagExcess LITERAL1 @@ -2486,6 +2623,7 @@ kLastSwinghEnum LITERAL1 kLastSwingvEnum LITERAL1 kLeft LITERAL1 kLeftMax LITERAL1 +kLeftMaxNoSpaceStr LITERAL1 kLeftMaxStr LITERAL1 kLeftStr LITERAL1 kLegoPfBitMark LITERAL1 @@ -2569,7 +2707,9 @@ kLgRptSpace LITERAL1 kLgZeroSpace LITERAL1 kLightStr LITERAL1 kLightToggleStr LITERAL1 +kLkeStr LITERAL1 kLoStr LITERAL1 +kLockStr LITERAL1 kLoudStr LITERAL1 kLow LITERAL1 kLowNibble LITERAL1 @@ -2603,7 +2743,9 @@ kMarkExcess LITERAL1 kMarkState LITERAL1 kMax LITERAL1 kMaxAccurateUsecDelay LITERAL1 +kMaxLeftNoSpaceStr LITERAL1 kMaxLeftStr LITERAL1 +kMaxRightNoSpaceStr LITERAL1 kMaxRightStr LITERAL1 kMaxStr LITERAL1 kMaxTimeoutMs LITERAL1 @@ -2686,6 +2828,34 @@ kMinStr LITERAL1 kMinimumStr LITERAL1 kMinuteStr LITERAL1 kMinutesStr LITERAL1 +kMirageAcCool LITERAL1 +kMirageAcDry LITERAL1 +kMirageAcFan LITERAL1 +kMirageAcFanAuto LITERAL1 +kMirageAcFanHigh LITERAL1 +kMirageAcFanLow LITERAL1 +kMirageAcFanMed LITERAL1 +kMirageAcHeat LITERAL1 +kMirageAcKKG29AC1FanAuto LITERAL1 +kMirageAcKKG29AC1FanHigh LITERAL1 +kMirageAcKKG29AC1FanLow LITERAL1 +kMirageAcKKG29AC1FanMed LITERAL1 +kMirageAcKKG29AC1PowerOff LITERAL1 +kMirageAcKKG29AC1PowerOn LITERAL1 +kMirageAcMaxTemp LITERAL1 +kMirageAcMinTemp LITERAL1 +kMirageAcPowerOff LITERAL1 +kMirageAcRecycle LITERAL1 +kMirageAcSensorTempMax LITERAL1 +kMirageAcSensorTempOffset LITERAL1 +kMirageAcSwingVAuto LITERAL1 +kMirageAcSwingVHigh LITERAL1 +kMirageAcSwingVHighest LITERAL1 +kMirageAcSwingVLow LITERAL1 +kMirageAcSwingVLowest LITERAL1 +kMirageAcSwingVMiddle LITERAL1 +kMirageAcSwingVOff LITERAL1 +kMirageAcTempOffset LITERAL1 kMirageBitMark LITERAL1 kMirageBits LITERAL1 kMirageFreq LITERAL1 @@ -2769,6 +2939,8 @@ kMitsubishiACStateLength LITERAL1 kMitsubishiAcAuto LITERAL1 kMitsubishiAcBitMark LITERAL1 kMitsubishiAcCool LITERAL1 +kMitsubishiAcDirect LITERAL1 +kMitsubishiAcDirectOff LITERAL1 kMitsubishiAcDry LITERAL1 kMitsubishiAcExtraTolerance LITERAL1 kMitsubishiAcFan LITERAL1 @@ -2780,6 +2952,7 @@ kMitsubishiAcFanSilent LITERAL1 kMitsubishiAcHdrMark LITERAL1 kMitsubishiAcHdrSpace LITERAL1 kMitsubishiAcHeat LITERAL1 +kMitsubishiAcIndirect LITERAL1 kMitsubishiAcMaxTemp LITERAL1 kMitsubishiAcMinTemp LITERAL1 kMitsubishiAcNoTimer LITERAL1 @@ -2979,6 +3152,7 @@ kNikaiOneSpaceTicks LITERAL1 kNikaiTick LITERAL1 kNikaiZeroSpace LITERAL1 kNikaiZeroSpaceTicks LITERAL1 +kNkeStr LITERAL1 kNoRepeat LITERAL1 kNoStr LITERAL1 kNowStr LITERAL1 @@ -3067,20 +3241,27 @@ kPanasonicAcTolerance LITERAL1 kPanasonicBitMark LITERAL1 kPanasonicBits LITERAL1 kPanasonicCkp LITERAL1 +kPanasonicCkpStr LITERAL1 kPanasonicDke LITERAL1 +kPanasonicDkeStr LITERAL1 kPanasonicEndGap LITERAL1 kPanasonicFreq LITERAL1 kPanasonicHdrMark LITERAL1 kPanasonicHdrSpace LITERAL1 kPanasonicJke LITERAL1 +kPanasonicJkeStr LITERAL1 kPanasonicKnownGoodState LITERAL1 kPanasonicLke LITERAL1 +kPanasonicLkeStr LITERAL1 kPanasonicManufacturer LITERAL1 kPanasonicMinCommandLength LITERAL1 kPanasonicMinGap LITERAL1 kPanasonicNke LITERAL1 +kPanasonicNkeStr LITERAL1 kPanasonicOneSpace LITERAL1 +kPanasonicPkrStr LITERAL1 kPanasonicRkr LITERAL1 +kPanasonicRkrStr LITERAL1 kPanasonicUnknown LITERAL1 kPanasonicZeroSpace LITERAL1 kPeriodOffset LITERAL1 @@ -3093,6 +3274,7 @@ kPioneerMinGap LITERAL1 kPioneerOneSpace LITERAL1 kPioneerTick LITERAL1 kPioneerZeroSpace LITERAL1 +kPkrStr LITERAL1 kPowerButtonStr LITERAL1 kPowerStr LITERAL1 kPowerToggleStr LITERAL1 @@ -3170,10 +3352,44 @@ kRcz01SignatureMask LITERAL1 kRecycleStr LITERAL1 kRepeat LITERAL1 kRepeatStr LITERAL1 +kRhossBitMark LITERAL1 +kRhossBits LITERAL1 +kRhossDefaultFan LITERAL1 +kRhossDefaultMode LITERAL1 +kRhossDefaultPower LITERAL1 +kRhossDefaultRepeat LITERAL1 +kRhossDefaultSwing LITERAL1 +kRhossDefaultTemp LITERAL1 +kRhossFanAuto LITERAL1 +kRhossFanMax LITERAL1 +kRhossFanMed LITERAL1 +kRhossFanMin LITERAL1 +kRhossFreq LITERAL1 +kRhossGap LITERAL1 +kRhossHdrMark LITERAL1 +kRhossHdrSpace LITERAL1 +kRhossModeAuto LITERAL1 +kRhossModeCool LITERAL1 +kRhossModeDry LITERAL1 +kRhossModeFan LITERAL1 +kRhossModeHeat LITERAL1 +kRhossOneSpace LITERAL1 +kRhossPowerOff LITERAL1 +kRhossPowerOn LITERAL1 +kRhossStateLength LITERAL1 +kRhossSwingOff LITERAL1 +kRhossSwingOn LITERAL1 +kRhossTempMax LITERAL1 +kRhossTempMin LITERAL1 +kRhossZeroSpace LITERAL1 kRight LITERAL1 kRightMax LITERAL1 +kRightMaxNoSpaceStr LITERAL1 kRightMaxStr LITERAL1 kRightStr LITERAL1 +kRkrStr LITERAL1 +kRlt0541htaaStr LITERAL1 +kRlt0541htabStr LITERAL1 kRoomStr LITERAL1 kSamsung36BitMark LITERAL1 kSamsung36Bits LITERAL1 @@ -3189,6 +3405,7 @@ kSamsungAcBreezeOn LITERAL1 kSamsungAcCool LITERAL1 kSamsungAcDefaultRepeat LITERAL1 kSamsungAcDry LITERAL1 +kSamsungAcEconoOn LITERAL1 kSamsungAcExtendedBits LITERAL1 kSamsungAcExtendedStateLength LITERAL1 kSamsungAcFan LITERAL1 @@ -3197,6 +3414,7 @@ kSamsungAcFanAuto2 LITERAL1 kSamsungAcFanHigh LITERAL1 kSamsungAcFanLow LITERAL1 kSamsungAcFanMed LITERAL1 +kSamsungAcFanSpecialOff LITERAL1 kSamsungAcFanTurbo LITERAL1 kSamsungAcHdrMark LITERAL1 kSamsungAcHdrSpace LITERAL1 @@ -3205,16 +3423,17 @@ kSamsungAcMaxTemp LITERAL1 kSamsungAcMinTemp LITERAL1 kSamsungAcOneSpace LITERAL1 kSamsungAcPowerSection LITERAL1 -kSamsungAcPowerful10On LITERAL1 -kSamsungAcPowerfulMask8 LITERAL1 +kSamsungAcPowerfulOn LITERAL1 kSamsungAcSectionGap LITERAL1 kSamsungAcSectionLength LITERAL1 kSamsungAcSectionMark LITERAL1 kSamsungAcSectionSpace LITERAL1 kSamsungAcSections LITERAL1 kSamsungAcStateLength LITERAL1 -kSamsungAcSwingMove LITERAL1 -kSamsungAcSwingStop LITERAL1 +kSamsungAcSwingBoth LITERAL1 +kSamsungAcSwingH LITERAL1 +kSamsungAcSwingOff LITERAL1 +kSamsungAcSwingV LITERAL1 kSamsungAcZeroSpace LITERAL1 kSamsungBitMark LITERAL1 kSamsungBitMarkTicks LITERAL1 @@ -3429,6 +3648,7 @@ kSymphonyOneMark LITERAL1 kSymphonyOneSpace LITERAL1 kSymphonyZeroMark LITERAL1 kSymphonyZeroSpace LITERAL1 +kTac09chsdStr LITERAL1 kTcl112AcAuto LITERAL1 kTcl112AcBitMark LITERAL1 kTcl112AcBits LITERAL1 @@ -3660,6 +3880,8 @@ kUnknownThreshold LITERAL1 kUpStr LITERAL1 kUpperStr LITERAL1 kUseDefTol LITERAL1 +kV9014557AStr LITERAL1 +kV9014557BStr LITERAL1 kVaneStr LITERAL1 kVestelAcAuto LITERAL1 kVestelAcBitMark LITERAL1 @@ -3780,6 +4002,8 @@ kXmpRepeatCodeAlt LITERAL1 kXmpSections LITERAL1 kXmpSpaceStep LITERAL1 kXmpWordSize LITERAL1 +kYaw1fStr LITERAL1 +kYbofbStr LITERAL1 kYesStr LITERAL1 kZepealBits LITERAL1 kZepealCommandOffOn LITERAL1 diff --git a/lib/IRremoteESP8266/library.json b/lib/IRremoteESP8266/library.json index 5bce2238e..e1777f1c4 100644 --- a/lib/IRremoteESP8266/library.json +++ b/lib/IRremoteESP8266/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.7.20", + "version": "2.8.1", "keywords": "infrared, ir, remote, esp8266, esp32", "description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)", "repository": diff --git a/lib/IRremoteESP8266/library.properties b/lib/IRremoteESP8266/library.properties index e4985e2a2..5ee1f5229 100644 --- a/lib/IRremoteESP8266/library.properties +++ b/lib/IRremoteESP8266/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.7.20 +version=2.8.1 author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff maintainer=David Conran, Mark Szabo, Sebastien Warin, Roi Dayan, Massimiliano Pinto, Christian Nilsson sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32) diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp index e3e0874ba..f9d9647d3 100644 --- a/lib/IRremoteESP8266/src/IRac.cpp +++ b/lib/IRremoteESP8266/src/IRac.cpp @@ -16,6 +16,7 @@ #include "IRremoteESP8266.h" #include "IRtext.h" #include "IRutils.h" +#include "ir_Airton.h" #include "ir_Airwell.h" #include "ir_Amcor.h" #include "ir_Argo.h" @@ -56,7 +57,7 @@ #ifndef STRCASECMP #if defined(ESP8266) #define STRCASECMP(LHS, RHS) \ - strcasecmp_P(LHS, reinterpret_cast(RHS)) + strcasecmp_P(LHS, reinterpret_cast(RHS)) #else // ESP8266 #define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS) #endif // ESP8266 @@ -152,9 +153,12 @@ stdAc::state_t IRac::getStatePrev(void) { return _prev; } /// @return true if the protocol is supported by this class, otherwise false. bool IRac::isProtocolSupported(const decode_type_t protocol) { switch (protocol) { +#if SEND_AIRTON + case decode_type_t::AIRTON: +#endif // SEND_AIRTON #if SEND_AIRWELL case decode_type_t::AIRWELL: -#endif +#endif // SEND_AIRWELL #if SEND_AMCOR case decode_type_t::AMCOR: #endif @@ -245,7 +249,10 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #endif #if SEND_MIDEA case decode_type_t::MIDEA: -#endif +#endif // SEND_MIDEA +#if SEND_MIRAGE + case decode_type_t::MIRAGE: +#endif // SEND_MIRAGE #if SEND_MITSUBISHI_AC case decode_type_t::MITSUBISHI_AC: #endif @@ -323,6 +330,44 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { } } +#if SEND_AIRTON +/// Send an Airton 56-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRAirtonAc object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +/// @param[in] swingv The vertical swing setting. +/// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] light Turn on the LED/Display mode. +/// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (ion/pollen/health/etc) filter mode. +/// @param[in] sleep Nr. of minutes for sleep mode. +/// @note -1 is Off, >= 0 is on. +void IRac::airton(IRAirtonAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool light, const bool econo, const bool filter, + const int16_t sleep) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(swingv != stdAc::swingv_t::kOff); + // No Quiet setting available. + ac->setLight(light); + ac->setHealth(filter); + ac->setTurbo(turbo); + ac->setEcono(econo); + // No Clean setting available. + // No Beep setting available. + ac->setSleep(sleep >= 0); // Convert to a boolean. + ac->send(); +} +#endif // SEND_AIRTON + #if SEND_AIRWELL /// Send an Airwell A/C message with the supplied settings. /// @param[in, out] ac A Ptr to an IRAirwellAc object to use. @@ -1036,15 +1081,18 @@ void IRac::goodweather(IRGoodweatherAc *ac, /// @param[in] degrees The temperature setting in degrees. /// @param[in] fan The speed setting for the fan. /// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. /// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Toggle the device's economical mode. /// @param[in] light Turn on the LED/Display mode. /// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc /// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const bool celsius, const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool light, const bool clean, const int16_t sleep) { + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, + const bool clean, const int16_t sleep) { ac->begin(); ac->setModel(model); ac->setPower(on); @@ -1053,11 +1101,12 @@ void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model, ac->setFan(ac->convertFan(fan)); ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. ac->convertSwingV(swingv)); + ac->setSwingHorizontal(ac->convertSwingH(swingh)); ac->setLight(light); ac->setTurbo(turbo); + ac->setEcono(econo); ac->setXFan(clean); ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. - // No Horizontal Swing setting available. // No Econo setting available. // No Filter setting available. // No Beep setting available. @@ -1086,7 +1135,7 @@ void IRac::haier(IRHaierAC *ac, ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); + ac->setSwingV(ac->convertSwingV(swingv)); // No Horizontal Swing setting available. // No Quiet setting available. // No Turbo setting available. @@ -1107,26 +1156,34 @@ void IRac::haier(IRHaierAC *ac, #if SEND_HAIER_AC176 /// Send a Haier 176 bit A/C message with the supplied settings. /// @param[in, out] ac A Ptr to an IRHaierAC176 object to use. +/// @param[in] model The A/C model to use. /// @param[in] on The power setting. /// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. /// @param[in] degrees The temperature setting in degrees. /// @param[in] fan The speed setting for the fan. /// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. /// @param[in] turbo Run the device in turbo/powerful mode. /// @param[in] quiet Run the device in quiet mode. /// @param[in] filter Turn on the (ion/pollen/etc) filter mode. /// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. -void IRac::haier176(IRHaierAC176 *ac, +void IRac::haier176(IRHaierAC176 *ac, const haier_ac176_remote_model_t model, const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool quiet, const bool filter, const int16_t sleep) { + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, + const int16_t sleep) { ac->begin(); + ac->setModel(model); ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); ac->setQuiet(quiet); ac->setTurbo(turbo); // No Light setting available. @@ -1144,25 +1201,30 @@ void IRac::haier176(IRHaierAC176 *ac, /// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use. /// @param[in] on The power setting. /// @param[in] mode The operation mode setting. +/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit. /// @param[in] degrees The temperature setting in degrees. /// @param[in] fan The speed setting for the fan. /// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. /// @param[in] turbo Run the device in turbo/powerful mode. /// @param[in] quiet Run the device in quiet mode. /// @param[in] filter Turn on the (ion/pollen/etc) filter mode. /// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. void IRac::haierYrwo2(IRHaierACYRW02 *ac, const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, - const bool quiet, const bool filter, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool turbo, const bool quiet, const bool filter, const int16_t sleep) { ac->begin(); ac->setMode(ac->convertMode(mode)); + ac->setUseFahrenheit(!celsius); ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); - ac->setSwing(ac->convertSwingV(swingv)); - // No Horizontal Swing setting available. + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); ac->setQuiet(quiet); ac->setTurbo(turbo); // No Light setting available. @@ -1471,6 +1533,17 @@ void IRac::midea(IRMideaAC *ac, } #endif // SEND_MIDEA +#if SEND_MIRAGE +/// Send a Mirage 120-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. +/// @param[in] state The desired state to send. +void IRac::mirage(IRMirageAc *ac, const stdAc::state_t state) { + ac->begin(); + ac->fromCommon(state); + ac->send(); +} +#endif // SEND_MIRAGE + #if SEND_MITSUBISHI_AC /// Send a Mitsubishi A/C message with the supplied settings. /// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use. @@ -1499,6 +1572,7 @@ void IRac::mitsubishi(IRMitsubishiAC *ac, ac->setVane(ac->convertSwingV(swingv)); ac->setWideVane(ac->convertSwingH(swingh)); if (quiet) ac->setFan(kMitsubishiAcFanSilent); + ac->setISave10C(false); // No Turbo setting available. // No Light setting available. // No Filter setting available. @@ -1787,38 +1861,45 @@ void IRac::panasonic32(IRPanasonicAc32 *ac, /// @param[in] degrees The temperature setting in degrees. /// @param[in] fan The speed setting for the fan. /// @param[in] swingv The vertical swing setting. +/// @param[in] swingh The horizontal swing setting. /// @param[in] quiet Run the device in quiet/silent mode. /// @param[in] turbo Run the device in turbo/powerful mode. +/// @param[in] econo Run the device in economical mode. /// @param[in] light Turn on the LED/Display mode. /// @param[in] filter Turn on the (ion/pollen/etc) filter mode. -/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc -/// @param[in] beep Enable/Disable beeps when receiving IR messages. +/// @param[in] clean Toggle the self-cleaning mode. e.g. Mould, dry filters etc +/// @param[in] beep Toggle beep setting for receiving IR messages. +/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on. /// @param[in] prevpower The power setting from the previous A/C state. -/// @param[in] forcepower Do we force send the special power message? +/// @param[in] prevsleep Nr. of minutes for sleep from the previous A/C state. +/// @param[in] forceextended Do we force sending the special extended message? void IRac::samsung(IRSamsungAc *ac, const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool light, + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, - const bool beep, const bool prevpower, - const bool forcepower) { + const bool beep, const int16_t sleep, + const bool prevpower, const int16_t prevsleep, + const bool forceextended) { ac->begin(); - ac->stateReset(forcepower, prevpower); + ac->stateReset(forceextended || (sleep != prevsleep), prevpower); ac->setPower(on); ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); ac->setSwing(swingv != stdAc::swingv_t::kOff); - // No Horizontal swing setting available. + ac->setSwingH(swingh != stdAc::swingh_t::kOff); ac->setQuiet(quiet); - ac->setPowerful(turbo); + ac->setPowerful(turbo); // FYI, `setEcono(true)` will override this. ac->setDisplay(light); - // No Econo setting available. + ac->setEcono(econo); ac->setIon(filter); - ac->setClean(clean); - ac->setBeep(beep); - // No Sleep setting available. + ac->setClean(clean); // Toggle + ac->setBeep(beep); // Toggle + ac->setSleepTimer((sleep <= 0) ? 0 : sleep); // No Clock setting available. // Do setMode() again as it can affect fan speed. ac->setMode(ac->convertMode(mode)); @@ -2078,11 +2159,12 @@ void IRac::teco(IRTecoAc *ac, /// @param[in] swingv The vertical swing setting. /// @param[in] turbo Run the device in turbo/powerful mode. /// @param[in] econo Run the device in economical mode. +/// @param[in] filter Turn on the (Pure/ion/pollen/etc) filter mode. void IRac::toshiba(IRToshibaAC *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool econo) { + const bool turbo, const bool econo, const bool filter) { ac->begin(); ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); @@ -2095,7 +2177,7 @@ void IRac::toshiba(IRToshibaAC *ac, ac->setTurbo(turbo); ac->setEcono(econo); // No Light setting available. - // No Filter setting available. + ac->setFilter(filter); // No Clean setting available. // No Beep setting available. // No Sleep setting available. @@ -2479,11 +2561,20 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, case decode_type_t::WHIRLPOOL_AC: result.power = desired.power ^ prev->power; break; + case decode_type_t::MIRAGE: + if (desired.model == mirage_ac_remote_model_t::KKG29AC1) + result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; + break; case decode_type_t::PANASONIC_AC: // CKP models use a power mode toggle. if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp) result.power = desired.power ^ prev->power; break; + case decode_type_t::SAMSUNG_AC: + result.beep = desired.beep ^ prev->beep; + result.clean = desired.clean ^ prev->clean; + break; default: {}; } @@ -2548,6 +2639,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { // Construct a pointer-safe previous power state incase prev is NULL/NULLPTR. #if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) const bool prev_power = (prev != NULL) ? prev->power : !send.power; + const int16_t prev_sleep = (prev != NULL) ? prev->sleep : -1; #endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC) #if (SEND_LG || SEND_SHARP_AC) const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv @@ -2555,6 +2647,16 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { #endif // (SEND_LG || SEND_SHARP_AC) // Per vendor settings & setup. switch (send.protocol) { +#if SEND_AIRTON + case AIRTON: + { + IRAirtonAc ac(_pin, _inverted, _modulation); + airton(&ac, send.power, send.mode, degC, send.fanspeed, + send.swingv, send.turbo, send.light, send.econo, send.filter, + send.sleep); + break; + } +#endif // SEND_AIRTON #if SEND_AIRWELL case AIRWELL: { @@ -2732,8 +2834,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted, _modulation); gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode, - send.celsius, send.degrees, send.fanspeed, send.swingv, send.turbo, - send.light, send.clean, send.sleep); + send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh, + send.turbo, send.econo, send.light, send.clean, send.sleep); break; } #endif // SEND_GREE @@ -2750,8 +2852,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { case HAIER_AC176: { IRHaierAC176 ac(_pin, _inverted, _modulation); - haier176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep); + haier176(&ac, (haier_ac176_remote_model_t)send.model, send.power, + send.mode, send.celsius, send.degrees, send.fanspeed, + send.swingv, send.swingh, send.turbo, send.filter, send.sleep); break; } #endif // SEND_HAIER_AC176 @@ -2759,8 +2862,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { case HAIER_AC_YRW02: { IRHaierACYRW02 ac(_pin, _inverted, _modulation); - haierYrwo2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.filter, send.sleep); + haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees, + send.fanspeed, send.swingv, send.swingh, send.turbo, + send.filter, send.sleep); break; } #endif // SEND_HAIER_AC_YRW02 @@ -2846,6 +2950,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_MIDEA +#if SEND_MIRAGE + case MIRAGE: + { + IRMirageAc ac(_pin, _inverted, _modulation); + mirage(&ac, send); + break; + } +#endif // SEND_MIRAGE #if SEND_MITSUBISHI_AC case MITSUBISHI_AC: { @@ -2933,8 +3045,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { { IRSamsungAc ac(_pin, _inverted, _modulation); samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.quiet, send.turbo, send.light, send.filter, send.clean, - send.beep, prev_power); + send.swingh, send.quiet, send.turbo, send.econo, send.light, + send.filter, send.clean, send.beep, send.sleep, + prev_power, prev_sleep); break; } #endif // SEND_SAMSUNG_AC @@ -3003,7 +3116,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { { IRToshibaAC ac(_pin, _inverted, _modulation); toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv, - send.turbo, send.econo); + send.turbo, send.econo, send.filter); break; } #endif // SEND_TOSHIBA_AC @@ -3260,12 +3373,18 @@ stdAc::swingh_t IRac::strToSwingH(const char *str, /// @param[in] str A Ptr to a C-style string to be converted. /// @param[in] def The enum to return if no conversion was possible. /// @return The equivalent enum. +/// @note After adding a new model you should update modelToStr() too. int16_t IRac::strToModel(const char *str, const int16_t def) { // Gree if (!STRCASECMP(str, kYaw1fStr)) { return gree_ac_remote_model_t::YAW1F; } else if (!STRCASECMP(str, kYbofbStr)) { return gree_ac_remote_model_t::YBOFB; + // Haier models + } else if (!STRCASECMP(str, kV9014557AStr)) { + return haier_ac176_remote_model_t::V9014557_A; + } else if (!STRCASECMP(str, kV9014557BStr)) { + return haier_ac176_remote_model_t::V9014557_B; // HitachiAc1 models } else if (!STRCASECMP(str, kRlt0541htaaStr)) { return hitachi_ac1_remote_model_t::R_LT0541_HTA_A; @@ -3381,7 +3500,7 @@ String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) { case stdAc::opmode_t::kCool: return kCoolStr; case stdAc::opmode_t::kHeat: return kHeatStr; case stdAc::opmode_t::kDry: return kDryStr; - case stdAc::opmode_t::kFan: return ha ? kFanOnlyStr : kFanStr; + case stdAc::opmode_t::kFan: return ha ? kFan_OnlyStr : kFanStr; default: return kUnknownStr; } } @@ -3442,6 +3561,13 @@ namespace IRAcUtils { /// An empty string if we can't. String resultAcToString(const decode_results * const result) { switch (result->decode_type) { +#if DECODE_AIRTON + case decode_type_t::AIRTON: { + IRAirtonAc ac(kGpioUnused); + ac.setRaw(result->value); // AIRTON uses value instead of state. + return ac.toString(); + } +#endif // DECODE_AIRTON #if DECODE_AIRWELL case decode_type_t::AIRWELL: { IRAirwellAc ac(kGpioUnused); @@ -3470,6 +3596,21 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_CARRIER_AC64 +#if DECODE_COOLIX + case decode_type_t::COOLIX: { + IRCoolixAC ac(kGpioUnused); + ac.on(); + ac.setRaw(result->value); // Coolix uses value instead of state. + return ac.toString(); + } +#endif // DECODE_COOLIX +#if DECODE_CORONA_AC + case decode_type_t::CORONA_AC: { + IRCoronaAc ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_CORONA_AC #if DECODE_DAIKIN case decode_type_t::DAIKIN: { IRDaikinESP ac(kGpioUnused); @@ -3557,6 +3698,69 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_FUJITSU_AC +#if DECODE_GOODWEATHER + case decode_type_t::GOODWEATHER: { + IRGoodweatherAc ac(kGpioUnused); + ac.setRaw(result->value); // Goodweather uses value instead of state. + return ac.toString(); + } +#endif // DECODE_GOODWEATHER +#if DECODE_GREE + case decode_type_t::GREE: { + IRGreeAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_GREE +#if DECODE_HAIER_AC + case decode_type_t::HAIER_AC: { + IRHaierAC ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC +#if DECODE_HAIER_AC176 + case decode_type_t::HAIER_AC176: { + IRHaierAC176 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC176 +#if DECODE_HAIER_AC_YRW02 + case decode_type_t::HAIER_AC_YRW02: { + IRHaierACYRW02 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HAIER_AC_YRW02 +#if DECODE_HITACHI_AC + case decode_type_t::HITACHI_AC: { + IRHitachiAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC +#if DECODE_HITACHI_AC1 + case decode_type_t::HITACHI_AC1: { + IRHitachiAc1 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC344 + case decode_type_t::HITACHI_AC344: { + IRHitachiAc344 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC424 + case decode_type_t::HITACHI_AC424: { + IRHitachiAc424 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC424 #if DECODE_KELON case decode_type_t::KELON: { IRKelonAc ac(kGpioUnused); @@ -3571,6 +3775,28 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_KELVINATOR +#if DECODE_LG + case decode_type_t::LG: + case decode_type_t::LG2: { + IRLgAc ac(kGpioUnused); + ac.setRaw(result->value, result->decode_type); // Use value, not state. + return ac.isValidLgAc() ? ac.toString() : ""; + } +#endif // DECODE_LG +#if DECODE_MIDEA + case decode_type_t::MIDEA: { + IRMideaAC ac(kGpioUnused); + ac.setRaw(result->value); // Midea uses value instead of state. + return ac.toString(); + } +#endif // DECODE_MIDEA +#if DECODE_MIRAGE + case decode_type_t::MIRAGE: { + IRMirageAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MIRAGE #if DECODE_MITSUBISHI_AC case decode_type_t::MITSUBISHI_AC: { IRMitsubishiAC ac(kGpioUnused); @@ -3611,76 +3837,33 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_NEOCLIMA -#if DECODE_TOSHIBA_AC - case decode_type_t::TOSHIBA_AC: { - IRToshibaAC ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); +#if DECODE_PANASONIC_AC + case decode_type_t::PANASONIC_AC: { + if (result->bits > kPanasonicAcShortBits) { + IRPanasonicAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } + return ""; } -#endif // DECODE_TOSHIBA_AC -#if DECODE_TROTEC - case decode_type_t::TROTEC: { - IRTrotecESP ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); +#endif // DECODE_PANASONIC_AC +#if DECODE_PANASONIC_AC32 + case decode_type_t::PANASONIC_AC32: { + if (result->bits >= kPanasonicAc32Bits) { + IRPanasonicAc32 ac(kGpioUnused); + ac.setRaw(result->value); // Uses value instead of state. + return ac.toString(); + } + return ""; } -#endif // DECODE_TROTEC -#if DECODE_TROTEC_3550 - case decode_type_t::TROTEC_3550: { - IRTrotec3550 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_TROTEC_3550 -#if DECODE_TRUMA - case decode_type_t::TRUMA: { - IRTrumaAc ac(kGpioUnused); - ac.setRaw(result->value); // Truma uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TRUMA -#if DECODE_GOODWEATHER - case decode_type_t::GOODWEATHER: { - IRGoodweatherAc ac(kGpioUnused); - ac.setRaw(result->value); // Goodweather uses value instead of state. - return ac.toString(); - } -#endif // DECODE_GOODWEATHER -#if DECODE_GREE - case decode_type_t::GREE: { - IRGreeAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_GREE -#if DECODE_MIDEA - case decode_type_t::MIDEA: { - IRMideaAC ac(kGpioUnused); - ac.setRaw(result->value); // Midea uses value instead of state. - return ac.toString(); - } -#endif // DECODE_MIDEA -#if DECODE_HAIER_AC - case decode_type_t::HAIER_AC: { - IRHaierAC ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC -#if DECODE_HAIER_AC176 - case decode_type_t::HAIER_AC176: { - IRHaierAC176 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC176 -#if DECODE_HAIER_AC_YRW02 - case decode_type_t::HAIER_AC_YRW02: { - IRHaierACYRW02 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HAIER_AC_YRW02 +#endif // DECODE_PANASONIC_AC +#if DECODE_RHOSS + case decode_type_t::RHOSS: { + IRRhossAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_RHOSS #if DECODE_SAMSUNG_AC case decode_type_t::SAMSUNG_AC: { IRSamsungAc ac(kGpioUnused); @@ -3709,104 +3892,6 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_SHARP_AC -#if DECODE_COOLIX - case decode_type_t::COOLIX: { - IRCoolixAC ac(kGpioUnused); - ac.on(); - ac.setRaw(result->value); // Coolix uses value instead of state. - return ac.toString(); - } -#endif // DECODE_COOLIX -#if DECODE_CORONA_AC - case decode_type_t::CORONA_AC: { - IRCoronaAc ac(kGpioUnused); - ac.setRaw(result->state, result->bits / 8); - return ac.toString(); - } -#endif // DECODE_CORONA_AC -#if DECODE_PANASONIC_AC - case decode_type_t::PANASONIC_AC: { - if (result->bits > kPanasonicAcShortBits) { - IRPanasonicAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_PANASONIC_AC32 - case decode_type_t::PANASONIC_AC32: { - if (result->bits >= kPanasonicAc32Bits) { - IRPanasonicAc32 ac(kGpioUnused); - ac.setRaw(result->value); // Uses value instead of state. - return ac.toString(); - } - return ""; - } -#endif // DECODE_PANASONIC_AC -#if DECODE_HITACHI_AC - case decode_type_t::HITACHI_AC: { - IRHitachiAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC -#if DECODE_HITACHI_AC1 - case decode_type_t::HITACHI_AC1: { - IRHitachiAc1 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC1 -#if DECODE_HITACHI_AC344 - case decode_type_t::HITACHI_AC344: { - IRHitachiAc344 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC344 -#if DECODE_HITACHI_AC424 - case decode_type_t::HITACHI_AC424: { - IRHitachiAc424 ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_HITACHI_AC424 -#if DECODE_WHIRLPOOL_AC - case decode_type_t::WHIRLPOOL_AC: { - IRWhirlpoolAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_WHIRLPOOL_AC -#if DECODE_VESTEL_AC - case decode_type_t::VESTEL_AC: { - IRVestelAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_VESTEL_AC -#if DECODE_TECHNIBEL_AC - case decode_type_t::TECHNIBEL_AC: { - IRTechnibelAc ac(kGpioUnused); - ac.setRaw(result->value); // TechnibelAc uses value instead of state. - return ac.toString(); - } -#endif // DECODE_TECHNIBEL_AC -#if DECODE_VOLTAS - case decode_type_t::VOLTAS: { - IRVoltas ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_VOLTAS -#if DECODE_TECO - case decode_type_t::TECO: { - IRTecoAc ac(kGpioUnused); - ac.setRaw(result->value); // Like Coolix, use value instead of state. - return ac.toString(); - } -#endif // DECODE_TECO #if (DECODE_TCL112AC || DECODE_TEKNOPOINT) case decode_type_t::TCL112AC: case decode_type_t::TEKNOPOINT: { @@ -3815,14 +3900,27 @@ namespace IRAcUtils { return ac.toString(); } #endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT) -#if DECODE_LG - case decode_type_t::LG: - case decode_type_t::LG2: { - IRLgAc ac(kGpioUnused); - ac.setRaw(result->value, result->decode_type); // Use value, not state. - return ac.isValidLgAc() ? ac.toString() : ""; +#if DECODE_TECHNIBEL_AC + case decode_type_t::TECHNIBEL_AC: { + IRTechnibelAc ac(kGpioUnused); + ac.setRaw(result->value); // TechnibelAc uses value instead of state. + return ac.toString(); } -#endif // DECODE_LG +#endif // DECODE_TECHNIBEL_AC +#if DECODE_TECO + case decode_type_t::TECO: { + IRTecoAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_TECO +#if DECODE_TOSHIBA_AC + case decode_type_t::TOSHIBA_AC: { + IRToshibaAC ac(kGpioUnused); + ac.setRaw(result->state, result->bits / 8); + return ac.toString(); + } +#endif // DECODE_TOSHIBA_AC #if DECODE_TRANSCOLD case decode_type_t::TRANSCOLD: { IRTranscoldAc ac(kGpioUnused); @@ -3831,13 +3929,48 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_TRANSCOLD -#if DECODE_RHOSS - case decode_type_t::RHOSS: { - IRRhossAc ac(kGpioUnused); - ac.setRaw(result->state); - return ac.toString(); - } -#endif // DECODE_RHOSS +#if DECODE_TROTEC + case decode_type_t::TROTEC: { + IRTrotecESP ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC +#if DECODE_TROTEC_3550 + case decode_type_t::TROTEC_3550: { + IRTrotec3550 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_TROTEC_3550 +#if DECODE_TRUMA + case decode_type_t::TRUMA: { + IRTrumaAc ac(kGpioUnused); + ac.setRaw(result->value); // Truma uses value instead of state. + return ac.toString(); + } +#endif // DECODE_TRUMA +#if DECODE_VESTEL_AC + case decode_type_t::VESTEL_AC: { + IRVestelAc ac(kGpioUnused); + ac.setRaw(result->value); // Like Coolix, use value instead of state. + return ac.toString(); + } +#endif // DECODE_VESTEL_AC +#if DECODE_VOLTAS + case decode_type_t::VOLTAS: { + IRVoltas ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_VOLTAS +#if DECODE_WHIRLPOOL_AC + case decode_type_t::WHIRLPOOL_AC: { + IRWhirlpoolAc ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_WHIRLPOOL_AC default: return ""; } @@ -3859,6 +3992,14 @@ namespace IRAcUtils { ) { if (decode == NULL || result == NULL) return false; // Safety check. switch (decode->decode_type) { +#if DECODE_AIRTON + case decode_type_t::AIRTON: { + IRAirtonAc ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_AIRTON #if DECODE_AIRWELL case decode_type_t::AIRWELL: { IRAirwellAc ac(kGpioUnused); @@ -3883,6 +4024,14 @@ namespace IRAcUtils { break; } #endif // DECODE_ARGO +#if DECODE_CARRIER_AC64 + case decode_type_t::CARRIER_AC64: { + IRCarrierAc64 ac(kGpioUnused); + ac.setRaw(decode->value); // Uses value instead of state. + *result = ac.toCommon(); + break; + } +#endif // DECODE_CARRIER_AC64 #if DECODE_COOLIX case decode_type_t::COOLIX: { IRCoolixAC ac(kGpioUnused); @@ -3899,14 +4048,6 @@ namespace IRAcUtils { break; } #endif // DECODE_CARRIER_AC64 -#if DECODE_CARRIER_AC64 - case decode_type_t::CARRIER_AC64: { - IRCarrierAc64 ac(kGpioUnused); - ac.setRaw(decode->value); // Uses value instead of state. - *result = ac.toCommon(); - break; - } -#endif // DECODE_CARRIER_AC64 #if DECODE_DAIKIN case decode_type_t::DAIKIN: { IRDaikinESP ac(kGpioUnused); @@ -4113,6 +4254,14 @@ namespace IRAcUtils { break; } #endif // DECODE_MIDEA +#if DECODE_MIRAGE + case decode_type_t::MIRAGE: { + IRMirageAc ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MIRAGE #if DECODE_MITSUBISHI_AC case decode_type_t::MITSUBISHI_AC: { IRMitsubishiAC ac(kGpioUnused); @@ -4255,6 +4404,14 @@ namespace IRAcUtils { break; } #endif // DECODE_TOSHIBA_AC +#if DECODE_TRANSCOLD + case decode_type_t::TRANSCOLD: { + IRTranscoldAc ac(kGpioUnused); + ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. + *result = ac.toCommon(prev); + break; + } +#endif // DECODE_TRANSCOLD #if DECODE_TROTEC case decode_type_t::TROTEC: { IRTrotecESP ac(kGpioUnused); @@ -4303,14 +4460,6 @@ namespace IRAcUtils { break; } #endif // DECODE_WHIRLPOOL_AC -#if DECODE_TRANSCOLD - case decode_type_t::TRANSCOLD: { - IRTranscoldAc ac(kGpioUnused); - ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state. - *result = ac.toCommon(prev); - break; - } -#endif // DECODE_TRANSCOLD default: return false; } diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h index 7ef3adf4a..9954eecb0 100644 --- a/lib/IRremoteESP8266/src/IRac.h +++ b/lib/IRremoteESP8266/src/IRac.h @@ -7,6 +7,7 @@ #include #endif #include "IRremoteESP8266.h" +#include "ir_Airton.h" #include "ir_Airwell.h" #include "ir_Amcor.h" #include "ir_Argo.h" @@ -26,6 +27,7 @@ #include "ir_Kelvinator.h" #include "ir_LG.h" #include "ir_Midea.h" +#include "ir_Mirage.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" #include "ir_Neoclima.h" @@ -108,6 +110,14 @@ class IRac { bool _inverted; ///< IR LED is lit when GPIO is LOW (true) or HIGH (false)? bool _modulation; ///< Is frequency modulation to be used? stdAc::state_t _prev; ///< The state we expect the device to currently be in. +#if SEND_AIRTON + void airton(IRAirtonAc *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const bool turbo, + const bool light, const bool econo, const bool filter, + const int16_t sleep = -1); +#endif // SEND_AIRTON #if SEND_AIRWELL void airwell(IRAirwellAc *ac, const bool on, const stdAc::opmode_t mode, const float degrees, @@ -247,7 +257,8 @@ void electra(IRElectraAc *ac, void gree(IRGreeAC *ac, const gree_ac_remote_model_t model, const bool on, const stdAc::opmode_t mode, const bool celsius, const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, const bool turbo, const bool light, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool turbo, const bool econo, const bool light, const bool clean, const int16_t sleep = -1); #endif // SEND_GREE #if SEND_HAIER_AC @@ -259,18 +270,20 @@ void electra(IRElectraAc *ac, #endif // SEND_HAIER_AC #if SEND_HAIER_AC176 void haier176(IRHaierAC176 *ac, - const bool on, const stdAc::opmode_t mode, + const haier_ac176_remote_model_t model, const bool on, + const stdAc::opmode_t mode, const bool celsius, const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, const bool turbo, const bool quiet, const bool filter, const int16_t sleep = -1); #endif // SEND_HAIER_AC176 #if SEND_HAIER_AC_YRW02 void haierYrwo2(IRHaierACYRW02 *ac, const bool on, const stdAc::opmode_t mode, - const float degrees, const stdAc::fanspeed_t fan, - const stdAc::swingv_t swingv, - const bool turbo, const bool quiet, const bool filter, + const bool celsius, const float degrees, + const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, const bool turbo, + const bool quiet, const bool filter, const int16_t sleep = -1); #endif // SEND_HAIER_AC_YRW02 #if SEND_HITACHI_AC @@ -328,6 +341,9 @@ void electra(IRElectraAc *ac, const stdAc::swingv_t swingv, const bool turbo, const bool econo, const bool light, const int16_t sleep = -1); #endif // SEND_MIDEA +#if SEND_MIRAGE + void mirage(IRMirageAc *ac, const stdAc::state_t state); +#endif // SEND_MIRAGE #if SEND_MITSUBISHI_AC void mitsubishi(IRMitsubishiAC *ac, const bool on, const stdAc::opmode_t mode, @@ -396,11 +412,13 @@ void electra(IRElectraAc *ac, #if SEND_SAMSUNG_AC void samsung(IRSamsungAc *ac, const bool on, const stdAc::opmode_t mode, const float degrees, - const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool quiet, const bool turbo, const bool light, - const bool filter, const bool clean, - const bool beep, const bool prevpower = true, - const bool forcepower = true); + const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, + const bool quiet, const bool turbo, const bool econo, + const bool light, const bool filter, const bool clean, + const bool beep, const int16_t sleep = -1, + const bool prevpower = true, const int16_t prevsleep = -1, + const bool forceextended = true); #endif // SEND_SAMSUNG_AC #if SEND_SANYO_AC void sanyo(IRSanyoAc *ac, @@ -448,7 +466,7 @@ void electra(IRElectraAc *ac, void toshiba(IRToshibaAC *ac, const bool on, const stdAc::opmode_t mode, const float degrees, const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, - const bool turbo, const bool econo); + const bool turbo, const bool econo, const bool filter); #endif // SEND_TOSHIBA_AC #if SEND_TROTEC void trotec(IRTrotecESP *ac, diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp index 5dc585bc7..5b9d91b88 100644 --- a/lib/IRremoteESP8266/src/IRrecv.cpp +++ b/lib/IRremoteESP8266/src/IRrecv.cpp @@ -13,7 +13,7 @@ extern "C" { } #endif // ESP8266 #include -#endif +#endif // UNIT_TEST #include #ifdef UNIT_TEST #include @@ -56,6 +56,20 @@ static ETSTimer timer; } // namespace _IRrecv #endif // ESP8266 #if defined(ESP32) +// We need a horrible timer hack for ESP32 Arduino framework < v2.0.0 +#if !defined(_ESP32_IRRECV_TIMER_HACK) +// Version check +#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 2) ) +// No need for the hack if we are running version >= 2.0.0 +#define _ESP32_IRRECV_TIMER_HACK false +#else // Version check +// If no ESP_ARDUINO_VERSION_MAJOR is defined, or less than 2, then we are +// using an old ESP32 core, so we need the hack. +#define _ESP32_IRRECV_TIMER_HACK true +#endif // Version check +#endif // !defined(_ESP32_IRRECV_TIMER_HACK) + +#if _ESP32_IRRECV_TIMER_HACK // Required structs/types from: // https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L28-L58 // These are needed to be able to directly manipulate the timer registers from @@ -117,7 +131,7 @@ typedef struct hw_timer_s { uint8_t timer; portMUX_TYPE lock; } hw_timer_t; -// End of Horrible Hack. +#endif // _ESP32_IRRECV_TIMER_HACK / End of Horrible Hack. namespace _IRrecv { static hw_timer_t * timer = NULL; @@ -211,6 +225,7 @@ static void USE_IRAM_ATTR gpio_intr() { #if defined(ESP32) // Reset the timeout. // +#if _ESP32_IRRECV_TIMER_HACK // The following three lines of code are the equiv of: // `timerWrite(timer, 0);` // We can't call that routine safely from inside an ISR as that procedure @@ -226,6 +241,10 @@ static void USE_IRAM_ATTR gpio_intr() { // @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1350 // @see https://github.com/espressif/arduino-esp32/blob/6b0114366baf986c155e8173ab7c22bc0c5fcedc/cores/esp32/esp32-hal-timer.c#L176-L178 timer->dev->config.alarm_en = 1; +#else // _ESP32_IRRECV_TIMER_HACK + timerWrite(timer, 0); + timerAlarmEnable(timer); +#endif // _ESP32_IRRECV_TIMER_HACK #endif // ESP32 } #endif // UNIT_TEST @@ -337,7 +356,9 @@ void IRrecv::enableIRIn(const bool pullup) { // Set the timer so it only fires once, and set it's trigger in uSeconds. timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); // Note: Interrupt needs to be attached before it can be enabled or disabled. - timerAttachInterrupt(timer, &read_timeout, true); + // Note: EDGE (true) is not supported, use LEVEL (false). Ref: #1713 + // See: https://github.com/espressif/arduino-esp32/blob/caef4006af491130136b219c1205bdcf8f08bf2b/cores/esp32/esp32-hal-timer.c#L224-L227 + timerAttachInterrupt(timer, &read_timeout, false); #endif // ESP32 // Initialise state machine variables @@ -695,9 +716,9 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, if (decodeSharp(results, offset)) return true; #endif #if DECODE_COOLIX - DPRINTLN("Attempting Coolix decode"); + DPRINTLN("Attempting Coolix 24-bit decode"); if (decodeCOOLIX(results, offset)) return true; -#endif +#endif // DECODE_COOLIX #if DECODE_NIKAI DPRINTLN("Attempting Nikai decode"); if (decodeNikai(results, offset)) return true; @@ -830,8 +851,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Samsung AC (extended) decode"); // Check the extended size first, as it should fail fast due to longer // length. - if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits, false)) - return true; + if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits)) return true; // Now check for the more common length. DPRINTLN("Attempting Samsung AC decode"); if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true; @@ -1044,6 +1064,14 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Rhoss decode"); if (decodeRhoss(results, offset)) return true; #endif // DECODE_RHOSS +#if DECODE_AIRTON + DPRINTLN("Attempting Airton decode"); + if (decodeAirton(results, offset)) return true; +#endif // DECODE_AIRTON +#if DECODE_COOLIX48 + DPRINTLN("Attempting Coolix 48-bit decode"); + if (decodeCoolix48(results, offset)) return true; +#endif // DECODE_COOLIX48 // Typically new protocols are added above this line. } #if DECODE_HASH @@ -1076,7 +1104,7 @@ uint32_t IRrecv::ticksLow(const uint32_t usecs, const uint8_t tolerance, // max() used to ensure the result can't drop below 0 before the cast. return ((uint32_t)std::max( (int32_t)(usecs * (1.0 - _validTolerance(tolerance) / 100.0) - delta), - 0)); + (int32_t)0)); } /// Calculate the upper bound of the nr. of ticks. @@ -1159,7 +1187,8 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, // We really should never get a value of 0, except as the last value // in the buffer. If that is the case, then assume infinity and return true. if (measured == 0) return true; - return measured >= ticksLow(std::min(desired, MS_TO_USEC(params.timeout)), + return measured >= ticksLow(std::min(desired, + (uint32_t)MS_TO_USEC(params.timeout)), tolerance, delta); } diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 13aadb657..75070172d 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -419,7 +419,12 @@ class IRrecv { bool decodeCOOLIX(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kCoolixBits, const bool strict = true); -#endif +#endif // DECODE_COOLIX +#if DECODE_COOLIX48 + bool decodeCoolix48(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kCoolix48Bits, + const bool strict = true); +#endif // DECODE_COOLIX48 #if DECODE_DENON bool decodeDenon(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kDenonBits, @@ -774,6 +779,11 @@ class IRrecv { bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kRhossBits, const bool strict = true); #endif // DECODE_RHOSS +#if DECODE_AIRTON + bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAirtonBits, + const bool strict = true); +#endif // DECODE_AIRTON }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266/src/IRremoteESP8266.h b/lib/IRremoteESP8266/src/IRremoteESP8266.h index 29e51c610..3cd19a81d 100644 --- a/lib/IRremoteESP8266/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266/src/IRremoteESP8266.h @@ -52,8 +52,29 @@ #include #endif // UNIT_TEST -// Library Version -#define _IRREMOTEESP8266_VERSION_ "2.7.20" +// Library Version Information +// Major version number (X.x.x) +#define _IRREMOTEESP8266_VERSION_MAJOR 2 +// Minor version number (x.X.x) +#define _IRREMOTEESP8266_VERSION_MINOR 8 +// Patch version number (x.x.X) +#define _IRREMOTEESP8266_VERSION_PATCH 1 +// Macro to convert version info into an integer +#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \ + ((major << 16) | (minor << 8) | (patch)) +// Macro to convert literal into a string +#define MKSTR(x) #x +// Integer version +#define _IRREMOTEESP8266_VERSION _IRREMOTEESP8266_VERSION_VAL(\ + _IRREMOTEESP8266_VERSION_MAJOR, \ + _IRREMOTEESP8266_VERSION_MINOR, \ + _IRREMOTEESP8266_VERSION_PATCH) +// String version +#define _IRREMOTEESP8266_VERSION_STR MKSTR(_IRREMOTEESP8266_VERSION_MAJOR) "." \ + MKSTR(_IRREMOTEESP8266_VERSION_MINOR) "." \ + MKSTR(_IRREMOTEESP8266_VERSION_PATCH) +// String version (DEPRECATED) +#define _IRREMOTEESP8266_VERSION_ _IRREMOTEESP8266_VERSION_STR // Set the language & locale for the library. See the `locale` dir for options. #ifndef _IR_LOCALE_ @@ -314,6 +335,13 @@ #define SEND_COOLIX _IR_ENABLE_DEFAULT_ #endif // SEND_COOLIX +#ifndef DECODE_COOLIX48 +#define DECODE_COOLIX48 _IR_ENABLE_DEFAULT_ +#endif // DECODE_COOLIX48 +#ifndef SEND_COOLIX48 +#define SEND_COOLIX48 _IR_ENABLE_DEFAULT_ +#endif // SEND_COOLIX48 + #ifndef DECODE_GLOBALCACHE #define DECODE_GLOBALCACHE false // Not applicable. #endif // DECODE_GLOBALCACHE @@ -798,12 +826,19 @@ #endif // SEND_ARRIS #ifndef DECODE_RHOSS -#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ +#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ #endif // DECODE_RHOSS #ifndef SEND_RHOSS -#define SEND_RHOSS _IR_ENABLE_DEFAULT_ +#define SEND_RHOSS _IR_ENABLE_DEFAULT_ #endif // SEND_RHOSS +#ifndef DECODE_AIRTON +#define DECODE_AIRTON _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIRTON +#ifndef SEND_AIRTON +#define SEND_AIRTON _IR_ENABLE_DEFAULT_ +#endif // SEND_AIRTON + #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 || \ @@ -967,14 +1002,18 @@ enum decode_type_t { BOSE, ARRIS, RHOSS, + AIRTON, + COOLIX48, // 110 // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = RHOSS, + kLastDecodeType = COOLIX48, }; // Message lengths & required repeat values const uint16_t kNoRepeat = 0; const uint16_t kSingleRepeat = 1; +const uint16_t kAirtonBits = 56; +const uint16_t kAirtonDefaultRepeat = kNoRepeat; const uint16_t kAirwellBits = 34; const uint16_t kAirwellMinRepeats = 2; const uint16_t kAiwaRcT501Bits = 15; @@ -988,6 +1027,7 @@ const uint16_t kArgoBits = kArgoStateLength * 8; const uint16_t kArgoDefaultRepeat = kNoRepeat; const uint16_t kArrisBits = 32; const uint16_t kCoolixBits = 24; +const uint16_t kCoolix48Bits = kCoolixBits * 2; const uint16_t kCoolixDefaultRepeat = kSingleRepeat; const uint16_t kCarrierAcBits = 32; const uint16_t kCarrierAcMinRepeat = kNoRepeat; diff --git a/lib/IRremoteESP8266/src/IRsend.cpp b/lib/IRremoteESP8266/src/IRsend.cpp index 62556b7e9..e0a66e068 100644 --- a/lib/IRremoteESP8266/src/IRsend.cpp +++ b/lib/IRremoteESP8266/src/IRsend.cpp @@ -560,6 +560,7 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) { case AIWA_RC_T501: case AMCOR: case COOLIX: + case COOLIX48: case ELITESCREENS: case GICABLE: case INAX: @@ -661,11 +662,13 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kDoshishaBits; // 40 case SANYO_LC7461: return kSanyoLC7461Bits; // 42 + case COOLIX48: case GOODWEATHER: case KELON: case MIDEA: case PANASONIC: return 48; + case AIRTON: case ECOCLIM: case MAGIQUEST: case VESTEL_AC: @@ -784,6 +787,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, uint16_t min_repeat __attribute__((unused)) = std::max(IRsend::minRepeats(type), repeat); switch (type) { +#if SEND_AIRTON + case AIRTON: + sendAirton(data, nbits, min_repeat); + break; +#endif // SEND_AIRTON #if SEND_AIRWELL case AIRWELL: sendAirwell(data, nbits, min_repeat); @@ -823,7 +831,12 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, case COOLIX: sendCOOLIX(data, nbits, min_repeat); break; -#endif +#endif // SEND_COOLIX +#if SEND_COOLIX48 + case COOLIX48: + sendCoolix48(data, nbits, min_repeat); + break; +#endif // SEND_COOLIX48 #if SEND_DAIKIN64 case DAIKIN64: sendDaikin64(data, nbits, min_repeat); diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index c09ec3d4c..3696eb40f 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -42,78 +42,78 @@ const uint32_t kDefaultMessageGap = 100000; /// Enumerators and Structures for the Common A/C API. namespace stdAc { - /// Common A/C settings for A/C operating modes. - enum class opmode_t { - kOff = -1, - kAuto = 0, - kCool = 1, - kHeat = 2, - kDry = 3, - kFan = 4, - // Add new entries before this one, and update it to point to the last entry - kLastOpmodeEnum = kFan, - }; +/// Common A/C settings for A/C operating modes. +enum class opmode_t { + kOff = -1, + kAuto = 0, + kCool = 1, + kHeat = 2, + kDry = 3, + kFan = 4, + // Add new entries before this one, and update it to point to the last entry + kLastOpmodeEnum = kFan, +}; - /// Common A/C settings for Fan Speeds. - enum class fanspeed_t { - kAuto = 0, - kMin = 1, - kLow = 2, - kMedium = 3, - kHigh = 4, - kMax = 5, - // Add new entries before this one, and update it to point to the last entry - kLastFanspeedEnum = kMax, - }; +/// Common A/C settings for Fan Speeds. +enum class fanspeed_t { + kAuto = 0, + kMin = 1, + kLow = 2, + kMedium = 3, + kHigh = 4, + kMax = 5, + // Add new entries before this one, and update it to point to the last entry + kLastFanspeedEnum = kMax, +}; - /// Common A/C settings for Vertical Swing. - enum class swingv_t { - kOff = -1, - kAuto = 0, - kHighest = 1, - kHigh = 2, - kMiddle = 3, - kLow = 4, - kLowest = 5, - // Add new entries before this one, and update it to point to the last entry - kLastSwingvEnum = kLowest, - }; +/// Common A/C settings for Vertical Swing. +enum class swingv_t { + kOff = -1, + kAuto = 0, + kHighest = 1, + kHigh = 2, + kMiddle = 3, + kLow = 4, + kLowest = 5, + // Add new entries before this one, and update it to point to the last entry + kLastSwingvEnum = kLowest, +}; - /// Common A/C settings for Horizontal Swing. - enum class swingh_t { - kOff = -1, - kAuto = 0, // a.k.a. On. - kLeftMax = 1, - kLeft = 2, - kMiddle = 3, - kRight = 4, - kRightMax = 5, - kWide = 6, // a.k.a. left & right at the same time. - // Add new entries before this one, and update it to point to the last entry - kLastSwinghEnum = kWide, - }; +/// Common A/C settings for Horizontal Swing. +enum class swingh_t { + kOff = -1, + kAuto = 0, // a.k.a. On. + kLeftMax = 1, + kLeft = 2, + kMiddle = 3, + kRight = 4, + kRightMax = 5, + kWide = 6, // a.k.a. left & right at the same time. + // Add new entries before this one, and update it to point to the last entry + kLastSwinghEnum = kWide, +}; - /// Structure to hold a common A/C state. - 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; +/// Structure to hold a common A/C state. +struct state_t { + 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; +}; }; // namespace stdAc /// Fujitsu A/C model numbers @@ -136,12 +136,24 @@ enum gree_ac_remote_model_t { YBOFB, // (2) Green, YBOFB2, YAPOF3 }; +/// HAIER_AC176 A/C model numbers +enum haier_ac176_remote_model_t { + V9014557_A = 1, // (1) V9014557 Remote in "A" setting. (Default) + V9014557_B, // (2) V9014557 Remote in "B" setting. +}; + /// HITACHI_AC1 A/C model numbers enum hitachi_ac1_remote_model_t { R_LT0541_HTA_A = 1, // (1) R-LT0541-HTA Remote in "A" setting. (Default) R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting. }; +/// MIRAGE A/C model numbers +enum mirage_ac_remote_model_t { + KKG9AC1 = 1, // (1) KKG9A-C1 Remote. (Default) + KKG29AC1, // (2) KKG29A-C1 Remote. +}; + /// Panasonic A/C model numbers enum panasonic_ac_remote_model_t { kPanasonicUnknown = 0, @@ -369,9 +381,13 @@ class IRsend { uint16_t repeat = kNoRepeat); #endif #if SEND_COOLIX - void sendCOOLIX(uint64_t data, uint16_t nbits = kCoolixBits, - uint16_t repeat = kCoolixDefaultRepeat); -#endif + void sendCOOLIX(const uint64_t data, const uint16_t nbits = kCoolixBits, + const uint16_t repeat = kCoolixDefaultRepeat); +#endif // SEND_COOLIX +#if SEND_COOLIX48 + void sendCoolix48(const uint64_t data, const uint16_t nbits = kCoolix48Bits, + const uint16_t repeat = kCoolixDefaultRepeat); +#endif // SEND_COOLIX48 #if SEND_WHYNTER void sendWhynter(const uint64_t data, const uint16_t nbits = kWhynterBits, const uint16_t repeat = kNoRepeat); @@ -754,6 +770,10 @@ class IRsend { const uint16_t nbytes = kRhossStateLength, const uint16_t repeat = kRhossDefaultRepeat); #endif // SEND_RHOSS +#if SEND_AIRTON + void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits, + const uint16_t repeat = kAirtonDefaultRepeat); +#endif // SEND_AIRTON protected: #ifdef UNIT_TEST diff --git a/lib/IRremoteESP8266/src/IRtext.cpp b/lib/IRremoteESP8266/src/IRtext.cpp index 7866b47f9..077e7b0e7 100644 --- a/lib/IRremoteESP8266/src/IRtext.cpp +++ b/lib/IRremoteESP8266/src/IRtext.cpp @@ -81,6 +81,14 @@ IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold" IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button" IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat" IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat" +IRTEXT_CONST_STRING(kISeeStr, D_STR_ISEE); ///< "ISee" +IRTEXT_CONST_STRING(kAbsenseDetectStr, D_STR_ABSENSEDETECT); + ///< "AbsenseDetect" +IRTEXT_CONST_STRING(kDirectIndirectModeStr, D_STR_DIRECTINDIRECTMODE); + ///< "Direct/Indirect mode" +IRTEXT_CONST_STRING(kDirectStr, D_STR_DIRECT); ///< "Direct" +IRTEXT_CONST_STRING(kIndirectStr, D_STR_INDIRECT); ///< "Indirect" + IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night" IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent" IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter" @@ -123,6 +131,7 @@ IRTEXT_CONST_STRING(kTypeStr, D_STR_TYPE); ///< "Type" IRTEXT_CONST_STRING(kSpecialStr, D_STR_SPECIAL); ///< "Special" IRTEXT_CONST_STRING(kIdStr, D_STR_ID); ///< "Id" / Device Identifier IRTEXT_CONST_STRING(kVaneStr, D_STR_VANE); ///< "Vane" +IRTEXT_CONST_STRING(kLockStr, D_STR_LOCK); ///< "Lock" IRTEXT_CONST_STRING(kAutoStr, D_STR_AUTO); ///< "Auto" IRTEXT_CONST_STRING(kAutomaticStr, D_STR_AUTOMATIC); ///< "Automatic" @@ -139,7 +148,7 @@ IRTEXT_CONST_STRING(kFanStr, D_STR_FAN); ///< "Fan" // HomeAssistant & Google Home Climate integration. For compatibility only. // Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes IRTEXT_CONST_STRING(kFanOnlyStr, D_STR_FANONLY); ///< "fan-only" -IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (legacy) +IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (HA/legacy) IRTEXT_CONST_STRING(kFanOnlyWithSpaceStr, D_STR_FANSPACEONLY); ///< "Fan Only" IRTEXT_CONST_STRING(kFanOnlyNoSpaceStr, D_STR_FANONLYNOSPACE); ///< "FanOnly" @@ -227,6 +236,8 @@ IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits" // Model Names IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F" IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB" +IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A" +IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B" IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A" IRTEXT_CONST_STRING(kRlt0541htabStr, D_STR_RLT0541HTA_B); ///< "R-LT0541-HTA-B" IRTEXT_CONST_STRING(kArrah2eStr, D_STR_ARRAH2E); ///< "ARRAH2E" @@ -240,6 +251,8 @@ IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///< IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403" IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603" IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604" +IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1" +IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1" IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE" IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE" IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE" @@ -376,6 +389,8 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { D_STR_BOSE "\x0" D_STR_ARRIS "\x0" D_STR_RHOSS "\x0" + D_STR_AIRTON "\x0" + D_STR_COOLIX48 "\x0" ///< New protocol strings should be added just above this line. "\x0" ///< This string requires double null termination. }; diff --git a/lib/IRremoteESP8266/src/IRtext.h b/lib/IRremoteESP8266/src/IRtext.h index f8a3be4ed..031f73879 100644 --- a/lib/IRremoteESP8266/src/IRtext.h +++ b/lib/IRremoteESP8266/src/IRtext.h @@ -111,6 +111,8 @@ extern IRTEXT_CONST_PTR(kIFeelStr); extern IRTEXT_CONST_PTR(kInsideStr); extern IRTEXT_CONST_PTR(kIonStr); extern IRTEXT_CONST_PTR(kJkeStr); +extern IRTEXT_CONST_PTR(kKkg29ac1Str); +extern IRTEXT_CONST_PTR(kKkg9ac1Str); extern IRTEXT_CONST_PTR(kLastStr); extern IRTEXT_CONST_PTR(kLeftMaxNoSpaceStr); extern IRTEXT_CONST_PTR(kLeftMaxStr); @@ -119,6 +121,7 @@ extern IRTEXT_CONST_PTR(kLightStr); extern IRTEXT_CONST_PTR(kLightToggleStr); extern IRTEXT_CONST_PTR(kLkeStr); extern IRTEXT_CONST_PTR(kLoStr); +extern IRTEXT_CONST_PTR(kLockStr); extern IRTEXT_CONST_PTR(kLoudStr); extern IRTEXT_CONST_PTR(kLowerStr); extern IRTEXT_CONST_PTR(kLowestStr); @@ -215,6 +218,8 @@ extern IRTEXT_CONST_PTR(kTypeStr); extern IRTEXT_CONST_PTR(kUnknownStr); extern IRTEXT_CONST_PTR(kUpperStr); extern IRTEXT_CONST_PTR(kUpStr); +extern IRTEXT_CONST_PTR(kV9014557AStr); +extern IRTEXT_CONST_PTR(kV9014557BStr); extern IRTEXT_CONST_PTR(kVaneStr); extern IRTEXT_CONST_PTR(kWallStr); extern IRTEXT_CONST_PTR(kWeeklyTimerStr); @@ -226,5 +231,11 @@ extern IRTEXT_CONST_PTR(kYbofbStr); extern IRTEXT_CONST_PTR(kYesStr); extern IRTEXT_CONST_PTR(kZoneFollowStr); extern IRTEXT_CONST_PTR(kAllProtocolNamesStr); +extern IRTEXT_CONST_PTR(kISeeStr); +extern IRTEXT_CONST_PTR(kEcocoolStr); +extern IRTEXT_CONST_PTR(kAbsenseDetectStr); +extern IRTEXT_CONST_PTR(kDirectIndirectModeStr); +extern IRTEXT_CONST_PTR(kDirectStr); +extern IRTEXT_CONST_PTR(kIndirectStr); #endif // IRTEXT_H_ diff --git a/lib/IRremoteESP8266/src/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp index bebb61077..e1f889966 100644 --- a/lib/IRremoteESP8266/src/IRutils.cpp +++ b/lib/IRremoteESP8266/src/IRutils.cpp @@ -95,6 +95,9 @@ String uint64ToString(uint64_t input, uint8_t base) { /// @returns A String representation of the integer. String int64ToString(int64_t input, uint8_t base) { if (input < 0) { + // Using String(kDashStr) to keep compatible with old arduino + // frameworks. Not needed with 3.0.2. + ///> @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1639#issuecomment-944906016 return String(kDashStr) + uint64ToString(-input, base); } return uint64ToString(input, base); @@ -583,6 +586,7 @@ namespace irutils { /// @param[in] protocol The IR protocol. /// @param[in] model The model number for that protocol. /// @return The resulting String. + /// @note After adding a new model you should update IRac::strToModel() too. String modelToStr(const decode_type_t protocol, const int16_t model) { switch (protocol) { case decode_type_t::FUJITSU_AC: @@ -603,6 +607,16 @@ namespace irutils { default: return kUnknownStr; } break; + case decode_type_t::HAIER_AC176: + switch (model) { + case haier_ac176_remote_model_t::V9014557_A: + return kV9014557AStr; + case haier_ac176_remote_model_t::V9014557_B: + return kV9014557BStr; + default: + return kUnknownStr; + } + break; case decode_type_t::HITACHI_AC1: switch (model) { case hitachi_ac1_remote_model_t::R_LT0541_HTA_A: @@ -623,6 +637,13 @@ namespace irutils { default: return kUnknownStr; } break; + case decode_type_t::MIRAGE: + switch (model) { + case mirage_ac_remote_model_t::KKG9AC1: return kKkg9ac1Str; + case mirage_ac_remote_model_t::KKG29AC1: return kKkg29ac1Str; + default: return kUnknownStr; + } + break; case decode_type_t::PANASONIC_AC: switch (model) { case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr; @@ -1028,6 +1049,21 @@ namespace irutils { return nibbleonly ? sum & 0xF : sum; } + /// Sum all the bytes together in an integer. + /// @param[in] data The integer to be summed. + /// @param[in] count The number of bytes to sum. Starts from LSB. Max of 8. + /// @param[in] init Starting value of the calculation to use. (Default is 0) + /// @param[in] byteonly true, the result is 8 bits. false, it's 16 bits. + /// @return The 8/16-bit calculated result of all the bytes and init value. + uint16_t sumBytes(const uint64_t data, const uint8_t count, + const uint8_t init, const bool byteonly) { + uint16_t sum = init; + uint64_t copy = data; + const uint8_t nrofbytes = (count < 8) ? count : (64 / 8); + for (uint8_t i = 0; i < nrofbytes; i++, copy >>= 8) sum += (copy & 0xFF); + return byteonly ? sum & 0xFF : sum; + } + /// Convert a byte of Binary Coded Decimal(BCD) into an Integer. /// @param[in] bcd The BCD value. /// @return A normal Integer value. diff --git a/lib/IRremoteESP8266/src/IRutils.h b/lib/IRremoteESP8266/src/IRutils.h index 61fe8b269..a5dcde043 100644 --- a/lib/IRremoteESP8266/src/IRutils.h +++ b/lib/IRremoteESP8266/src/IRutils.h @@ -94,6 +94,8 @@ namespace irutils { const uint8_t init = 0); uint8_t sumNibbles(const uint64_t data, const uint8_t count = 16, const uint8_t init = 0, const bool nibbleonly = true); + uint16_t sumBytes(const uint64_t data, const uint8_t count = 8, + const uint8_t init = 0, const bool byteonly = true); uint8_t bcdToUint8(const uint8_t bcd); uint8_t uint8ToBcd(const uint8_t integer); bool getBit(const uint64_t data, const uint8_t position, diff --git a/lib/IRremoteESP8266/src/ir_Airton.cpp b/lib/IRremoteESP8266/src/ir_Airton.cpp new file mode 100644 index 000000000..83ad95ef7 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Airton.cpp @@ -0,0 +1,362 @@ +// Copyright 2021 David Conran (crankyoldgit) +/// @file +/// @brief Support for Airton protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1670 + +#include "ir_Airton.h" +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +const uint16_t kAirtonHdrMark = 6630; +const uint16_t kAirtonBitMark = 400; +const uint16_t kAirtonHdrSpace = 3350; +const uint16_t kAirtonOneSpace = 1260; +const uint16_t kAirtonZeroSpace = 430; +const uint16_t kAirtonFreq = 38000; // Hz. (Just a guess) + +using irutils::addBoolToString; +using irutils::addModeToString; +using irutils::addFanToString; +using irutils::addTempToString; +using irutils::sumBytes; + +#if SEND_AIRTON +// Function should be safe up to 64 bits. +/// Send a Airton formatted message. +/// Status: STABLE / Confirmed working. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. usually kAirtonBits +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendAirton(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kAirtonHdrMark, kAirtonHdrSpace, + kAirtonBitMark, kAirtonOneSpace, + kAirtonBitMark, kAirtonZeroSpace, + kAirtonBitMark, kDefaultMessageGap, + data, nbits, kAirtonFreq, false, repeat, kDutyDefault); +} +#endif // SEND_AIRTON + +#if DECODE_AIRTON +/// Decode the supplied Airton message. +/// Status: STABLE / Confirmed working. LSBF ordering confirmed via temperature. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeAirton(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - offset) + return false; // Too short a message to match. + if (strict && nbits != kAirtonBits) + return false; + + // Header + Data + Footer + if (!matchGeneric(&(results->rawbuf[offset]), &(results->value), + results->rawlen - offset, nbits, + kAirtonHdrMark, kAirtonHdrSpace, + kAirtonBitMark, kAirtonOneSpace, + kAirtonBitMark, kAirtonZeroSpace, + kAirtonBitMark, kDefaultMessageGap, + true, kUseDefTol, kMarkExcess, false)) return false; + // Compliance + if (strict && !IRAirtonAc::validChecksum(results->value)) return false; + // Success + results->decode_type = decode_type_t::AIRTON; + results->bits = nbits; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_AIRTON + +/// Class constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRAirtonAc::IRAirtonAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +/// Set up hardware to be able to send a message. +void IRAirtonAc::begin(void) { _irsend.begin(); } + +#if SEND_AIRTON +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRAirtonAc::send(const uint16_t repeat) { + _irsend.sendAirton(getRaw(), kAirtonBits, repeat); +} +#endif // SEND_AIRTON + +/// Calculate the checksum for the supplied state. +/// @param[in] state The source state to generate the checksum from. +/// @return The checksum value. +uint8_t IRAirtonAc::calcChecksum(const uint64_t state) { + return (uint8_t)(0x7F - sumBytes(state, 6)) ^ 0x2C; +} + +/// Verify the checksum is valid for a given state. +/// @param[in] state The value to verify the checksum of. +/// @return A boolean indicating if it's checksum is valid. +bool IRAirtonAc::validChecksum(const uint64_t state) { + AirtonProtocol p; + p.raw = state; + return p.Sum == IRAirtonAc::calcChecksum(state); +} + +/// Update the checksum value for the internal state. +void IRAirtonAc::checksum(void) { _.Sum = IRAirtonAc::calcChecksum(_.raw); } + +/// Reset the internals of the object to a known good state. +void IRAirtonAc::stateReset(void) { setRaw(0x11D3); } + +/// Get the raw state of the object, suitable to be sent with the appropriate +/// IRsend object method. +/// @return A copy to the internal state. +uint64_t IRAirtonAc::getRaw(void) { + checksum(); // Ensure correct bit array before returning + return _.raw; +} + +/// Set the raw state of the object. +/// @param[in] state The raw state from the native IR message. +void IRAirtonAc::setRaw(const uint64_t state) { _.raw = state; } + + +/// Set the internal state to have the power on. +void IRAirtonAc::on(void) { setPower(true); } + +/// Set the internal state to have the power off. +void IRAirtonAc::off(void) { setPower(false); } + +/// Set the internal state to have the desired power. +/// @param[in] on The desired power state. +void IRAirtonAc::setPower(const bool on) { + _.Power = on; + setMode(getMode()); // Re-do the mode incase we need to do something special. +} + +/// Get the power setting from the internal state. +/// @return A boolean indicating the power setting. +bool IRAirtonAc::getPower(void) const { return _.Power; } + +/// Get the current operation mode setting. +/// @return The current operation mode. +uint8_t IRAirtonAc::getMode(void) const { return _.Mode; } + +/// Set the desired operation mode. +/// @param[in] mode The desired operation mode. +void IRAirtonAc::setMode(const uint8_t mode) { + // Changing the mode always removes the sleep setting. + if (mode != _.Mode) setSleep(false); + // Set the actual mode. + _.Mode = (mode > kAirtonHeat) ? kAirtonAuto : mode; + // Handle special settings for each mode. + switch (_.Mode) { + case kAirtonAuto: + setTemp(25); // Auto has a fixed temp. + _.NotAutoOn = !getPower(); + break; + case kAirtonHeat: + // When powered on and in Heat mode, set a special bit. + _.HeatOn = getPower(); + // FALL-THRU + default: + _.NotAutoOn = true; + } + // Reset the economy setting if we need to. + setEcono(getEcono()); +} + +/// Convert a stdAc::opmode_t enum into its native mode. +/// @param[in] mode The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRAirtonAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kAirtonCool; + case stdAc::opmode_t::kHeat: return kAirtonHeat; + case stdAc::opmode_t::kDry: return kAirtonDry; + case stdAc::opmode_t::kFan: return kAirtonFan; + default: return kAirtonAuto; + } +} + +/// Convert a native mode into its stdAc equivalent. +/// @param[in] mode The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::opmode_t IRAirtonAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kAirtonCool: return stdAc::opmode_t::kCool; + case kAirtonHeat: return stdAc::opmode_t::kHeat; + case kAirtonDry: return stdAc::opmode_t::kDry; + case kAirtonFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRAirtonAc::setTemp(const uint8_t degrees) { + uint8_t temp = std::max(kAirtonMinTemp, degrees); + temp = std::min(kAirtonMaxTemp, temp); + if (_.Mode == kAirtonAuto) temp = kAirtonMaxTemp; // Auto has a fixed temp. + _.Temp = temp - kAirtonMinTemp; +} + +/// Get the current temperature setting. +/// @return Get current setting for temp. in degrees celsius. +uint8_t IRAirtonAc::getTemp(void) const { return _.Temp + kAirtonMinTemp; } + + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRAirtonAc::setFan(const uint8_t speed) { + _.Fan = (speed > kAirtonFanMax) ? kAirtonFanAuto : speed; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRAirtonAc::getFan(void) const { return _.Fan; } + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRAirtonAc::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kAirtonFanMin; + case stdAc::fanspeed_t::kLow: return kAirtonFanLow; + case stdAc::fanspeed_t::kMedium: return kAirtonFanMed; + case stdAc::fanspeed_t::kHigh: return kAirtonFanHigh; + case stdAc::fanspeed_t::kMax: return kAirtonFanMax; + default: return kAirtonFanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] speed The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRAirtonAc::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kAirtonFanMax: return stdAc::fanspeed_t::kMax; + case kAirtonFanHigh: return stdAc::fanspeed_t::kHigh; + case kAirtonFanMed: return stdAc::fanspeed_t::kMedium; + case kAirtonFanLow: return stdAc::fanspeed_t::kLow; + case kAirtonFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Set the Vertical Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRAirtonAc::setSwingV(const bool on) { _.SwingV = on; } + +/// Get the Vertical Swing setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRAirtonAc::getSwingV(void) const { return _.SwingV; } + +/// Set the Light/LED/Display setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRAirtonAc::setLight(const bool on) { _.Light = on; } + +/// Get the Light/LED/Display setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRAirtonAc::getLight(void) const { return _.Light; } + +/// Set the Economy setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note Only available in Cool mode. +void IRAirtonAc::setEcono(const bool on) { + _.Econo = on && (getMode() == kAirtonCool); +} + +/// Get the Economy setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRAirtonAc::getEcono(void) const { return _.Econo; } + +/// Set the Turbo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRAirtonAc::setTurbo(const bool on) { + _.Turbo = on; + // Pressing the turbo button sets the fan to max as well. + if (on) setFan(kAirtonFanMax); +} + +/// Get the Turbo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRAirtonAc::getTurbo(void) const { return _.Turbo; } + +/// Set the Sleep setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note Sleep not available in fan or auto mode. +void IRAirtonAc::setSleep(const bool on) { + switch (getMode()) { + case kAirtonAuto: + case kAirtonFan: _.Sleep = false; break; + default: _.Sleep = on; + } +} + +/// Get the Sleep setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRAirtonAc::getSleep(void) const { return _.Sleep; } + +/// Set the Health/Filter setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRAirtonAc::setHealth(const bool on) { _.Health = on; } + +/// Get the Health/Filter setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRAirtonAc::getHealth(void) const { return _.Health; } + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRAirtonAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::AIRTON; + result.power = getPower(); + result.mode = toCommonMode(getMode()); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan()); + result.swingv = getSwingV() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.econo = getEcono(); + result.turbo = getTurbo(); + result.filter = getHealth(); + result.light = getLight(); + result.sleep = getSleep() ? 0 : -1; + // Not supported. + result.model = -1; + result.swingh = stdAc::swingh_t::kOff; + result.quiet = false; + result.clean = false; + result.beep = false; + result.clock = -1; + return result; +} + +/// Convert the current internal state into a human readable string. +/// @return A human readable string. +String IRAirtonAc::toString(void) const { + String result = ""; + result.reserve(135); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), kPowerStr, false); + result += addModeToString(_.Mode, kAirtonAuto, kAirtonCool, + kAirtonHeat, kAirtonDry, kAirtonFan); + result += addFanToString(_.Fan, kAirtonFanHigh, kAirtonFanLow, + kAirtonFanAuto, kAirtonFanMin, kAirtonFanMed, + kAirtonFanMax); + result += addTempToString(getTemp()); + result += addBoolToString(getSwingV(), kSwingVStr); + result += addBoolToString(getEcono(), kEconoStr); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getLight(), kLightStr); + result += addBoolToString(getHealth(), kHealthStr); + result += addBoolToString(getSleep(), kSleepStr); + return result; +} diff --git a/lib/IRremoteESP8266/src/ir_Airton.h b/lib/IRremoteESP8266/src/ir_Airton.h new file mode 100644 index 000000000..9b5e89f3f --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Airton.h @@ -0,0 +1,134 @@ +// Copyright 2021 David Conran (crankyoldgit) +/// @file +/// @brief Support for Airton protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1670 + +// Supports: +// Brand: Airton, Model: SMVH09B-2A2A3NH ref. 409730 A/C +// Brand: Airton, Model: RD1A1 remote + +#ifndef IR_AIRTON_H_ +#define IR_AIRTON_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 + +/// Native representation of a Airton 56 A/C message. +/// @see https://docs.google.com/spreadsheets/d/1Kpq7WCkh85heLnTQGlwUfCR6eeu_vfBHvhii8wtP4LU/edit?usp=sharing +union AirtonProtocol{ + uint64_t raw; ///< The state in code form. + struct { // Common + // Byte 1 & 0 (LSB) + uint16_t Header :16; // Header. (0x11D3) + // Byte 2 + uint8_t Mode :3; // Operating Mode + uint8_t Power :1; // Power Control + uint8_t Fan :3; + uint8_t Turbo :1; + // Byte 3 + uint8_t Temp :4; // Degrees Celsius (+16 offset) + uint8_t :4; // Unknown / Unused. + // Byte 4 + uint8_t SwingV :1; + uint8_t :7; // Unknown / Unused. + // Byte 5 + uint8_t Econo :1; + uint8_t Sleep :1; + uint8_t NotAutoOn :1; + uint8_t :1; // Unknown / Unused. + uint8_t HeatOn :1; + uint8_t :1; // Unknown / Unused. + uint8_t Health :1; + uint8_t Light :1; + // Byte 6 + uint8_t Sum :8; // Sepecial checksum value + }; +}; + +// Constants +const uint8_t kAirtonAuto = 0b000; // 0 +const uint8_t kAirtonCool = 0b001; // 1 +const uint8_t kAirtonDry = 0b010; // 2 +const uint8_t kAirtonFan = 0b011; // 3 +const uint8_t kAirtonHeat = 0b100; // 4 + +const uint8_t kAirtonFanAuto = 0b000; // 0 +const uint8_t kAirtonFanMin = 0b001; // 1 +const uint8_t kAirtonFanLow = 0b010; // 2 +const uint8_t kAirtonFanMed = 0b011; // 3 +const uint8_t kAirtonFanHigh = 0b100; // 4 +const uint8_t kAirtonFanMax = 0b101; // 5 + +const uint8_t kAirtonMinTemp = 16; // 16C +const uint8_t kAirtonMaxTemp = 25; // 25C + + +/// Class for handling detailed Airton 56-bit A/C messages. +class IRAirtonAc { + public: + explicit IRAirtonAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(void); +#if SEND_AIRTON + void send(const uint16_t repeat = kAirtonDefaultRepeat); + /// Run the calibration to calculate uSec timing offsets for this platform. + /// @return The uSec timing offset needed per modulation of the IR Led. + /// @note This will produce a 65ms IR signal pulse at 38kHz. + /// Only ever needs to be run once per object instantiation, if at all. + int8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_AIRTON + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void) const; + void setTemp(const uint8_t degrees); + uint8_t getTemp(void) const; + void setFan(const uint8_t speed); + uint8_t getFan(void) const; + void setMode(const uint8_t mode); + uint8_t getMode(void) const; + uint64_t getRaw(void); + void setRaw(const uint64_t data); + void setLight(const bool on); + bool getLight(void) const; + void setEcono(const bool on); + bool getEcono(void) const; + void setTurbo(const bool on); + bool getTurbo(void) const; + void setHealth(const bool on); + bool getHealth(void) const; + void setSleep(const bool on); + bool getSleep(void) const; + void setSwingV(const bool on); + bool getSwingV(void) const; + static bool validChecksum(const uint64_t data); + static uint8_t calcChecksum(const uint64_t data); + 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) const; + String toString(void) const; +#ifndef UNIT_TEST + + private: + IRsend _irsend; ///< Instance of the IR send class +#else // UNIT_TEST + /// @cond IGNORE + IRsendTest _irsend; ///< Instance of the testing IR send class + /// @endcond +#endif // UNIT_TEST + AirtonProtocol _; + void checksum(void); +}; +#endif // IR_AIRTON_H_ diff --git a/lib/IRremoteESP8266/src/ir_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp index 2f16a6d1f..7b5b140f0 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266/src/ir_Coolix.cpp @@ -21,17 +21,18 @@ // pulse parameters in usec const uint16_t kCoolixTick = 276; // Approximately 10.5 cycles at 38kHz const uint16_t kCoolixBitMarkTicks = 2; -const uint16_t kCoolixBitMark = kCoolixBitMarkTicks * kCoolixTick; +const uint16_t kCoolixBitMark = kCoolixBitMarkTicks * kCoolixTick; // 552us const uint16_t kCoolixOneSpaceTicks = 6; -const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick; +const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick; // 1656us const uint16_t kCoolixZeroSpaceTicks = 2; -const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick; +const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick; // 552us const uint16_t kCoolixHdrMarkTicks = 17; -const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick; +const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick; // 4692us const uint16_t kCoolixHdrSpaceTicks = 16; -const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; +const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; // 4416us const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks; -const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; +const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; // 5244us +const uint8_t kCoolix48ExtraTolerance = 5; // Percent using irutils::addBoolToString; using irutils::addIntToString; @@ -40,7 +41,7 @@ using irutils::addModeToString; using irutils::addTempToString; #if SEND_COOLIX -/// Send a Coolix message +/// Send a Coolix 24-bit message /// Status: STABLE / Confirmed Working. /// @param[in] data The message to be sent. /// @param[in] nbits The number of bits of message to be sent. @@ -611,12 +612,16 @@ String IRCoolixAC::toString(void) const { result += addBoolToString(getZoneFollow(), kZoneFollowStr); result += addLabeledString( (getSensorTemp() == kCoolixSensorTempIgnoreCode) - ? kOffStr : String(uint64ToString(getSensorTemp()) + 'C'), kSensorTempStr); + // Encasing with String(blah) to keep compatible with old arduino + // frameworks. Not needed with 3.0.2. + ///> @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1639#issuecomment-944906016 + ? kOffStr : String(uint64ToString(getSensorTemp()) + 'C'), + kSensorTempStr); return result; } #if DECODE_COOLIX -/// Decode the supplied Coolix A/C message. +/// Decode the supplied Coolix 24-bit A/C message. /// Status: STABLE / Known Working. /// @param[in,out] results Ptr to the data to decode & where to store the decode /// result. @@ -695,3 +700,58 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t offset, return true; } #endif // DECODE_COOLIX + +#if SEND_COOLIX48 +/// Send a Coolix 48-bit message. +/// Status: ALPHA / Untested. +/// @param[in] data The message to be sent. +/// @param[in] nbits The number of bits of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694 +/// @note This is effectively the same as `sendCOOLIX()` except requiring the +/// bit flipping be done prior to the call. +void IRsend::sendCoolix48(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + // Header + Data + Footer + sendGeneric(kCoolixHdrMark, kCoolixHdrSpace, + kCoolixBitMark, kCoolixOneSpace, + kCoolixBitMark, kCoolixZeroSpace, + kCoolixBitMark, kCoolixMinGap, + data, nbits, 38000, true, repeat, 33); +} +#endif // SEND_COOLIX48 + +#if DECODE_COOLIX +/// Decode the supplied Coolix 48-bit A/C message. +/// Status: BETA / Probably Working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// result. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694 +bool IRrecv::decodeCoolix48(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kCoolix48Bits) + return false; // Not strictly a COOLIX48 message. + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &(results->value), + results->rawlen - offset, nbits, + kCoolixHdrMark, kCoolixHdrSpace, + kCoolixBitMark, kCoolixOneSpace, + kCoolixBitMark, kCoolixZeroSpace, + kCoolixBitMark, kCoolixMinGap, + true, _tolerance + kCoolix48ExtraTolerance, 0, true)) + return false; + + // Success + results->decode_type = COOLIX48; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_COOLIX48 diff --git a/lib/IRremoteESP8266/src/ir_Coolix.h b/lib/IRremoteESP8266/src/ir_Coolix.h index 42a2528d7..2155b54b0 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.h +++ b/lib/IRremoteESP8266/src/ir_Coolix.h @@ -6,6 +6,8 @@ /// @note Kudos: /// Hamper: For the breakdown and mapping of the bit values. /// fraschizzato: For additional ZoneFollow & SwingVStep analysis. +/// @note Timers seem to use the `COOLIX48` protocol. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694 // Supports: // Brand: Beko, Model: RG57K7(B)/BGEF Remote @@ -21,6 +23,8 @@ // Brand: Toshiba, Model: RAS-M13YKV-E A/C // Brand: Toshiba, Model: RAS-4M27YAV-E A/C // Brand: Toshiba, Model: WH-E1YE remote +// Brand: Bosch, Model: RG36B4/BGE remote +// Brand: Bosch, Model: B1ZAI2441W/B1ZAO2441W A/C #ifndef IR_COOLIX_H_ #define IR_COOLIX_H_ diff --git a/lib/IRremoteESP8266/src/ir_Electra.cpp b/lib/IRremoteESP8266/src/ir_Electra.cpp index 7945cb0ad..b4d670b88 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.cpp +++ b/lib/IRremoteESP8266/src/ir_Electra.cpp @@ -1,4 +1,4 @@ -// Copyright 2018, 2019 David Conran +// Copyright 2018-2021 David Conran /// @file /// @brief Support for Electra A/C protocols. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp @@ -310,6 +310,52 @@ bool IRElectraAc::getTurbo(void) const { return _.Turbo; } +/// Get the IFeel mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRElectraAc::getIFeel(void) const { return _.IFeel; } + +/// Set the IFeel mode of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRElectraAc::setIFeel(const bool on) { + _.IFeel = on; + if (_.IFeel) + // Make sure there is a reasonable value in _.SensorTemp + setSensorTemp(getSensorTemp()); + else + // Clear any previous stored temp.. + _.SensorTemp = kElectraAcSensorMinTemp; +} + +/// Get the silent Sensor Update setting of the message. +/// i.e. Is this _just_ a sensor temp update message from the remote? +/// @note The A/C just takes the sensor temp value from the message and +/// will not follow any of the other settings in the message. +/// @return true, the setting is on. false, the setting is off. +bool IRElectraAc::getSensorUpdate(void) const { return _.SensorUpdate; } + +/// Set the silent Sensor Update setting of the message. +/// i.e. Is this _just_ a sensor temp update message from the remote? +/// @note The A/C will just take the sensor temp value from the message and +/// will not follow any of the other settings in the message. If set, the A/C +/// unit will also not beep in response to the message. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRElectraAc::setSensorUpdate(const bool on) { _.SensorUpdate = on; } + +/// Set the Sensor temperature for the IFeel mode. +/// @param[in] temp The temperature in degrees celsius. +void IRElectraAc::setSensorTemp(const uint8_t temp) { + _.SensorTemp = std::min(kElectraAcSensorMaxTemp, + std::max(kElectraAcSensorMinTemp, temp)) + + kElectraAcSensorTempDelta; +} + +/// Get the current sensor temperature setting for the IFeel mode. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRElectraAc::getSensorTemp(void) const { + return std::max(kElectraAcSensorTempDelta, _.SensorTemp) - + kElectraAcSensorTempDelta; +} + /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRElectraAc::toCommon(void) const { @@ -342,19 +388,26 @@ stdAc::state_t IRElectraAc::toCommon(void) const { /// @return A human readable string. String IRElectraAc::toString(void) const { String result = ""; - result.reserve(130); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); - result += addModeToString(_.Mode, kElectraAcAuto, kElectraAcCool, - kElectraAcHeat, kElectraAcDry, kElectraAcFan); - result += addTempToString(getTemp()); - result += addFanToString(_.Fan, kElectraAcFanHigh, kElectraAcFanLow, - kElectraAcFanAuto, kElectraAcFanAuto, - kElectraAcFanMed); - result += addBoolToString(getSwingV(), kSwingVStr); - result += addBoolToString(getSwingH(), kSwingHStr); - result += addToggleToString(getLightToggle(), kLightStr); - result += addBoolToString(_.Clean, kCleanStr); - result += addBoolToString(_.Turbo, kTurboStr); + result.reserve(160); // Reserve some heap for the string to reduce fragging. + if (!_.SensorUpdate) { + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kElectraAcAuto, kElectraAcCool, + kElectraAcHeat, kElectraAcDry, kElectraAcFan); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kElectraAcFanHigh, kElectraAcFanLow, + kElectraAcFanAuto, kElectraAcFanAuto, + kElectraAcFanMed); + result += addBoolToString(getSwingV(), kSwingVStr); + result += addBoolToString(getSwingH(), kSwingHStr); + result += addToggleToString(getLightToggle(), kLightStr); + result += addBoolToString(_.Clean, kCleanStr); + result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.IFeel, kIFeelStr); + } + if (_.IFeel || _.SensorUpdate) { + result += addIntToString(getSensorTemp(), kSensorTempStr, !_.SensorUpdate); + result += 'C'; + } return result; } diff --git a/lib/IRremoteESP8266/src/ir_Electra.h b/lib/IRremoteESP8266/src/ir_Electra.h index 886a92c56..8fd4ee182 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.h +++ b/lib/IRremoteESP8266/src/ir_Electra.h @@ -1,4 +1,4 @@ -// Copyright 2019 David Conran +// Copyright 2019-2021 David Conran /// @file /// @brief Support for Electra A/C protocols. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp @@ -9,6 +9,10 @@ // Brand: Electra, Model: Classic INV 17 / AXW12DCS A/C // Brand: Electra, Model: YKR-M/003E remote // Brand: Frigidaire, Model: FGPC102AB1 A/C +// Brand: Subtropic, Model: SUB-07HN1_18Y A/C +// Brand: Subtropic, Model: YKR-H/102E remote +// Brand: Centek, Model: SCT-65Q09 A/C +// Brand: Centek, Model: YKR-P/002E remote #ifndef IR_ELECTRA_H_ #define IR_ELECTRA_H_ @@ -37,7 +41,9 @@ union ElectraProtocol { uint8_t :5; uint8_t SwingH :3; // Byte 3 - uint8_t :8; + uint8_t :6; + uint8_t SensorUpdate :1; + uint8_t :1; // Byte 4 uint8_t :5; uint8_t Fan :3; @@ -46,10 +52,12 @@ union ElectraProtocol { uint8_t Turbo :1; uint8_t :1; // Byte 6 - uint8_t :5; + uint8_t :3; + uint8_t IFeel :1; + uint8_t :1; uint8_t Mode :3; // Byte 7 - uint8_t :8; + uint8_t SensorTemp :8; // Byte 8 uint8_t :8; // Byte 9 @@ -93,6 +101,11 @@ const uint8_t kElectraAcLightToggleMask = 0x11; // and known OFF values of 0x08 (0b00001000) & 0x05 (0x00000101) const uint8_t kElectraAcLightToggleOff = 0x08; +// Re: Byte[7]. Or Delta == 0xA and Temperature are stored in last 6 bits, +// and bit 7 stores Unknown flag +const uint8_t kElectraAcSensorTempDelta = 0x4A; +const uint8_t kElectraAcSensorMinTemp = 0; // 0C +const uint8_t kElectraAcSensorMaxTemp = 50; // 50C // Classes /// Class for handling detailed Electra A/C messages. @@ -130,6 +143,12 @@ class IRElectraAc { bool getLightToggle(void) const; void setTurbo(const bool on); bool getTurbo(void) const; + void setIFeel(const bool on); + bool getIFeel(void) const; + void setSensorUpdate(const bool on); + bool getSensorUpdate(void) const; + void setSensorTemp(const uint8_t temp); + uint8_t getSensorTemp(void) const; uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kElectraAcStateLength); diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index 994b6f4a3..70c0a4cf0 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -33,6 +33,10 @@ // Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E) // Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E) // Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E) +// Brand: Fujitsu, Model: AR-REB4E remote (ARREB1E) +// Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E) +// Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E) +// Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E) #ifndef IR_FUJITSU_H_ #define IR_FUJITSU_H_ diff --git a/lib/IRremoteESP8266/src/ir_Gree.cpp b/lib/IRremoteESP8266/src/ir_Gree.cpp index 1d1371b2d..3c3a8583e 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266/src/ir_Gree.cpp @@ -35,6 +35,7 @@ using irutils::addLabeledString; using irutils::addModeToString; using irutils::addModelToString; using irutils::addFanToString; +using irutils::addSwingHToString; using irutils::addTempToString; using irutils::minsToString; @@ -220,15 +221,11 @@ bool IRGreeAC::getPower(void) const { /// Set the default temperature units to use. /// @param[in] on Use Fahrenheit as the units. /// true is Fahrenheit, false is Celsius. -void IRGreeAC::setUseFahrenheit(const bool on) { - _.UseFahrenheit = on; -} +void IRGreeAC::setUseFahrenheit(const bool on) { _.UseFahrenheit = on; } /// Get the default temperature units in use. /// @return true is Fahrenheit, false is Celsius. -bool IRGreeAC::getUseFahrenheit(void) const { - return _.UseFahrenheit; -} +bool IRGreeAC::getUseFahrenheit(void) const { return _.UseFahrenheit; } /// Set the temp. in degrees /// @param[in] temp Desired temperature in Degrees. @@ -281,9 +278,7 @@ void IRGreeAC::setFan(const uint8_t speed) { /// Get the current fan speed setting. /// @return The current fan speed. -uint8_t IRGreeAC::getFan(void) const { - return _.Fan; -} +uint8_t IRGreeAC::getFan(void) const { return _.Fan; } /// Set the operating mode of the A/C. /// @param[in] new_mode The desired operating mode. @@ -305,81 +300,63 @@ void IRGreeAC::setMode(const uint8_t new_mode) { /// Get the operating mode setting of the A/C. /// @return The current operating mode setting. -uint8_t IRGreeAC::getMode(void) const { - return _.Mode; -} +uint8_t IRGreeAC::getMode(void) const { return _.Mode; } /// Set the Light (LED) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setLight(const bool on) { - _.Light = on; -} +void IRGreeAC::setLight(const bool on) { _.Light = on; } /// Get the Light (LED) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getLight(void) const { - return _.Light; -} +bool IRGreeAC::getLight(void) const { return _.Light; } /// Set the IFeel setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setIFeel(const bool on) { - _.IFeel = on; -} +void IRGreeAC::setIFeel(const bool on) { _.IFeel = on; } /// Get the IFeel setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getIFeel(void) const { - return _.IFeel; -} +bool IRGreeAC::getIFeel(void) const { return _.IFeel; } /// Set the Wifi (enabled) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setWiFi(const bool on) { - _.WiFi = on; -} +void IRGreeAC::setWiFi(const bool on) { _.WiFi = on; } /// Get the Wifi (enabled) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getWiFi(void) const { - return _.WiFi; -} +bool IRGreeAC::getWiFi(void) const { return _.WiFi; } /// Set the XFan (Mould) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setXFan(const bool on) { - _.Xfan = on; -} +void IRGreeAC::setXFan(const bool on) { _.Xfan = on; } /// Get the XFan (Mould) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getXFan(void) const { - return _.Xfan; -} +bool IRGreeAC::getXFan(void) const { return _.Xfan; } /// Set the Sleep setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setSleep(const bool on) { - _.Sleep = on; -} +void IRGreeAC::setSleep(const bool on) { _.Sleep = on; } /// Get the Sleep setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getSleep(void) const { - return _.Sleep; -} +bool IRGreeAC::getSleep(void) const { return _.Sleep; } /// Set the Turbo setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setTurbo(const bool on) { - _.Turbo = on; -} +void IRGreeAC::setTurbo(const bool on) { _.Turbo = on; } /// Get the Turbo setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getTurbo(void) const { - return _.Turbo; -} +bool IRGreeAC::getTurbo(void) const { return _.Turbo; } + +/// Set the Econo setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRGreeAC::setEcono(const bool on) { _.Econo = on; } + +/// Get the Econo setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRGreeAC::getEcono(void) const { return _.Econo; } /// Set the Vertical Swing mode of the A/C. /// @param[in] automatic Do we use the automatic setting? @@ -409,32 +386,37 @@ void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { new_position = kGreeSwingAuto; } } - _.Swing = new_position; + _.SwingV = new_position; } /// Get the Vertical Swing Automatic mode setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getSwingVerticalAuto(void) const { - return _.SwingAuto; -} +bool IRGreeAC::getSwingVerticalAuto(void) const { return _.SwingAuto; } /// Get the Vertical Swing position setting of the A/C. /// @return The native position/mode. -uint8_t IRGreeAC::getSwingVerticalPosition(void) const { - return _.Swing; +uint8_t IRGreeAC::getSwingVerticalPosition(void) const { return _.SwingV; } + +/// Get the Horizontal Swing position setting of the A/C. +/// @return The native position/mode. +uint8_t IRGreeAC::getSwingHorizontal(void) const { return _.SwingH; } + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] position The position/mode to set the vanes to. +void IRGreeAC::setSwingHorizontal(const uint8_t position) { + if (position <= kGreeSwingHMaxRight) + _.SwingH = position; + else + _.SwingH = kGreeSwingHOff; } /// Set the timer enable setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setTimerEnabled(const bool on) { - _.TimerEnabled = on; -} +void IRGreeAC::setTimerEnabled(const bool on) { _.TimerEnabled = on; } /// Get the timer enabled setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getTimerEnabled(void) const { - return _.TimerEnabled; -} +bool IRGreeAC::getTimerEnabled(void) const { return _.TimerEnabled; } /// Get the timer time value from the A/C. /// @return The number of minutes the timer is set for. @@ -478,9 +460,7 @@ void IRGreeAC::setDisplayTempSource(const uint8_t mode) { /// Get the temperature display mode. /// i.e. Internal, External temperature sensing. /// @return The current temp source being displayed. -uint8_t IRGreeAC::getDisplayTempSource(void) const { - return _.DisplayTemp; -} +uint8_t IRGreeAC::getDisplayTempSource(void) const { return _.DisplayTemp; } /// Convert a stdAc::opmode_t enum into its native mode. /// @param[in] mode The enum to be converted. @@ -523,6 +503,21 @@ uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) { } } +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] swingh The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRGreeAC::convertSwingH(const stdAc::swingh_t swingh) { + switch (swingh) { + case stdAc::swingh_t::kAuto: return kGreeSwingHAuto; + case stdAc::swingh_t::kLeftMax: return kGreeSwingHMaxLeft; + case stdAc::swingh_t::kLeft: return kGreeSwingHLeft; + case stdAc::swingh_t::kMiddle: return kGreeSwingHMiddle; + case stdAc::swingh_t::kRight: return kGreeSwingHRight; + case stdAc::swingh_t::kRightMax: return kGreeSwingHMaxRight; + default: return kGreeSwingHOff; + } +} + /// Convert a native mode into its stdAc equivalent. /// @param[in] mode The native setting to be converted. /// @return The stdAc equivalent of the native setting. @@ -530,9 +525,9 @@ 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; + case kGreeDry: return stdAc::opmode_t::kDry; + case kGreeFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; } } @@ -541,10 +536,10 @@ stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) { /// @return The stdAc equivalent of the native setting. stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) { switch (speed) { - case kGreeFanMax: return stdAc::fanspeed_t::kMax; + 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; + case kGreeFanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; } } @@ -553,12 +548,27 @@ stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) { /// @return The stdAc equivalent of the native setting. 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 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; + case kGreeSwingDown: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert a native Horizontal Swing into its stdAc equivalent. +/// @param[in] pos The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::swingh_t IRGreeAC::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kGreeSwingHAuto: return stdAc::swingh_t::kAuto; + case kGreeSwingHMaxLeft: return stdAc::swingh_t::kLeftMax; + case kGreeSwingHLeft: return stdAc::swingh_t::kLeft; + case kGreeSwingHMiddle: return stdAc::swingh_t::kMiddle; + case kGreeSwingHRight: return stdAc::swingh_t::kRight; + case kGreeSwingHMaxRight: return stdAc::swingh_t::kRightMax; + default: return stdAc::swingh_t::kOff; } } @@ -576,15 +586,15 @@ stdAc::state_t IRGreeAC::toCommon(void) { if (_.SwingAuto) result.swingv = stdAc::swingv_t::kAuto; else - result.swingv = toCommonSwingV(_.Swing); + result.swingv = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH); result.turbo = _.Turbo; + result.econo = _.Econo; result.light = _.Light; result.clean = _.Xfan; result.sleep = _.Sleep ? 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; @@ -604,6 +614,7 @@ String IRGreeAC::toString(void) { result += addFanToString(_.Fan, kGreeFanMax, kGreeFanMin, kGreeFanAuto, kGreeFanAuto, kGreeFanMed); result += addBoolToString(_.Turbo, kTurboStr); + result += addBoolToString(_.Econo, kEconoStr); result += addBoolToString(_.IFeel, kIFeelStr); result += addBoolToString(_.WiFi, kWifiStr); result += addBoolToString(_.Xfan, kXFanStr); @@ -611,9 +622,9 @@ String IRGreeAC::toString(void) { result += addBoolToString(_.Sleep, kSleepStr); result += addLabeledString(_.SwingAuto ? kAutoStr : kManualStr, kSwingVModeStr); - result += addIntToString(_.Swing, kSwingVStr); + result += addIntToString(_.SwingV, kSwingVStr); result += kSpaceLBraceStr; - switch (_.Swing) { + switch (_.SwingV) { case kGreeSwingLastPos: result += kLastStr; break; @@ -623,6 +634,12 @@ String IRGreeAC::toString(void) { default: result += kUnknownStr; } result += ')'; + result += addSwingHToString(_.SwingH, kGreeSwingHAuto, kGreeSwingHMaxLeft, + kGreeSwingHLeft, kGreeSwingHMiddle, + kGreeSwingHRight, kGreeSwingHMaxRight, + kGreeSwingHOff, + // rest are unused. + 0xFF, 0xFF, 0xFF, 0xFF); result += addLabeledString( _.TimerEnabled ? minsToString(getTimer()) : kOffStr, kTimerStr); uint8_t src = _.DisplayTemp; diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index d93b42d8d..be5ac31ce 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -21,8 +21,8 @@ // Brand: Amana, Model: YX1FF remote // Brand: Cooper & Hunter, Model: YB1F2 remote // Brand: Cooper & Hunter, Model: CH-S09FTXG A/C -// Brand: Vaillant, Model: YACIFB remote -// Brand: Vaillant, Model: VAI5-035WNI A/C +// Brand: Vailland, Model: YACIFB remote +// Brand: Vailland, Model: VAI5-035WNI A/C #ifndef IR_GREE_H_ #define IR_GREE_H_ @@ -65,19 +65,22 @@ union GreeProtocol{ uint8_t UseFahrenheit :1; uint8_t unknown1 :4; // value=0b0101 // Byte 4 - uint8_t Swing:4; - uint8_t :0; + uint8_t SwingV :4; + uint8_t SwingH :3; + uint8_t :1; // Byte 5 uint8_t DisplayTemp :2; uint8_t IFeel :1; uint8_t unknown2 :3; // value = 0b100 uint8_t WiFi :1; - uint8_t :0; + uint8_t :1; // Byte 6 - uint8_t :8; + uint8_t :8; // Byte 7 - uint8_t :4; - uint8_t Sum:4; + uint8_t :2; + uint8_t Econo :1; + uint8_t :1; + uint8_t Sum :4; }; }; @@ -100,16 +103,24 @@ const uint8_t kGreeMinTempF = 61; // Fahrenheit const uint8_t kGreeMaxTempF = 86; // Fahrenheit const uint16_t kGreeTimerMax = 24 * 60; -const uint8_t kGreeSwingLastPos = 0b0000; -const uint8_t kGreeSwingAuto = 0b0001; -const uint8_t kGreeSwingUp = 0b0010; -const uint8_t kGreeSwingMiddleUp = 0b0011; -const uint8_t kGreeSwingMiddle = 0b0100; -const uint8_t kGreeSwingMiddleDown = 0b0101; -const uint8_t kGreeSwingDown = 0b0110; -const uint8_t kGreeSwingDownAuto = 0b0111; -const uint8_t kGreeSwingMiddleAuto = 0b1001; -const uint8_t kGreeSwingUpAuto = 0b1011; +const uint8_t kGreeSwingLastPos = 0b0000; // 0 +const uint8_t kGreeSwingAuto = 0b0001; // 1 +const uint8_t kGreeSwingUp = 0b0010; // 2 +const uint8_t kGreeSwingMiddleUp = 0b0011; // 3 +const uint8_t kGreeSwingMiddle = 0b0100; // 4 +const uint8_t kGreeSwingMiddleDown = 0b0101; // 5 +const uint8_t kGreeSwingDown = 0b0110; // 6 +const uint8_t kGreeSwingDownAuto = 0b0111; // 7 +const uint8_t kGreeSwingMiddleAuto = 0b1001; // 9 +const uint8_t kGreeSwingUpAuto = 0b1011; // 11 + +const uint8_t kGreeSwingHOff = 0b000; // 0 +const uint8_t kGreeSwingHAuto = 0b001; // 1 +const uint8_t kGreeSwingHMaxLeft = 0b010; // 2 +const uint8_t kGreeSwingHLeft = 0b011; // 3 +const uint8_t kGreeSwingHMiddle = 0b100; // 4 +const uint8_t kGreeSwingHRight = 0b101; // 5 +const uint8_t kGreeSwingHMaxRight = 0b110; // 6 const uint8_t kGreeDisplayTempOff = 0b00; // 0 const uint8_t kGreeDisplayTempSet = 0b01; // 1 @@ -176,6 +187,8 @@ class IRGreeAC { bool getSleep(void) const; void setTurbo(const bool on); bool getTurbo(void) const; + void setEcono(const bool on); + bool getEcono(void) const; void setIFeel(const bool on); bool getIFeel(void) const; void setWiFi(const bool on); @@ -183,6 +196,8 @@ class IRGreeAC { void setSwingVertical(const bool automatic, const uint8_t position); bool getSwingVerticalAuto(void) const; uint8_t getSwingVerticalPosition(void) const; + void setSwingHorizontal(const uint8_t position); + uint8_t getSwingHorizontal(void) const; uint16_t getTimer(void) const; void setTimer(const uint16_t minutes); void setDisplayTempSource(const uint8_t mode); @@ -190,9 +205,11 @@ class IRGreeAC { 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 swingv); + static uint8_t convertSwingH(const stdAc::swingh_t swingh); 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); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[]); diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp index 41e40541a..5c9f2a36d 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -32,6 +32,8 @@ using irutils::addBoolToString; using irutils::addIntToString; using irutils::addLabeledString; using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addSwingHToString; using irutils::addFanToString; using irutils::addTempToString; using irutils::minsToString; @@ -322,22 +324,22 @@ void IRHaierAC::setCurrTime(const uint16_t nr_mins) { } /// Get the Vertical Swing position setting of the A/C. -/// @return The native swing mode. -uint8_t IRHaierAC::getSwing(void) const { - return _.Swing; +/// @return The native vertical swing mode. +uint8_t IRHaierAC::getSwingV(void) const { + return _.SwingV; } /// Set the Vertical Swing mode of the A/C. /// @param[in] state The mode to set the vanes to. -void IRHaierAC::setSwing(const uint8_t state) { - if (state == _.Swing) return; // Nothing to do. +void IRHaierAC::setSwingV(const uint8_t state) { + if (state == _.SwingV) return; // Nothing to do. switch (state) { - case kHaierAcSwingOff: - case kHaierAcSwingUp: - case kHaierAcSwingDown: - case kHaierAcSwingChg: + case kHaierAcSwingVOff: + case kHaierAcSwingVUp: + case kHaierAcSwingVDown: + case kHaierAcSwingVChg: _.Command = kHaierAcCmdSwing; - _.Swing = state; + _.SwingV = state; break; } } @@ -376,11 +378,11 @@ uint8_t IRHaierAC::convertSwingV(const stdAc::swingv_t position) { switch (position) { case stdAc::swingv_t::kHighest: case stdAc::swingv_t::kHigh: - case stdAc::swingv_t::kMiddle: return kHaierAcSwingUp; + case stdAc::swingv_t::kMiddle: return kHaierAcSwingVUp; case stdAc::swingv_t::kLow: - case stdAc::swingv_t::kLowest: return kHaierAcSwingDown; - case stdAc::swingv_t::kOff: return kHaierAcSwingOff; - default: return kHaierAcSwingChg; + case stdAc::swingv_t::kLowest: return kHaierAcSwingVDown; + case stdAc::swingv_t::kOff: return kHaierAcSwingVOff; + default: return kHaierAcSwingVChg; } } @@ -414,10 +416,10 @@ stdAc::fanspeed_t IRHaierAC::toCommonFanSpeed(const uint8_t speed) { /// @return The native equivalent of the enum. 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; + case kHaierAcSwingVUp: return stdAc::swingv_t::kHighest; + case kHaierAcSwingVDown: return stdAc::swingv_t::kLowest; + case kHaierAcSwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; } } @@ -433,7 +435,7 @@ stdAc::state_t IRHaierAC::toCommon(void) const { result.celsius = true; result.degrees = getTemp(); result.fanspeed = toCommonFanSpeed(getFan()); - result.swingv = toCommonSwingV(_.Swing); + result.swingv = toCommonSwingV(_.SwingV); result.filter = _.Health; result.sleep = _.Sleep ? 0 : -1; // Not supported. @@ -452,7 +454,7 @@ stdAc::state_t IRHaierAC::toCommon(void) const { /// @return A human readable string. String IRHaierAC::toString(void) const { String result = ""; - result.reserve(150); // Reserve some heap for the string to reduce fragging. + result.reserve(170); // Reserve some heap for the string to reduce fragging. uint8_t cmd = _.Command; result += addIntToString(cmd, kCommandStr, false); result += kSpaceLBraceStr; @@ -492,7 +494,7 @@ String IRHaierAC::toString(void) const { result += kHealthStr; break; case kHaierAcCmdSwing: - result += kSwingStr; + result += kSwingVStr; break; default: result += kUnknownStr; @@ -503,19 +505,19 @@ String IRHaierAC::toString(void) const { result += addTempToString(getTemp()); result += addFanToString(getFan(), kHaierAcFanHigh, kHaierAcFanLow, kHaierAcFanAuto, kHaierAcFanAuto, kHaierAcFanMed); - result += addIntToString(_.Swing, kSwingStr); + result += addIntToString(_.SwingV, kSwingVStr); result += kSpaceLBraceStr; - switch (_.Swing) { - case kHaierAcSwingOff: + switch (_.SwingV) { + case kHaierAcSwingVOff: result += kOffStr; break; - case kHaierAcSwingUp: + case kHaierAcSwingVUp: result += kUpStr; break; - case kHaierAcSwingDown: + case kHaierAcSwingVDown: result += kDownStr; break; - case kHaierAcSwingChg: + case kHaierAcSwingVChg: result += kChangeStr; break; default: @@ -581,9 +583,9 @@ bool IRHaierAC176::validChecksum(const uint8_t state[], const uint16_t length) { /// Reset the internal state to a fixed known good state. void IRHaierAC176::stateReset(void) { std::memset(_.raw, 0, sizeof _.raw); - _.Prefix = kHaierAcYrw02Prefix; + _.Model = kHaierAcYrw02ModelA; _.Prefix2 = kHaierAc176Prefix; - _.Temp = kHaierAcDefTemp - kHaierAcMinTemp; + _.Temp = kHaierAcYrw02DefTempC - kHaierAcYrw02MinTempC; _.Health = true; setFan(kHaierAcYrw02FanAuto); _.Power = true; @@ -609,17 +611,42 @@ void IRHaierAC176::setButton(uint8_t button) { switch (button) { case kHaierAcYrw02ButtonTempUp: case kHaierAcYrw02ButtonTempDown: - case kHaierAcYrw02ButtonSwing: + case kHaierAcYrw02ButtonSwingV: + case kHaierAcYrw02ButtonSwingH: case kHaierAcYrw02ButtonFan: case kHaierAcYrw02ButtonPower: case kHaierAcYrw02ButtonMode: case kHaierAcYrw02ButtonHealth: case kHaierAcYrw02ButtonTurbo: case kHaierAcYrw02ButtonSleep: + case kHaierAcYrw02ButtonLock: + case kHaierAcYrw02ButtonCFAB: _.Button = button; } } +/// Get/Detect the model of the A/C. +/// @return The enum of the compatible model. +haier_ac176_remote_model_t IRHaierAC176::getModel(void) const { + switch (_.Model) { + case kHaierAcYrw02ModelB: return haier_ac176_remote_model_t::V9014557_B; + default: return haier_ac176_remote_model_t::V9014557_A; + } +} + +/// Set the model of the A/C to emulate. +/// @param[in] model The enum of the appropriate model. +void IRHaierAC176::setModel(haier_ac176_remote_model_t model) { + _.Button = kHaierAcYrw02ButtonCFAB; + switch (model) { + case haier_ac176_remote_model_t::V9014557_B: + _.Model = kHaierAcYrw02ModelB; + break; + default: + _.Model = kHaierAcYrw02ModelA; + } +} + /// Get the Button/Command setting of the A/C. /// @return The value of the button/command that was pressed. uint8_t IRHaierAC176::getButton(void) const { @@ -651,27 +678,97 @@ void IRHaierAC176::setMode(uint8_t mode) { /// @return The current operating mode setting. uint8_t IRHaierAC176::getMode(void) const { return _.Mode; } -/// Set the temperature. -/// @param[in] celsius The temperature in degrees celsius. -void IRHaierAC176::setTemp(const uint8_t celsius) { - uint8_t temp = celsius; - if (temp < kHaierAcMinTemp) - temp = kHaierAcMinTemp; - else if (temp > kHaierAcMaxTemp) - temp = kHaierAcMaxTemp; +/// Set the default temperature units to use. +/// @param[in] on Use Fahrenheit as the units. +/// true is Fahrenheit, false is Celsius. +void IRHaierAC176::setUseFahrenheit(const bool on) { _.UseFahrenheit = on; } +/// Get the default temperature units in use. +/// @return true is Fahrenheit, false is Celsius. +bool IRHaierAC176::getUseFahrenheit(void) const { return _.UseFahrenheit; } + +/// Set the temperature. +/// @param[in] degree The temperature in degrees. +/// @param[in] fahrenheit Use units of Fahrenheit and set that as units used. +void IRHaierAC176::setTemp(const uint8_t degree, const bool fahrenheit) { uint8_t old_temp = getTemp(); - if (old_temp == temp) return; - if (old_temp > temp) - _.Button = kHaierAcYrw02ButtonTempDown; - else - _.Button = kHaierAcYrw02ButtonTempUp; - _.Temp = temp - kHaierAcMinTemp; + if (old_temp == degree) return; + + if (_.UseFahrenheit == fahrenheit) { + if (old_temp > degree) + _.Button = kHaierAcYrw02ButtonTempDown; + else + _.Button = kHaierAcYrw02ButtonTempUp; + } else { + _.Button = kHaierAcYrw02ButtonCFAB; + } + _.UseFahrenheit = fahrenheit; + + uint8_t temp = degree; + if (fahrenheit) { + if (temp < kHaierAcYrw02MinTempF) + temp = kHaierAcYrw02MinTempF; + else if (temp > kHaierAcYrw02MaxTempF) + temp = kHaierAcYrw02MaxTempF; + if (degree >= 77) { temp++; } + if (degree >= 79) { temp++; } + // See at IRHaierAC176::getTemp() comments for clarification + _.ExtraDegreeF = temp % 2; + _.Temp = (temp - kHaierAcYrw02MinTempF -_.ExtraDegreeF) >> 1; + } else { + if (temp < kHaierAcYrw02MinTempC) + temp = kHaierAcYrw02MinTempC; + else if (temp > kHaierAcYrw02MaxTempC) + temp = kHaierAcYrw02MaxTempC; + _.Temp = temp - kHaierAcYrw02MinTempC; + } } /// Get the current temperature setting. -/// @return The current setting for temp. in degrees celsius. -uint8_t IRHaierAC176::getTemp(void) const { return _.Temp + kHaierAcMinTemp; } +/// The unit of temperature is specified by UseFahrenheit value. +/// @return The current setting for temperature. +uint8_t IRHaierAC176::getTemp(void) const { + if (!_.UseFahrenheit) { return _.Temp + kHaierAcYrw02MinTempC; } + uint8_t degree = _.Temp*2 + kHaierAcYrw02MinTempF + _.ExtraDegreeF; + // The way of coding the temperature in degree Fahrenheit is + // kHaierAcYrw02MinTempF + Temp*2 + ExtraDegreeF, for example + // Temp = 0b0011, ExtraDegreeF = 0b1, temperature is 60 + 3*2 + 1 = 67F + // But around 78F there is unconsistency, see table below + // + // | Fahrenheit | Temp | ExtraDegreeF | + // | 60F | 0b0000 | 0b0 | + // | 61F | 0b0000 | 0b1 | + // | 62F | 0b0001 | 0b0 | + // | 63F | 0b0001 | 0b1 | + // | 64F | 0b0010 | 0b0 | + // | 65F | 0b0010 | 0b1 | + // | 66F | 0b0011 | 0b0 | + // | 67F | 0b0011 | 0b1 | + // | 68F | 0b0100 | 0b0 | + // | 69F | 0b0100 | 0b1 | + // | 70F | 0b0101 | 0b0 | + // | 71F | 0b0101 | 0b1 | + // | 72F | 0b0110 | 0b0 | + // | 73F | 0b0110 | 0b1 | + // | 74F | 0b0111 | 0b0 | + // | 75F | 0b0111 | 0b1 | + // | 76F | 0b1000 | 0b0 | + // | Not Used | 0b1000 | 0b1 | + // | 77F | 0b1001 | 0b0 | + // | Not Used | 0b1001 | 0b1 | + // | 78F | 0b1010 | 0b0 | + // | 79F | 0b1010 | 0b1 | + // | 80F | 0b1011 | 0b0 | + // | 81F | 0b1011 | 0b1 | + // | 82F | 0b1100 | 0b0 | + // | 83F | 0b1100 | 0b1 | + // | 84F | 0b1101 | 0b0 | + // | 86F | 0b1110 | 0b0 | + // | 85F | 0b1101 | 0b1 | + if (degree >= 77) { degree--; } + if (degree >= 79) { degree--; } + return degree; +} /// Set the Health (filter) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. @@ -764,30 +861,61 @@ void IRHaierAC176::setFan(uint8_t speed) { } } +/// For backward compatibility. Use getSwingV() instead. /// Get the Vertical Swing position setting of the A/C. /// @return The native position/mode. -uint8_t IRHaierAC176::getSwing(void) const { return _.Swing; } +uint8_t IRHaierAC176::getSwing(void) const { + return IRHaierAC176::getSwingV(); +} + +/// For backward compatibility. Use setSwingV() instead. +/// Set the Vertical Swing mode of the A/C. +/// @param[in] pos The position/mode to set the vanes to. +void IRHaierAC176::setSwing(uint8_t pos) { setSwingV(pos); } + +/// Get the Vertical Swing position setting of the A/C. +/// @return The native position/mode. +uint8_t IRHaierAC176::getSwingV(void) const { return _.SwingV; } /// Set the Vertical Swing mode of the A/C. /// @param[in] pos The position/mode to set the vanes to. -void IRHaierAC176::setSwing(uint8_t pos) { +void IRHaierAC176::setSwingV(uint8_t pos) { uint8_t newpos = pos; switch (pos) { - case kHaierAcYrw02SwingOff: - case kHaierAcYrw02SwingAuto: - case kHaierAcYrw02SwingTop: - case kHaierAcYrw02SwingMiddle: - case kHaierAcYrw02SwingBottom: - case kHaierAcYrw02SwingDown: _.Button = kHaierAcYrw02ButtonSwing; break; + case kHaierAcYrw02SwingVOff: + case kHaierAcYrw02SwingVAuto: + case kHaierAcYrw02SwingVTop: + case kHaierAcYrw02SwingVMiddle: + case kHaierAcYrw02SwingVBottom: + case kHaierAcYrw02SwingVDown: _.Button = kHaierAcYrw02ButtonSwingV; break; default: return; // Unexpected value so don't do anything. } // Heat mode has no MIDDLE setting, use BOTTOM instead. - if (pos == kHaierAcYrw02SwingMiddle && _.Mode == kHaierAcYrw02Heat) - newpos = kHaierAcYrw02SwingBottom; + if (pos == kHaierAcYrw02SwingVMiddle && _.Mode == kHaierAcYrw02Heat) + newpos = kHaierAcYrw02SwingVBottom; // BOTTOM is only allowed if we are in Heat mode, otherwise MIDDLE. - if (pos == kHaierAcYrw02SwingBottom && _.Mode != kHaierAcYrw02Heat) - newpos = kHaierAcYrw02SwingMiddle; - _.Swing = newpos; + if (pos == kHaierAcYrw02SwingVBottom && _.Mode != kHaierAcYrw02Heat) + newpos = kHaierAcYrw02SwingVMiddle; + _.SwingV = newpos; +} + +/// Get the Horizontal Swing position setting of the A/C. +/// @return The native position/mode. +uint8_t IRHaierAC176::getSwingH(void) const { return _.SwingH; } + +/// Set the Horizontal Swing mode of the A/C. +/// @param[in] pos The position/mode to set the vanes to. +void IRHaierAC176::setSwingH(uint8_t pos) { + switch (pos) { + case kHaierAcYrw02SwingHMiddle: + case kHaierAcYrw02SwingHLeftMax: + case kHaierAcYrw02SwingHLeft: + case kHaierAcYrw02SwingHRight: + case kHaierAcYrw02SwingHRightMax: + case kHaierAcYrw02SwingHAuto: _.Button = kHaierAcYrw02ButtonSwingH; break; + default: return; // Unexpected value so don't do anything. + } + _.SwingH = pos; } @@ -874,6 +1002,17 @@ uint16_t IRHaierAC176::getOffTimer(void) const { return _.OffTimerHrs * 60 + _.OffTimerMins; } +/// Get the Lock setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRHaierAC176::getLock(void) const { return _.Lock; } + +/// Set the Lock setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHaierAC176::setLock(const bool on) { + _.Button = kHaierAcYrw02ButtonLock; + _.Lock = on; +} + /// Convert a stdAc::opmode_t enum into its native mode. /// @param[in] mode The enum to be converted. /// @return The native equivalent of the enum. @@ -907,12 +1046,27 @@ uint8_t IRHaierAC176::convertFan(const stdAc::fanspeed_t speed) { uint8_t IRHaierAC176::convertSwingV(const stdAc::swingv_t position) { switch (position) { case stdAc::swingv_t::kHighest: - case stdAc::swingv_t::kHigh: return kHaierAcYrw02SwingTop; - case stdAc::swingv_t::kMiddle: return kHaierAcYrw02SwingMiddle; - case stdAc::swingv_t::kLow: return kHaierAcYrw02SwingDown; - case stdAc::swingv_t::kLowest: return kHaierAcYrw02SwingBottom; - case stdAc::swingv_t::kOff: return kHaierAcYrw02SwingOff; - default: return kHaierAcYrw02SwingAuto; + case stdAc::swingv_t::kHigh: return kHaierAcYrw02SwingVTop; + case stdAc::swingv_t::kMiddle: return kHaierAcYrw02SwingVMiddle; + case stdAc::swingv_t::kLow: return kHaierAcYrw02SwingVDown; + case stdAc::swingv_t::kLowest: return kHaierAcYrw02SwingVBottom; + case stdAc::swingv_t::kOff: return kHaierAcYrw02SwingVOff; + default: return kHaierAcYrw02SwingVAuto; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRHaierAC176::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kMiddle: return kHaierAcYrw02SwingHMiddle; + case stdAc::swingh_t::kLeftMax: return kHaierAcYrw02SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kHaierAcYrw02SwingHLeft; + case stdAc::swingh_t::kRight: return kHaierAcYrw02SwingHRight; + case stdAc::swingh_t::kRightMax: return kHaierAcYrw02SwingHRightMax; + case stdAc::swingh_t::kAuto: return kHaierAcYrw02SwingHAuto; + default: return kHaierAcYrw02SwingHMiddle; } } @@ -946,12 +1100,27 @@ stdAc::fanspeed_t IRHaierAC176::toCommonFanSpeed(const uint8_t speed) { /// @return The native equivalent of the enum. stdAc::swingv_t IRHaierAC176::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; + case kHaierAcYrw02SwingVTop: return stdAc::swingv_t::kHighest; + case kHaierAcYrw02SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kHaierAcYrw02SwingVDown: return stdAc::swingv_t::kLow; + case kHaierAcYrw02SwingVBottom: return stdAc::swingv_t::kLowest; + case kHaierAcYrw02SwingVOff: return stdAc::swingv_t::kOff; + default: return stdAc::swingv_t::kAuto; + } +} + +/// Convert a stdAc::swingh_t enum into it's native setting. +/// @param[in] pos The enum to be converted. +/// @return The native equivalent of the enum. +stdAc::swingh_t IRHaierAC176::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kHaierAcYrw02SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kHaierAcYrw02SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kHaierAcYrw02SwingHLeft: return stdAc::swingh_t::kLeft; + case kHaierAcYrw02SwingHRight: return stdAc::swingh_t::kRight; + case kHaierAcYrw02SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kHaierAcYrw02SwingHAuto: return stdAc::swingh_t::kAuto; + default: return stdAc::swingh_t::kOff; } } @@ -960,19 +1129,19 @@ stdAc::swingv_t IRHaierAC176::toCommonSwingV(const uint8_t pos) { stdAc::state_t IRHaierAC176::toCommon(void) const { stdAc::state_t result; result.protocol = decode_type_t::HAIER_AC_YRW02; - result.model = -1; // No models used. + result.model = getModel(); result.power = _.Power; result.mode = toCommonMode(_.Mode); - result.celsius = true; + result.celsius = !_.UseFahrenheit; result.degrees = getTemp(); result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = toCommonSwingV(_.Swing); + result.swingv = toCommonSwingV(_.SwingV); + result.swingh = toCommonSwingH(_.SwingH); result.filter = _.Health; result.sleep = _.Sleep ? 0 : -1; result.turbo = _.Turbo; result.quiet = _.Quiet; // Not supported. - result.swingh = stdAc::swingh_t::kOff; result.econo = false; result.light = false; result.clean = false; @@ -985,8 +1154,9 @@ stdAc::state_t IRHaierAC176::toCommon(void) const { /// @return A human readable string. String IRHaierAC176::toString(void) const { String result = ""; - result.reserve(130); // Reserve some heap for the string to reduce fragging. - result += addBoolToString(_.Power, kPowerStr, false); + result.reserve(280); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::HAIER_AC176, getModel(), false); + result += addBoolToString(_.Power, kPowerStr); uint8_t cmd = _.Button; result += addIntToString(cmd, kButtonStr); result += kSpaceLBraceStr; @@ -1012,12 +1182,24 @@ String IRHaierAC176::toString(void) const { case kHaierAcYrw02ButtonHealth: result += kHealthStr; break; - case kHaierAcYrw02ButtonSwing: - result += kSwingStr; + case kHaierAcYrw02ButtonSwingV: + result += kSwingVStr; + break; + case kHaierAcYrw02ButtonSwingH: + result += kSwingHStr; break; case kHaierAcYrw02ButtonTurbo: result += kTurboStr; break; + case kHaierAcYrw02ButtonTimer: + result += kTimerStr; + break; + case kHaierAcYrw02ButtonLock: + result += kLockStr; + break; + case kHaierAcYrw02ButtonCFAB: + result += kCelsiusFahrenheitStr; + break; default: result += kUnknownStr; } @@ -1025,37 +1207,49 @@ String IRHaierAC176::toString(void) const { result += addModeToString(_.Mode, kHaierAcYrw02Auto, kHaierAcYrw02Cool, kHaierAcYrw02Heat, kHaierAcYrw02Dry, kHaierAcYrw02Fan); - result += addTempToString(getTemp()); + result += addTempToString(getTemp(), !_.UseFahrenheit); result += addFanToString(_.Fan, kHaierAcYrw02FanHigh, kHaierAcYrw02FanLow, kHaierAcYrw02FanAuto, kHaierAcYrw02FanAuto, kHaierAcYrw02FanMed); result += addBoolToString(_.Turbo, kTurboStr); result += addBoolToString(_.Quiet, kQuietStr); - result += addIntToString(_.Swing, kSwingStr); + result += addIntToString(_.SwingV, kSwingVStr); result += kSpaceLBraceStr; - switch (_.Swing) { - case kHaierAcYrw02SwingOff: + switch (_.SwingV) { + case kHaierAcYrw02SwingVOff: result += kOffStr; break; - case kHaierAcYrw02SwingAuto: + case kHaierAcYrw02SwingVAuto: result += kAutoStr; break; - case kHaierAcYrw02SwingBottom: + case kHaierAcYrw02SwingVBottom: result += kLowestStr; break; - case kHaierAcYrw02SwingDown: + case kHaierAcYrw02SwingVDown: result += kLowStr; break; - case kHaierAcYrw02SwingTop: + case kHaierAcYrw02SwingVTop: result += kHighestStr; break; - case kHaierAcYrw02SwingMiddle: + case kHaierAcYrw02SwingVMiddle: result += kMiddleStr; break; default: result += kUnknownStr; } result += ')'; + result += addSwingHToString(_.SwingH, kHaierAcYrw02SwingHAuto, + kHaierAcYrw02SwingHLeftMax, + kHaierAcYrw02SwingHLeft, + kHaierAcYrw02SwingHMiddle, + kHaierAcYrw02SwingHRight, + kHaierAcYrw02SwingHRightMax, + // Below are unused. + kHaierAcYrw02SwingHMiddle, + kHaierAcYrw02SwingHMiddle, + kHaierAcYrw02SwingHMiddle, + kHaierAcYrw02SwingHMiddle, + kHaierAcYrw02SwingHMiddle); result += addBoolToString(_.Sleep, kSleepStr); result += addBoolToString(_.Health, kHealthStr); const uint8_t tmode = getTimerMode(); @@ -1091,6 +1285,7 @@ String IRHaierAC176::toString(void) const { result += addLabeledString((tmode != kHaierAcYrw02NoTimers && tmode != kHaierAcYrw02OnTimer) ? minsToString(getOffTimer()) : kOffStr, kOffTimerStr); + result += addBoolToString(_.Lock, kLockStr); return result; } // End of IRHaierAC176 class. @@ -1196,7 +1391,7 @@ bool IRrecv::decodeHaierACYRW02(decode_results* results, uint16_t offset, // Compliance if (strict) { - if (results->state[0] != kHaierAcYrw02Prefix) return false; + if (results->state[0] != kHaierAcYrw02ModelA) return false; if (!IRHaierACYRW02::validChecksum(results->state, nbits / 8)) return false; } @@ -1229,7 +1424,8 @@ bool IRrecv::decodeHaierAC176(decode_results* results, uint16_t offset, // Compliance if (strict) { - if (results->state[0] != kHaierAcYrw02Prefix) return false; + if ((results->state[0] != kHaierAcYrw02ModelA) && + (results->state[0] != kHaierAcYrw02ModelB)) return false; if (!IRHaierAC176::validChecksum(results->state, nbits / 8)) return false; } diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index 6cc31b890..4e5c8e64c 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -13,8 +13,10 @@ // Brand: Haier, Model: HSU07-HEA03 remote (HAIER_AC) // Brand: Haier, Model: YR-W02 remote (HAIER_AC_YRW02) // Brand: Haier, Model: HSU-09HMC203 A/C (HAIER_AC_YRW02) +// Brand: Haier, Model: V9014557 M47 8D remote (HAIER_AC176) // Brand: Mabe, Model: MMI18HDBWCA6MI8 A/C (HAIER_AC176) // Brand: Mabe, Model: V12843 HJ200223 remote (HAIER_AC176) +// Brand: Daichi, Model: D-H A/C (HAIER_AC176) #ifndef IR_HAIER_H_ #define IR_HAIER_H_ @@ -41,7 +43,7 @@ union HaierProtocol{ // Byte 2 uint8_t CurrHours:5; uint8_t unknown :1; // value=1 - uint8_t Swing :2; + uint8_t SwingV :2; // Byte 3 uint8_t CurrMins:6; uint8_t OffTimer:1; @@ -84,10 +86,10 @@ const uint8_t kHaierAcCmdTimerCancel = 0b1010; const uint8_t kHaierAcCmdHealth = 0b1100; const uint8_t kHaierAcCmdSwing = 0b1101; -const uint8_t kHaierAcSwingOff = 0b00; -const uint8_t kHaierAcSwingUp = 0b01; -const uint8_t kHaierAcSwingDown = 0b10; -const uint8_t kHaierAcSwingChg = 0b11; +const uint8_t kHaierAcSwingVOff = 0b00; +const uint8_t kHaierAcSwingVUp = 0b01; +const uint8_t kHaierAcSwingVDown = 0b10; +const uint8_t kHaierAcSwingVChg = 0b11; const uint8_t kHaierAcAuto = 0; const uint8_t kHaierAcCool = 1; @@ -118,11 +120,11 @@ const uint8_t kHaierAcSleepBit = 0b01000000; #define HAIER_AC_CMD_TIMER_SET kHaierAcCmdTimerSet #define HAIER_AC_CMD_TIMER_CANCEL kHaierAcCmdTimerCancel #define HAIER_AC_CMD_HEALTH kHaierAcCmdHealth -#define HAIER_AC_CMD_SWING kHaierAcCmdSwing -#define HAIER_AC_SWING_OFF kHaierAcSwingOff -#define HAIER_AC_SWING_UP kHaierAcSwingUp -#define HAIER_AC_SWING_DOWN kHaierAcSwingDown -#define HAIER_AC_SWING_CHG kHaierAcSwingChg +#define HAIER_AC_CMD_SWINGV kHaierAcCmdSwing +#define HAIER_AC_SWINGV_OFF kHaierAcSwingVOff +#define HAIER_AC_SWINGV_UP kHaierAcSwingVUp +#define HAIER_AC_SWINGV_DOWN kHaierAcSwingVDown +#define HAIER_AC_SWINGV_CHG kHaierAcSwingVChg #define HAIER_AC_AUTO kHaierAcAuto #define HAIER_AC_COOL kHaierAcCool #define HAIER_AC_DRY kHaierAcDry @@ -133,15 +135,29 @@ const uint8_t kHaierAcSleepBit = 0b01000000; #define HAIER_AC_FAN_MED kHaierAcFanMed #define HAIER_AC_FAN_HIGH kHaierAcFanHigh -const uint8_t kHaierAcYrw02Prefix = 0xA6; +const uint8_t kHaierAcYrw02MinTempC = 16; +const uint8_t kHaierAcYrw02MaxTempC = 30; +const uint8_t kHaierAcYrw02MinTempF = 60; +const uint8_t kHaierAcYrw02MaxTempF = 86; +const uint8_t kHaierAcYrw02DefTempC = 25; + +const uint8_t kHaierAcYrw02ModelA = 0xA6; +const uint8_t kHaierAcYrw02ModelB = 0x59; const uint8_t kHaierAc176Prefix = 0xB7; -const uint8_t kHaierAcYrw02SwingOff = 0x0; -const uint8_t kHaierAcYrw02SwingTop = 0x1; -const uint8_t kHaierAcYrw02SwingMiddle = 0x2; // Not available in heat mode. -const uint8_t kHaierAcYrw02SwingBottom = 0x3; // Only available in heat mode. -const uint8_t kHaierAcYrw02SwingDown = 0xA; -const uint8_t kHaierAcYrw02SwingAuto = 0xC; // Airflow +const uint8_t kHaierAcYrw02SwingVOff = 0x0; +const uint8_t kHaierAcYrw02SwingVTop = 0x1; +const uint8_t kHaierAcYrw02SwingVMiddle = 0x2; // Not available in heat mode. +const uint8_t kHaierAcYrw02SwingVBottom = 0x3; // Only available in heat mode. +const uint8_t kHaierAcYrw02SwingVDown = 0xA; +const uint8_t kHaierAcYrw02SwingVAuto = 0xC; // Airflow + +const uint8_t kHaierAcYrw02SwingHMiddle = 0x0; +const uint8_t kHaierAcYrw02SwingHLeftMax = 0x3; +const uint8_t kHaierAcYrw02SwingHLeft = 0x4; +const uint8_t kHaierAcYrw02SwingHRight = 0x5; +const uint8_t kHaierAcYrw02SwingHRightMax = 0x6; +const uint8_t kHaierAcYrw02SwingHAuto = 0x7; const uint8_t kHaierAcYrw02FanHigh = 0b001; const uint8_t kHaierAcYrw02FanMed = 0b010; @@ -154,15 +170,19 @@ const uint8_t kHaierAcYrw02Dry = 0b010; // 2 const uint8_t kHaierAcYrw02Heat = 0b100; // 4 const uint8_t kHaierAcYrw02Fan = 0b110; // 5 -const uint8_t kHaierAcYrw02ButtonTempUp = 0x0; -const uint8_t kHaierAcYrw02ButtonTempDown = 0x1; -const uint8_t kHaierAcYrw02ButtonSwing = 0x2; -const uint8_t kHaierAcYrw02ButtonFan = 0x4; -const uint8_t kHaierAcYrw02ButtonPower = 0x5; -const uint8_t kHaierAcYrw02ButtonMode = 0x6; -const uint8_t kHaierAcYrw02ButtonHealth = 0x7; -const uint8_t kHaierAcYrw02ButtonTurbo = 0x8; -const uint8_t kHaierAcYrw02ButtonSleep = 0xB; +const uint8_t kHaierAcYrw02ButtonTempUp = 0b00000; +const uint8_t kHaierAcYrw02ButtonTempDown = 0b00001; +const uint8_t kHaierAcYrw02ButtonSwingV = 0b00010; +const uint8_t kHaierAcYrw02ButtonSwingH = 0b00011; +const uint8_t kHaierAcYrw02ButtonFan = 0b00100; +const uint8_t kHaierAcYrw02ButtonPower = 0b00101; +const uint8_t kHaierAcYrw02ButtonMode = 0b00110; +const uint8_t kHaierAcYrw02ButtonHealth = 0b00111; +const uint8_t kHaierAcYrw02ButtonTurbo = 0b01000; +const uint8_t kHaierAcYrw02ButtonSleep = 0b01011; +const uint8_t kHaierAcYrw02ButtonTimer = 0b10000; +const uint8_t kHaierAcYrw02ButtonLock = 0b10100; +const uint8_t kHaierAcYrw02ButtonCFAB = 0b11010; const uint8_t kHaierAcYrw02NoTimers = 0b000; const uint8_t kHaierAcYrw02OffTimer = 0b001; @@ -175,12 +195,13 @@ union HaierAc176Protocol{ uint8_t raw[kHaierAC176StateLength]; ///< The state in native form struct { // Byte 0 - uint8_t Prefix :8; + uint8_t Model :8; // Byte 1 - uint8_t Swing :4; + uint8_t SwingV :4; uint8_t Temp :4; // 16C~30C // Byte 2 - uint8_t :8; + uint8_t :5; + uint8_t SwingH :3; // Byte 3 uint8_t :1; uint8_t Health :1; @@ -207,12 +228,16 @@ union HaierAc176Protocol{ // Byte 9 uint8_t :8; // Byte 10 - uint8_t :8; + uint8_t ExtraDegreeF :1; + uint8_t :4; + uint8_t UseFahrenheit:1; + uint8_t :2; // Byte 11 uint8_t :8; // Byte 12 - uint8_t Button :4; - uint8_t :4; + uint8_t Button :5; + uint8_t Lock :1; + uint8_t :2; // Byte 13 uint8_t Sum :8; // Byte 14 @@ -305,8 +330,8 @@ class IRHaierAC { uint16_t getCurrTime(void) const; void setCurrTime(const uint16_t mins); - uint8_t getSwing(void) const; - void setSwing(const uint8_t state); + uint8_t getSwingV(void) const; + void setSwingV(const uint8_t state); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[]); @@ -350,10 +375,15 @@ class IRHaierAC176 { void begin(void); void stateReset(void); + void setModel(const haier_ac176_remote_model_t model); + haier_ac176_remote_model_t getModel(void) const; + void setButton(const uint8_t button); uint8_t getButton(void) const; - void setTemp(const uint8_t temp); + void setUseFahrenheit(const bool on); + bool getUseFahrenheit(void) const; + void setTemp(const uint8_t temp, const bool fahrenheit = false); uint8_t getTemp(void) const; void setFan(const uint8_t speed); @@ -377,6 +407,13 @@ class IRHaierAC176 { bool getQuiet(void) const; void setQuiet(const bool on); + uint8_t getSwingV(void) const; + void setSwingV(const uint8_t pos); + uint8_t getSwingH(void) const; + void setSwingH(const uint8_t pos); + + /// These functions are for backward compatibility. + /// Use getSwingV() and setSwingV() instead. uint8_t getSwing(void) const; void setSwing(const uint8_t pos); @@ -387,6 +424,9 @@ class IRHaierAC176 { void setOffTimer(const uint16_t mins); uint16_t getOffTimer(void) const; + bool getLock(void) const; + void setLock(const bool on); + uint8_t* getRaw(void); virtual void setRaw(const uint8_t new_code[]); static bool validChecksum(const uint8_t state[], @@ -394,9 +434,11 @@ class IRHaierAC176 { static uint8_t convertMode(const stdAc::opmode_t mode); static uint8_t convertFan(const stdAc::fanspeed_t speed); static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); static stdAc::opmode_t toCommonMode(const uint8_t mode); static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); static bool toCommonTurbo(const uint8_t speed); static bool toCommonQuiet(const uint8_t speed); stdAc::state_t toCommon(void) const; diff --git a/lib/IRremoteESP8266/src/ir_LG.h b/lib/IRremoteESP8266/src/ir_LG.h index 6010282ca..2bd7dbc1c 100644 --- a/lib/IRremoteESP8266/src/ir_LG.h +++ b/lib/IRremoteESP8266/src/ir_LG.h @@ -15,6 +15,8 @@ // Brand: LG, Model: AMNW09GSJA0 A/C (LG2 - AKB74955603) // Brand: LG, Model: AMNW24GTPA1 A/C (LG2 - AKB73757604) // Brand: LG, Model: AKB73757604 remote (LG2 - AKB73757604) +// Brand: LG, Model: AKB73315611 remote (LG2 - AKB74955603) +// Brand: LG, Model: MS05SQ NW0 A/C (LG2 - AKB74955603) // Brand: General Electric, Model: AG1BH09AW101 Split A/C (LG) // Brand: General Electric, Model: 6711AR2853M A/C Remote (LG) diff --git a/lib/IRremoteESP8266/src/ir_Mirage.cpp b/lib/IRremoteESP8266/src/ir_Mirage.cpp index 8a573a8dc..a806937b1 100644 --- a/lib/IRremoteESP8266/src/ir_Mirage.cpp +++ b/lib/IRremoteESP8266/src/ir_Mirage.cpp @@ -1,15 +1,35 @@ -// Copyright 2020 David Conran (crankyoldgit) +// Copyright 2020-2021 David Conran (crankyoldgit) /// @file /// @brief Support for Mirage protocol /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1573 -// Supports: -// Brand: Mirage, Model: VLU series A/C +#include "ir_Mirage.h" +#include +#include +#ifndef ARDUINO +#include +#endif #include "IRrecv.h" #include "IRsend.h" +#include "IRtext.h" #include "IRutils.h" +using irutils::addBoolToString; +using irutils::addFanToString; +using irutils::addIntToString; +using irutils::addLabeledString; +using irutils::addModeToString; +using irutils::addModelToString; +using irutils::addSwingHToString; +using irutils::addSwingVToString; +using irutils::addTempToString; +using irutils::addToggleToString; +using irutils::minsToString; +using irutils::bcdToUint8; +using irutils::uint8ToBcd; +using irutils::sumNibbles; // Constants const uint16_t kMirageHdrMark = 8360; ///< uSeconds @@ -20,6 +40,9 @@ const uint16_t kMirageZeroSpace = 545; ///< uSeconds const uint32_t kMirageGap = kDefaultMessageGap; ///< uSeconds (just a guess) const uint16_t kMirageFreq = 38000; ///< Hz. (Just a guess) +const uint8_t kMirageAcKKG29AC1PowerOn = 0b00; // 0 +const uint8_t kMirageAcKKG29AC1PowerOff = 0b11; // 3 + #if SEND_MIRAGE /// Send a Mirage formatted message. @@ -58,6 +81,8 @@ bool IRrecv::decodeMirage(decode_results *results, uint16_t offset, kMirageBitMark, kMirageZeroSpace, kMirageBitMark, kMirageGap, true, kUseDefTol, kMarkExcess, false)) return false; + // Compliance + if (strict && !IRMirageAc::validChecksum(results->state)) return false; // Success results->decode_type = decode_type_t::MIRAGE; @@ -67,4 +92,756 @@ bool IRrecv::decodeMirage(decode_results *results, uint16_t offset, // is a union data type. return true; } + +// Code to emulate Mirage A/C IR remote control unit. + +/// Class constructor +/// @param[in] pin GPIO to be used when sending. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRMirageAc::IRMirageAc(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +/// Reset the state of the remote to a known good state/sequence. +void IRMirageAc::stateReset(void) { + // The state of the IR remote in IR code form. + static const uint8_t kReset[kMirageStateLength] = { + 0x56, 0x6C, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x42}; + setRaw(kReset); + _model = mirage_ac_remote_model_t::KKG9AC1; +} + +/// Set up hardware to be able to send a message. +void IRMirageAc::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHI_AC +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRMirageAc::send(const uint16_t repeat) { + _irsend.sendMirage(getRaw(), kMirageStateLength, repeat); + // Reset any toggles after a send. + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + setCleanToggle(false); + setLight(false); // For this model (only), Light is a toggle. + break; + default: + break; + } +} +#endif // SEND_MITSUBISHI_AC + +/// Get a PTR to the internal state/code for this protocol. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t *IRMirageAc::getRaw(void) { + checksum(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] data A valid code for this protocol. +void IRMirageAc::setRaw(const uint8_t *data) { + std::memcpy(_.raw, data, kMirageStateLength); + _model = getModel(true); +} + +/// Guess the Mirage remote model from the supplied state code. +/// @param[in] state A valid state code for this protocol. +/// @return The model code. +/// @note This result isn't perfect. Both protocols can look the same but have +/// wildly different settings. +mirage_ac_remote_model_t IRMirageAc::getModel(const uint8_t *state) { + Mirage120Protocol p; + std::memcpy(p.raw, state, kMirageStateLength); + // Check for KKG29AC1 specific settings. + if (p.RecycleHeat || p.Filter || p.Sleep_Kkg29ac1 || p.CleanToggle || + p.IFeel || p.OffTimerEnable || p.OnTimerEnable) + return mirage_ac_remote_model_t::KKG29AC1; + // Check for things specific to KKG9AC1 + if ((p.Minutes || p.Seconds) || // Is part of the clock set? + // Are the timer times set, but not enabled? (enable check filtered above) + (p.OffTimerHours || p.OffTimerMins) || + (p.OnTimerHours || p.OnTimerMins)) + return mirage_ac_remote_model_t::KKG9AC1; + // As the above test has a 1 in 3600+ (for 1 second an hour) chance of a false + // negative in theory, we are going assume that anything left should be a + // KKG29AC1 model. + return mirage_ac_remote_model_t::KKG29AC1; // Default. +} + +/// Get the model code of the interal message state. +/// @param[in] useRaw If set, we try to get the model info from just the state. +/// @return The model code. +mirage_ac_remote_model_t IRMirageAc::getModel(const bool useRaw) const { + return useRaw ? getModel(_.raw) : _model; +} + +/// Set the model code of the interal message state. +/// @param[in] model The desired model to use for the settings. +void IRMirageAc::setModel(const mirage_ac_remote_model_t model) { + if (model != _model) { // Only change things if we need to. + // Save the old settings. + stdAc::state_t state = toCommon(); + const uint16_t ontimer = getOnTimer(); + const uint16_t offtimer = getOffTimer(); + const bool ifeel = getIFeel(); + const uint8_t sensor = getSensorTemp(); + // Change the model. + state.model = model; + // Restore/Convert the settings. + fromCommon(state); + setOnTimer(ontimer); + setOffTimer(offtimer); + setIFeel(ifeel); + setSensorTemp(sensor); + } +} + +/// Calculate and set the checksum values for the internal state. +void IRMirageAc::checksum(void) { _.Sum = calculateChecksum(_.raw); } + +/// Verify the checksum is valid for a given state. +/// @param[in] data The array to verify the checksum of. +/// @return true, if the state has a valid checksum. Otherwise, false. +bool IRMirageAc::validChecksum(const uint8_t *data) { + return calculateChecksum(data) == data[kMirageStateLength - 1]; +} + +/// Calculate the checksum for a given state. +/// @param[in] data The value to calc the checksum of. +/// @return The calculated checksum value. +uint8_t IRMirageAc::calculateChecksum(const uint8_t *data) { + return sumNibbles(data, kMirageStateLength - 1); +} + +/// Set the requested power state of the A/C to on. +void IRMirageAc::on(void) { setPower(true); } + +/// Set the requested power state of the A/C to off. +void IRMirageAc::off(void) { setPower(false); } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setPower(bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.Power = on ? kMirageAcKKG29AC1PowerOn : kMirageAcKKG29AC1PowerOff; + break; + default: + // In order to change the power setting, it seems must be less than + // kMirageAcPowerOff. kMirageAcPowerOff is larger than half of the + // possible value stored in the allocated bit space. + // Thus if the value is larger than kMirageAcPowerOff the power is off. + // Less than, then power is on. + // We can't just aribitarily add or subtract the value (which analysis + // indicates is how the power status changes. Very weird, I know!) as that + // is not an idempotent action, we must check if the addition or + // substraction is needed first. e.g. via getPower() + // i.e. If we added or subtracted twice, we would cause a wrap of the + // integer and not get the desired result. + if (on) + _.SwingAndPower -= getPower() ? 0 : kMirageAcPowerOff; + else + _.SwingAndPower += getPower() ? kMirageAcPowerOff : 0; + } +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRMirageAc::getPower(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + return _.Power == kMirageAcKKG29AC1PowerOn; + default: + return _.SwingAndPower < kMirageAcPowerOff; + } +} + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRMirageAc::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRMirageAc::setMode(const uint8_t mode) { + switch (mode) { + case kMirageAcCool: + case kMirageAcDry: + case kMirageAcHeat: + case kMirageAcFan: + case kMirageAcRecycle: + _.Mode = mode; + // Reset turbo if we have to. + setTurbo(getTurbo()); + break; + default: // Default to cool mode for anything else. + setMode(kMirageAcCool); + } +} + +/// Set the temperature. +/// @param[in] degrees The temperature in degrees celsius. +void IRMirageAc::setTemp(const uint8_t degrees) { + // Make sure we have desired temp in the correct range. + uint8_t celsius = std::max(degrees, kMirageAcMinTemp); + _.Temp = std::min(celsius, kMirageAcMaxTemp) + kMirageAcTempOffset; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRMirageAc::getTemp(void) const { return _.Temp - kMirageAcTempOffset; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRMirageAc::setFan(const uint8_t speed) { + _.Fan = (speed <= kMirageAcFanLow) ? speed : kMirageAcFanAuto; +} + +/// Get the current fan speed setting. +/// @return The current fan speed/mode. +uint8_t IRMirageAc::getFan(void) const { return _.Fan; } + +/// Change the Turbo setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setTurbo(bool on) { + const bool value = (on && (getMode() == kMirageAcCool)); + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.Turbo_Kkg29ac1 = value; + break; + default: + _.Turbo_Kkg9ac1 = value; + } +} + +/// Get the value of the current Turbo setting. +/// @return true, the setting is on. false, the setting is off. +bool IRMirageAc::getTurbo(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.Turbo_Kkg29ac1; + default: return _.Turbo_Kkg9ac1; + } +} + +/// Change the Sleep setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setSleep(bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.Sleep_Kkg29ac1 = on; + break; + default: + _.Sleep_Kkg9ac1 = on; + } +} + +/// Get the value of the current Sleep setting. +/// @return true, the setting is on. false, the setting is off. +bool IRMirageAc::getSleep(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.Sleep_Kkg29ac1; + default: return _.Sleep_Kkg9ac1; + } +} + +/// Change the Light/Display setting. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @note Light is a toggle on the KKG29AC1 model. +void IRMirageAc::setLight(bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.LightToggle_Kkg29ac1 = on; + break; + default: + _.Light_Kkg9ac1 = on; + } +} + +/// Get the value of the current Light/Display setting. +/// @return true, the setting is on. false, the setting is off. +/// @note Light is a toggle on the KKG29AC1 model. +bool IRMirageAc::getLight(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.LightToggle_Kkg29ac1; + default: return _.Light_Kkg9ac1; + } +} + +/// Get the clock time of the A/C unit. +/// @return Nr. of seconds past midnight. +uint32_t IRMirageAc::getClock(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + return 0; + default: + return ((bcdToUint8(_.Hours) * 60) + bcdToUint8(_.Minutes)) * 60 + + bcdToUint8(_.Seconds); + } +} + +/// Set the clock time on the A/C unit. +/// @param[in] nr_of_seconds Nr. of seconds past midnight. +void IRMirageAc::setClock(const uint32_t nr_of_seconds) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.Minutes = _.Seconds = 0; // No clock setting. Clear it just in case. + break; + default: + uint32_t remaining = std::min( + nr_of_seconds, (uint32_t)(24 * 60 * 60 - 1)); // Limit to 23:59:59. + _.Seconds = uint8ToBcd(remaining % 60); + remaining /= 60; + _.Minutes = uint8ToBcd(remaining % 60); + remaining /= 60; + _.Hours = uint8ToBcd(remaining); + } +} + +/// Set the Vertical Swing setting/position of the A/C. +/// @param[in] position The desired swing setting. +void IRMirageAc::setSwingV(const uint8_t position) { + switch (position) { + case kMirageAcSwingVOff: + case kMirageAcSwingVLowest: + case kMirageAcSwingVLow: + case kMirageAcSwingVMiddle: + case kMirageAcSwingVHigh: + case kMirageAcSwingVHighest: + case kMirageAcSwingVAuto: + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.SwingV = (position != kMirageAcSwingVOff); + break; + default: + const bool power = getPower(); + _.SwingAndPower = position; + // Power needs to be reapplied after overwriting SwingAndPower + setPower(power); + } + break; + default: // Default to Auto for anything else. + setSwingV(kMirageAcSwingVAuto); + } +} + +/// Get the Vertical Swing setting/position of the A/C. +/// @return The desired Vertical Swing setting/position. +uint8_t IRMirageAc::getSwingV(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + return _.SwingV ? kMirageAcSwingVAuto : kMirageAcSwingVOff; + default: + return _.SwingAndPower - (getPower() ? 0 : kMirageAcPowerOff); + } +} + +/// Set the Horizontal Swing setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setSwingH(const bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.SwingH = on; + break; + default: + break; + } +} + +/// Get the Horizontal Swing setting of the A/C. +/// @return on true, the setting is on. false, the setting is off. +bool IRMirageAc::getSwingH(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.SwingH; + default: return false; + } +} + +/// Set the Quiet setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setQuiet(const bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.Quiet = on; + break; + default: + break; + } +} + +/// Get the Quiet setting of the A/C. +/// @return on true, the setting is on. false, the setting is off. +bool IRMirageAc::getQuiet(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.Quiet; + default: return false; + } +} + +/// Set the CleanToggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setCleanToggle(const bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.CleanToggle = on; + break; + default: + break; + } +} + +/// Get the Clean Toggle setting of the A/C. +/// @return on true, the setting is on. false, the setting is off. +bool IRMirageAc::getCleanToggle(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.CleanToggle; + default: return false; + } +} + +/// Set the Filter setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setFilter(const bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.Filter = on; + break; + default: + break; + } +} + +/// Get the Filter setting of the A/C. +/// @return on true, the setting is on. false, the setting is off. +bool IRMirageAc::getFilter(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.Filter; + default: return false; + } +} + +/// Set the IFeel setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMirageAc::setIFeel(const bool on) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.IFeel = on; + if (on) { + // If no previous sensor temp, default to currently desired temp. + if (!_.SensorTemp) _.SensorTemp = getTemp(); + } else { + _.SensorTemp = 0; // When turning it off, clear the Sensor Temp. + } + break; + default: + break; + } +} + +/// Get the IFeel setting of the A/C. +/// @return on true, the setting is on. false, the setting is off. +bool IRMirageAc::getIFeel(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: return _.IFeel; + default: return false; + } +} + +/// Set the Sensor Temp setting of the A/C's remote. +/// @param[in] degrees The desired sensor temp. in degrees celsius. +void IRMirageAc::setSensorTemp(const uint8_t degrees) { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.SensorTemp = std::min(kMirageAcSensorTempMax, degrees) + + kMirageAcSensorTempOffset; + break; + default: + break; + } +} + +/// Get the Sensor Temp setting of the A/C's remote. +/// @return The current setting for the sensor temp. in degrees celsius. +uint16_t IRMirageAc::getSensorTemp(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + return _.SensorTemp - kMirageAcSensorTempOffset; + default: + return false; + } +} + +/// Get the number of minutes the On Timer is currently set for. +/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use. +uint16_t IRMirageAc::getOnTimer(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + return _.OnTimerEnable ? _.OnTimerHours * 60 + _.OnTimerMins : 0; + default: + return 0; + } +} + +/// Set the number of minutes for the On Timer. +/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer. +void IRMirageAc::setOnTimer(const uint16_t nr_of_mins) { + uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60)); + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.OnTimerEnable = (mins > 0); + _.OnTimerHours = mins / 60; + _.OnTimerMins = mins % 60; + break; + default: + break; + } +} + +/// Get the number of minutes the Off Timer is currently set for. +/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use. +uint16_t IRMirageAc::getOffTimer(void) const { + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + return _.OffTimerEnable ? _.OffTimerHours * 60 + _.OffTimerMins : 0; + default: + return 0; + } +} + +/// Set the number of minutes for the Off Timer. +/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer. +void IRMirageAc::setOffTimer(const uint16_t nr_of_mins) { + uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60)); + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + _.OffTimerEnable = (mins > 0); + _.OffTimerHours = mins / 60; + _.OffTimerMins = mins % 60; + break; + default: + break; + } +} + +/// Convert a native mode into its stdAc equivalent. +/// @param[in] mode The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::opmode_t IRMirageAc::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMirageAcHeat: return stdAc::opmode_t::kHeat; + case kMirageAcDry: return stdAc::opmode_t::kDry; + case kMirageAcFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kCool; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] speed The native setting to be converted. +/// @param[in] model The model type to use to influence the conversion. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRMirageAc::toCommonFanSpeed(const uint8_t speed, + const mirage_ac_remote_model_t model) { + switch (model) { + case mirage_ac_remote_model_t::KKG29AC1: + switch (speed) { + case kMirageAcKKG29AC1FanHigh: return stdAc::fanspeed_t::kHigh; + case kMirageAcKKG29AC1FanMed: return stdAc::fanspeed_t::kMedium; + case kMirageAcKKG29AC1FanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } + break; + default: + switch (speed) { + case kMirageAcFanHigh: return stdAc::fanspeed_t::kHigh; + case kMirageAcFanMed: return stdAc::fanspeed_t::kMedium; + case kMirageAcFanLow: return stdAc::fanspeed_t::kLow; + default: return stdAc::fanspeed_t::kAuto; + } + } +} + +/// Convert a stdAc::opmode_t enum into its native mode. +/// @param[in] mode The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMirageAc::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kHeat: return kMirageAcHeat; + case stdAc::opmode_t::kDry: return kMirageAcDry; + case stdAc::opmode_t::kFan: return kMirageAcFan; + default: return kMirageAcCool; + } +} + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @param[in] model The model type to use to influence the conversion. +/// @return The native equivalent of the enum. +uint8_t IRMirageAc::convertFan(const stdAc::fanspeed_t speed, + const mirage_ac_remote_model_t model) { + uint8_t low; + uint8_t med; + switch (model) { + case mirage_ac_remote_model_t::KKG29AC1: + low = kMirageAcKKG29AC1FanLow; + med = kMirageAcKKG29AC1FanMed; + break; + default: + low = kMirageAcFanLow; + med = kMirageAcFanMed; + } + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return low; + case stdAc::fanspeed_t::kMedium: return med; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kMirageAcFanHigh; + default: return kMirageAcFanAuto; + } +} + +/// Convert a stdAc::swingv_t enum into it's native setting. +/// @param[in] position The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRMirageAc::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kMirageAcSwingVHighest; + case stdAc::swingv_t::kHigh: return kMirageAcSwingVHigh; + case stdAc::swingv_t::kMiddle: return kMirageAcSwingVMiddle; + case stdAc::swingv_t::kLow: return kMirageAcSwingVLow; + case stdAc::swingv_t::kLowest: return kMirageAcSwingVLowest; + case stdAc::swingv_t::kOff: return kMirageAcSwingVOff; + default: return kMirageAcSwingVAuto; + } +} + +/// Convert a native vertical swing postion to it's common equivalent. +/// @param[in] pos A native position to convert. +/// @return The common vertical swing position. +stdAc::swingv_t IRMirageAc::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMirageAcSwingVHighest: return stdAc::swingv_t::kHighest; + case kMirageAcSwingVHigh: return stdAc::swingv_t::kHigh; + case kMirageAcSwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMirageAcSwingVLow: return stdAc::swingv_t::kLow; + case kMirageAcSwingVLowest: return stdAc::swingv_t::kLowest; + case kMirageAcSwingVAuto: return stdAc::swingv_t::kAuto; + default: return stdAc::swingv_t::kOff; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRMirageAc::toCommon(void) const { + stdAc::state_t result; + result.protocol = decode_type_t::MIRAGE; + result.model = _model; + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = getTemp(); + result.fanspeed = toCommonFanSpeed(getFan(), _model); + result.swingv = toCommonSwingV(getSwingV()); + result.swingh = getSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; + result.turbo = getTurbo(); + result.light = getLight(); + result.clean = getCleanToggle(); + result.filter = getFilter(); + result.sleep = getSleep() ? 0 : -1; + result.quiet = getQuiet(); + result.clock = getClock() / 60; + // Not supported. + result.econo = false; + result.beep = false; + return result; +} + +/// Convert & set a stdAc::state_t to its equivalent internal settings. +/// @param[in] state The desired state in stdAc::state_t form. +void IRMirageAc::fromCommon(const stdAc::state_t state) { + stateReset(); + _model = (mirage_ac_remote_model_t)state.model; // Set directly to avoid loop + setPower(state.power); + setTemp(state.celsius ? state.degrees : fahrenheitToCelsius(state.degrees)); + setMode(convertMode(state.mode)); + setFan(convertFan(state.fanspeed, _model)); + setTurbo(state.turbo); + setSleep(state.sleep >= 0); + setLight(state.light); + setSwingV(convertSwingV(state.swingv)); + setSwingH(state.swingh != stdAc::swingh_t::kOff); + setQuiet(state.quiet); + setCleanToggle(state.clean); + setFilter(state.filter); + // setClock() expects seconds, not minutes. + setClock((state.clock > 0) ? state.clock * 60 : 0); + // Non-common settings. + setOnTimer(0); + setOffTimer(0); + setIFeel(false); +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRMirageAc::toString(void) const { + String result = ""; + result.reserve(240); // Reserve some heap for the string to reduce fragging. + result += addModelToString(decode_type_t::MIRAGE, _model, false); + result += addBoolToString(getPower(), kPowerStr); + result += addModeToString(_.Mode, 0xFF, kMirageAcCool, + kMirageAcHeat, kMirageAcDry, + kMirageAcFan); + result += addTempToString(getTemp()); + uint8_t fanlow; + uint8_t fanmed; + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + fanlow = kMirageAcKKG29AC1FanLow; + fanmed = kMirageAcKKG29AC1FanMed; + break; + default: // e.g. Model KKG9AC1 + fanlow = kMirageAcFanLow; + fanmed = kMirageAcFanMed; + } + result += addFanToString(_.Fan, kMirageAcFanHigh, fanlow, kMirageAcFanAuto, + kMirageAcFanAuto, fanmed); + result += addBoolToString(getTurbo(), kTurboStr); + result += addBoolToString(getSleep(), kSleepStr); + switch (_model) { + case mirage_ac_remote_model_t::KKG29AC1: + result += addBoolToString(_.Quiet, kQuietStr); + result += addToggleToString(getLight(), kLightStr); + result += addBoolToString(_.SwingV, kSwingVStr); + result += addBoolToString(_.SwingH, kSwingHStr); + result += addBoolToString(_.Filter, kFilterStr); + result += addToggleToString(_.CleanToggle, kCleanStr); + result += addLabeledString(getOnTimer() ? minsToString(getOnTimer()) + : kOffStr, + kOnTimerStr); + result += addLabeledString(getOffTimer() ? minsToString(getOffTimer()) + : kOffStr, + kOffTimerStr); + result += addBoolToString(_.IFeel, kIFeelStr); + if (_.IFeel) { + result += addIntToString(getSensorTemp(), kSensorTempStr); + result += 'C'; + } + break; + default: // e.g. Model KKG9AC1 + result += addBoolToString(getLight(), kLightStr); + result += addSwingVToString(getSwingV(), + kMirageAcSwingVAuto, + kMirageAcSwingVHighest, + kMirageAcSwingVHigh, + 0xFF, // Unused. + kMirageAcSwingVMiddle, + 0xFF, // Unused. + kMirageAcSwingVLow, + kMirageAcSwingVLowest, + kMirageAcSwingVOff, + 0xFF, 0xFF, 0xFF); // Unused. + result += addLabeledString(minsToString(getClock() / 60), kClockStr); + } + return result; +} #endif // DECODE_MIRAGE diff --git a/lib/IRremoteESP8266/src/ir_Mirage.h b/lib/IRremoteESP8266/src/ir_Mirage.h new file mode 100644 index 000000000..b2b39e759 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Mirage.h @@ -0,0 +1,277 @@ +// Copyright 2020-2021 David Conran (crankyoldgit) +/// @file +/// @brief Support for Mirage protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1573 + + +// Supports: +// Brand: Mirage, Model: VLU series A/C +// Brand: Maxell, Model: MX-CH18CF A/C +// Brand: Maxell, Model: KKG9A-C1 remote +// Brand: Tronitechnik, Model: Reykir 9000 A/C +// Brand: Tronitechnik, Model: KKG29A-C1 remote + +#ifndef IR_MIRAGE_H_ +#define IR_MIRAGE_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 + +/// Native representation of a Mirage 120-bit A/C message. +/// @see https://docs.google.com/spreadsheets/d/1Ucu9mOOIIJoWQjUJq_VCvwgV3EwKaRk8K2AuZgccYEk/edit#gid=0 +union Mirage120Protocol{ + uint8_t raw[kMirageStateLength]; ///< The state in code form. + struct { // Common + // Byte 0 + uint8_t Header :8; // Header. (0x56) + // Byte 1 + uint8_t Temp :8; // Celsius minus 0x5C. + // Byte 2 + uint8_t :8; // Unknown / Unused. + // Byte 3 + uint8_t :8; // Unknown / Unused. + // Byte 4 + uint8_t Fan :2; // Fan Speed. + uint8_t :2; // Unknown / Unused. + uint8_t Mode :4; // Cool, Heat, Dry, Fan, Recycle + // Byte 5 + uint8_t :8; + // Byte 6 + uint8_t :8; + // Byte 7 + uint8_t :8; + // Byte 8 + uint8_t :8; + // Byte 9 + uint8_t :8; + // Byte 10 + uint8_t :8; + // Byte 11 + uint8_t :8; + // Byte 12 + uint8_t :8; + // Byte 13 + uint8_t :8; + // Byte 14 + uint8_t Sum :8; // Sum of all the previous nibbles. + }; + struct { // KKG9AC1 remote + // Byte 0 + uint8_t :8; // Header + // Byte 1 + uint8_t :8; // Temp + // Byte 2 + uint8_t :8; // Unknown / Unused. + // Byte 3 + uint8_t :3; // Unknown / Unused. + uint8_t Light_Kkg9ac1 :1; // Aka. Display. Seems linked to Sleep mode. + uint8_t :4; // Unknown / Unused. + // Byte 4 + uint8_t :8; // Fan & Mode + // Byte 5 + uint8_t :1; // Unknown + uint8_t SwingAndPower :7; + // Byte 6 + uint8_t :7; // Unknown / Unused. + uint8_t Sleep_Kkg9ac1 :1; // Sleep mode on or off. + // Byte 7 + uint8_t :3; // Unknown / Unused. + uint8_t Turbo_Kkg9ac1 :1; // Turbo mode on or off. Only works in Cool mode. + uint8_t :4; // Unknown / Unused. + // Byte 8 + uint8_t :8; // Unknown / Unused. + // Byte 9 + uint8_t :8; // Unknown / Unused. + // Byte 10 + uint8_t :8; // Unknown / Unused. + // Byte 11 + uint8_t Seconds :8; // Nr. of Seconds in BCD. + // Byte 12 + uint8_t Minutes :8; // Nr. of Minutes in BCD. + // Byte 13 + uint8_t Hours :8; // Nr. of Hours in BCD. + // Byte 14 + uint8_t :8; // Sum + }; + struct { // KKG29A-C1 remote + // Byte 0 + uint8_t :8; // Header + // Byte 1 + uint8_t :8; // Temp + // Byte 2 + uint8_t :8; + // Byte 3 + uint8_t Quiet :1; + uint8_t :7; + // Byte 4 + uint8_t :2; // Fan + uint8_t OffTimerEnable :1; + uint8_t OnTimerEnable :1; + uint8_t :3; // Mode + uint8_t :1; + // Byte 5 + uint8_t SwingH :1; + uint8_t SwingV :1; + uint8_t LightToggle_Kkg29ac1 :1; // Aka. Display Toggle. + uint8_t :3; + uint8_t Power :2; + // Byte 6 + uint8_t :1; + uint8_t Filter :1; // Aka. UVC + uint8_t :1; + uint8_t Sleep_Kkg29ac1 :1; // Sleep mode on or off. + uint8_t :2; + uint8_t RecycleHeat :1; + uint8_t :1; + // Byte 7 + uint8_t SensorTemp :6; // Temperature at the remote + uint8_t CleanToggle :1; + uint8_t IFeel :1; + // Byte 8 + uint8_t OnTimerHours :5; + uint8_t :2; + uint8_t Turbo_Kkg29ac1 :1; // Turbo mode on or off. + // Byte 9 + uint8_t OnTimerMins :6; + uint8_t :2; + // Byte 10 + uint8_t OffTimerHours :5; + uint8_t :3; + // Byte 11 + uint8_t OffTimerMins :6; + uint8_t :2; + // Byte 12 + uint8_t :8; + // Byte 13 + uint8_t :8; + // Byte 14 + uint8_t :8; // Sum + }; +}; + +// Constants +const uint8_t kMirageAcHeat = 0b001; // 1 +const uint8_t kMirageAcCool = 0b010; // 2 +const uint8_t kMirageAcDry = 0b011; // 3 +const uint8_t kMirageAcRecycle = 0b100; // 4 +const uint8_t kMirageAcFan = 0b101; // 5 + +const uint8_t kMirageAcFanAuto = 0b00; // 0 +const uint8_t kMirageAcFanHigh = 0b01; // 1 +const uint8_t kMirageAcFanMed = 0b10; // 2 +const uint8_t kMirageAcFanLow = 0b11; // 3 +const uint8_t kMirageAcKKG29AC1FanAuto = 0b00; // 0 +const uint8_t kMirageAcKKG29AC1FanHigh = 0b01; // 1 +const uint8_t kMirageAcKKG29AC1FanLow = 0b10; // 2 +const uint8_t kMirageAcKKG29AC1FanMed = 0b11; // 3 + +const uint8_t kMirageAcMinTemp = 16; // 16C +const uint8_t kMirageAcMaxTemp = 32; // 32C +const uint8_t kMirageAcTempOffset = 0x5C; +const uint8_t kMirageAcSensorTempOffset = 20; +const uint8_t kMirageAcSensorTempMax = 43; // Celsius + +const uint8_t kMirageAcPowerOff = 0x5F; +const uint8_t kMirageAcSwingVOff = 0b0000; // 0 +const uint8_t kMirageAcSwingVLowest = 0b0011; // 3 +const uint8_t kMirageAcSwingVLow = 0b0101; // 5 +const uint8_t kMirageAcSwingVMiddle = 0b0111; // 7 +const uint8_t kMirageAcSwingVHigh = 0b1001; // 9 +const uint8_t kMirageAcSwingVHighest = 0b1011; // 11 +const uint8_t kMirageAcSwingVAuto = 0b1101; // 13 + + +/// Class for handling detailed Mirage 120-bit A/C messages. +/// @note Inspired and derived from the work done at: https://github.com/r45635/HVAC-IR-Control +/// @warning Consider this very alpha code. Seems to work, but not validated. +class IRMirageAc { + public: + explicit IRMirageAc(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(void); +#if SEND_MIRAGE + void send(const uint16_t repeat = kMirageMinRepeat); + /// Run the calibration to calculate uSec timing offsets for this platform. + /// @return The uSec timing offset needed per modulation of the IR Led. + /// @note This will produce a 65ms IR signal pulse at 38kHz. + /// Only ever needs to be run once per object instantiation, if at all. + int8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_MIRAGE + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void) const; + void setTemp(const uint8_t degrees); + uint8_t getTemp(void) const; + void setFan(const uint8_t speed); + uint8_t getFan(void) const; + void setMode(const uint8_t mode); + uint8_t getMode(void) const; + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + uint32_t getClock(void) const; + void setClock(const uint32_t nr_of_seconds); + void setTurbo(const bool on); + bool getTurbo(void) const; + void setLight(const bool on); + bool getLight(void) const; + void setSleep(const bool on); + bool getSleep(void) const; + void setSwingV(const uint8_t position); + uint8_t getSwingV(void) const; + void setSwingH(const bool on); + bool getSwingH(void) const; + void setQuiet(const bool on); + bool getQuiet(void) const; + void setCleanToggle(const bool on); + bool getCleanToggle(void) const; + void setFilter(const bool on); + bool getFilter(void) const; + void setIFeel(const bool on); + bool getIFeel(void) const; + void setSensorTemp(const uint8_t degrees); + uint16_t getSensorTemp(void) const; + uint16_t getOnTimer(void) const; + uint16_t getOffTimer(void) const; + void setOnTimer(const uint16_t nr_of_mins); + void setOffTimer(const uint16_t nr_of_mins); + mirage_ac_remote_model_t getModel(const bool useRaw = false) const; + void setModel(const mirage_ac_remote_model_t model); + static mirage_ac_remote_model_t getModel(const uint8_t *state); + static bool validChecksum(const uint8_t* data); + static uint8_t calculateChecksum(const uint8_t* data); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed, + const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1); + 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, + const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + stdAc::state_t toCommon(void) const; + void fromCommon(const stdAc::state_t state); + String toString(void) const; +#ifndef UNIT_TEST + + private: + IRsend _irsend; ///< Instance of the IR send class +#else // UNIT_TEST + /// @cond IGNORE + IRsendTest _irsend; ///< Instance of the testing IR send class + /// @endcond +#endif // UNIT_TEST + Mirage120Protocol _; + mirage_ac_remote_model_t _model; + void checksum(void); +}; +#endif // IR_MIRAGE_H_ diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp index 160a882bf..1e826a72d 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp @@ -399,6 +399,8 @@ void IRMitsubishiAC::setTemp(const float degrees) { // Do we have a half degree celsius? _.HalfDegree = nrHalfDegrees & 1; _.Temp = static_cast(nrHalfDegrees / 2 - kMitsubishiAcMinTemp); + // If temp is modified, iSave10C cannot be ON (because temp is then > 10C) + setISave10C(false); } /// Get the current temperature setting. @@ -452,8 +454,104 @@ void IRMitsubishiAC::setMode(const uint8_t mode) { return; } _.Mode = mode; + // iSave10C can only be on in Heat mode. + if (mode != kMitsubishiAcHeat) { + setISave10C(false); + } } +/// Set the iSave10C (i-SAVE) mode of the A/C. +/// @param[in] state true, the setting is on. false, the setting is off. +/// @note Normal minimum temp is 16C; i-SAVE mode works as gate to enable AC +/// to use 10C as setting. However, when Remote control shows 10C, it still +/// emits 16C on the "Temp" bits, and instead it uses other bits to indicate +/// a target temp of 10C. +/// Slightly strange, but I guess it's to keep compatibility to systems +/// without i-SAVE. +/// i-SAVE only has this 10C functionality when the AC is already in Heat mode. +/// In all other modes, minimum temp is 16C. +/// I have found no other difference between normal Heat mode and i-SAVE +/// other than the ability to go to 10C. +/// In this implementation, i-SAVE mode is ONLY used to enable the AC +/// temperature setting to 10C. Therefore "Temp" is set to 16 disregarding +/// what the remote shows, and mode is set to Heat. +void IRMitsubishiAC::setISave10C(const bool state) { + if (state) setMode(kMitsubishiAcHeat); + if (state) setTemp(kMitsubishiAcMinTemp); + _.iSave10C = state; +} + +/// Get the iSave10C (i-SAVE) mode of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMitsubishiAC::getISave10C(void) const { + return _.iSave10C; +} + +/// Set the requested iSee mode. +/// @param[in] state requested iSee mode. +void IRMitsubishiAC::setISee(const bool state) { + _.ISee = state; +} + +/// Get the iSee mode of the A/C. +/// @return The iSee mode setting. +bool IRMitsubishiAC::getISee(void) const { + return _.ISee; +} + +/// Set the requested Ecocool mode. +/// @param[in] state requested Ecocool mode. +void IRMitsubishiAC::setEcocool(const bool state) { + _.Ecocool = state; +} + +/// Get the Ecocool mode of the A/C. +/// @return The Ecocool mode setting. +bool IRMitsubishiAC::getEcocool(void) const { + return _.Ecocool; +} + +/// Set the requested Absense Detect mode. +/// @param[in] state requested Absense Detect mode. +void IRMitsubishiAC::setAbsenseDetect(const bool state) { + _.AbsenseDetect = state; +} + +/// Get the Absense Detect mode of the A/C. +/// @return The Absense Detect mode setting. +bool IRMitsubishiAC::getAbsenseDetect(void) const { + return _.AbsenseDetect; +} + +/// Set the requested Direct/Indirect mode. Only works if I-See mode is ON. +/// @param[in] mode requested Direct/Indirect mode. +void IRMitsubishiAC::setDirectIndirect(const uint8_t mode) { + if (_.ISee) { + _.DirectIndirect = std::min(mode, kMitsubishiAcDirect); // bounds check + } else { + _.DirectIndirect = 0; + } +} + +/// Get the Direct/Indirect mode of the A/C. +/// @return The native mode setting. +uint8_t IRMitsubishiAC::getDirectIndirect(void) const { + return _.DirectIndirect; +} + +/// Set the requested Natural Flow mode. +/// @param[in] state requested Natural Flow mode. +void IRMitsubishiAC::setNaturalFlow(const bool state) { + _.NaturalFlow = state; +} + +/// Get the Natural Flow mode of the A/C. +/// @return The Natural Flow mode setting. +bool IRMitsubishiAC::getNaturalFlow(void) const { + return _.NaturalFlow; +} + + /// Set the requested vane (Vertical Swing) operation mode of the a/c unit. /// @note On some models, this represents the Right vertical vane. /// @param[in] position The position/mode to set the vane to. @@ -463,12 +561,6 @@ void IRMitsubishiAC::setVane(const uint8_t position) { _.Vane = pos; } -/// Set the requested wide-vane (Horizontal Swing) operation mode of the a/c. -/// @param[in] position The position/mode to set the wide vane to. -void IRMitsubishiAC::setWideVane(const uint8_t position) { - _.WideVane = std::min(position, kMitsubishiAcWideVaneAuto); -} - /// Get the Vane (Vertical Swing) mode of the A/C. /// @note On some models, this represents the Right vertical vane. /// @return The native position/mode setting. @@ -476,12 +568,6 @@ uint8_t IRMitsubishiAC::getVane(void) const { return _.Vane; } -/// Get the Wide Vane (Horizontal Swing) mode of the A/C. -/// @return The native position/mode setting. -uint8_t IRMitsubishiAC::getWideVane(void) const { - return _.WideVane; -} - /// Set the requested Left Vane (Vertical Swing) operation mode of the a/c unit. /// @param[in] position The position/mode to set the vane to. void IRMitsubishiAC::setVaneLeft(const uint8_t position) { @@ -492,6 +578,18 @@ void IRMitsubishiAC::setVaneLeft(const uint8_t position) { /// @return The native position/mode setting. uint8_t IRMitsubishiAC::getVaneLeft(void) const { return _.VaneLeft; } +/// Set the requested wide-vane (Horizontal Swing) operation mode of the a/c. +/// @param[in] position The position/mode to set the wide vane to. +void IRMitsubishiAC::setWideVane(const uint8_t position) { + _.WideVane = std::min(position, kMitsubishiAcWideVaneAuto); +} + +/// Get the Wide Vane (Horizontal Swing) mode of the A/C. +/// @return The native position/mode setting. +uint8_t IRMitsubishiAC::getWideVane(void) const { + return _.WideVane; +} + /// Get the clock time of the A/C unit. /// @return Nr. of 10 minute increments past midnight. /// @note 1 = 1/6 hour (10 minutes). e.g. 4pm = 48. @@ -777,6 +875,12 @@ String IRMitsubishiAC::toString(void) const { result += ')'; } result += addBoolToString(_.WeeklyTimer, kWeeklyTimerStr); + result += addBoolToString(_.iSave10C, k10CHeatStr); + result += addBoolToString(_.ISee, kISeeStr); + result += addBoolToString(_.Ecocool, kEconoStr); + result += addBoolToString(_.AbsenseDetect, kAbsenseDetectStr); + result += addIntToString(_.DirectIndirect, kDirectIndirectModeStr); + result += addBoolToString(_.NaturalFlow, kFreshStr); return result; } diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index 1f3a42184..55afcdce6 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -34,6 +34,7 @@ // Brand: Mitsubishi Electric, Model: MSZ-SF25VE3 A/C (MITSUBISHI_AC) // Brand: Mitsubishi Electric, Model: SG15D remote (MITSUBISHI_AC) // Brand: Mitsubishi Electric, Model: MSZ-ZW4017S A/C (MITSUBISHI_AC) +// Brand: Mitsubishi Electric, Model: MSZ-FHnnVE A/C (MITSUBISHI_AC) #ifndef IR_MITSUBISHI_H_ #define IR_MITSUBISHI_H_ @@ -62,7 +63,8 @@ union Mitsubishi144Protocol{ // Byte 6 uint8_t :3; uint8_t Mode :3; - uint8_t :2; + uint8_t ISee : 1; + uint8_t :1; // Byte 7 uint8_t Temp :4; uint8_t HalfDegree :1; @@ -72,7 +74,7 @@ union Mitsubishi144Protocol{ uint8_t WideVane:4; // SwingH // Byte 9 uint8_t Fan :3; - uint8_t Vane :3; // SwingV + uint8_t Vane :3; // SwingV or VaneRight uint8_t VaneBit :1; uint8_t FanAuto :1; // Byte 10 @@ -86,13 +88,21 @@ union Mitsubishi144Protocol{ uint8_t WeeklyTimer :1; uint8_t :4; // Byte 14 - uint8_t :8; + uint8_t :5; + uint8_t Ecocool :1; + uint8_t :2; // Byte 15 - uint8_t :8; + uint8_t DirectIndirect:2; + uint8_t AbsenseDetect :1; + uint8_t :2; + uint8_t iSave10C :1; // i-SAVE:mode=Heat & iSave=on AND 10C on remote + uint8_t :2; // Byte 16 - uint8_t :3; - uint8_t VaneLeft :3; // SwingV(Left) - uint8_t :2; + uint8_t :1; + uint8_t NaturalFlow :1; + uint8_t :1; + uint8_t VaneLeft :3; // SwingV(Left) + uint8_t :2; // Byte 17 uint8_t Sum :8; }; @@ -126,6 +136,9 @@ const uint8_t kMitsubishiAcWideVaneRight = 0b0100; // 4 const uint8_t kMitsubishiAcWideVaneRightMax = 0b0101; // 5 const uint8_t kMitsubishiAcWideVaneWide = 0b0110; // 6 const uint8_t kMitsubishiAcWideVaneAuto = 0b1000; // 8 +const uint8_t kMitsubishiAcDirectOff = 0b00; // Vanes move when AC wants to. +const uint8_t kMitsubishiAcIndirect = 0b01; +const uint8_t kMitsubishiAcDirect = 0b11; const uint8_t kMitsubishiAcNoTimer = 0; const uint8_t kMitsubishiAcStartTimer = 5; const uint8_t kMitsubishiAcStopTimer = 3; @@ -274,12 +287,24 @@ class IRMitsubishiAC { uint8_t getFan(void) const; void setMode(const uint8_t mode); uint8_t getMode(void) const; - void setVane(const uint8_t position); - void setWideVane(const uint8_t position); + void setISave10C(const bool state); + bool getISave10C(void) const; + void setISee(const bool state); + bool getISee(void) const; + void setDirectIndirect(const uint8_t position); + uint8_t getDirectIndirect(void) const; + void setEcocool(const bool state); + bool getEcocool(void) const; + void setAbsenseDetect(const bool state); + bool getAbsenseDetect(void) const; + void setNaturalFlow(const bool state); + bool getNaturalFlow(void) const; + void setVane(const uint8_t position); // controls RIGHT vane on some models uint8_t getVane(void) const; - uint8_t getWideVane(void) const; void setVaneLeft(const uint8_t position); uint8_t getVaneLeft(void) const; + void setWideVane(const uint8_t position); + uint8_t getWideVane(void) const; uint8_t* getRaw(void); void setRaw(const uint8_t* data); uint8_t getClock(void) const; diff --git a/lib/IRremoteESP8266/src/ir_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp index 9e1ad4669..94cb52bba 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266/src/ir_Samsung.cpp @@ -1,5 +1,5 @@ // Copyright 2009 Ken Shirriff -// Copyright 2017, 2018, 2019 David Conran +// Copyright 2017-2021 David Conran /// @file /// @brief Support for Samsung protocols. /// Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ @@ -62,12 +62,25 @@ const uint16_t kSamsung36BitMark = 512; /// < uSeconds const uint16_t kSamsung36OneSpace = 1468; /// < uSeconds const uint16_t kSamsung36ZeroSpace = 490; /// < uSeconds +// _.Swing +const uint8_t kSamsungAcSwingV = 0b010; +const uint8_t kSamsungAcSwingH = 0b011; +const uint8_t kSamsungAcSwingBoth = 0b100; +const uint8_t kSamsungAcSwingOff = 0b111; +// _.FanSpecial +const uint8_t kSamsungAcFanSpecialOff = 0b000; +const uint8_t kSamsungAcPowerfulOn = 0b011; +const uint8_t kSamsungAcBreezeOn = 0b101; +const uint8_t kSamsungAcEconoOn = 0b111; + using irutils::addBoolToString; using irutils::addFanToString; using irutils::addIntToString; using irutils::addLabeledString; using irutils::addModeToString; using irutils::addTempToString; +using irutils::addToggleToString; +using irutils::minsToString; #if SEND_SAMSUNG /// Send a 32-bit Samsung formatted message. @@ -275,17 +288,22 @@ IRSamsungAc::IRSamsungAc(const uint16_t pin, const bool inverted, } /// Reset the internal state of the emulation. -/// @param[in] forcepower A flag indicating if force sending a special power +/// @param[in] extended A flag indicating if force sending a special extended /// message with the first `send()` call. /// @param[in] initialPower Set the initial power state. True, on. False, off. -void IRSamsungAc::stateReset(const bool forcepower, const bool initialPower) { +void IRSamsungAc::stateReset(const bool extended, const bool initialPower) { static const uint8_t kReset[kSamsungAcExtendedStateLength] = { 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x02, 0xAE, 0x71, 0x00, 0x15, 0xF0}; std::memcpy(_.raw, kReset, kSamsungAcExtendedStateLength); - _forcepower = forcepower; + _forceextended = extended; _lastsentpowerstate = initialPower; setPower(initialPower); + _OnTimerEnable = false; + _OffTimerEnable = false; + _Sleep = false; + _lastSleep = false; + _OnTimer = _OffTimer = _lastOnTimer = _lastOffTimer = 0; } /// Set up hardware to be able to send a message. @@ -351,30 +369,28 @@ void IRSamsungAc::checksum(void) { #if SEND_SAMSUNG_AC /// Send the current internal state as an IR message. /// @param[in] repeat Nr. of times the message will be repeated. -/// @param[in] calcchecksum Do we update the checksum before sending? /// @note 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) { - // Do we need to send a the special power on/off message? i.e. An Extended Msg - if (getPower() != _lastsentpowerstate || _forcepower) { // We do. - sendExtended(repeat, calcchecksum); - _forcepower = false; // It has now been sent, so clear the flag if set. - } else { // No, it's just a normal message. - if (calcchecksum) checksum(); - _irsend.sendSamsungAC(_.raw, kSamsungAcStateLength, repeat); - } +void IRSamsungAc::send(const uint16_t repeat) { + // Do we need to send a special (extended) message? + if (getPower() != _lastsentpowerstate || _forceextended || + (_lastOnTimer != _OnTimer) || (_lastOffTimer != _OffTimer) || + (_Sleep != _lastSleep)) // We do. + sendExtended(repeat); + else // No, it's just a normal message. + _irsend.sendSamsungAC(getRaw(), kSamsungAcStateLength, repeat); } /// Send the extended current internal state as an IR message. /// @param[in] repeat Nr. of times the message will be repeated. -/// @param[in] calcchecksum Do we update the checksum before sending? -/// @note Use this for when you need to power on/off the device. -/// Samsung A/C requires an extended length message when you want to -/// change the power operating mode of the A/C unit. -void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { +/// @note Samsung A/C requires an extended length message when you want to +/// change the power operating mode, Timers, or Sleep setting of the A/C unit. +void IRSamsungAc::sendExtended(const uint16_t repeat) { + _lastsentpowerstate = getPower(); // Remember the last power state sent. + _lastOnTimer = _OnTimer; + _lastOffTimer = _OffTimer; static const uint8_t extended_middle_section[kSamsungAcSectionLength] = { 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00}; - if (calcchecksum) checksum(); // Copy/convert the internal state to an extended state by // copying the second section to the third section, and inserting the extended // middle (second) section. @@ -383,13 +399,16 @@ void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { kSamsungAcSectionLength); std::memcpy(_.raw + kSamsungAcSectionLength, extended_middle_section, kSamsungAcSectionLength); + _setOnTimer(); + _setSleepTimer(); // This also sets any Off Timer if needed too. // Send it. - _irsend.sendSamsungAC(_.raw, kSamsungAcExtendedStateLength, repeat); + _irsend.sendSamsungAC(getRaw(), kSamsungAcExtendedStateLength, repeat); // Now revert it by copying the third section over the second section. std::memcpy(_.raw + kSamsungAcSectionLength, - _.raw + 2* kSamsungAcSectionLength, + _.raw + 2 * kSamsungAcSectionLength, kSamsungAcSectionLength); - _lastsentpowerstate = getPower(); // Remember the last power state sent. + + _forceextended = false; // It has now been sent, so clear the flag if set. } /// Send the special extended "On" message as the library can't seem to @@ -434,6 +453,11 @@ void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { kSamsungAcExtendedStateLength)); // Shrink the extended state into a normal state. if (length > kSamsungAcStateLength) { + _OnTimerEnable = _.OnTimerEnable; + _OffTimerEnable = _.OffTimerEnable; + _Sleep = _.Sleep5 && _.Sleep12; + _OnTimer = _getOnTimer(); + _OffTimer = _getOffTimer(); for (uint8_t i = kSamsungAcStateLength; i < length; i++) _.raw[i - kSamsungAcSectionLength] = _.raw[i]; } @@ -448,14 +472,13 @@ void IRSamsungAc::off(void) { setPower(false); } /// Change the power setting. /// @param[in] on true, the setting is on. false, the setting is off. void IRSamsungAc::setPower(const bool on) { - _.Power1 = !on; // Cleared when on. - _.Power6 = (on ? 0b11 : 0b00); + _.Power1 = _.Power2 = (on ? 0b11 : 0b00); } /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. bool IRSamsungAc::getPower(void) const { - return (_.Power6 == 0b11) && !_.Power1; + return _.Power1 == 0b11 && _.Power2 == 0b11; } /// Set the temperature. @@ -524,56 +547,79 @@ uint8_t IRSamsungAc::getFan(void) const { /// Get the vertical swing setting of the A/C. /// @return true, the setting is on. false, the setting is off. -/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1. -/// e.g. 0xAE or 0XAF for swing move. bool IRSamsungAc::getSwing(void) const { - return _.Swing == kSamsungAcSwingMove; + switch (_.Swing) { + case kSamsungAcSwingV: + case kSamsungAcSwingBoth: return true; + default: return false; + } } /// Set the vertical swing setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1. -/// e.g. 0xAE or 0XAF for swing move. void IRSamsungAc::setSwing(const bool on) { - _.Swing = (on ? kSamsungAcSwingMove : kSamsungAcSwingStop); + switch (_.Swing) { + case kSamsungAcSwingBoth: + case kSamsungAcSwingH: + _.Swing = on ? kSamsungAcSwingBoth : kSamsungAcSwingH; + break; + default: + _.Swing = on ? kSamsungAcSwingV : kSamsungAcSwingOff; + } } -/// Get the Beep setting of the A/C. +/// Get the horizontal swing setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getBeep(void) const { - return _.Beep; +bool IRSamsungAc::getSwingH(void) const { + switch (_.Swing) { + case kSamsungAcSwingH: + case kSamsungAcSwingBoth: return true; + default: return false; + } } -/// Set the Beep setting of the A/C. +/// Set the horizontal swing setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setBeep(const bool on) { - _.Beep = on; +void IRSamsungAc::setSwingH(const bool on) { + switch (_.Swing) { + case kSamsungAcSwingV: + case kSamsungAcSwingBoth: + _.Swing = on ? kSamsungAcSwingBoth : kSamsungAcSwingV; + break; + default: + _.Swing = on ? kSamsungAcSwingH : kSamsungAcSwingOff; + } } -/// Get the Clean setting of the A/C. +/// Get the Beep toggle setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getBeep(void) const { return _.BeepToggle; } + +/// Set the Beep toggle setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setBeep(const bool on) { _.BeepToggle = on; } + +/// Get the Clean toggle setting of the A/C. /// @return true, the setting is on. false, the setting is off. bool IRSamsungAc::getClean(void) const { - return _.Clean10 && _.Clean11; + return _.CleanToggle10 && _.CleanToggle11; } -/// Set the Clean setting of the A/C. +/// Set the Clean toggle setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. void IRSamsungAc::setClean(const bool on) { - _.Clean10 = on; - _.Clean11 = on; + _.CleanToggle10 = on; + _.CleanToggle11 = on; } /// Get the Quiet setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getQuiet(void) const { - return !_.Quiet1 && _.Quiet5; -} +bool IRSamsungAc::getQuiet(void) const { return _.Quiet; } /// Set the Quiet setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. void IRSamsungAc::setQuiet(const bool on) { - _.Quiet1 = !on; // Cleared when on. - _.Quiet5 = on; + _.Quiet = on; if (on) { // Quiet mode seems to set fan speed to auto. setFan(kSamsungAcFanAuto); @@ -584,25 +630,20 @@ void IRSamsungAc::setQuiet(const bool on) { /// Get the Powerful (Turbo) setting of the A/C. /// @return true, the setting is on. false, the setting is off. bool IRSamsungAc::getPowerful(void) const { - return !(_.Powerful8 & kSamsungAcPowerfulMask8) && - (_.Powerful10 == kSamsungAcPowerful10On) && + return (_.FanSpecial == kSamsungAcPowerfulOn) && (_.Fan == kSamsungAcFanTurbo); } /// Set the Powerful (Turbo) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. void IRSamsungAc::setPowerful(const bool on) { - uint8_t off_value = getBreeze() ? kSamsungAcBreezeOn : 0b000; - _.Powerful10 = (on ? kSamsungAcPowerful10On : off_value); + uint8_t off_value = (getBreeze() || getEcono()) ? _.FanSpecial + : kSamsungAcFanSpecialOff; + _.FanSpecial = (on ? kSamsungAcPowerfulOn : off_value); if (on) { - _.Powerful8 &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared. // Powerful mode sets fan speed to Turbo. setFan(kSamsungAcFanTurbo); setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet. - } else { - _.Powerful8 |= kSamsungAcPowerfulMask8; // Bit needs to be set. - // Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode - if (_.Fan == kSamsungAcFanTurbo) setFan(kSamsungAcFanAuto); } } @@ -610,7 +651,7 @@ void IRSamsungAc::setPowerful(const bool on) { /// @return true, the setting is on. false, the setting is off. /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 bool IRSamsungAc::getBreeze(void) const { - return (_.Breeze == kSamsungAcBreezeOn) && + return (_.FanSpecial == kSamsungAcBreezeOn) && (_.Fan == kSamsungAcFanAuto && !getSwing()); } @@ -618,36 +659,155 @@ bool IRSamsungAc::getBreeze(void) const { /// @param[in] on true, the setting is on. false, the setting is off. /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 void IRSamsungAc::setBreeze(const bool on) { - uint8_t off_value = getPowerful() ? kSamsungAcPowerful10On : 0b000; - _.Breeze = (on ? kSamsungAcBreezeOn : off_value); + const uint8_t off_value = (getPowerful() || + getEcono()) ? _.FanSpecial + : kSamsungAcFanSpecialOff; + _.FanSpecial = (on ? kSamsungAcBreezeOn : off_value); if (on) { setFan(kSamsungAcFanAuto); setSwing(false); } } +/// Get the current Economy (Eco) setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRSamsungAc::getEcono(void) const { + return (_.FanSpecial == kSamsungAcEconoOn) && + (_.Fan == kSamsungAcFanAuto && getSwing()); +} + +/// Set the current Economy (Eco) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRSamsungAc::setEcono(const bool on) { + const uint8_t off_value = (getBreeze() || + getPowerful()) ? _.FanSpecial + : kSamsungAcFanSpecialOff; + _.FanSpecial = (on ? kSamsungAcEconoOn : off_value); + if (on) { + setFan(kSamsungAcFanAuto); + setSwing(true); + } +} + /// Get the Display (Light/LED) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getDisplay(void) const { - return _.Display; -} +bool IRSamsungAc::getDisplay(void) const { return _.Display; } /// Set the Display (Light/LED) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setDisplay(const bool on) { - _.Display = on; -} +void IRSamsungAc::setDisplay(const bool on) { _.Display = on; } /// Get the Ion (Filter) setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRSamsungAc::getIon(void) const { - return _.Ion; -} +bool IRSamsungAc::getIon(void) const { return _.Ion; } /// Set the Ion (Filter) setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRSamsungAc::setIon(const bool on) { - _.Ion = on; +void IRSamsungAc::setIon(const bool on) { _.Ion = on; } + +/// Get the On Timer setting of the A/C from a raw extended state. +/// @return The Nr. of minutes the On Timer is set for. +uint16_t IRSamsungAc::_getOnTimer(void) const { + if (_.OnTimeDay) return 24 * 60; + return (_.OnTimeHrs2 * 2 + _.OnTimeHrs1) * 60 + _.OnTimeMins * 10; +} + +/// Set the current On Timer value of the A/C into the raw extended state. +void IRSamsungAc::_setOnTimer(void) { + _.OnTimerEnable = _OnTimerEnable = (_OnTimer > 0); + _.OnTimeDay = (_OnTimer >= 24 * 60); + if (_.OnTimeDay) { + _.OnTimeHrs2 = _.OnTimeHrs1 = _.OnTimeMins = 0; + return; + } + _.OnTimeMins = (_OnTimer % 60) / 10; + const uint8_t hours = _OnTimer / 60; + _.OnTimeHrs1 = hours & 0b1; + _.OnTimeHrs2 = hours >> 1; +} + +/// Get the Off Timer setting of the A/C from a raw extended state. +/// @return The Nr. of minutes the Off Timer is set for. +uint16_t IRSamsungAc::_getOffTimer(void) const { + if (_.OffTimeDay) return 24 * 60; + return (_.OffTimeHrs2 * 2 + _.OffTimeHrs1) * 60 + _.OffTimeMins * 10; +} + +/// Set the current Off Timer value of the A/C into the raw extended state. +void IRSamsungAc::_setOffTimer(void) { + _.OffTimerEnable = _OffTimerEnable = (_OffTimer > 0); + _.OffTimeDay = (_OffTimer >= 24 * 60); + if (_.OffTimeDay) { + _.OffTimeHrs2 = _.OffTimeHrs1 = _.OffTimeMins = 0; + return; + } + _.OffTimeMins = (_OffTimer % 60) / 10; + const uint8_t hours = _OffTimer / 60; + _.OffTimeHrs1 = hours & 0b1; + _.OffTimeHrs2 = hours >> 1; +} + +// Set the current Sleep Timer value of the A/C into the raw extended state. +void IRSamsungAc::_setSleepTimer(void) { + _setOffTimer(); + // The Sleep mode/timer should only be engaged if an off time has been set. + _.Sleep5 = _Sleep && _OffTimerEnable; + _.Sleep12 = _.Sleep5; +} + +/// Get the On Timer setting of the A/C. +/// @return The Nr. of minutes the On Timer is set for. +uint16_t IRSamsungAc::getOnTimer(void) const { return _OnTimer; } + +/// Get the Off Timer setting of the A/C. +/// @return The Nr. of minutes the Off Timer is set for. +/// @note Sleep & Off Timer share the same timer. +uint16_t IRSamsungAc::getOffTimer(void) const { + return _Sleep ? 0 : _OffTimer; +} + +/// Get the Sleep Timer setting of the A/C. +/// @return The Nr. of minutes the Off Timer is set for. +/// @note Sleep & Off Timer share the same timer. +uint16_t IRSamsungAc::getSleepTimer(void) const { + return _Sleep ? _OffTimer : 0; +} + +#define TIMER_RESOLUTION(mins) \ + (((std::min((mins), (uint16_t)(24 * 60))) / 10) * 10) + +/// Set the On Timer value of the A/C. +/// @param[in] nr_of_mins The number of minutes the timer should be. +/// @note The timer time only has a resolution of 10 mins. +/// @note Setting the On Timer active will cancel the Sleep timer/setting. +void IRSamsungAc::setOnTimer(const uint16_t nr_of_mins) { + // Limit to one day, and round down to nearest 10 min increment. + _OnTimer = TIMER_RESOLUTION(nr_of_mins); + _OnTimerEnable = _OnTimer > 0; + if (_OnTimer) _Sleep = false; +} + +/// Set the Off Timer value of the A/C. +/// @param[in] nr_of_mins The number of minutes the timer should be. +/// @note The timer time only has a resolution of 10 mins. +/// @note Setting the Off Timer active will cancel the Sleep timer/setting. +void IRSamsungAc::setOffTimer(const uint16_t nr_of_mins) { + // Limit to one day, and round down to nearest 10 min increment. + _OffTimer = TIMER_RESOLUTION(nr_of_mins); + _OffTimerEnable = _OffTimer > 0; + if (_OffTimer) _Sleep = false; +} + +/// Set the Sleep Timer value of the A/C. +/// @param[in] nr_of_mins The number of minutes the timer should be. +/// @note The timer time only has a resolution of 10 mins. +/// @note Sleep timer acts as an Off timer, and cancels any On Timer. +void IRSamsungAc::setSleepTimer(const uint16_t nr_of_mins) { + // Limit to one day, and round down to nearest 10 min increment. + _OffTimer = TIMER_RESOLUTION(nr_of_mins); + if (_OffTimer) setOnTimer(0); // Clear the on timer if set. + _Sleep = _OffTimer > 0; + _OffTimerEnable = _Sleep; } /// Convert a stdAc::opmode_t enum into its native mode. @@ -714,18 +874,17 @@ stdAc::state_t IRSamsungAc::toCommon(void) const { result.celsius = true; result.degrees = getTemp(); result.fanspeed = toCommonFanSpeed(_.Fan); - result.swingv = getSwing() ? stdAc::swingv_t::kAuto : - stdAc::swingv_t::kOff; + result.swingv = getSwing() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff; + result.swingh = getSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff; result.quiet = getQuiet(); result.turbo = getPowerful(); + result.econo = getEcono(); result.clean = getClean(); - result.beep = _.Beep; + result.beep = _.BeepToggle; result.light = _.Display; result.filter = _.Ion; + result.sleep = _Sleep ? getSleepTimer() : -1; // Not supported. - result.swingh = stdAc::swingh_t::kOff; - result.econo = false; - result.sleep = -1; result.clock = -1; return result; } @@ -734,7 +893,7 @@ stdAc::state_t IRSamsungAc::toCommon(void) const { /// @return A human readable string. String IRSamsungAc::toString(void) const { String result = ""; - result.reserve(115); // Reserve some heap for the string to reduce fragging. + result.reserve(230); // Reserve some heap for the string to reduce fragging. result += addBoolToString(getPower(), kPowerStr, false); result += addModeToString(_.Mode, kSamsungAcAuto, kSamsungAcCool, kSamsungAcHeat, kSamsungAcDry, @@ -764,14 +923,21 @@ String IRSamsungAc::toString(void) const { break; } result += ')'; - result += addBoolToString(getSwing(), kSwingStr); - result += addBoolToString(_.Beep, kBeepStr); - result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getSwing(), kSwingVStr); + result += addBoolToString(getSwingH(), kSwingHStr); + result += addToggleToString(_.BeepToggle, kBeepStr); + result += addToggleToString(getClean(), kCleanStr); result += addBoolToString(getQuiet(), kQuietStr); result += addBoolToString(getPowerful(), kPowerfulStr); + result += addBoolToString(getEcono(), kEconoStr); result += addBoolToString(getBreeze(), kBreezeStr); result += addBoolToString(_.Display, kLightStr); result += addBoolToString(_.Ion, kIonStr); + if (_OnTimerEnable) + result += addLabeledString(minsToString(_OnTimer), kOnTimerStr); + if (_OffTimerEnable) + result += addLabeledString(minsToString(_OffTimer), + _Sleep ? kSleepTimerStr : kOffTimerStr); return result; } @@ -811,9 +977,6 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t offset, offset += used; } // Compliance - // Is the signature correct? - DPRINTLN("DEBUG: Checking signature."); - if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; if (strict) { // Is the checksum valid? if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) { diff --git a/lib/IRremoteESP8266/src/ir_Samsung.h b/lib/IRremoteESP8266/src/ir_Samsung.h index bf9215edc..acabb3648 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.h +++ b/lib/IRremoteESP8266/src/ir_Samsung.h @@ -1,4 +1,4 @@ -// Copyright 2018 David Conran +// Copyright 2018-2021 David Conran /// @file /// @brief Support for Samsung protocols. /// Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ @@ -7,6 +7,7 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062 /// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538 (Checksum) +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1277 (Timers) // Supports: // Brand: Samsung, Model: UA55H6300 TV (SAMSUNG) @@ -18,11 +19,13 @@ // Brand: Samsung, Model: AH59-02692E Soundbar remote (SAMSUNG36) // Brand: Samsung, Model: HW-J551 Soundbar (SAMSUNG36) // Brand: Samsung, Model: AR09FSSDAWKNFA A/C (SAMSUNG_AC) +// Brand: Samsung, Model: AR09HSFSBWKN A/C (SAMSUNG_AC) // Brand: Samsung, Model: AR12KSFPEWQNET A/C (SAMSUNG_AC) // Brand: Samsung, Model: AR12HSSDBWKNEU A/C (SAMSUNG_AC) // Brand: Samsung, Model: AR12NXCXAWKXEU A/C (SAMSUNG_AC) -// Brand: Samsung, Model: AR09HSFSBWKN A/C (SAMSUNG_AC) +// Brand: Samsung, Model: AR12TXEAAWKNEU A/C (SAMSUNG_AC) // Brand: Samsung, Model: DB93-14195A remote (SAMSUNG_AC) +// Brand: Samsung, Model: DB96-24901C remote (SAMSUNG_AC) #ifndef IR_SAMSUNG_H_ #define IR_SAMSUNG_H_ @@ -41,116 +44,125 @@ /// Native representation of a Samsung A/C message. union SamsungProtocol{ uint8_t raw[kSamsungAcExtendedStateLength]; ///< State in code form. - struct { + struct { // Standard message map // Byte 0 - uint8_t :8; + uint8_t :8; // Byte 1 uint8_t :4; - uint8_t Quiet1 :1; - uint8_t Power1 :1; - uint8_t :2; - // Byte 2~4 - uint8_t pad0[3]; + uint8_t :4; // Sum1Lower + // Byte 2 + uint8_t :4; // Sum1Upper + uint8_t :4; + // Byte 3 + uint8_t :8; + // Byte 4 + uint8_t :8; // Byte 5 - uint8_t :5; - uint8_t Quiet5 :1; + uint8_t :4; + uint8_t Sleep5 :1; + uint8_t Quiet :1; uint8_t :2; // Byte 6 uint8_t :4; - uint8_t Power6 :2; + uint8_t Power1 :2; uint8_t :2; // Byte 7 - uint8_t :8; + uint8_t :8; // Byte 8 - uint8_t Powerful8 :8; + uint8_t :4; + uint8_t :4; // Sum2Lower // Byte 9 - uint8_t :4; - uint8_t Swing :3; - uint8_t :1; + uint8_t :4; // Sum1Upper + uint8_t Swing :3; + uint8_t :1; // Byte 10 - uint8_t :1; - uint8_t Powerful10 :3; - uint8_t Display :1; - uint8_t :2; - uint8_t Clean10 :1; + uint8_t :1; + uint8_t FanSpecial :3; // Powerful, Breeze/WindFree, Econo + uint8_t Display :1; + uint8_t :2; + uint8_t CleanToggle10 :1; // Byte 11 - uint8_t Ion :1; - uint8_t Clean11 :1; - uint8_t :2; - uint8_t Temp :4; + uint8_t Ion :1; + uint8_t CleanToggle11 :1; + uint8_t :2; + uint8_t Temp :4; // Byte 12 uint8_t :1; uint8_t Fan :3; uint8_t Mode :3; uint8_t :1; // Byte 13 - uint8_t :1; - uint8_t Beep :1; - uint8_t :6; + uint8_t :2; + uint8_t BeepToggle :1; + uint8_t :1; + uint8_t Power2 :2; + uint8_t :2; }; - struct { + struct { // Extended message map // 1st Section // Byte 0 - uint8_t :8; + uint8_t :8; // Byte 1 - uint8_t :4; - uint8_t Sum1Lower :4; + uint8_t :4; + uint8_t Sum1Lower :4; // Byte 2 - uint8_t Sum1Upper :4; - uint8_t :4; + uint8_t Sum1Upper :4; + uint8_t :4; // Byte 3 - uint8_t :8; + uint8_t :8; // Byte 4 - uint8_t :8; + uint8_t :8; // Byte 5 - uint8_t :8; + uint8_t :8; // Byte 6 - uint8_t :8; + uint8_t :8; // 2nd Section // Byte 7 - uint8_t :8; + uint8_t :8; // Byte 8 - uint8_t :4; - uint8_t Sum2Lower :4; + uint8_t :4; + uint8_t Sum2Lower :4; // Byte 9 - uint8_t Sum2Upper :4; - uint8_t :4; + uint8_t Sum2Upper :4; + uint8_t OffTimeMins :3; // In units of 10's of mins + uint8_t OffTimeHrs1 :1; // LSB of the number of hours. // Byte 10 - uint8_t :1; - uint8_t Breeze :3; // WindFree - uint8_t :4; + uint8_t OffTimeHrs2 :4; // MSBs of the number of hours. + uint8_t OnTimeMins :3; // In units of 10's of mins + uint8_t OnTimeHrs1 :1; // LSB of the number of hours. // Byte 11 - uint8_t :8; + uint8_t OnTimeHrs2 :4; // MSBs of the number of hours. + uint8_t :4; // Byte 12 - uint8_t :8; + uint8_t OffTimeDay :1; + uint8_t OnTimerEnable :1; + uint8_t OffTimerEnable :1; + uint8_t Sleep12 :1; + uint8_t OnTimeDay :1; + uint8_t :3; // Byte 13 - uint8_t :8; + uint8_t :8; // 3rd Section // Byte 14 - uint8_t :8; + uint8_t :8; // Byte 15 - uint8_t :4; - uint8_t Sum3Lower :4; + uint8_t :4; + uint8_t Sum3Lower :4; // Byte 16 - uint8_t Sum3Upper :4; - uint8_t :4; + uint8_t Sum3Upper :4; + uint8_t :4; // Byte 17 - uint8_t :8; + uint8_t :8; // Byte 18 - uint8_t :8; + uint8_t :8; // Byte 19 - uint8_t :8; + uint8_t :8; // Byte 20 - uint8_t :8; + uint8_t :8; }; }; // Constants -const uint8_t kSamsungAcPowerfulMask8 = 0b01010000; -const uint8_t kSamsungAcSwingMove = 0b010; -const uint8_t kSamsungAcSwingStop = 0b111; -const uint8_t kSamsungAcPowerful10On = 0b011; -const uint8_t kSamsungAcBreezeOn = 0b101; const uint8_t kSamsungAcMinTemp = 16; // C Mask 0b11110000 const uint8_t kSamsungAcMaxTemp = 30; // C Mask 0b11110000 const uint8_t kSamsungAcAutoTemp = 25; // C Mask 0b11110000 @@ -174,12 +186,10 @@ class IRSamsungAc { public: explicit IRSamsungAc(const uint16_t pin, const bool inverted = false, const bool use_modulation = true); - void stateReset(const bool forcepower = true, const bool initialPower = true); + void stateReset(const bool extended = true, const bool initialPower = true); #if SEND_SAMSUNG_AC - void send(const uint16_t repeat = kSamsungAcDefaultRepeat, - const bool calcchecksum = true); - void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat, - const bool calcchecksum = true); + void send(const uint16_t repeat = kSamsungAcDefaultRepeat); + void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat); void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat); void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat); /// Run the calibration to calculate uSec timing offsets for this platform. @@ -201,6 +211,8 @@ class IRSamsungAc { uint8_t getMode(void) const; void setSwing(const bool on); bool getSwing(void) const; + void setSwingH(const bool on); + bool getSwingH(void) const; void setBeep(const bool on); bool getBeep(void) const; void setClean(const bool on); @@ -211,10 +223,18 @@ class IRSamsungAc { bool getPowerful(void) const; void setBreeze(const bool on); bool getBreeze(void) const; + void setEcono(const bool on); + bool getEcono(void) const; void setDisplay(const bool on); bool getDisplay(void) const; void setIon(const bool on); bool getIon(void) const; + uint16_t getOnTimer(void) const; + void setOnTimer(const uint16_t nr_of_mins); + uint16_t getOffTimer(void) const; + void setOffTimer(const uint16_t nr_of_mins); + uint16_t getSleepTimer(void) const; + void setSleepTimer(const uint16_t nr_of_mins); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kSamsungAcStateLength); @@ -238,9 +258,22 @@ class IRSamsungAc { /// @endcond #endif // UNIT_TEST SamsungProtocol _; - bool _forcepower; ///< Hack to know when we need to send a special power mesg + bool _forceextended; ///< Flag to know when we need to send an extended mesg. bool _lastsentpowerstate; + bool _OnTimerEnable; + bool _OffTimerEnable; + bool _Sleep; + bool _lastSleep; + uint16_t _OnTimer; + uint16_t _OffTimer; + uint16_t _lastOnTimer; + uint16_t _lastOffTimer; void checksum(void); + uint16_t _getOnTimer(void) const; + uint16_t _getOffTimer(void) const; + void _setOnTimer(void); + void _setOffTimer(void); + void _setSleepTimer(void); }; #endif // IR_SAMSUNG_H_ diff --git a/lib/IRremoteESP8266/src/ir_Sony.cpp b/lib/IRremoteESP8266/src/ir_Sony.cpp index 0dbbec3c8..5746ee9b1 100644 --- a/lib/IRremoteESP8266/src/ir_Sony.cpp +++ b/lib/IRremoteESP8266/src/ir_Sony.cpp @@ -120,7 +120,7 @@ uint32_t IRsend::encodeSony(const uint16_t nbits, const uint16_t command, /// bits long. bool IRrecv::decodeSony(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - if (results->rawlen <= 2 * nbits + kHeader - 1 + offset) + if (results->rawlen < 2 * nbits + kHeader - 1 + offset) return false; // Message is smaller than we expected. // Compliance diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index e1696c42a..c7ae038d0 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -8,6 +8,8 @@ // Brand: TCL, Model: TAC-09CHSD/XA31I A/C (TAC09CHSD) // Brand: Teknopoint, Model: Allegro SSA-09H A/C (GZ055BE1) // Brand: Teknopoint, Model: GZ-055B-E1 remote (GZ055BE1) +// Brand: Daewoo, Model: DSB-F0934ELH-V A/C +// Brand: Daewoo, Model: GYKQ-52E remote #ifndef IR_TCL_H_ #define IR_TCL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.cpp b/lib/IRremoteESP8266/src/ir_Toshiba.cpp index 0e3ac8ba2..7333493ef 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266/src/ir_Toshiba.cpp @@ -216,9 +216,7 @@ void IRToshibaAC::setTemp(const uint8_t degrees) { /// Get the current temperature setting. /// @return The current setting for temp. in degrees celsius. -uint8_t IRToshibaAC::getTemp(void) const { - return _.Temp + kToshibaAcMinTemp; -} +uint8_t IRToshibaAC::getTemp(void) const { return _.Temp + kToshibaAcMinTemp; } /// Set the speed of the fan. /// @param[in] speed The desired setting (0 is Auto, 1-5 is the speed, 5 is Max) @@ -339,6 +337,19 @@ void IRToshibaAC::setEcono(const bool on) { } } +/// Get the filter (Pure/Ion Filter) setting of the A/C. +/// @return true, if the current setting is on. Otherwise, false. +bool IRToshibaAC::getFilter(void) const { + return (getStateLength() >= kToshibaACStateLength) ? _.Filter : false; +} + +/// Set the filter (Pure/Ion Filter) setting of the A/C. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRToshibaAC::setFilter(const bool on) { + _.Filter = on; + if (on) setStateLength(std::min(kToshibaACStateLength, getStateLength())); +} + /// Convert a stdAc::opmode_t enum into its native mode. /// @param[in] mode The enum to be converted. /// @return The native equivalent of the enum. @@ -421,6 +432,7 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const { result.fanspeed = toCommonFanSpeed(getFan()); result.turbo = getTurbo(); result.econo = getEcono(); + result.filter = getFilter(); } switch (getSwing()) { case kToshibaAcSwingOn: @@ -436,7 +448,6 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const { } // Not supported. result.light = false; - result.filter = false; result.swingh = stdAc::swingh_t::kOff; result.quiet = false; result.clean = false; @@ -450,7 +461,7 @@ stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const { /// @return A human readable string. String IRToshibaAC::toString(void) const { String result = ""; - result.reserve(80); + result.reserve(95); result += addTempToString(getTemp(), true, false); switch (getStateLength()) { case kToshibaACStateLengthShort: @@ -477,6 +488,7 @@ String IRToshibaAC::toString(void) const { kToshibaAcFanMed); result += addBoolToString(getTurbo(), kTurboStr); result += addBoolToString(getEcono(), kEconoStr); + result += addBoolToString(getFilter(), kFilterStr); } return result; } diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.h b/lib/IRremoteESP8266/src/ir_Toshiba.h index 3ebf5e693..1314cf54d 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.h +++ b/lib/IRremoteESP8266/src/ir_Toshiba.h @@ -8,6 +8,7 @@ /// @see https://docs.google.com/spreadsheets/d/1yidE2fvaO9kpCHfKafIdH31q4uaskYR1OwwrkyOxbp0/edit?usp=drivesdk /// @see https://www.toshiba-carrier.co.jp/global/about/index.htm /// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1692 // Supports: // Brand: Toshiba, Model: RAS-B13N3KV2 @@ -18,6 +19,8 @@ // Brand: Toshiba, Model: WC-L03SE // Brand: Toshiba, Model: WH-UB03NJ remote // Brand: Toshiba, Model: RAS-2558V A/C +// Brand: Toshiba, Model: WH-TA01JE remote +// Brand: Toshiba, Model: RAS-25SKVP2-ND A/C // Brand: Carrier, Model: 42NQV060M2 / 38NYV060M2 A/C // Brand: Carrier, Model: 42NQV050M2 / 38NYV050M2 A/C // Brand: Carrier, Model: 42NQV035M2 / 38NYV035M2 A/C @@ -50,28 +53,31 @@ union ToshibaProtocol{ ///< 1 (56 bit message) ///< 3 (72 bit message) ///< 4 (80 bit message) - uint8_t Length :8; + uint8_t Length :8; // Byte[3] - The bit-inverted value of the "length" byte. - uint8_t :8; + uint8_t :8; // Byte[4] - uint8_t :3; - uint8_t LongMsg :1; - uint8_t :1; - uint8_t ShortMsg:1; - uint8_t :2; + uint8_t :3; + uint8_t LongMsg :1; + uint8_t :1; + uint8_t ShortMsg :1; + uint8_t :2; // Byte[5] - uint8_t Swing :3; - uint8_t :1; - uint8_t Temp :4; + uint8_t Swing :3; + uint8_t :1; + uint8_t Temp :4; // Byte[6] - uint8_t Mode :3; - uint8_t :2; - uint8_t Fan :3; + uint8_t Mode :3; + uint8_t :2; + uint8_t Fan :3; // Byte[7] - uint8_t :8; + uint8_t :4; + uint8_t Filter :1; + uint8_t :3; + // Byte[8] // (Checksum for 72 bit messages, Eco/Turbo for long 80 bit messages) - uint8_t EcoTurbo :8; + uint8_t EcoTurbo :8; }; }; @@ -144,6 +150,8 @@ class IRToshibaAC { bool getTurbo(void) const; void setEcono(const bool on); bool getEcono(void) const; + void setFilter(const bool on); + bool getFilter(void) const; void setMode(const uint8_t mode); uint8_t getMode(const bool raw = false) const; void setRaw(const uint8_t newState[], diff --git a/lib/IRremoteESP8266/src/locale/defaults.h b/lib/IRremoteESP8266/src/locale/defaults.h index 2e9c58139..d8142e20e 100644 --- a/lib/IRremoteESP8266/src/locale/defaults.h +++ b/lib/IRremoteESP8266/src/locale/defaults.h @@ -120,6 +120,9 @@ #ifndef D_STR_IFEEL #define D_STR_IFEEL "IFeel" #endif // D_STR_IFEEL +#ifndef D_STR_ISEE +#define D_STR_ISEE "ISee" +#endif // D_STR_ISEE #ifndef D_STR_HUMID #define D_STR_HUMID "Humid" #endif // D_STR_HUMID @@ -207,6 +210,19 @@ #ifndef D_STR_SENSOR #define D_STR_SENSOR "Sensor" #endif // D_STR_SENSOR +#ifndef D_STR_ABSENSEDETECT +#define D_STR_ABSENSEDETECT "Absense detect" +#endif // D_STR_ABSENSEDETECT +#ifndef D_STR_DIRECT +#define D_STR_DIRECT "Direct" +#endif // D_STR_DIRECT +#ifndef D_STR_INDIRECT +#define D_STR_INDIRECT "Indirect" +#endif // D_STR_INDIRECT +#ifndef D_STR_DIRECTINDIRECTMODE +#define D_STR_DIRECTINDIRECTMODE D_STR_DIRECT " / " \ +D_STR_INDIRECT " " D_STR_MODE +#endif // D_STR_DIRECTINDIRECTMODE #ifndef D_STR_DISPLAY #define D_STR_DISPLAY "Display" #endif // D_STR_DISPLAY @@ -291,6 +307,9 @@ #ifndef D_STR_VANE #define D_STR_VANE "Vane" #endif // D_STR_VANE +#ifndef D_STR_LOCK +#define D_STR_LOCK "Lock" +#endif // D_STR_LOCK #ifndef D_STR_AUTO #define D_STR_AUTO "Auto" @@ -547,6 +566,12 @@ #ifndef D_STR_YBOFB #define D_STR_YBOFB "YBOFB" #endif // D_STR_YBOFB +#ifndef D_STR_V9014557_A +#define D_STR_V9014557_A "V9014557-A" +#endif // D_STR_V9014557_A +#ifndef D_STR_V9014557_B +#define D_STR_V9014557_B "V9014557-B" +#endif // D_STR_V9014557_B #ifndef D_STR_RLT0541HTA_A #define D_STR_RLT0541HTA_A "R-LT0541-HTA-A" #endif // D_STR_RLT0541HTA_A @@ -583,6 +608,12 @@ #ifndef D_STR_AKB73757604 #define D_STR_AKB73757604 "AKB73757604" #endif // D_STR_AKB73757604 +#ifndef D_STR_KKG9AC1 +#define D_STR_KKG9AC1 "KKG9AC1" +#endif // D_STR_KKG9AC1 +#ifndef D_STR_KKG29AC1 +#define D_STR_KKG29AC1 "KKG29AC1" +#endif // D_STR_KKG9AC1 #ifndef D_STR_LKE #define D_STR_LKE "LKE" #endif // D_STR_LKE @@ -654,6 +685,9 @@ #endif // D_STR_DG11J191 // Protocols Names +#ifndef D_STR_AIRTON +#define D_STR_AIRTON "AIRTON" +#endif // D_STR_AIRTON #ifndef D_STR_AIRWELL #define D_STR_AIRWELL "AIRWELL" #endif // D_STR_AIRWELL @@ -684,6 +718,9 @@ #ifndef D_STR_COOLIX #define D_STR_COOLIX "COOLIX" #endif // D_STR_COOLIX +#ifndef D_STR_COOLIX48 +#define D_STR_COOLIX48 D_STR_COOLIX "48" +#endif // D_STR_COOLIX48 #ifndef D_STR_CORONA_AC #define D_STR_CORONA_AC "CORONA_AC" #endif // D_STR_CORONA_AC diff --git a/lib/IRremoteESP8266/src/locale/ru-RU.h b/lib/IRremoteESP8266/src/locale/ru-RU.h new file mode 100644 index 000000000..02a892403 --- /dev/null +++ b/lib/IRremoteESP8266/src/locale/ru-RU.h @@ -0,0 +1,152 @@ +// Copyright 2021 - PtilopsisLeucotis (@PtilopsisLeucotis) +// Locale/language file for Russian / Russia. +// This file will override the default values located in `defaults.h`. +#ifndef LOCALE_RU_RU_H_ +#define LOCALE_RU_RU_H_ + +#define D_STR_UNKNOWN "НЕИЗВЕСТНО" +#define D_STR_PROTOCOL "Протокол" +#define D_STR_POWER "Питание" +#define D_STR_PREVIOUS "Предыдущий" +#define D_STR_ON "Вкл" +#define D_STR_OFF "Выкл" +#define D_STR_MODE "Режим" +#define D_STR_TOGGLE "Переключить" +#define D_STR_TURBO "Турбо" +#define D_STR_SUPER "Супер" +#define D_STR_SLEEP "Сон" +#define D_STR_LIGHT "Свет" +#define D_STR_POWERFUL "Мощный" +#define D_STR_QUIET "Тихий" +#define D_STR_ECONO "Экономичный" +#define D_STR_SWING "Качание" +#define D_STR_SWINGH D_STR_SWING"(Г)" +#define D_STR_SWINGV D_STR_SWING"(В)" +#define D_STR_BEEP "Звук" +#define D_STR_MOULD "Плесень" +#define D_STR_CLEAN "Чистый" +#define D_STR_PURIFY "Очистка" +#define D_STR_TIMER "Таймер" +#define D_STR_ONTIMER "Таймер Включения" +#define D_STR_OFFTIMER "Таймер Выключения" +#define D_STR_TIMERMODE "Режим Таймера" +#define D_STR_CLOCK "Часы" +#define D_STR_COMMAND "Команда" +#define D_STR_HEALTH "Здоровье" +#define D_STR_MODEL "Модель" +#define D_STR_TEMP "Температура" +#define D_STR_HUMID "Влажность" +#define D_STR_SAVE "Сохранить" +#define D_STR_EYE "Глаз" +#define D_STR_FOLLOW "Следовать" +#define D_STR_ION "Ион" +#define D_STR_FRESH "Свежесть" +#define D_STR_HOLD "Удержать" +#define D_STR_BUTTON "Кнопка" +#define D_STR_NIGHT "Ночь" +#define D_STR_SILENT "Тихий" +#define D_STR_FILTER "Фильтр" +#define D_STR_CELSIUS "Цельсий" +#define D_STR_FAHRENHEIT "Фаренгейт" +#define D_STR_UP "Выше" +#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP +#define D_STR_DOWN "Ниже" +#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN +#define D_STR_CHANGE "Изменение" +#define D_STR_START "Запуск" +#define D_STR_STOP "Остановка" +#define D_STR_MOVE "Перемещение" +#define D_STR_SET "Установка" +#define D_STR_CANCEL "Отмена" +#define D_STR_COMFORT "Комфорт" +#define D_STR_SENSOR "Сенсор" +#define D_STR_DISPLAY "Дисплей" +#define D_STR_WEEKLY "Недельный" +#define D_STR_LAST "Последний" +#define D_STR_FAST "Быстро" +#define D_STR_SLOW "Медленно" +#define D_STR_AIRFLOW "Воздушный Поток" +#define D_STR_STEP "Шаг" +#define D_STR_NA "Н/Д" +#define D_STR_INSIDE "Внутри" +#define D_STR_OUTSIDE "Снаружи" +#define D_STR_LOUD "Громко" +#define D_STR_UPPER "Верхнее" +#define D_STR_LOWER "Нижнее" +#define D_STR_BREEZE "Бриз" +#define D_STR_CIRCULATE "Циркуляция" +#define D_STR_CEILING "Потолок" +#define D_STR_WALL "Стена" +#define D_STR_ROOM "Комната" +#define D_STR_6THSENSE "6-ое чувство" +#define D_STR_FIXED "Фиксированный" +#define D_STR_TYPE "Тип" +#define D_STR_SPECIAL "Специальный" +#define D_STR_RECYCLE "Рециркуляция" +#define D_STR_VANE "Жалюзи" +#define D_STR_LOCK "Блокировка" +#define D_STR_AUTO "Авто" +#define D_STR_AUTOMATIC "Автоматический" +#define D_STR_MANUAL "Ручной" +#define D_STR_COOL "Охл" +#define D_STR_COOLING "Охлаждение" +#define D_STR_HEAT "Нагр" +#define D_STR_HEATING "Обогрев" +#define D_STR_FAN "Вентиляция" +#define D_STR_ONLY "Только" +#define D_STR_FANSPACEONLY D_STR_ONLY " " D_STR_FAN +#define D_STR_FANONLYNOSPACE D_STR_ONLY D_STR_FAN +#define D_STR_DRY "Сухо" +#define D_STR_DRYING "Сушка" +#define D_STR_DEHUMIDIFY "Осушение" +#define D_STR_MAX "Макс" +#define D_STR_MAXIMUM "Максимум" +#define D_STR_MIN "Мин" +#define D_STR_MINIMUM "Минимум" +#define D_STR_MED "Сред" +#define D_STR_MEDIUM "Среднее" +#define D_STR_HIGHEST "Верхнее" +#define D_STR_HIGH "Верх" +#define D_STR_HI "Верх" +#define D_STR_MID "Сред" +#define D_STR_MIDDLE "Середина" +#define D_STR_LOW "Низ" +#define D_STR_LO "Низ" +#define D_STR_LOWEST "Нижнее" +#define D_STR_RIGHT "Право" +#define D_STR_LEFT "Лево" +#define D_STR_WIDE "Широкий" +#define D_STR_CENTRE "Центр" +#define D_STR_TOP "Наивысший" +#define D_STR_BOTTOM "Наинизший" +#define D_STR_DAY "День" +#define D_STR_DAYS "Дней" +#define D_STR_HOUR "Час" +#define D_STR_HOURS "Часов" +#define D_STR_MINUTE "Минута" +#define D_STR_MINUTES "Минут" +#define D_STR_SECOND "Секунда" +#define D_STR_SECONDS "Секунд" +#define D_STR_NOW "Сейчас" +#define D_STR_THREELETTERDAYS "ВскПндВтрСреЧтвПтнСуб" +#define D_STR_YES "Да" +#define D_STR_NO "Нет" +#define D_STR_TRUE "Истина" +#define D_STR_FALSE "Ложь" +#define D_STR_REPEAT "Повтор" +#define D_STR_CODE "Код" +#define D_STR_BITS "Бит" + +// IRrecvDumpV2+ +#define D_STR_TIMESTAMP "Метка Времени" +#define D_STR_LIBRARY "Библиотека" +#define D_STR_MESGDESC "Описание Сообщения." +#define D_STR_TOLERANCE "Допуск" +#define D_STR_IRRECVDUMP_STARTUP \ + "IRrecvDump запущен и ождает ИК команды на Входе %d" +#define D_WARN_BUFFERFULL \ + "ПРЕДУПРЕЖДЕНИЕ: ИК код слишком велик для буфера (>= %d). " \ + "Этому результату не следует доверять, пока это не будет исправлено. " \ + "Исправьте и увеличьте `kCaptureBufferSize`." + +#endif // LOCALE_RU_RU_H_ diff --git a/lib/IRremoteESP8266/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp index b2eff6efa..2988e4b4b 100644 --- a/lib/IRremoteESP8266/test/IRac_test.cpp +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -1,6 +1,7 @@ // Copyright 2019-2021 David Conran #include +#include "ir_Airton.h" #include "ir_Airwell.h" #include "ir_Amcor.h" #include "ir_Argo.h" @@ -19,6 +20,7 @@ #include "ir_Kelvinator.h" #include "ir_LG.h" #include "ir_Midea.h" +#include "ir_Mirage.h" #include "ir_Mitsubishi.h" #include "ir_MitsubishiHeavy.h" #include "ir_Neoclima.h" @@ -43,6 +45,36 @@ // Tests for IRac class. +TEST(TestIRac, Airton) { + IRAirtonAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + const char expected[] = + "Power: On, Mode: 1 (Cool), Fan: 5 (Maximum), Temp: 18C, " + "Swing(V): On, Econo: On, Turbo: On, Light: On, Health: On, Sleep: On"; + + ac.begin(); + irac.airton(&ac, + true, // Power + stdAc::opmode_t::kCool, // Mode + 18, // Celsius + stdAc::fanspeed_t::kMax, // Fan speed + stdAc::swingv_t::kAuto, // Vertical Swing + true, // Turbo + true, // Light/Display/LED + true, // Econo (Eco) + true, // Filter (Health) + 9 * 60 + 12); // Sleep (09:12) + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(AIRTON, ac._irsend.capture.decode_type); + ASSERT_EQ(kAirtonBits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); +} + TEST(TestIRac, Airwell) { IRAirwellAc ac(kGpioUnused); IRac irac(kGpioUnused); @@ -560,7 +592,8 @@ TEST(TestIRac, Electra) { IRrecv capture(kGpioUnused); char expected[] = "Power: On, Mode: 6 (Fan), Temp: 26C, Fan: 1 (High), " - "Swing(V): On, Swing(H): On, Light: Toggle, Clean: On, Turbo: On"; + "Swing(V): On, Swing(H): On, Light: Toggle, Clean: On, Turbo: On, " + "IFeel: Off"; ac.begin(); irac.electra(&ac, @@ -727,9 +760,10 @@ TEST(TestIRac, Gree) { IRrecv capture(kGpioUnused); char expected[] = "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 71F, " - "Fan: 2 (Medium), Turbo: Off, IFeel: Off, WiFi: Off, XFan: On, " - "Light: On, Sleep: On, Swing(V) Mode: Manual, " - "Swing(V): 3 (UNKNOWN), Timer: Off, Display Temp: 0 (Off)"; + "Fan: 2 (Medium), Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, " + "XFan: On, Light: On, Sleep: On, Swing(V) Mode: Manual, " + "Swing(V): 3 (UNKNOWN), Swing(H): 5 (Right), Timer: Off, " + "Display Temp: 0 (Off)"; ac.begin(); irac.gree(&ac, @@ -740,7 +774,9 @@ TEST(TestIRac, Gree) { 71, // Degrees (F) stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kHigh, // Vertical swing + stdAc::swingh_t::kRight, // Horizontal swing false, // Turbo + false, // Econo true, // Light true, // Clean (aka Mold/XFan) 8 * 60 + 0); // Sleep time @@ -760,7 +796,7 @@ TEST(TestIRac, Haier) { IRrecv capture(kGpioUnused); char expected[] = "Command: 1 (On), Mode: 1 (Cool), Temp: 24C, Fan: 2 (Medium), " - "Swing: 1 (Up), Sleep: On, Health: On, Clock: 13:45, " + "Swing(V): 1 (Up), Sleep: On, Health: On, Clock: 13:45, " "On Timer: Off, Off Timer: Off"; ac.begin(); @@ -788,20 +824,24 @@ TEST(TestIRac, Haier176) { IRac irac(kGpioUnused); IRrecv capture(kGpioUnused); const char expected[] = - "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 23C, " - "Fan: 2 (Medium), Turbo: On, Quiet: Off, Swing: 1 (Highest), Sleep: On, " - "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off"; + "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), " + "Mode: 1 (Cool), Temp: 23C, Fan: 2 (Medium), Turbo: On, Quiet: Off, " + "Swing(V): 1 (Highest), Swing(H): 0 (Middle), Sleep: On, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off"; ac.begin(); irac.haier176(&ac, - true, // Power - stdAc::opmode_t::kCool, // Mode - 23, // Celsius - stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kHigh, // Vertical swing - true, // Turbo - false, // Quiet - true, // Filter - 8 * 60 + 0); // Sleep time + haier_ac176_remote_model_t::V9014557_A, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + true, // Celsius + 23, // Degrees + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kHigh, // Vertical swing + stdAc::swingh_t::kOff, // Horizontal swing + true, // Turbo + false, // Quiet + true, // Filter + 8 * 60 + 0); // Sleep time ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); @@ -817,17 +857,20 @@ TEST(TestIRac, HaierYrwo2) { IRac irac(kGpioUnused); IRrecv capture(kGpioUnused); char expected[] = - "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 23C, " - "Fan: 2 (Medium), Turbo: Off, Quiet: On, Swing: 1 (Highest), Sleep: On, " - "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off"; + "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), " + "Mode: 1 (Cool), Temp: 23C, Fan: 2 (Medium), Turbo: Off, Quiet: On, " + "Swing(V): 1 (Highest), Swing(H): 7 (Auto), Sleep: On, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off"; ac.begin(); irac.haierYrwo2(&ac, true, // Power stdAc::opmode_t::kCool, // Mode - 23, // Celsius + true, // Celsius + 23, // Degrees stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kHigh, // Vertical swing + stdAc::swingh_t::kAuto, // Vertical swing false, // Turbo true, // Quiet true, // Filter @@ -1127,7 +1170,22 @@ TEST(TestIRac, Issue1513) { stdAc::swingh_t::kOff, // Horizontal swing true); // Light ac._irsend.makeDecodeResult(); - // All sent, we assume the above works. Just need to switch to swing off now. + EXPECT_EQ(121, ac._irsend.capture.rawlen); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(LG2, ac._irsend.capture.decode_type); // Not "LG" + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + ASSERT_EQ( + "Model: 2 (AKB75215403), Power: On, Mode: 4 (Heat), Temp: 26C, " + "Fan: 0 (Quiet)", + IRAcUtils::resultAcToString(&ac._irsend.capture)); + + // The next should be a SwingV On. + EXPECT_TRUE(capture.decodeLG(&ac._irsend.capture, 61)); + ASSERT_EQ(LG2, ac._irsend.capture.decode_type); // Not "LG" + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + EXPECT_EQ(kLgAcSwingVSwing, ac._irsend.capture.value); + ASSERT_EQ("Model: 3 (AKB74955603), Swing(V): 20 (Swing)", + IRAcUtils::resultAcToString(&ac._irsend.capture)); ac._irsend.reset(); ac.stateReset(); ac.send(); @@ -1165,6 +1223,79 @@ TEST(TestIRac, Issue1513) { IRAcUtils::resultAcToString(&ac._irsend.capture)); } +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1651#issuecomment-952811720 +TEST(TestIRac, Issue1651) { + // Simulate sending a state with a SwingV off, then followed by a SwingV Auto. + IRLgAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + ac.begin(); + // IRhvac {"Vendor":"LG2","Model":3,"Mode":"Auto","Power":"On","Celsius":"On", + // "Temp":15,"FanSpeed":"Auto","SwingV":"Off","SwingH":"Off", + // "Quiet":"Off","Turbo":"Off","Econo":"Off","Light":"On", + // "Filter":"Off","Clean":"Off","Beep":"Off","Sleep":-1} + ac._irsend.reset(); + irac.lg(&ac, + lg_ac_remote_model_t::AKB74955603, // Model + true, // Power + stdAc::opmode_t::kAuto, // Mode + 15, // Degrees C (Note: 16C is min) + stdAc::fanspeed_t::kAuto, // Fan speed + stdAc::swingv_t::kOff, // Vertical swing + stdAc::swingv_t::kOff, // Vertical swing (previous) + stdAc::swingh_t::kOff, // Horizontal swing + true); // Light + ac._irsend.makeDecodeResult(); + // As we are not making a change of the SwingV state, there should only be + // one message. (i.e. 60 + 1) + EXPECT_EQ(61, ac._irsend.capture.rawlen); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(LG2, ac._irsend.capture.decode_type); // Not "LG" + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + ASSERT_EQ( + "Model: 2 (AKB75215403), Power: On, Mode: 3 (Auto), Temp: 16C, " + "Fan: 5 (Auto)", + IRAcUtils::resultAcToString(&ac._irsend.capture)); + ac._irsend.reset(); + ac.stateReset(); + ac.send(); + // IRhvac {"Vendor":"LG2","Model":3,"Mode":"Auto","Power":"On","Celsius":"On", + // "Temp":15,"FanSpeed":"Max","SwingV":"Auto","SwingH":"Off", + // "Quiet":"Off","Turbo":"Off","Econo":"Off","Light":"On", + // "Filter":"Off","Clean":"Off","Beep":"Off","Sleep":-1} + ac._irsend.makeDecodeResult(); + ac._irsend.reset(); + irac.lg(&ac, + lg_ac_remote_model_t::AKB74955603, // Model + true, // Power + stdAc::opmode_t::kAuto, // Mode + 15, // Degrees C (Note: 16C is min) + stdAc::fanspeed_t::kMax, // Fan speed + stdAc::swingv_t::kAuto, // Vertical swing + stdAc::swingv_t::kOff, // Vertical swing (previous) + stdAc::swingh_t::kOff, // Horizontal swing + true); // Light + ac._irsend.makeDecodeResult(); + // There should only be two messages. + EXPECT_EQ(121, ac._irsend.capture.rawlen); + // First message should be normal. + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(LG2, ac._irsend.capture.decode_type); // Not "LG" + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + ASSERT_EQ( + "Model: 2 (AKB75215403), Power: On, Mode: 3 (Auto), Temp: 16C, " + "Fan: 4 (Maximum)", + IRAcUtils::resultAcToString(&ac._irsend.capture)); + // The next should be a SwingV Off. + EXPECT_TRUE(capture.decodeLG(&ac._irsend.capture, 61)); + ASSERT_EQ(LG2, ac._irsend.capture.decode_type); // Not "LG" + ASSERT_EQ(kLgBits, ac._irsend.capture.bits); + EXPECT_EQ(kLgAcSwingVSwing, ac._irsend.capture.value); + ASSERT_EQ("Model: 3 (AKB74955603), Swing(V): 20 (Swing)", + IRAcUtils::resultAcToString(&ac._irsend.capture)); +} + TEST(TestIRac, LG2_AKB73757604) { IRLgAc ac(kGpioUnused); IRac irac(kGpioUnused); @@ -1254,6 +1385,62 @@ TEST(TestIRac, Midea) { ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } +TEST(TestIRac, Mirage) { + IRMirageAc ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + stdAc::state_t state, r, p; + const char expected_KKG9AC1[] = + "Model: 1 (KKG9AC1), Power: On, Mode: 3 (Dry), Temp: 27C, " + "Fan: 2 (Medium), Turbo: Off, Sleep: On, Light: Off, " + "Swing(V): 9 (High), Clock: 17:31"; + + ac.begin(); + + state.model = mirage_ac_remote_model_t::KKG9AC1; + state.power = true; + state.mode = stdAc::opmode_t::kDry; + state.celsius = true; + state.degrees = 27; + state.fanspeed = stdAc::fanspeed_t::kMedium; + state.swingv = stdAc::swingv_t::kHigh; + state.swingh = stdAc::swingh_t::kLeft; + state.turbo = false; + state.quiet = true; + state.light = false; + state.filter = true; + state.clean = false; + state.sleep = 8 * 60 + 0; + state.clock = 17 * 60 + 31; + state.beep = false; + irac.mirage(&ac, state); + + ASSERT_EQ(expected_KKG9AC1, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MIRAGE, ac._irsend.capture.decode_type); + ASSERT_EQ(kMirageBits, ac._irsend.capture.bits); + ASSERT_EQ(expected_KKG9AC1, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); + + const char expected_KKG29AC1[] = + "Model: 2 (KKG29AC1), Power: On, Mode: 3 (Dry), Temp: 27C, " + "Fan: 3 (Medium), Turbo: Off, Sleep: On, Quiet: On, Light: -, " + "Swing(V): On, Swing(H): On, Filter: On, Clean: -, " + "On Timer: Off, Off Timer: Off, IFeel: Off"; + ac._irsend.reset(); + state.model = mirage_ac_remote_model_t::KKG29AC1; + irac.mirage(&ac, state); + ASSERT_EQ(expected_KKG29AC1, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(MIRAGE, ac._irsend.capture.decode_type); + ASSERT_EQ(kMirageBits, ac._irsend.capture.bits); + ASSERT_EQ(expected_KKG29AC1, + IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); +} + TEST(TestIRac, Mitsubishi) { IRMitsubishiAC ac(kGpioUnused); IRac irac(kGpioUnused); @@ -1262,7 +1449,9 @@ TEST(TestIRac, Mitsubishi) { "Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 2 (Medium), " "Swing(V): 0 (Auto), Swing(H): 3 (Middle), " "Clock: 14:30, On Timer: 00:00, Off Timer: 00:00, Timer: -, " - "Weekly Timer: Off"; + "Weekly Timer: Off" + ", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, " + "Direct / Indirect Mode: 0, Fresh: Off"; ac.begin(); irac.mitsubishi(&ac, @@ -1495,9 +1684,10 @@ TEST(TestIRac, Samsung) { IRSamsungAc ac(kGpioUnused); IRac irac(kGpioUnused); IRrecv capture(kGpioUnused); - char expected[] = - "Power: On, Mode: 0 (Auto), Temp: 28C, Fan: 6 (Auto), Swing: On, " - "Beep: On, Clean: On, Quiet: On, Powerful: Off, Breeze: Off, " + const char expected[] = + "Power: On, Mode: 0 (Auto), Temp: 28C, Fan: 6 (Auto), " + "Swing(V): On, Swing(H): On, Beep: Toggle, " + "Clean: Toggle, Quiet: On, Powerful: Off, ""Econo: Off, Breeze: Off, " "Light: On, Ion: Off"; ac.begin(); @@ -1507,14 +1697,18 @@ TEST(TestIRac, Samsung) { 28, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kAuto, // Vertical swing + stdAc::swingh_t::kAuto, // Horizontal swing true, // Quiet false, // Turbo + false, // Econo true, // Light (Display) false, // Filter (Ion) - true, // Clean + true, // Clean (Toggle) true, // Beep + -1, // Sleep true, // Previous power state - false); // with dopower Off + -1, // Previous Sleep + false); // Force Extended ASSERT_EQ(expected, ac.toString()); ac._irsend.makeDecodeResult(); EXPECT_TRUE(capture.decode(&ac._irsend.capture)); @@ -1531,22 +1725,59 @@ TEST(TestIRac, Samsung) { 28, // Celsius stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kAuto, // Vertical swing + stdAc::swingh_t::kAuto, // Horizontal swing true, // Quiet false, // Turbo + false, // Econo true, // Light (Display) false, // Filter (Ion) - true, // Clean + true, // Clean (Toggle) true, // Beep + -1, // Sleep true, // Previous power state - true); // with dopower On + -1, // Previous Sleep + true); // Force Extended 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); - // We expect an extended state because of `dopower`. + // We expect an extended state because of `Force Extended`. ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); + + ac._irsend.reset(); + const char sleep[] = + "Power: On, Mode: 0 (Auto), Temp: 28C, Fan: 6 (Auto), " + "Swing(V): On, Swing(H): Off, Beep: -, Clean: Toggle, " + "Quiet: On, Powerful: Off, Econo: Off, Breeze: Off, " + "Light: On, Ion: Off, Sleep Timer: 08:00"; + irac.samsung(&ac, + true, // Power + stdAc::opmode_t::kAuto, // Mode + 28, // Celsius + stdAc::fanspeed_t::kMedium, // Fan speed + stdAc::swingv_t::kAuto, // Vertical swing + stdAc::swingh_t::kOff, // Horizontal swing + true, // Quiet + false, // Turbo + false, // Econo + true, // Light (Display) + false, // Filter (Ion) + true, // Clean (Toggle) + false, // Beep + 8 * 60, // Sleep + true, // Previous power state + -1, // Previous Sleep + false); // Force Extended + ASSERT_EQ(sleep, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + // We expect an extended state because of the change in `sleep`. + ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + ASSERT_EQ(sleep, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } TEST(TestIRac, Sanyo) { @@ -1760,7 +1991,7 @@ TEST(TestIRac, Toshiba) { IRrecv capture(kGpioUnused); char expected[] = "Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), " - "Turbo: Off, Econo: On"; + "Turbo: Off, Econo: On, Filter: Off"; ac.begin(); irac.toshiba(&ac, @@ -1770,7 +2001,8 @@ TEST(TestIRac, Toshiba) { stdAc::fanspeed_t::kLow, // Fan speed stdAc::swingv_t::kOff, // Vertical Swing false, // Turbo - true); // Econo + true, // Econo + false); // Filter ASSERT_EQ(expected, ac.toString()); ASSERT_EQ(kToshibaACStateLengthLong, ac.getStateLength()); ac._irsend.makeDecodeResult(); @@ -2343,7 +2575,7 @@ TEST(TestIRac, opmodeToString) { EXPECT_EQ("UNKNOWN", IRac::opmodeToString((stdAc::opmode_t)500)); // Home Assistant/Google Home differences. EXPECT_EQ("Fan", IRac::opmodeToString(stdAc::opmode_t::kFan, false)); - EXPECT_EQ("fan-only", IRac::opmodeToString(stdAc::opmode_t::kFan, true)); + EXPECT_EQ("fan_only", IRac::opmodeToString(stdAc::opmode_t::kFan, true)); EXPECT_EQ("Fan", IRac::opmodeToString(stdAc::opmode_t::kFan)); // Default } @@ -2675,7 +2907,7 @@ TEST(TestIRac, Issue1250) { // Now send the state so we can actually decode/capture what we sent. char expected_on[] = "Temp: 19C, Power: On, Mode: 4 (Fan), Fan: 0 (Auto), " - "Turbo: Off, Econo: Off"; + "Turbo: Off, Econo: Off, Filter: Off"; ac._irsend.reset(); irac.toshiba(&ac, irac.next.power, // Power @@ -2684,7 +2916,8 @@ TEST(TestIRac, Issue1250) { irac.next.fanspeed, // Fan speed irac.next.swingv, // Vertical Swing irac.next.turbo, // Turbo - irac.next.econo); // Econo + irac.next.econo, // Econo + irac.next.filter); // Filter ASSERT_EQ(expected_on, ac.toString()); ASSERT_EQ(kToshibaACStateLength, ac.getStateLength()); ac._irsend.makeDecodeResult(); @@ -2700,7 +2933,8 @@ TEST(TestIRac, Issue1250) { irac.sendAc(); // Now send the state so we can actually decode/capture what we sent. char expected_off[] = - "Temp: 19C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off"; + "Temp: 19C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off, " + "Filter: Off"; ac._irsend.reset(); irac.toshiba(&ac, irac.next.power, // Power @@ -2709,7 +2943,8 @@ TEST(TestIRac, Issue1250) { irac.next.fanspeed, // Fan speed irac.next.swingv, // Vertical Swing irac.next.turbo, // Turbo - irac.next.econo); // Econo + irac.next.econo, // Econo + irac.next.filter); // Filter ASSERT_EQ(expected_off, ac.toString()); ASSERT_EQ(kToshibaACStateLength, ac.getStateLength()); ac._irsend.makeDecodeResult(); diff --git a/lib/IRremoteESP8266/test/ir_Airton_test.cpp b/lib/IRremoteESP8266/test/ir_Airton_test.cpp new file mode 100644 index 000000000..2f2bb0bac --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Airton_test.cpp @@ -0,0 +1,328 @@ +// Copyright 2021 crankyoldgit + +#include "ir_Airton.h" +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for decodeAirton(). + +TEST(TestDecodeAirton, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint16_t rawData[115] = { + 6632, 3352, + 404, 1266, 404, 1264, 406, 430, 406, 430, 400, 1264, 406, 430, 402, 1264, + 408, 1262, 406, 1264, 404, 430, 402, 434, 402, 432, 402, 1264, 406, 430, + 404, 432, 400, 456, 376, 432, 402, 430, 402, 1264, 404, 1264, 404, 432, + 402, 434, 398, 434, 402, 430, 404, 1264, 404, 432, 402, 430, 404, 1264, + 406, 430, 402, 432, 400, 434, 402, 430, 402, 430, 404, 432, 402, 430, + 402, 432, 402, 432, 402, 430, 402, 432, 402, 430, 402, 434, 400, 432, + 402, 1264, 404, 430, 404, 1264, 404, 432, 402, 454, 378, 432, 402, 430, + 404, 1264, 404, 1264, 404, 1264, 378, 1292, 404, 432, 402, 1264, 404, 432, + 402}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 115, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRTON, irsend.capture.decode_type); + ASSERT_EQ(kAirtonBits, irsend.capture.bits); + EXPECT_EQ(0x5E1400090C11D3, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Fan: 0 (Auto), Temp: 25C, " + "Swing(V): Off, Econo: Off, Turbo: Off, Light: Off, " + "Health: Off, Sleep: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestDecodeAirton, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + irsend.sendAirton(0x5E1400090C11D3); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRTON, irsend.capture.decode_type); + ASSERT_EQ(kAirtonBits, irsend.capture.bits); + EXPECT_EQ(0x5E1400090C11D3, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("AIRTON", typeToString(decode_type_t::AIRTON)); + ASSERT_EQ(decode_type_t::AIRTON, strToDecodeType("AIRTON")); + ASSERT_FALSE(hasACState(decode_type_t::AIRTON)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::AIRTON)); + ASSERT_EQ(kAirtonBits, IRsend::defaultBits(decode_type_t::AIRTON)); + ASSERT_EQ(kAirtonDefaultRepeat, IRsend::minRepeats(decode_type_t::AIRTON)); +} + +// Tests for IRAirtonAc class. + +TEST(TestIRAirtonAcClass, Power) { + IRAirtonAc ac(kGpioUnused); + 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(TestIRAirtonAcClass, Checksums) { + ASSERT_TRUE(IRAirtonAc::validChecksum(0x5E1400090C11D3)); + ASSERT_EQ(0x5E, IRAirtonAc::calcChecksum(0x5E1400090C11D3)); + ASSERT_FALSE(IRAirtonAc::validChecksum(0x551400090C11D3)); + ASSERT_TRUE(IRAirtonAc::validChecksum(0x2F8801060911D3)); + ASSERT_TRUE(IRAirtonAc::validChecksum(0xDB8800021A11D3)); +} + +TEST(TestIRAirtonAcClass, Temperature) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + ac.setMode(kAirtonCool); // Cool mode allows the entire temp range. + ac.setTemp(0); + EXPECT_EQ(kAirtonMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kAirtonMaxTemp, ac.getTemp()); + + ac.setTemp(kAirtonMinTemp); + EXPECT_EQ(kAirtonMinTemp, ac.getTemp()); + + ac.setTemp(kAirtonMaxTemp); + EXPECT_EQ(kAirtonMaxTemp, ac.getTemp()); + + ac.setTemp(kAirtonMinTemp - 1); + EXPECT_EQ(kAirtonMinTemp, ac.getTemp()); + + ac.setTemp(kAirtonMaxTemp + 1); + EXPECT_EQ(kAirtonMaxTemp, ac.getTemp()); + + ac.setTemp(17); + EXPECT_EQ(17, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(20); + EXPECT_EQ(20, ac.getTemp()); +} + +TEST(TestIRAirtonAcClass, OperatingMode) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + + ac.setMode(kAirtonCool); + EXPECT_EQ(kAirtonCool, ac.getMode()); + ac.setMode(kAirtonDry); + EXPECT_EQ(kAirtonDry, ac.getMode()); + ac.setMode(kAirtonFan); + EXPECT_EQ(kAirtonFan, ac.getMode()); + EXPECT_NE(kAirtonMaxTemp, ac.getTemp()); + ac.setMode(kAirtonAuto); + EXPECT_EQ(kAirtonAuto, ac.getMode()); + EXPECT_EQ(kAirtonMaxTemp, ac.getTemp()); + ac.setMode(kAirtonHeat); + EXPECT_EQ(kAirtonHeat, ac.getMode()); + + ac.setMode(kAirtonHeat + 1); + EXPECT_EQ(kAirtonAuto, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kAirtonAuto, ac.getMode()); +} + +TEST(TestIRAirtonAcClass, FanSpeed) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + ac.setMode(kAirtonCool); // All fan speeds available in this mode. + + ac.setFan(0); + EXPECT_EQ(kAirtonFanAuto, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kAirtonFanAuto, ac.getFan()); + + ac.setFan(kAirtonFanHigh); + EXPECT_EQ(kAirtonFanHigh, ac.getFan()); + + ac.setFan(kAirtonFanLow); + EXPECT_EQ(kAirtonFanLow, ac.getFan()); + + ac.setFan(kAirtonFanMax); + EXPECT_EQ(kAirtonFanMax, ac.getFan()); + + ac.setFan(kAirtonFanMin); + EXPECT_EQ(kAirtonFanMin, ac.getFan()); + + ac.setFan(kAirtonFanMed); + EXPECT_EQ(kAirtonFanMed, ac.getFan()); + + ac.setFan(kAirtonFanMax + 1); + EXPECT_EQ(kAirtonFanAuto, ac.getFan()); +} + +TEST(TestIRAirtonAcClass, SwingV) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + + ac.setSwingV(false); + EXPECT_FALSE(ac.getSwingV()); + ac.setSwingV(true); + EXPECT_TRUE(ac.getSwingV()); + ac.setSwingV(false); + EXPECT_FALSE(ac.getSwingV()); + + // Known swingv on state + ac.setRaw(0xBC0401050111D3); + EXPECT_TRUE(ac.getSwingV()); +} + +TEST(TestIRAirtonAcClass, Light) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + + // Known light on state + ac.setRaw(0x298801040911D3); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestIRAirtonAcClass, ConstructKnownExamples) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + ac.stateReset(); + ac.on(); + ac.setMode(kAirtonHeat); + ac.setFan(kAirtonFanAuto); + ac.setTemp(25); + ac.setSwingV(false); + ac.setLight(false); + ac.setTurbo(false); + ac.setSleep(false); + ac.setEcono(false); + ac.setHealth(false); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Fan: 0 (Auto), Temp: 25C, " + "Swing(V): Off, Econo: Off, Turbo: Off, Light: Off, " + "Health: Off, Sleep: Off", + ac.toString()); + EXPECT_EQ(0x5E1400090C11D3, ac.getRaw()); +} + +TEST(TestIRAirtonAcClass, Turbo) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + EXPECT_NE(kAirtonFanMax, ac.getFan()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + EXPECT_EQ(kAirtonFanMax, ac.getFan()); + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + + // Known Turbo on state + ac.setRaw(0x92040000D911D3); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestIRAirtonAcClass, Sleep) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + + ac.setMode(kAirtonCool); // Sleep is available in Cool mode. + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + + ac.setSleep(true); + // Sleep is available in Heat mode, but changing modes resets it. + ac.setMode(kAirtonHeat); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + + ac.setMode(kAirtonAuto); // Sleep is NOT available in Auto mode. + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_FALSE(ac.getSleep()); + + ac.setMode(kAirtonFan); // Sleep is NOT available in Fan mode. + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_FALSE(ac.getSleep()); + + // Known Sleep on state + ac.setRaw(0xA00600000911D3); + EXPECT_TRUE(ac.getSleep()); + EXPECT_NE(kAirtonAuto, ac.getMode()); + EXPECT_NE(kAirtonFan, ac.getMode()); +} + +TEST(TestIRAirtonAcClass, Health) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + + ac.setHealth(false); + EXPECT_FALSE(ac.getHealth()); + ac.setHealth(true); + EXPECT_TRUE(ac.getHealth()); + ac.setHealth(false); + EXPECT_FALSE(ac.getHealth()); + + // Known Health on state + ac.setRaw(0xE5C900000911D3); + EXPECT_TRUE(ac.getHealth()); +} + +TEST(TestIRAirtonAcClass, Econo) { + IRAirtonAc ac(kGpioUnused); + ac.begin(); + ac.setMode(kAirtonCool); // Econo is only available in Cool. + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + ac.setMode(kAirtonHeat); // Econo is only available in Cool, not Heat! + EXPECT_FALSE(ac.getEcono()); + ac.setEcono(true); + EXPECT_FALSE(ac.getEcono()); + + // Known Econo on state + ac.setRaw(0xE5C900000911D3); + EXPECT_TRUE(ac.getEcono()); +} diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index 46c554882..62e0d6b0e 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -6,6 +6,25 @@ #include "IRsend_test.h" #include "gtest/gtest.h" + +TEST(TestUtils, Housekeeping) { + // COOLIX + ASSERT_EQ("COOLIX", typeToString(decode_type_t::COOLIX)); + ASSERT_EQ(decode_type_t::COOLIX, strToDecodeType("COOLIX")); + ASSERT_FALSE(hasACState(decode_type_t::COOLIX)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::COOLIX)); + ASSERT_EQ(kCoolixBits, IRsend::defaultBits(decode_type_t::COOLIX)); + ASSERT_EQ(kSingleRepeat, IRsend::minRepeats(decode_type_t::COOLIX)); + + // COOLIX48 + ASSERT_EQ("COOLIX48", typeToString(decode_type_t::COOLIX48)); + ASSERT_EQ(decode_type_t::COOLIX48, strToDecodeType("COOLIX48")); + ASSERT_FALSE(hasACState(decode_type_t::COOLIX48)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::COOLIX48)); + ASSERT_EQ(kCoolix48Bits, IRsend::defaultBits(decode_type_t::COOLIX48)); + ASSERT_EQ(kSingleRepeat, IRsend::minRepeats(decode_type_t::COOLIX48)); +} + // Tests for sendCOOLIX(). // Test sending typical data only. @@ -941,3 +960,76 @@ TEST(TestCoolixACClass, VerifyZoneFollowFan) { "Zone Follow: On, Sensor Temp: 19C", ac.toString()); } + +TEST(TestDecodeCoolix48, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1694#issue-1068786691 + // Off Timer: 1 hour + const uint16_t rawData[199] = { + 4342, 4454, 486, 1724, 436, 658, 438, 1748, 464, 1718, 462, 634, 440, 656, + 462, 1696, 488, 634, 462, 634, 436, 1722, 516, 608, 462, 660, 436, 1694, + 488, 1720, 440, 630, 488, 1700, 488, 1704, 458, 660, 462, 1698, 490, 632, + 462, 634, 436, 684, 436, 1700, 464, 1748, 462, 634, 462, 1720, 436, 658, + 462, 1700, 488, 1692, 512, 1696, 438, 684, 410, 686, 434, 688, 408, 1696, + 488, 1694, 464, 682, 414, 1748, 436, 1722, 488, 632, 438, 686, 408, 662, + 462, 1696, 488, 1722, 462, 1696, 462, 1746, 436, 1798, 386, 1694, 490, + 1720, 516, 5234, 4370, 4446, 490, 1690, 492, 658, 434, 1726, 436, 1746, + 464, 604, 488, 658, 412, 1718, 490, 636, 460, 660, 438, 1698, 460, 662, + 458, 632, 436, 1718, 490, 1720, 488, 608, 436, 1754, 462, 1726, 438, 682, + 414, 1748, 464, 632, 460, 660, 410, 658, 438, 1748, 464, 1694, 464, 660, + 436, 1720, 488, 634, 460, 1726, 462, 1724, 462, 1692, 490, 606, 462, 714, + 384, 660, 460, 1722, 460, 1722, 490, 606, 464, 1718, 490, 1670, 486, 634, + 462, 662, 410, 660, 460, 1722, 464, 1718, 460, 1696, 464, 1720, 462, 1720, + 462, 1722, 486, 1700, 462}; // UNKNOWN 1F691B97 + + irsend.begin(); + irsend.reset(); + + irsend.sendRaw(rawData, 199, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX48, irsend.capture.decode_type); + EXPECT_EQ(kCoolix48Bits, irsend.capture.bits); + EXPECT_EQ(0xB24DA35C6C7F, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestDecodeCoolix48, SyntheticSelfDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + irsend.sendCoolix48(0xB24DA35C6C7F); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX48, irsend.capture.decode_type); + EXPECT_EQ(kCoolix48Bits, irsend.capture.bits); + EXPECT_EQ(0xB24DA35C6C7F, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + EXPECT_EQ( + "f38000d33" + "m4692s4416" // Message. + "m552s1656m552s552m552s1656m552s1656m552s552m552s552m552s1656m552s552" + "m552s552m552s1656m552s552m552s552m552s1656m552s1656m552s552m552s1656" + "m552s1656m552s552m552s1656m552s552m552s552m552s552m552s1656m552s1656" + "m552s552m552s1656m552s552m552s1656m552s1656m552s1656m552s552m552s552" + "m552s552m552s1656m552s1656m552s552m552s1656m552s1656m552s552m552s552" + "m552s552m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656" + "m552s5244" + "m4692s4416" // Repeat + "m552s1656m552s552m552s1656m552s1656m552s552m552s552m552s1656m552s552" + "m552s552m552s1656m552s552m552s552m552s1656m552s1656m552s552m552s1656" + "m552s1656m552s552m552s1656m552s552m552s552m552s552m552s1656m552s1656" + "m552s552m552s1656m552s552m552s1656m552s1656m552s1656m552s552m552s552" + "m552s552m552s1656m552s1656m552s552m552s1656m552s1656m552s552m552s552" + "m552s552m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656m552s1656" + "m552s5244", + irsend.outputStr()); +} diff --git a/lib/IRremoteESP8266/test/ir_Electra_test.cpp b/lib/IRremoteESP8266/test/ir_Electra_test.cpp index 1fddf537b..9c1dedd09 100644 --- a/lib/IRremoteESP8266/test/ir_Electra_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Electra_test.cpp @@ -101,7 +101,8 @@ TEST(TestDecodeElectraAC, RealExampleDecode) { 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, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -235,7 +236,8 @@ TEST(TestIRElectraAcClass, HumanReadable) { 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, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", ac.toString()); uint8_t on_cool_16C_auto_voff[13] = { 0xC3, 0x47, 0xE0, 0x00, 0xA0, 0x00, 0x20, @@ -243,7 +245,8 @@ TEST(TestIRElectraAcClass, HumanReadable) { 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, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", ac.toString()); uint8_t on_cool_16C_low_voff[13] = { 0xC3, 0x47, 0xE0, 0x00, 0x60, 0x00, 0x20, @@ -251,7 +254,8 @@ TEST(TestIRElectraAcClass, HumanReadable) { 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, Light: -, Clean: Off, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: Off", ac.toString()); } @@ -275,7 +279,8 @@ TEST(TestIRElectraAcClass, Clean) { ac.setRaw(on); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, " + "IFeel: Off", ac.toString()); } @@ -301,7 +306,8 @@ TEST(TestIRElectraAcClass, Turbo) { ac.setRaw(on); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: On", + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: On, " + "IFeel: Off", ac.toString()); } @@ -325,7 +331,8 @@ TEST(TestIRElectraAcClass, LightToggle) { ac.setRaw(on); EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, " + "IFeel: Off", ac.toString()); } @@ -352,8 +359,76 @@ TEST(TestIRElectraAcClass, ConstructKnownState) { EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), " - "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off", + "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, " + "IFeel: Off", ac.toString()); EXPECT_STATE_EQ(on_cool_24C_fan1_swing_off_turbo_off_clean_on, ac.getRaw(), kElectraAcBits); } + +TEST(TestIRElectraAcClass, IFeelAndSensor) { + IRElectraAc ac(kGpioUnused); + ac.stateReset(); + // Test a real example. + const uint8_t ifeel_on[kElectraAcStateLength] = { + 0xC3, 0x6F, 0xE0, 0x00, 0xA0, 0x00, 0x28, + 0x64, 0x00, 0x20, 0x00, 0x1E, 0x7C}; + ac.setRaw(ifeel_on); + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(26, ac.getSensorTemp()); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 5 (Auto), " + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: On, Sensor Temp: 26C", + ac.toString()); + + ac.stateReset(); + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp()); + + ac.setIFeel(true); + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp()); + + ac.setSensorTemp(kElectraAcSensorMaxTemp); + EXPECT_EQ(kElectraAcSensorMaxTemp, ac.getSensorTemp()); + + ac.setSensorTemp(kElectraAcSensorMaxTemp + 1); + EXPECT_EQ(kElectraAcSensorMaxTemp, ac.getSensorTemp()); + + ac.setIFeel(false); + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp()); + EXPECT_EQ(0, ac._.SensorTemp); + + ac.setIFeel(true); + ac.setSensorTemp(kElectraAcSensorMinTemp); + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp()); + + ac.setSensorTemp(26); // Celsius + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(26, ac.getSensorTemp()); + + EXPECT_FALSE(ac.getSensorUpdate()); + ac.setSensorUpdate(true); + EXPECT_TRUE(ac.getSensorUpdate()); + EXPECT_EQ("Sensor Temp: 26C", ac.toString()); + ac.setSensorUpdate(false); + EXPECT_FALSE(ac.getSensorUpdate()); + + const uint8_t sensor_update_28C[kElectraAcStateLength] = { + 0xC3, 0x9F, 0xE0, 0x40, 0xA0, 0x00, 0x88, + 0x66, 0x00, 0x30, 0x00, 0x1E, 0x5E}; + ac.setRaw(sensor_update_28C); + EXPECT_TRUE(ac.getSensorUpdate()); + EXPECT_EQ(28, ac.getSensorTemp()); + EXPECT_EQ("Sensor Temp: 28C", ac.toString()); + ac.setSensorUpdate(false); + EXPECT_FALSE(ac.getSensorUpdate()); + EXPECT_EQ( + "Power: On, Mode: 4 (Heat), Temp: 27C, Fan: 5 (Auto), " + "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, " + "IFeel: On, Sensor Temp: 28C", + ac.toString()); +} diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index 597fd3c09..cb1832f61 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -303,9 +303,10 @@ TEST(TestGreeClass, Temperature) { EXPECT_EQ(63, ac.getTemp()); EXPECT_EQ( "Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 63F, Fan: 0 (Auto), " - "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " - "Swing(V) Mode: Manual, Swing(V): 0 (Last), Timer: Off, " - "Display Temp: 0 (Off)", ac.toString()); + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " + "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), " + "Timer: Off, Display Temp: 0 (Off)", ac.toString()); } TEST(TestGreeClass, OperatingMode) { @@ -385,6 +386,20 @@ TEST(TestGreeClass, Turbo) { EXPECT_TRUE(ac.getTurbo()); } +TEST(TestGreeClass, Econo) { + IRGreeAC ac(kGpioUnused); + ac.begin(); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); +} + TEST(TestGreeClass, IFeel) { IRGreeAC ac(kGpioUnused); ac.begin(); @@ -506,6 +521,24 @@ TEST(TestGreeClass, VerticalSwing) { EXPECT_EQ(kGreeSwingAuto, ac.getSwingVerticalPosition()); } +TEST(TestGreeClass, HorizontalSwing) { + IRGreeAC ac(kGpioUnused); + ac.begin(); + + ac.setSwingHorizontal(kGreeSwingHAuto); + EXPECT_EQ(kGreeSwingHAuto, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kGreeSwingHMiddle); + EXPECT_EQ(kGreeSwingHMiddle, ac.getSwingHorizontal()); + + ac.setSwingHorizontal(kGreeSwingHMaxRight); + EXPECT_EQ(kGreeSwingHMaxRight, ac.getSwingHorizontal()); + + // Out of bounds. + ac.setSwingHorizontal(kGreeSwingHMaxRight + 1); + EXPECT_EQ(kGreeSwingHOff, ac.getSwingHorizontal()); +} + TEST(TestGreeClass, SetAndGetRaw) { IRGreeAC ac(kGpioUnused); uint8_t initialState[kGreeStateLength] = {0x00, 0x09, 0x20, 0x50, @@ -543,8 +576,9 @@ TEST(TestGreeClass, HumanReadable) { EXPECT_EQ( "Model: 1 (YAW1F), Power: Off, Mode: 0 (Auto), Temp: 25C, Fan: 0 (Auto), " - "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " - "Swing(V) Mode: Manual, Swing(V): 0 (Last), " + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " + "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), " "Timer: Off, Display Temp: 0 (Off)", ac.toString()); ac.on(); @@ -562,9 +596,10 @@ TEST(TestGreeClass, HumanReadable) { ac.setDisplayTempSource(3); EXPECT_EQ( "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 3 (High), " - "Turbo: On, IFeel: On, WiFi: On, XFan: On, Light: Off, Sleep: On, " - "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Timer: 12:30, " - "Display Temp: 3 (Outside)", + "Turbo: On, Econo: Off, IFeel: On, WiFi: On, XFan: On, Light: Off, " + "Sleep: On, " + "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), " + "Timer: 12:30, Display Temp: 3 (Outside)", ac.toString()); } @@ -623,9 +658,10 @@ TEST(TestDecodeGree, NormalRealExample) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 26C, Fan: 1 (Low), " - "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " - "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Timer: Off, " - "Display Temp: 3 (Outside)", + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " + "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Swing(H): 0 (Off), " + "Timer: Off, Display Temp: 3 (Outside)", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -681,8 +717,9 @@ TEST(TestGreeClass, Issue814Power) { EXPECT_EQ(gree_ac_remote_model_t::YBOFB, ac.getModel()); EXPECT_EQ( "Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 1 (Low), " - "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, " - "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Timer: Off, " + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, " + "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), Timer: Off, " "Display Temp: 0 (Off)", ac.toString()); ac.off(); diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index 0c63b089f..b60a08f23 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -240,25 +240,25 @@ TEST(TestHaierACClass, FanSpeed) { EXPECT_EQ(kHaierAcCmdOn, ac.getCommand()); } -TEST(TestHaierACClass, Swing) { +TEST(TestHaierACClass, SwingV) { IRHaierAC ac(kGpioUnused); ac.begin(); ac.setFan(kHaierAcFanLow); ac.setCommand(kHaierAcCmdOn); - ac.setSwing(kHaierAcSwingOff); - EXPECT_EQ(kHaierAcSwingOff, ac.getSwing()); + ac.setSwingV(kHaierAcSwingVOff); + EXPECT_EQ(kHaierAcSwingVOff, ac.getSwingV()); - ac.setSwing(kHaierAcSwingUp); - EXPECT_EQ(kHaierAcSwingUp, ac.getSwing()); + ac.setSwingV(kHaierAcSwingVUp); + EXPECT_EQ(kHaierAcSwingVUp, ac.getSwingV()); EXPECT_EQ(kHaierAcCmdSwing, ac.getCommand()); - ac.setSwing(kHaierAcSwingDown); - EXPECT_EQ(kHaierAcSwingDown, ac.getSwing()); + ac.setSwingV(kHaierAcSwingVDown); + EXPECT_EQ(kHaierAcSwingVDown, ac.getSwingV()); EXPECT_EQ(kHaierAcCmdSwing, ac.getCommand()); - ac.setSwing(kHaierAcSwingChg); - EXPECT_EQ(kHaierAcSwingChg, ac.getSwing()); + ac.setSwingV(kHaierAcSwingVChg); + EXPECT_EQ(kHaierAcSwingVChg, ac.getSwingV()); EXPECT_EQ(kHaierAcCmdSwing, ac.getCommand()); } @@ -351,7 +351,7 @@ TEST(TestHaierACClass, MessageConstuction) { EXPECT_EQ( "Command: 1 (On), Mode: 0 (Auto), Temp: 25C, Fan: 1 (Low), " - "Swing: 0 (Off), Sleep: Off, Health: Off, " + "Swing(V): 0 (Off), Sleep: Off, Health: Off, " "Clock: 00:00, On Timer: Off, Off Timer: Off", ac.toString()); ac.setMode(kHaierAcCool); @@ -359,16 +359,16 @@ TEST(TestHaierACClass, MessageConstuction) { ac.setFan(kHaierAcFanHigh); EXPECT_EQ( "Command: 3 (Fan), Mode: 1 (Cool), Temp: 21C, Fan: 3 (High), " - "Swing: 0 (Off), Sleep: Off, Health: Off, " + "Swing(V): 0 (Off), Sleep: Off, Health: Off, " "Clock: 00:00, On Timer: Off, Off Timer: Off", ac.toString()); - ac.setSwing(kHaierAcSwingChg); + ac.setSwingV(kHaierAcSwingVChg); ac.setHealth(true); ac.setSleep(true); ac.setCurrTime(615); // 10:15am EXPECT_EQ( "Command: 8 (Sleep), Mode: 1 (Cool), Temp: 21C, Fan: 3 (High), " - "Swing: 3 (Change), Sleep: On, Health: On, " + "Swing(V): 3 (Change), Sleep: On, Health: On, " "Clock: 10:15, On Timer: Off, Off Timer: Off", ac.toString()); ac.setOnTimer(800); // 1:20pm @@ -377,7 +377,7 @@ TEST(TestHaierACClass, MessageConstuction) { EXPECT_EQ( "Command: 1 (On), Mode: 1 (Cool), Temp: 21C, Fan: 3 (High), " - "Swing: 3 (Change), Sleep: On, Health: On, " + "Swing(V): 3 (Change), Sleep: On, Health: On, " "Clock: 10:15, On Timer: 13:20, Off Timer: 18:45", ac.toString()); @@ -385,14 +385,14 @@ TEST(TestHaierACClass, MessageConstuction) { ac.setMode(kHaierAcHeat); EXPECT_EQ( "Command: 2 (Mode), Mode: 3 (Heat), Temp: 21C, Fan: 3 (High), " - "Swing: 3 (Change), Sleep: On, Health: On, " + "Swing(V): 3 (Change), Sleep: On, Health: On, " "Clock: 10:15, On Timer: 13:20, Off Timer: 18:45", ac.toString()); ac.setTemp(25); EXPECT_EQ( "Command: 6 (Temp Up), Mode: 3 (Heat), Temp: 25C, Fan: 3 (High), " - "Swing: 3 (Change), Sleep: On, Health: On, " + "Swing(V): 3 (Change), Sleep: On, Health: On, " "Clock: 10:15, On Timer: 13:20, Off Timer: 18:45", ac.toString()); @@ -410,7 +410,7 @@ TEST(TestHaierACClass, MessageConstuction) { ac.setRaw(randomState); EXPECT_EQ( "Command: 9 (Timer Set), Mode: 3 (Heat), Temp: 20C, Fan: 3 (High), " - "Swing: 1 (Up), Sleep: On, Health: Off, " + "Swing(V): 1 (Up), Sleep: On, Health: Off, " "Clock: 16:32, On Timer: Off, Off Timer: Off", ac.toString()); // getRaw() should correct the checksum. @@ -476,24 +476,24 @@ TEST(TestHaierACYRW02Class, Temperature) { IRHaierACYRW02 ac(kGpioUnused); ac.begin(); - ac.setTemp(kHaierAcMinTemp); - EXPECT_EQ(kHaierAcMinTemp, ac.getTemp()); + ac.setTemp(kHaierAcYrw02MinTempC); + EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp()); ac.setButton(kHaierAcYrw02ButtonPower); - ac.setTemp(kHaierAcMinTemp + 1); - EXPECT_EQ(kHaierAcMinTemp + 1, ac.getTemp()); + ac.setTemp(kHaierAcYrw02MinTempC + 1); + EXPECT_EQ(kHaierAcYrw02MinTempC + 1, ac.getTemp()); EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); - ac.setTemp(kHaierAcMaxTemp); - EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp()); + ac.setTemp(kHaierAcYrw02MaxTempC); + EXPECT_EQ(kHaierAcYrw02MaxTempC, ac.getTemp()); EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); - ac.setTemp(kHaierAcMinTemp - 1); - EXPECT_EQ(kHaierAcMinTemp, ac.getTemp()); + ac.setTemp(kHaierAcYrw02MinTempC - 1); + EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp()); EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton()); - ac.setTemp(kHaierAcMaxTemp + 1); - EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp()); + ac.setTemp(kHaierAcYrw02MaxTempC + 1); + EXPECT_EQ(kHaierAcYrw02MaxTempC, ac.getTemp()); EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); ac.setTemp(23); @@ -504,8 +504,47 @@ TEST(TestHaierACYRW02Class, Temperature) { EXPECT_EQ(23, ac.getTemp()); EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton()); + ac.setTemp(kHaierAcYrw02MinTempF, true); + EXPECT_EQ(kHaierAcYrw02MinTempF, ac.getTemp()); + + ac.setButton(kHaierAcYrw02ButtonPower); + ac.setTemp(kHaierAcYrw02MinTempF + 1, true); + EXPECT_EQ(kHaierAcYrw02MinTempF + 1, ac.getTemp()); + EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); + + ac.setTemp(kHaierAcYrw02MaxTempF, true); + EXPECT_EQ(kHaierAcYrw02MaxTempF, ac.getTemp()); + EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); + + ac.setTemp(kHaierAcYrw02MinTempF - 1, true); + EXPECT_EQ(kHaierAcYrw02MinTempF, ac.getTemp()); + EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton()); + + ac.setTemp(kHaierAcYrw02MaxTempF + 1, true); + EXPECT_EQ(kHaierAcYrw02MaxTempF, ac.getTemp()); + EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); + + ac.setTemp(66, true); + EXPECT_EQ(66, ac.getTemp()); + EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton()); + ac.setButton(kHaierAcYrw02ButtonPower); + ac.setTemp(66, true); + EXPECT_EQ(66, ac.getTemp()); + EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton()); + + // Test specific cases for converting to Fahrenheit + ac.setTemp(76, true); + EXPECT_EQ(76, ac.getTemp()); + ac.setTemp(77, true); + EXPECT_EQ(77, ac.getTemp()); + ac.setTemp(78, true); + EXPECT_EQ(78, ac.getTemp()); + + ac.setTemp(24); + EXPECT_EQ(kHaierAcYrw02ButtonCFAB, ac.getButton()); + ac.setTemp(0); - EXPECT_EQ(kHaierAcMinTemp, ac.getTemp()); + EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp()); EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton()); ac.setTemp(255); @@ -641,77 +680,135 @@ TEST(TestHaierACYRW02Class, Fan) { EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); } -TEST(TestHaierACYRW02Class, Swing) { +TEST(TestHaierACYRW02Class, SwingV) { IRHaierACYRW02 ac(kGpioUnused); ac.begin(); - ac.setSwing(kHaierAcYrw02SwingOff); - EXPECT_EQ(kHaierAcYrw02SwingOff, ac.getSwing()); - EXPECT_EQ(kHaierAcYrw02ButtonSwing, ac.getButton()); + ac.setSwingV(kHaierAcYrw02SwingVOff); + EXPECT_EQ(kHaierAcYrw02SwingVOff, ac.getSwingV()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton()); ac.setButton(kHaierAcYrw02ButtonTempUp); - ac.setSwing(kHaierAcYrw02SwingAuto); - EXPECT_EQ(kHaierAcYrw02SwingAuto, ac.getSwing()); - EXPECT_EQ(kHaierAcYrw02ButtonSwing, ac.getButton()); + ac.setSwingV(kHaierAcYrw02SwingVAuto); + EXPECT_EQ(kHaierAcYrw02SwingVAuto, ac.getSwingV()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton()); - ac.setSwing(kHaierAcYrw02SwingTop); - EXPECT_EQ(kHaierAcYrw02SwingTop, ac.getSwing()); - EXPECT_EQ(kHaierAcYrw02ButtonSwing, ac.getButton()); + ac.setSwingV(kHaierAcYrw02SwingVTop); + EXPECT_EQ(kHaierAcYrw02SwingVTop, ac.getSwingV()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton()); - ac.setSwing(kHaierAcYrw02SwingDown); - EXPECT_EQ(kHaierAcYrw02SwingDown, ac.getSwing()); - EXPECT_EQ(kHaierAcYrw02ButtonSwing, ac.getButton()); + ac.setSwingV(kHaierAcYrw02SwingVDown); + EXPECT_EQ(kHaierAcYrw02SwingVDown, ac.getSwingV()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton()); // Test unexpected values. ac.setButton(kHaierAcYrw02ButtonTempUp); - ac.setSwing(0xFF); - EXPECT_EQ(kHaierAcYrw02SwingDown, ac.getSwing()); + ac.setSwingV(0xFF); + EXPECT_EQ(kHaierAcYrw02SwingVDown, ac.getSwingV()); EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); // Test the mode-dependant positions. ac.setMode(kHaierAcYrw02Auto); - ac.setSwing(kHaierAcYrw02SwingMiddle); - EXPECT_EQ(kHaierAcYrw02SwingMiddle, ac.getSwing()); - EXPECT_EQ(kHaierAcYrw02ButtonSwing, ac.getButton()); + ac.setSwingV(kHaierAcYrw02SwingVMiddle); + EXPECT_EQ(kHaierAcYrw02SwingVMiddle, ac.getSwingV()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton()); ac.setMode(kHaierAcYrw02Heat); - ac.setSwing(kHaierAcYrw02SwingMiddle); - EXPECT_EQ(kHaierAcYrw02SwingBottom, ac.getSwing()); - ac.setSwing(kHaierAcYrw02SwingAuto); - EXPECT_EQ(kHaierAcYrw02SwingAuto, ac.getSwing()); - ac.setSwing(kHaierAcYrw02SwingBottom); - EXPECT_EQ(kHaierAcYrw02SwingBottom, ac.getSwing()); + ac.setSwingV(kHaierAcYrw02SwingVMiddle); + EXPECT_EQ(kHaierAcYrw02SwingVBottom, ac.getSwingV()); + ac.setSwingV(kHaierAcYrw02SwingVAuto); + EXPECT_EQ(kHaierAcYrw02SwingVAuto, ac.getSwingV()); + ac.setSwingV(kHaierAcYrw02SwingVBottom); + EXPECT_EQ(kHaierAcYrw02SwingVBottom, ac.getSwingV()); ac.setMode(kHaierAcYrw02Cool); - ac.setSwing(kHaierAcYrw02SwingBottom); - EXPECT_EQ(kHaierAcYrw02SwingMiddle, ac.getSwing()); + ac.setSwingV(kHaierAcYrw02SwingVBottom); + EXPECT_EQ(kHaierAcYrw02SwingVMiddle, ac.getSwingV()); +} + +TEST(TestHaierACYRW02Class, SwingH) { + IRHaierACYRW02 ac(kGpioUnused); + ac.begin(); + + ac.setSwingH(kHaierAcYrw02SwingVOff); + EXPECT_EQ(kHaierAcYrw02SwingHMiddle, ac.getSwingH()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton()); + + ac.setButton(kHaierAcYrw02ButtonTempUp); + + ac.setSwingH(kHaierAcYrw02SwingHLeftMax); + EXPECT_EQ(kHaierAcYrw02SwingHLeftMax, ac.getSwingH()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton()); + + ac.setSwingH(kHaierAcYrw02SwingHLeft); + EXPECT_EQ(kHaierAcYrw02SwingHLeft, ac.getSwingH()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton()); + + ac.setSwingH(kHaierAcYrw02SwingHRight); + EXPECT_EQ(kHaierAcYrw02SwingHRight, ac.getSwingH()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton()); + + ac.setSwingH(kHaierAcYrw02SwingHRightMax); + EXPECT_EQ(kHaierAcYrw02SwingHRightMax, ac.getSwingH()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton()); + + ac.setSwingH(kHaierAcYrw02SwingHAuto); + EXPECT_EQ(kHaierAcYrw02SwingHAuto, ac.getSwingH()); + EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton()); + + // Test unexpected values. + ac.setButton(kHaierAcYrw02ButtonTempUp); + ac.setSwingH(0xFF); + EXPECT_EQ(kHaierAcYrw02SwingHAuto, ac.getSwingH()); + EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton()); +} + +TEST(TestHaierACYRW02Class, Lock) { + IRHaierACYRW02 ac(kGpioUnused); + ac.begin(); + + ac.setLock(true); + EXPECT_TRUE(ac.getLock()); + EXPECT_EQ(kHaierAcYrw02ButtonLock, ac.getButton()); + + ac.setButton(kHaierAcYrw02ButtonTempUp); + ac.setLock(false); + EXPECT_FALSE(ac.getLock()); + EXPECT_EQ(kHaierAcYrw02ButtonLock, ac.getButton()); + + ac.setLock(true); + EXPECT_TRUE(ac.getLock()); + EXPECT_EQ(kHaierAcYrw02ButtonLock, ac.getButton()); } TEST(TestHaierACYRW02Class, MessageConstuction) { IRHaierACYRW02 ac(kGpioUnused); EXPECT_EQ( - "Power: On, Button: 5 (Power), Mode: 0 (Auto), Temp: 25C," - " Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 0 (Off), Sleep: Off," - " Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), " + "Mode: 0 (Auto), Temp: 25C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); ac.setMode(kHaierAcYrw02Cool); ac.setTemp(21); ac.setFan(kHaierAcYrw02FanHigh); EXPECT_EQ( - "Power: On, Button: 4 (Fan), Mode: 1 (Cool), Temp: 21C," - " Fan: 1 (High), Turbo: Off, Quiet: Off, Swing: 0 (Off), Sleep: Off," - " Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 4 (Fan), " + "Mode: 1 (Cool), Temp: 21C, Fan: 1 (High), Turbo: Off, Quiet: Off, " + "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); - ac.setSwing(kHaierAcYrw02SwingMiddle); + ac.setTemp(75, true); + ac.setSwingV(kHaierAcYrw02SwingVMiddle); ac.setHealth(false); ac.setSleep(true); ac.setTurbo(true); EXPECT_EQ( - "Power: On, Button: 8 (Turbo), Mode: 1 (Cool), Temp: 21C, " - "Fan: 1 (High), Turbo: On, Quiet: Off, Swing: 2 (Middle), " - "Sleep: On, Health: Off, " - "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 8 (Turbo), " + "Mode: 1 (Cool), Temp: 75F, Fan: 1 (High), Turbo: On, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: On, Health: Off, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); } @@ -724,9 +821,10 @@ TEST(TestHaierACYRW02Class, RealStates) { IRHaierACYRW02 ac(kGpioUnused); ac.setRaw(expectedState1); EXPECT_EQ( - "Power: On, Button: 7 (Health), Mode: 4 (Heat), Temp: 30C, " - "Fan: 1 (High), Turbo: Off, Quiet: Off, Swing: 1 (Highest), Sleep: Off, " - "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 7 (Health), " + "Mode: 4 (Heat), Temp: 30C, Fan: 1 (High), Turbo: Off, Quiet: Off, " + "Swing(V): 1 (Highest), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); uint8_t expectedState2[kHaierACYRW02StateLength] = { @@ -734,9 +832,10 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75}; ac.setRaw(expectedState2); EXPECT_EQ( - "Power: Off, Button: 5 (Power), Mode: 4 (Heat), Temp: 30C, " - "Fan: 1 (High), Turbo: Off, Quiet: Off, Swing: 0 (Off), Sleep: Off, " - "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: Off, Button: 5 (Power), " + "Mode: 4 (Heat), Temp: 30C, Fan: 1 (High), Turbo: Off, Quiet: Off, " + "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); uint8_t expectedState3[kHaierACYRW02StateLength] = { @@ -744,31 +843,34 @@ TEST(TestHaierACYRW02Class, RealStates) { 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2B}; ac.setRaw(expectedState3); EXPECT_EQ( - "Power: On, Button: 1 (Temp Down), Mode: 1 (Cool), Temp: 16C, " - "Fan: 1 (High), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 1 (Temp Down), " + "Mode: 1 (Cool), Temp: 16C, Fan: 1 (High), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); - // cool 25, health, fan auto, swing auto, sleep on + // cool 25, health, fan auto, vertical swing auto, sleep on uint8_t expectedState4[kHaierACYRW02StateLength] = { 0xA6, 0x9C, 0x00, 0x02, 0x40, 0xA8, 0x00, 0x20, 0x80, 0x00, 0x00, 0x00, 0x0B, 0xD7}; ac.setRaw(expectedState4); EXPECT_EQ( - "Power: On, Button: 11 (Sleep), Mode: 1 (Cool), Temp: 25C, " - "Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 12 (Auto), Sleep: On, " - "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 11 (Sleep), " + "Mode: 1 (Cool), Temp: 25C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 12 (Auto), Swing(H): 0 (Middle), Sleep: On, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); - // cool 25, health, fan 3, swing auto, sleep on + // cool 25, health, fan 3, vertical swing auto, sleep on uint8_t expectedState5[kHaierACYRW02StateLength] = { 0xA6, 0x9C, 0x00, 0x02, 0x40, 0x27, 0x36, 0x20, 0x80, 0x00, 0x00, 0x00, 0x04, 0x85}; ac.setRaw(expectedState5); EXPECT_EQ( - "Power: On, Button: 4 (Fan), Mode: 1 (Cool), Temp: 25C, " - "Fan: 1 (High), Turbo: Off, Quiet: Off, Swing: 12 (Auto), Sleep: On, " - "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 4 (Fan), " + "Mode: 1 (Cool), Temp: 25C, Fan: 1 (High), Turbo: Off, Quiet: Off, " + "Swing(V): 12 (Auto), Swing(H): 0 (Middle), Sleep: On, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); } @@ -840,7 +942,7 @@ TEST(TestDecodeHaierAC, RealExample1) { EXPECT_EQ( "Command: 1 (On), Mode: 1 (Cool), Temp: 16C, Fan: 1 (Low), " - "Swing: 0 (Off), Sleep: Off, Health: Off, " + "Swing(V): 0 (Off), Sleep: Off, Health: Off, " "Clock: 00:01, On Timer: Off, Off Timer: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; @@ -885,7 +987,7 @@ TEST(TestDecodeHaierAC, RealExample2) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Command: 6 (Temp Up), Mode: 1 (Cool), Temp: 22C, Fan: 1 (Low), " - "Swing: 0 (Off), Sleep: Off, Health: Off, " + "Swing(V): 0 (Off), Sleep: Off, Health: Off, " "Clock: 00:01, On Timer: Off, Off Timer: Off", ac.toString()); } @@ -928,7 +1030,7 @@ TEST(TestDecodeHaierAC, RealExample3) { ac.setRaw(irsend.capture.state); EXPECT_EQ( "Command: 12 (Health), Mode: 1 (Cool), Temp: 30C, Fan: 1 (Low), " - "Swing: 0 (Off), Sleep: Off, Health: On, " + "Swing(V): 0 (Off), Sleep: Off, Health: On, " "Clock: 00:09, On Timer: Off, Off Timer: Off", ac.toString()); } @@ -997,9 +1099,10 @@ TEST(TestDecodeHaierAC_YRW02, RealExample) { IRHaierACYRW02 ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 17C, " - "Fan: 1 (High), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), " + "Mode: 1 (Cool), Temp: 17C, Fan: 1 (High), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); } @@ -1015,7 +1118,7 @@ TEST(TestHaierAcIssues, Issue668) { ac._irsend.reset(); char expected_on[] = "Command: 1 (On), Mode: 1 (Cool), Temp: 25C, Fan: 1 (Low), " - "Swing: 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, " + "Swing(V): 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, " "On Timer: Off, Off Timer: Off"; // State taken from real capture: // https://github.com/crankyoldgit/IRremoteESP8266/issues/668#issuecomment-483531895 @@ -1053,7 +1156,7 @@ TEST(TestHaierAcIssues, Issue668) { ac._irsend.reset(); char expected_temp_plus_one[] = "Command: 6 (Temp Up), Mode: 1 (Cool), Temp: 26C, Fan: 1 (Low), " - "Swing: 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, " + "Swing(V): 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, " "On Timer: Off, Off Timer: Off"; // State taken from real capture: // https://github.com/crankyoldgit/IRremoteESP8266/issues/668#issuecomment-483531895 @@ -1077,7 +1180,7 @@ TEST(TestHaierAcIssues, Issue668) { ac._irsend.reset(); char expected_temp_minus_one[] = "Command: 7 (Temp Down), Mode: 1 (Cool), Temp: 25C, Fan: 1 (Low), " - "Swing: 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, " + "Swing(V): 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, " "On Timer: Off, Off Timer: Off"; ASSERT_EQ(26, ac.getTemp()); ac.setTemp(ac.getTemp() - 1); @@ -1098,7 +1201,7 @@ TEST(TestHaierACClass, toCommon) { ac.setMode(kHaierAcCool); ac.setTemp(20); ac.setFan(kHaierAcFanHigh); - ac.setSwing(kHaierAcSwingChg); + ac.setSwingV(kHaierAcSwingVChg); ac.setHealth(true); ac.setSleep(true); // Now test it. @@ -1129,12 +1232,14 @@ TEST(TestHaierACYRW02Class, toCommon) { ac.setMode(kHaierAcYrw02Cool); ac.setTemp(20); ac.setFan(kHaierAcYrw02FanHigh); - ac.setSwing(kHaierAcYrw02SwingTop); + ac.setSwingV(kHaierAcYrw02SwingVTop); + ac.setSwingH(kHaierAcYrw02SwingHRightMax); ac.setHealth(true); ac.setSleep(true); + ac.setTurbo(true); // Now test it. ASSERT_EQ(decode_type_t::HAIER_AC_YRW02, ac.toCommon().protocol); - ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_EQ(1, ac.toCommon().model); ASSERT_TRUE(ac.toCommon().power); ASSERT_TRUE(ac.toCommon().celsius); ASSERT_EQ(20, ac.toCommon().degrees); @@ -1142,12 +1247,12 @@ TEST(TestHaierACYRW02Class, toCommon) { 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(stdAc::swingh_t::kRightMax, ac.toCommon().swingh); 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_TRUE(ac.toCommon().turbo); ASSERT_FALSE(ac.toCommon().quiet); + // Unsupported. + ASSERT_FALSE(ac.toCommon().light); ASSERT_FALSE(ac.toCommon().econo); ASSERT_FALSE(ac.toCommon().clean); ASSERT_TRUE(ac.toCommon().beep); @@ -1221,9 +1326,10 @@ TEST(TestDecodeHaierAC176, SyntheticDecode) { EXPECT_FALSE(irsend.capture.repeat); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 24C, Fan: 5 (Auto), " - "Turbo: Off, Quiet: Off, Swing: 6 (UNKNOWN), Sleep: Off, Health: Off, " - "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), " + "Mode: 1 (Cool), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 6 (UNKNOWN), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t result, prev; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); @@ -1262,9 +1368,10 @@ TEST(TestHaierAC176Class, BuildKnownState) { ac.setFan(kHaierAcYrw02FanHigh); EXPECT_TRUE(ac.validChecksum(ac.getRaw())); EXPECT_EQ( - "Power: On, Button: 4 (Fan), Mode: 4 (Heat), Temp: 24C, Fan: 1 (High), " - "Turbo: Off, Quiet: Off, Swing: 0 (Off), Sleep: Off, Health: On, " - "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 4 (Fan), " + "Mode: 4 (Heat), Temp: 24C, Fan: 1 (High), Turbo: Off, Quiet: Off, " + "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: On, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); /* Disabled pending: https://github.com/crankyoldgit/IRremoteESP8266/issues/1480#issuecomment-885636790 @@ -1340,56 +1447,91 @@ TEST(TestHaierAC176Class, Timers) { // Real data. // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1480#issuecomment-894804106 - const uint8_t timer30m[22] = { + const uint8_t timer30m[kHaierAC176StateLength] = { 0xA6, 0x82, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x10, 0x36, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7}; - const uint8_t timeroff[22] = { + const uint8_t timeroff[kHaierAC176StateLength] = { 0xA6, 0x82, 0x00, 0x00, 0x40, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x18, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7}; // https://docs.google.com/spreadsheets/d/1wdOVS08wgK2pEP7hTZLYMmrQ9FZVmLpZF2HjNQaVxlU/edit#gid=0&range=A65 - const uint8_t timeroffthenon[22] = { + const uint8_t timeroffthenon[kHaierAC176StateLength] = { 0xA6, 0x82, 0x00, 0xA0, 0x40, 0xA0, 0x1E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0xDE, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7}; ac.setRaw(timer30m); EXPECT_EQ(kHaierAcYrw02OnTimer, ac.getTimerMode()); EXPECT_EQ( - "Power: Off, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, " - "Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: Off, Timer Mode: 2 (On), On Timer: 00:30, Off Timer: Off", + "Model: 1 (V9014557-A), Power: Off, Button: 16 (Timer), " + "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 2 (On), On Timer: 00:30, Off Timer: Off, Lock: Off", ac.toString()); ac.setRaw(timeroff); EXPECT_EQ(kHaierAcYrw02NoTimers, ac.getTimerMode()); EXPECT_EQ( - "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, " - "Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), " + "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); ac.setRaw(timeroffthenon); EXPECT_EQ( - "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, " - "Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: Off, Timer Mode: 5 (Off-On), On Timer: 08:00, Off Timer: 00:30", + "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), " + "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 5 (Off-On), On Timer: 08:00, Off Timer: 00:30, Lock: Off", ac.toString()); ac.setTimerMode(kHaierAcYrw02OnThenOffTimer); EXPECT_EQ( - "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, " - "Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: Off, Timer Mode: 4 (On-Off), On Timer: 08:00, Off Timer: 00:30", + "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), " + "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 4 (On-Off), On Timer: 08:00, Off Timer: 00:30, Lock: Off", ac.toString()); ac.setTimerMode(kHaierAcYrw02OffTimer); EXPECT_EQ(0, ac.getOnTimer()); EXPECT_EQ(30, ac.getOffTimer()); EXPECT_EQ( - "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, " - "Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: Off, Timer Mode: 1 (Off), On Timer: Off, Off Timer: 00:30", + "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), " + "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 1 (Off), On Timer: Off, Off Timer: 00:30, Lock: Off", ac.toString()); ac.setTimerMode(kHaierAcYrw02NoTimers); EXPECT_EQ(0, ac.getOnTimer()); EXPECT_EQ(0, ac.getOffTimer()); EXPECT_EQ( - "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, " - "Fan: 5 (Auto), Turbo: Off, Quiet: Off, Swing: 2 (Middle), Sleep: Off, " - "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off", + "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), " + "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", + ac.toString()); +} + +TEST(TestHaierAC176Class, Models) { + IRHaierAC176 ac(kGpioUnused); + ac.begin(); + EXPECT_EQ(haier_ac176_remote_model_t::V9014557_A, ac.getModel()); + + ac.setButton(kHaierAcYrw02ButtonTempUp); + ac.setModel(haier_ac176_remote_model_t::V9014557_B); + EXPECT_EQ(haier_ac176_remote_model_t::V9014557_B, ac.getModel()); + EXPECT_EQ(kHaierAcYrw02ButtonCFAB, ac.getButton()); + + ac.setButton(kHaierAcYrw02ButtonTempDown); + ac.setModel(haier_ac176_remote_model_t::V9014557_A); + EXPECT_EQ(haier_ac176_remote_model_t::V9014557_A, ac.getModel()); + EXPECT_EQ(kHaierAcYrw02ButtonCFAB, ac.getButton()); + + // Real data. + const uint8_t setmodelb[kHaierAC176StateLength] = { + 0x59, 0x82, 0x00, 0x00, 0x40, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x1A, 0x55, 0xB7, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x77}; + ac.setRaw(setmodelb); + EXPECT_EQ(haier_ac176_remote_model_t::V9014557_B, ac.getModel()); + EXPECT_EQ( + "Model: 2 (V9014557-B), Power: On, Button: 26 (Celsius/Fahrenheit), " + "Mode: 6 (Fan), Temp: 24C, Fan: 3 (Low), Turbo: Off, Quiet: Off, " + "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, " + "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); } diff --git a/lib/IRremoteESP8266/test/ir_Mirage_test.cpp b/lib/IRremoteESP8266/test/ir_Mirage_test.cpp index 2530afc5c..ad08918e2 100644 --- a/lib/IRremoteESP8266/test/ir_Mirage_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mirage_test.cpp @@ -1,5 +1,6 @@ -// Copyright 2020 David Conran +// Copyright 2020-2021 David Conran +#include "ir_Mirage.h" #include "IRac.h" #include "IRrecv.h" #include "IRrecv_test.h" @@ -11,7 +12,7 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ("MIRAGE", typeToString(decode_type_t::MIRAGE)); ASSERT_EQ(decode_type_t::MIRAGE, strToDecodeType("MIRAGE")); ASSERT_TRUE(hasACState(decode_type_t::MIRAGE)); - ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::MIRAGE)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::MIRAGE)); ASSERT_EQ(kMirageBits, IRsend::defaultBits(decode_type_t::MIRAGE)); ASSERT_EQ(kMirageMinRepeat, IRsend::minRepeats(decode_type_t::MIRAGE)); } @@ -55,7 +56,9 @@ TEST(TestDecodeMirage, RealExample) { ASSERT_EQ(kMirageBits, irsend.capture.bits); EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "", + "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, " + "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, " + "Swing(V): 0 (Off), Clock: 14:16", IRAcUtils::resultAcToString(&irsend.capture)); } @@ -70,13 +73,14 @@ TEST(TestDecodeMirage, SyntheticExample) { irsend.sendMirage(expected); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(decode_type_t::MIRAGE, irsend.capture.decode_type); ASSERT_EQ(kMirageBits, irsend.capture.bits); EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "", + "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, " + "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, " + "Swing(V): 0 (Off), Clock: 14:16", IRAcUtils::resultAcToString(&irsend.capture)); } @@ -119,6 +123,469 @@ TEST(TestDecodeMirage, RealExampleWithDodgyHardwareCapture) { ASSERT_EQ(kMirageBits, irsend.capture.bits); EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "", + "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, " + "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, " + "Swing(V): 0 (Off), Clock: 14:16", IRAcUtils::resultAcToString(&irsend.capture)); } + +TEST(TestMirageAcClass, Power) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); + ac.on(); + EXPECT_TRUE(ac.getPower()); + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); + + const uint8_t on[kMirageStateLength] = { + 0x56, 0x75, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x14, 0x26}; + ac.setRaw(on); + EXPECT_TRUE(ac.getPower()); + const uint8_t off[kMirageStateLength] = { + 0x56, 0x6C, 0x00, 0x00, 0x21, 0xD8, 0x00, 0x00, + 0x0C, 0x00, 0x0C, 0x2C, 0x23, 0x01, 0x61}; + ac.setRaw(off); + EXPECT_FALSE(ac.getPower()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + 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(TestMirageAcClass, OperatingMode) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setMode(kMirageAcCool); + EXPECT_EQ(kMirageAcCool, ac.getMode()); + ac.setMode(kMirageAcHeat); + EXPECT_EQ(kMirageAcHeat, ac.getMode()); + ac.setMode(kMirageAcDry); + EXPECT_EQ(kMirageAcDry, ac.getMode()); + ac.setMode(kMirageAcFan); + EXPECT_EQ(kMirageAcFan, ac.getMode()); + ac.setMode(kMirageAcRecycle); + EXPECT_EQ(kMirageAcRecycle, ac.getMode()); + ac.setMode(255); + EXPECT_EQ(kMirageAcCool, ac.getMode()); +} + +TEST(TestMirageAcClass, HumanReadable) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + // Tests for the KKG9AC1 model. + EXPECT_EQ( + "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 16C, " + "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, " + "Swing(V): 13 (Auto), Clock: 00:00", + ac.toString()); + // Ref: https://docs.google.com/spreadsheets/d/1Ucu9mOOIIJoWQjUJq_VCvwgV3EwKaRk8K2AuZgccYEk/edit#gid=0&range=C7 + // 0x56710000201A00000C000C26010041 + const uint8_t cool_21c_auto[kMirageStateLength] = { + 0x56, 0x71, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00, + 0x0C, 0x00, 0x0C, 0x26, 0x01, 0x00, 0x41}; + ac.setRaw(cool_21c_auto); + EXPECT_EQ( + "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 21C, " + "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, " + "Swing(V): 13 (Auto), Clock: 00:01", + ac.toString()); + + const uint8_t SyntheticExample[kMirageStateLength] = { + 0x56, 0x75, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x16, 0x14, 0x26}; + ac.setRaw(SyntheticExample); + EXPECT_EQ( + "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, " + "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, " + "Swing(V): 0 (Off), Clock: 14:16", + ac.toString()); + + // Tests for the KKG29AC1 model. + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + EXPECT_EQ( + "Model: 2 (KKG29AC1), Power: On, Mode: 2 (Cool), Temp: 25C, " + "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Quiet: Off, Light: -, " + "Swing(V): Off, Swing(H): Off, " + "Filter: Off, Clean: -, On Timer: Off, Off Timer: Off, " + "IFeel: Off", + ac.toString()); +} + +TEST(TestMirageAcClass, Temperature) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kMirageAcMinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMirageAcMaxTemp, ac.getTemp()); + + ac.setTemp(kMirageAcMinTemp); + EXPECT_EQ(kMirageAcMinTemp, ac.getTemp()); + + ac.setTemp(kMirageAcMaxTemp); + EXPECT_EQ(kMirageAcMaxTemp, ac.getTemp()); + + ac.setTemp(kMirageAcMinTemp - 1); + EXPECT_EQ(kMirageAcMinTemp, ac.getTemp()); + + ac.setTemp(kMirageAcMaxTemp + 1); + EXPECT_EQ(kMirageAcMaxTemp, ac.getTemp()); + + ac.setTemp(17); + EXPECT_EQ(17, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(30); + EXPECT_EQ(30, ac.getTemp()); +} + +TEST(TestMirageAcClass, FanSpeed) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setFan(kMirageAcFanAuto); + EXPECT_EQ(kMirageAcFanAuto, ac.getFan()); + ac.setFan(kMirageAcFanLow); + EXPECT_EQ(kMirageAcFanLow, ac.getFan()); + ac.setFan(kMirageAcFanMed); + EXPECT_EQ(kMirageAcFanMed, ac.getFan()); + ac.setFan(kMirageAcFanHigh); + EXPECT_EQ(kMirageAcFanHigh, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kMirageAcFanAuto, ac.getFan()); +} + +TEST(TestMirageAcClass, Turbo) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); + ac.setTurbo(false); + EXPECT_FALSE(ac.getTurbo()); + ac.setTurbo(true); + EXPECT_TRUE(ac.getTurbo()); +} + +TEST(TestMirageAcClass, Light) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); + ac.setLight(false); + EXPECT_FALSE(ac.getLight()); + ac.setLight(true); + EXPECT_TRUE(ac.getLight()); +} + +TEST(TestMirageAcClass, Sleep) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); + ac.setSleep(false); + EXPECT_FALSE(ac.getSleep()); + ac.setSleep(true); + EXPECT_TRUE(ac.getSleep()); +} + +TEST(TestMirageAcClass, Clock) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // This model supports time. + ac.setClock(0); + EXPECT_EQ(0, ac.getClock()); + ac.setClock(12 * 60 * 60 + 30 * 60 + 59); // aka. 12:30:59 + EXPECT_EQ(12 * 60 * 60 + 30 * 60 + 59, ac.getClock()); + ac.setClock(23 * 60 * 60 + 59 * 60 + 59); // aka. 23:59:59 + EXPECT_EQ(23 * 60 * 60 + 59 * 60 + 59, ac.getClock()); + ac.setClock(24 * 60 * 60); // aka. 24:00:00 + EXPECT_EQ(23 * 60 * 60 + 59 * 60 + 59, ac.getClock()); // aka. 23:59:59 + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // This model has no clock. + EXPECT_EQ(0, ac.getClock()); + ac.setClock(12 * 60 * 60 + 30 * 60 + 59); // aka. 12:30:59 + EXPECT_EQ(0, ac.getClock()); +} + +TEST(TestMirageAcClass, Checksums) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + const uint8_t SyntheticExample[kMirageStateLength] = { + 0x56, 0x75, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x16, 0x14, 0x26}; + EXPECT_TRUE(IRMirageAc::validChecksum(SyntheticExample)); + EXPECT_EQ(0x26, IRMirageAc::calculateChecksum(SyntheticExample)); +} + +TEST(TestMirageAcClass, SwingV) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + // Set the model to one with full swingv support. + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); + + ac.setSwingV(kMirageAcSwingVAuto); + EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV()); + + ac.setSwingV(kMirageAcSwingVHigh); + EXPECT_EQ(kMirageAcSwingVHigh, ac.getSwingV()); + + ac.setSwingV(0xFF); + EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV()); + + ac.setSwingV(kMirageAcSwingVLowest); + EXPECT_EQ(kMirageAcSwingVLowest, ac.getSwingV()); + + ac.setSwingV(kMirageAcSwingVLowest - 1); + EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV()); + + // Set the model to one with limited swingv support. + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + + ac.setSwingV(kMirageAcSwingVAuto); + EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV()); + ac.setSwingV(kMirageAcSwingVOff); + EXPECT_EQ(kMirageAcSwingVOff, ac.getSwingV()); + ac.setSwingV(kMirageAcSwingVHigh); + EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV()); + ac.setSwingV(0xFF); + EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV()); + ac.setSwingV(kMirageAcSwingVOff); + EXPECT_EQ(kMirageAcSwingVOff, ac.getSwingV()); +} + +TEST(TestMirageAcClass, SwingH) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); + ac.setSwingH(true); + EXPECT_FALSE(ac.getSwingH()); + ac.setSwingH(false); + EXPECT_FALSE(ac.getSwingH()); + ac.setSwingH(true); + EXPECT_FALSE(ac.getSwingH()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + ac.setSwingH(true); + EXPECT_TRUE(ac.getSwingH()); + ac.setSwingH(false); + EXPECT_FALSE(ac.getSwingH()); + ac.setSwingH(true); + EXPECT_TRUE(ac.getSwingH()); +} + +TEST(TestMirageAcClass, Filter) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No Support + ac.setFilter(true); + EXPECT_FALSE(ac.getFilter()); + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + ac.setFilter(true); + EXPECT_FALSE(ac.getFilter()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); +} + +TEST(TestMirageAcClass, Quiet) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No Support + ac.setQuiet(true); + EXPECT_FALSE(ac.getQuiet()); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + ac.setQuiet(true); + EXPECT_FALSE(ac.getQuiet()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); + ac.setQuiet(false); + EXPECT_FALSE(ac.getQuiet()); + ac.setQuiet(true); + EXPECT_TRUE(ac.getQuiet()); +} + +TEST(TestMirageAcClass, CleanToggle) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); + ac.setCleanToggle(true); + EXPECT_FALSE(ac.getCleanToggle()); + ac.setCleanToggle(false); + EXPECT_FALSE(ac.getCleanToggle()); + ac.setCleanToggle(true); + EXPECT_FALSE(ac.getCleanToggle()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); + ac.setCleanToggle(true); + EXPECT_TRUE(ac.getCleanToggle()); + ac.setCleanToggle(false); + EXPECT_FALSE(ac.getCleanToggle()); + ac.setCleanToggle(true); + EXPECT_TRUE(ac.getCleanToggle()); + ac.send(); // Should be reset when sent. + EXPECT_FALSE(ac.getCleanToggle()); +} + +TEST(TestMirageAcClass, Timers) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No timer support + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + ac.setOnTimer(12 * 60 + 37); // 12:37 + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + ac.setOffTimer(17 * 60 + 5); // 17:05 + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Timer supported + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOnTimer(12 * 60 + 37); // 12:37 + EXPECT_EQ(12 * 60 + 37, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOffTimer(17 * 60 + 5); // 17:05 + EXPECT_EQ(17 * 60 + 5, ac.getOffTimer()); + EXPECT_EQ(12 * 60 + 37, ac.getOnTimer()); + ac.setOnTimer(0); // Off/Disabled + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(17 * 60 + 5, ac.getOffTimer()); + ac.setOffTimer(0); // Off/Disabled + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setOnTimer(12 * 60 + 37); // 12:37 + ac.setOffTimer(17 * 60 + 5); // 17:05 + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No timer support + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_EQ(0, ac.getOnTimer()); +} + +TEST(TestMirageAcClass, IFeelAndSensorTemp) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No support + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(0, ac.getSensorTemp()); + ac.setIFeel(true); + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(0, ac.getSensorTemp()); + ac.setSensorTemp(20); // 20C + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(0, ac.getSensorTemp()); + + ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported + EXPECT_FALSE(ac.getIFeel()); + EXPECT_EQ(0, ac.getSensorTemp()); + ac.setIFeel(true); + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(0, ac.getSensorTemp()); + ac.setSensorTemp(25); // 25C + EXPECT_TRUE(ac.getIFeel()); + EXPECT_EQ(25, ac.getSensorTemp()); + ac.setIFeel(false); + EXPECT_FALSE(ac.getIFeel()); +} + +TEST(TestMirageAcClass, getModel) { + IRMirageAc ac(kGpioUnused); + ac.begin(); + const uint8_t KKG9AC1[kMirageStateLength] = { + 0x56, 0x6C, 0x00, 0x00, 0x20, 0xD8, 0x00, 0x00, + 0x0C, 0x32, 0x0B, 0x00, 0x32, 0x0F, 0x64}; + EXPECT_EQ(mirage_ac_remote_model_t::KKG9AC1, IRMirageAc::getModel(KKG9AC1)); + + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1573#issuecomment-955722044 + const uint8_t KKG29AC1[kMirageStateLength] = { + 0x56, 0x74, 0x00, 0x00, 0x12, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D}; + EXPECT_EQ(mirage_ac_remote_model_t::KKG29AC1, IRMirageAc::getModel(KKG29AC1)); + + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1573#issuecomment-962362540 + const uint8_t KKG29AC1_2[kMirageStateLength] = { + 0x56, 0x72, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19}; + EXPECT_EQ(mirage_ac_remote_model_t::KKG29AC1, + IRMirageAc::getModel(KKG29AC1_2)); +} diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index 28cc04d8e..815d4f996 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -913,7 +913,9 @@ TEST(TestMitsubishiACClass, HumanReadable) { "Power: On, Mode: 1 (Heat), Temp: 22C, Fan: 6 (Quiet), " "Swing(V): 0 (Auto), Swing(H): 3 (Middle), " "Clock: 17:10, On Timer: 00:00, Off Timer: 00:00, Timer: -, " - "Weekly Timer: Off", + "Weekly Timer: Off" + ", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, " + "Direct / Indirect Mode: 0, Fresh: Off", ac.toString()); ac.setTemp(21.5); ac.setWeeklyTimerEnabled(true); @@ -921,7 +923,9 @@ TEST(TestMitsubishiACClass, HumanReadable) { "Power: On, Mode: 1 (Heat), Temp: 21.5C, Fan: 6 (Quiet), " "Swing(V): 0 (Auto), Swing(H): 3 (Middle), " "Clock: 17:10, On Timer: 00:00, Off Timer: 00:00, Timer: -, " - "Weekly Timer: On", + "Weekly Timer: On" + ", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, " + "Direct / Indirect Mode: 0, Fresh: Off", ac.toString()); } @@ -1434,7 +1438,9 @@ TEST(TestDecodeMitsubishiAC, Issue891) { "Power: Off, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), " "Swing(V): 0 (Auto), Swing(H): 3 (Middle), " "Clock: 00:00, On Timer: 00:00, Off Timer: 00:00, Timer: -, " - "Weekly Timer: Off", + "Weekly Timer: Off" + ", 10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, " + "Direct / Indirect Mode: 0, Fresh: Off", ac.toString()); } diff --git a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp index a4e0503cd..061067421 100644 --- a/lib/IRremoteESP8266/test/ir_Samsung_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Samsung_test.cpp @@ -426,16 +426,37 @@ TEST(TestIRSamsungAcClass, SetAndGetPower) { TEST(TestIRSamsungAcClass, SetAndGetSwing) { IRSamsungAc ac(kGpioUnused); + + // Vertical ac.setSwing(true); EXPECT_TRUE(ac.getSwing()); + EXPECT_FALSE(ac.getSwingH()); ac.setSwing(false); EXPECT_FALSE(ac.getSwing()); + EXPECT_FALSE(ac.getSwingH()); ac.setSwing(true); EXPECT_TRUE(ac.getSwing()); + EXPECT_FALSE(ac.getSwingH()); + // Horizontal + ac.setSwingH(true); + EXPECT_TRUE(ac.getSwing()); + EXPECT_TRUE(ac.getSwingH()); + ac.setSwingH(false); + EXPECT_TRUE(ac.getSwing()); + EXPECT_FALSE(ac.getSwingH()); + ac.setSwingH(true); + EXPECT_TRUE(ac.getSwing()); + EXPECT_TRUE(ac.getSwingH()); + + ac.setSwing(false); + EXPECT_FALSE(ac.getSwing()); + EXPECT_TRUE(ac.getSwingH()); + ac.setSwingH(false); + EXPECT_FALSE(ac.getSwing()); + EXPECT_FALSE(ac.getSwingH()); // Real examples from: // 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, 0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0}; @@ -576,14 +597,24 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) { EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan()); ac.setPowerful(false); EXPECT_FALSE(ac.getPowerful()); - EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); - // Breeze and Powerful/Turbo are mutually exclusive. + // Breeze, Econo, and Powerful/Turbo are mutually exclusive. ac.setPowerful(true); EXPECT_TRUE(ac.getPowerful()); + EXPECT_FALSE(ac.getBreeze()); + EXPECT_FALSE(ac.getEcono()); ac.setBreeze(true); EXPECT_TRUE(ac.getBreeze()); EXPECT_FALSE(ac.getPowerful()); + EXPECT_FALSE(ac.getEcono()); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + EXPECT_FALSE(ac.getBreeze()); + EXPECT_FALSE(ac.getPowerful()); + ac.setPowerful(true); + EXPECT_TRUE(ac.getPowerful()); + EXPECT_FALSE(ac.getBreeze()); + EXPECT_FALSE(ac.getEcono()); // Actual powerful on & off states from: // https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500120270 @@ -594,9 +625,10 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) { 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, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 7 (Turbo), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: On, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); uint8_t off[kSamsungAcStateLength] = { @@ -606,9 +638,10 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) { 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, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -633,6 +666,44 @@ TEST(TestIRSamsungAcClass, QuietAndPowerfulAreMutuallyExclusive) { EXPECT_NE(kSamsungAcFanTurbo, ac.getFan()); } +TEST(TestIRSamsungAcClass, SetAndGetEcono) { + IRSamsungAc ac(kGpioUnused); + ac.begin(); + EXPECT_FALSE(ac.getEcono()); + ac.setFan(kSamsungAcFanMed); + ac.setSwing(false); + ac.setEcono(true); + EXPECT_TRUE(ac.getEcono()); + EXPECT_FALSE(ac.getBreeze()); + EXPECT_FALSE(ac.getPowerful()); + EXPECT_TRUE(ac.getSwing()); // Econo turns on swingv. + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); // And sets the fan to Auto. + ac.setEcono(false); + EXPECT_FALSE(ac.getEcono()); + EXPECT_FALSE(ac.getBreeze()); + EXPECT_FALSE(ac.getPowerful()); + + // Breeze, Econo, and Powerful/Turbo are mutually exclusive. + // But that is tested in `SetAndGetPowerful` + + // Actual econo on state from: + // https://cryptpad.fr/sheet/#/2/sheet/view/r9k8pmELYEjLyC71cD7EsThEYgKGLJygREZ5pVfNkS8/ + // Row: 33 + uint8_t on[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0xAE, 0x7F, 0x80, 0x11, 0xF0}; + ac.setRaw(on, kSamsungAcStateLength); + EXPECT_TRUE(ac.getEcono()); + EXPECT_TRUE(ac.getSwing()); + EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), " + "Swing(V): On, Swing(H): Off, Beep: -, Clean: -, " + "Quiet: Off, Powerful: Off, Econo: On, Breeze: Off, " + "Light: On, Ion: Off", + ac.toString()); +} + TEST(TestIRSamsungAcClass, ChecksumCalculation) { IRSamsungAc ac(kGpioUnused); @@ -668,9 +739,10 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) { TEST(TestIRSamsungAcClass, HumanReadable) { IRSamsungAc ac(kGpioUnused); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), " + "Swing(V): On, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, Econo: Off, " + "Breeze: Off, Light: On, Ion: Off", ac.toString()); ac.setTemp(kSamsungAcMaxTemp); ac.setMode(kSamsungAcHeat); @@ -680,28 +752,32 @@ TEST(TestIRSamsungAcClass, HumanReadable) { ac.setBeep(true); ac.setClean(true); EXPECT_EQ( - "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 5 (High), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 5 (High), " + "Swing(V): Off, Swing(H): Off, " + "Beep: Toggle, Clean: Toggle, Quiet: Off, Powerful: Off, Econo: Off, " + "Breeze: Off, Light: On, Ion: Off", ac.toString()); ac.setQuiet(true); EXPECT_EQ( - "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), Swing: Off, " - "Beep: On, Clean: On, Quiet: On, Powerful: Off, Breeze: Off, " + "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, Beep: Toggle, " + "Clean: Toggle, Quiet: On, Powerful: Off, Econo: Off, Breeze: Off, " "Light: On, Ion: Off", ac.toString()); ac.setQuiet(false); ac.setPowerful(true); EXPECT_EQ( - "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off, Powerful: On, Breeze: Off, " + "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), " + "Swing(V): Off, Swing(H): Off, Beep: Toggle, " + "Clean: Toggle, Quiet: Off, Powerful: On, Econo: Off, Breeze: Off, " "Light: On, Ion: Off", ac.toString()); ac.setIon(true); ac.setDisplay(false); EXPECT_EQ( - "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), Swing: Off, " - "Beep: On, Clean: On, Quiet: Off, Powerful: On, Breeze: Off, " + "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), " + "Swing(V): Off, Swing(H): Off, Beep: Toggle, " + "Clean: Toggle, Quiet: Off, Powerful: On, Econo: Off, Breeze: Off, " "Light: Off, Ion: On", ac.toString()); } @@ -802,9 +878,10 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) { IRSamsungAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), " + "Swing(V): On, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -852,9 +929,10 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) { IRSamsungAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -912,9 +990,10 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) { IRSamsungAc ac(kGpioUnused); ac.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, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -973,9 +1052,10 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) { IRSamsungAc ac(kGpioUnused); ac.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, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: Off, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -1021,9 +1101,10 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) { IRSamsungAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( - "Power: On, Mode: 4 (Heat), Temp: 17C, Fan: 0 (Auto), Swing: On, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 4 (Heat), Temp: 17C, Fan: 0 (Auto), " + "Swing(V): On, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -1066,9 +1147,10 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) { EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -1127,9 +1209,10 @@ TEST(TestDecodeSamsungAC, Issue604DecodeExtended) { IRSamsungAc ac(kGpioUnused); ac.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, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), " + "Swing(V): On, Swing(H): On, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -1311,8 +1394,10 @@ TEST(TestIRSamsungAcClass, Issue604SendPowerHack) { "m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436" "m586s2886"; std::string text = "Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 4 (Med), " - "Swing: On, Beep: Off, Clean: Off, Quiet: Off, " - "Powerful: Off, Breeze: Off, Light: On, Ion: Off"; + "Swing(V): On, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, " + "Powerful: Off, Econo: Off, Breeze: Off, " + "Light: On, Ion: 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); @@ -1445,9 +1530,10 @@ TEST(TestDecodeSamsungAC, Issue734QuietSetting) { 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, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: On, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); // Make sure the ac class state is in something wildly different first. @@ -1468,9 +1554,10 @@ TEST(TestDecodeSamsungAC, Issue734QuietSetting) { 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, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: On, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); // Check it matches the known good/expected state. EXPECT_STATE_EQ(expectedState, ac.getRaw(), kSamsungAcBits); @@ -1519,9 +1606,10 @@ TEST(TestDecodeSamsungAC, Issue734PowerfulOff) { 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, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -1553,9 +1641,10 @@ TEST(TestIRSamsungAcClass, SetAndGetBreeze) { ac.setRaw(on); ASSERT_TRUE(ac.getBreeze()); EXPECT_EQ( - "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: On, " - "Light: On, Ion: Off", + "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: On, Light: On, Ion: Off", ac.toString()); // MODE FAN, 24C WINDFREE OFF, FAN = LOW const uint8_t off[14] = { @@ -1564,9 +1653,10 @@ TEST(TestIRSamsungAcClass, SetAndGetBreeze) { ac.setRaw(off); ASSERT_FALSE(ac.getBreeze()); EXPECT_EQ( - "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 2 (Low), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: On, Ion: Off", + "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 2 (Low), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off", ac.toString()); } @@ -1614,9 +1704,10 @@ TEST(TestDecodeSamsungAC, Issue1227VeryPoorSignal) { EXPECT_EQ(kSamsungAcBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), Swing: Off, " - "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, " - "Light: Off, Ion: Off", + "Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: Off, Ion: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -1671,3 +1762,362 @@ TEST(TestIRSamsungAcClass, SectionChecksums) { EXPECT_EQ(IRSamsungAc::getSectionChecksum(extended_off + 14), IRSamsungAc::calcSectionChecksum(extended_off + 14)); } + +TEST(TestIRSamsungAcClass, Issue1648) { + IRSamsungAc ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint8_t onState[kSamsungAcExtendedStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xC2, 0xFE, 0x71, 0x90, 0x15, 0xF0}; + const String onText = "Power: On, Mode: 1 (Cool), Temp: 25C, Fan: 2 (Low), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, " + "Powerful: Off, Econo: Off, Breeze: Off, " + "Light: On, Ion: Off"; + const uint8_t extended_offState[kSamsungAcExtendedStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x90, 0x15, 0xC0}; + const uint8_t short_offState[kSamsungAcStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xE2, 0xFE, 0x71, 0x90, 0x15, 0xC0}; + const String offText = "Power: Off, Mode: 1 (Cool), Temp: 25C, Fan: 2 (Low), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, " + "Powerful: Off, Econo: Off, Breeze: Off, " + "Light: On, Ion: Off"; + const uint8_t coolState[kSamsungAcStateLength] = { + 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0, + 0x01, 0xC2, 0xFE, 0x71, 0x90, 0x15, 0xF0}; + + // "setup()"" from provided code. + ac.begin(); // User code + ac.off(); // User code + ac.setFan(kSamsungAcFanLow); // User code + ac.setMode(kSamsungAcCool); // User code + ac.setTemp(25); // User code + ac.setSwing(false); // User code + + // Go through "loop()" from provided code. + for (uint8_t i = 0; i < 2; i++) { + ac.on(); // User code + ac.send(); // User code + + // Verify what was sent. + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(onState, ac._irsend.capture.state, ac._irsend.capture.bits); + EXPECT_EQ(onText, IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_TRUE(ac._lastsentpowerstate); + ac._irsend.reset(); + + ac.setMode(kSamsungAcCool); // User code + ac.send(); // User code + + // Verify what was sent. + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(coolState, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(onText, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ac._irsend.reset(); + EXPECT_TRUE(ac._lastsentpowerstate); + EXPECT_FALSE(ac._forceextended); + + ac.off(); // User code + ac.send(); // User code + + // Verify what was sent. + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(extended_offState, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(offText, IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_FALSE(ac._lastsentpowerstate); + ac._irsend.reset(); + + ac.off(); // User code + ac.send(); // User code + + // Verify what was sent. + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(short_offState, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(offText, IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_FALSE(ac._lastsentpowerstate); + ac._irsend.reset(); + // End of "loop()" code. + } + + // Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/1648#issuecomment-950822399 + const uint8_t expectedState[kSamsungAcExtendedStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x12, 0xAF, 0x71, 0x80, 0x15, 0xC0}; + const String expectedText = "Power: Off, Mode: 1 (Cool), Temp: 24C, " + "Fan: 2 (Low), Swing(V): On, Swing(H): Off, " + "Beep: -, Clean: -, " + "Quiet: Off, Powerful: Off, Econo: Off, " + "Breeze: Off, Light: On, Ion: Off"; + + ac.stateReset(); + ac.setRaw(expectedState, kSamsungAcExtendedStateLength); + EXPECT_EQ(expectedText, ac.toString()); + + // Try to generate the same message. + ac.stateReset(); + ac.off(); + ac.setMode(kSamsungAcCool); + ac.setTemp(24); + ac.setFan(kSamsungAcFanLow); + ac.setSwing(true); + ac.setBeep(false); + ac.setClean(false); + ac.setQuiet(false); + ac.setPowerful(false); + ac.setBreeze(false); + ac.setDisplay(true); + ac.setIon(false); + EXPECT_EQ(expectedText, ac.toString()); + + ac.send(); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expectedText, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ac._irsend.reset(); +} + +TEST(TestIRSamsungAcClass, Timers) { + IRSamsungAc ac(kGpioUnused); + ac.begin(); + + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1277#issuecomment-961836703 + const uint8_t on_timer_30m[kSamsungAcExtendedStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xA2, 0x0F, 0x30, 0x00, 0x02, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0}; + const uint8_t off_timer_1h_on_timer_10m[kSamsungAcExtendedStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0x92, 0x8F, 0x10, 0x00, 0x06, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0}; + const uint8_t off_timer_1h_on_timer_0m[kSamsungAcExtendedStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0xA2, 0x8F, 0x00, 0x00, 0x06, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0}; + + ac.setRaw(on_timer_30m, kSamsungAcExtendedStateLength); + EXPECT_EQ( + "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, Light: On, Ion: Off, On Timer: 00:30", + ac.toString()); + ac.setRaw(off_timer_1h_on_timer_10m, kSamsungAcExtendedStateLength); + EXPECT_EQ( + "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, " + "Light: On, Ion: Off, On Timer: 00:10, Off Timer: 01:00", + ac.toString()); + ac.setRaw(off_timer_1h_on_timer_0m, kSamsungAcExtendedStateLength); + EXPECT_EQ( + "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, " + "Light: On, Ion: Off, On Timer: 00:00, Off Timer: 01:00", + ac.toString()); + + // https://cryptpad.fr/sheet/#/2/sheet/view/r9k8pmELYEjLyC71cD7EsThEYgKGLJygREZ5pVfNkS8/ + // Row 155 + const uint8_t off_timer_11h_on_timer_6h[kSamsungAcExtendedStateLength] = { + 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0, + 0x01, 0x62, 0x8F, 0x05, 0x03, 0x06, 0x00, + 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0}; + ac.setRaw(off_timer_11h_on_timer_6h, kSamsungAcExtendedStateLength); + EXPECT_EQ( + "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, " + "Light: On, Ion: Off, On Timer: 06:00, Off Timer: 11:00", + ac.toString()); + + ac.stateReset(false); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOnTimer(0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOffTimer(0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + // On Timer only + ac.setOnTimer(30); + EXPECT_EQ(30, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOnTimer(90); // 1h30m + EXPECT_EQ(90, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOnTimer(85); // 1h25m -> 1h20m + EXPECT_EQ(80, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOnTimer(23 * 60 + 59); // 23:59 + EXPECT_EQ(23 * 60 + 50, ac.getOnTimer()); // 23:50 + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOnTimer(24 * 60 + 30); // 24:30 + EXPECT_EQ(24 * 60, ac.getOnTimer()); // 24:00 (Max) + EXPECT_EQ(0, ac.getOffTimer()); + + // Off Timer only + ac.setOnTimer(0); + ac.setOffTimer(0); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOffTimer(30); + EXPECT_EQ(30, ac.getOffTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setOffTimer(90); // 1h30m + EXPECT_EQ(90, ac.getOffTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setOffTimer(85); // 1h25m -> 1h20m + EXPECT_EQ(80, ac.getOffTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setOffTimer(23 * 60 + 59); // 23:59 + EXPECT_EQ(23 * 60 + 50, ac.getOffTimer()); // 23:50 + EXPECT_EQ(0, ac.getOnTimer()); + + ac.setOffTimer(24 * 60 + 30); // 24:30 + EXPECT_EQ(24 * 60, ac.getOffTimer()); // 24:00 (Max) + EXPECT_EQ(0, ac.getOnTimer()); + + // Both Timers + ac.setOnTimer(24 * 60); // 24:00 + EXPECT_EQ(24 * 60, ac.getOnTimer()); // 24:00 (Max) + EXPECT_EQ(24 * 60, ac.getOffTimer()); // 24:00 (Max) + + ac.setOnTimer(1 * 60 + 30); // 1:30 + ac.setOffTimer(11 * 60); // 11:00 + EXPECT_EQ(1 * 60 + 30, ac.getOnTimer()); + EXPECT_EQ(11 * 60, ac.getOffTimer()); +} + +TEST(TestIRSamsungAcClass, Sleep) { + IRSamsungAc ac(kGpioUnused); + ac.begin(); + + // https://cryptpad.fr/sheet/#/2/sheet/view/r9k8pmELYEjLyC71cD7EsThEYgKGLJygREZ5pVfNkS8/ + const uint8_t sleep_8h[kSamsungAcExtendedStateLength] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x10, 0xF0, + 0x01, 0xA2, 0x0F, 0x04, 0x00, 0x0C, 0x00, + 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0}; + ac.setRaw(sleep_8h, kSamsungAcExtendedStateLength); + EXPECT_EQ(8 * 60, ac.getSleepTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ( + "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), " + "Swing(V): Off, Swing(H): Off, " + "Beep: -, Clean: -, Quiet: Off, Powerful: Off, " + "Econo: Off, Breeze: Off, " + "Light: On, Ion: Off, Sleep Timer: 08:00", + ac.toString()); + + ac.stateReset(false); + EXPECT_EQ(0, ac.getSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setOnTimer(60); + ac.setOffTimer(90); + EXPECT_EQ(0, ac.getSleepTimer()); + EXPECT_EQ(60, ac.getOnTimer()); + EXPECT_EQ(90, ac.getOffTimer()); + + ac.setSleepTimer(120); + EXPECT_EQ(120, ac.getSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(0, ac.getOffTimer()); + + ac.setSleepTimer(24 * 60 + 31); // 24h31m + EXPECT_EQ(24 * 60, ac.getSleepTimer()); // 24h (Max) + + ac.setSleepTimer(35); // 45m + EXPECT_EQ(30, ac.getSleepTimer()); // 30m (Only stored in 10m increments). + + ac.setOnTimer(60); // Seting an On Timer should clear the sleep setting. + EXPECT_EQ(0, ac.getSleepTimer()); + EXPECT_EQ(60, ac.getOnTimer()); + + ac.setSleepTimer(120); + ac.setOffTimer(90); // Setting an Off Timer will clear the sleep setting. + EXPECT_EQ(0, ac.getSleepTimer()); + EXPECT_EQ(0, ac.getOnTimer()); + EXPECT_EQ(90, ac.getOffTimer()); +} + +TEST(TestIRSamsungAcClass, BuildKnownSleepSate) { + // For https://github.com/crankyoldgit/IRremoteESP8266/issues/1277#issuecomment-965047193 + IRSamsungAc ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + ac.begin(); + const uint8_t expectedState[kSamsungAcExtendedStateLength] = { + 0x02, 0x82, 0x0F, 0x00, 0x00, 0x10, 0xF0, + 0x01, 0xA2, 0x0F, 0x04, 0x00, 0x0C, 0x00, + 0x01, 0xD2, 0xFE, 0x71, 0x50, 0x41, 0xF0}; + const char expectedStr[] = "Power: On, Mode: 4 (Heat), Temp: 21C, " + "Fan: 0 (Auto), Swing(V): Off, Swing(H): Off, Beep: -, Clean: -, " + "Quiet: Off, Powerful: Off, Econo: Off, Breeze: Off, " + "Light: On, Ion: Off, Sleep Timer: 08:00"; + ac.setPower(true); + ac.setMode(kSamsungAcHeat); + ac.setTemp(21); + ac.setFan(kSamsungAcFanAuto); + ac.setSwing(false); + ac.setSwingH(false); + ac.setBeep(false); + ac.setClean(false); + ac.setQuiet(false); + ac.setPowerful(false); + ac.setEcono(false); + ac.setBreeze(false); + ac.setDisplay(true); + ac.setIon(false); + ac.setSleepTimer(8 * 60); + EXPECT_EQ(expectedStr, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type); + EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expectedStr, IRAcUtils::resultAcToString(&ac._irsend.capture)); + ac._irsend.reset(); +} diff --git a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp index 1785b3895..4a63780d5 100644 --- a/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Toshiba_test.cpp @@ -312,20 +312,21 @@ TEST(TestToshibaACClass, HumanReadableOutput) { ac.setRaw(initial_state); EXPECT_EQ("Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), " - "Turbo: Off, Econo: Off", + "Turbo: Off, Econo: Off, Filter: Off", ac.toString()); ac.setRaw(modified_state); EXPECT_EQ("Temp: 17C, Power: On, Mode: 1 (Cool), Fan: 5 (High), " - "Turbo: Off, Econo: Off", + "Turbo: Off, Econo: Off, Filter: Off", ac.toString()); ac.setTemp(25); ac.setFan(3); ac.setMode(kToshibaAcDry); EXPECT_EQ("Temp: 25C, Power: On, Mode: 2 (Dry), Fan: 3 (Medium), " - "Turbo: Off, Econo: Off", + "Turbo: Off, Econo: Off, Filter: Off", ac.toString()); ac.off(); - EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off", + EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off, " + "Filter: Off", ac.toString()); } @@ -379,7 +380,7 @@ TEST(TestDecodeToshibaAC, SyntheticExample) { EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( "Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: Off, " - "Econo: Off", + "Econo: Off, Filter: Off", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -555,6 +556,7 @@ TEST(TestToshibaACClass, toCommon) { ac.setMode(kToshibaAcCool); ac.setTemp(20); ac.setFan(kToshibaAcFanMax); + ac.setFilter(true); // Now test it. ASSERT_EQ(decode_type_t::TOSHIBA_AC, ac.toCommon().protocol); ASSERT_EQ(-1, ac.toCommon().model); @@ -563,13 +565,13 @@ TEST(TestToshibaACClass, toCommon) { 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_TRUE(ac.toCommon().filter); // 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); @@ -626,7 +628,7 @@ TEST(TestDecodeToshibaAC, RealLongExample) { EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( "Temp: 22C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: On, " - "Econo: Off", + "Econo: Off, Filter: Off", IRAcUtils::resultAcToString(&irsend.capture)); } @@ -731,7 +733,7 @@ TEST(TestToshibaACClass, ConstructLongState) { ac.setEcono(true); EXPECT_EQ( "Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), " - "Turbo: Off, Econo: On", + "Turbo: Off, Econo: On, Filter: Off", ac.toString()); EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength()); const uint8_t expectedState[kToshibaACStateLengthLong] = { @@ -781,7 +783,8 @@ TEST(TestDecodeToshibaAC, RealExample_WHUB03NJ) { EXPECT_EQ(kToshibaACBits, irsend.capture.bits); EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Temp: 20C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off", + "Temp: 20C, Power: Off, Fan: 0 (Auto), Turbo: Off, Econo: Off, " + "Filter: Off", IRAcUtils::resultAcToString(&irsend.capture)); } @@ -828,3 +831,34 @@ TEST(TestToshibaACClass, SwingCodes) { "Temp: 17C, Swing(V): 4 (Toggle)", ac.toString()); } + +// For https://github.com/crankyoldgit/IRremoteESP8266/issues/1692 +TEST(TestToshibaACClass, Filter) { + IRToshibaAC ac(kGpioUnused); + ac.begin(); + EXPECT_FALSE(ac.getFilter()); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + + + ac.setFilter(false); + EXPECT_FALSE(ac.getFilter()); + + ac.setFilter(true); + EXPECT_TRUE(ac.getFilter()); + + const uint8_t pure_off[kToshibaACStateLength] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x40, 0x03, 0x00, 0x42}; + ac.setRaw(pure_off); + EXPECT_FALSE(ac.getFilter()); + + const uint8_t pure_on[kToshibaACStateLength] = { + 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x40, 0x03, 0x10, 0x52}; + ac.setRaw(pure_on); + EXPECT_TRUE(ac.getFilter()); + + // Convert a known filter/pure on state to a known off filter/pure state. + ac.setFilter(false); + EXPECT_STATE_EQ(pure_off, ac.getRaw(), ac.getStateLength() * 8); +} diff --git a/lib/IRremoteESP8266/tools/Makefile b/lib/IRremoteESP8266/tools/Makefile index 15f192efd..bf65a996d 100644 --- a/lib/IRremoteESP8266/tools/Makefile +++ b/lib/IRremoteESP8266/tools/Makefile @@ -27,7 +27,9 @@ CPPFLAGS += -DUNIT_TEST -D_IR_LOCALE_=en-AU # Flags passed to the C++ compiler. CXXFLAGS += -g -Wall -Wextra -pthread -std=gnu++11 -all : gc_decode mode2_decode +objects = $(patsubst %.cpp,%,$(wildcard *.cpp)) + +all : $(objects) run_tests : all failed=""; \ @@ -35,6 +37,10 @@ run_tests : all echo "RUNNING: $${py_unittest}"; \ python3 ./$${py_unittest} || failed="$${failed} $${py_unittest}"; \ done; \ + for shell_unittest in *_test.sh; do \ + echo "RUNNING: $${shell_unittest}"; \ + bash ./$${shell_unittest} || failed="$${failed} $${shell_unittest}"; \ + done; \ if [ -n "$${failed}" ]; then \ echo "FAIL: :-( :-( Unit test(s)$${failed} failed! :-( :-("; exit 1; \ else \ @@ -46,7 +52,7 @@ run-% : all python3 ./$*.py; clean : - rm -f *.o *.pyc gc_decode mode2_decode + rm -f *.o *.pyc $(objects) # Keep all intermediate files. @@ -80,7 +86,7 @@ IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP82 # new specific targets goes above this line -%_decode : $(COMMON_OBJ) %_decode.o +$(objects) : %: $(COMMON_OBJ) %.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ ir_%.o : $(USER_DIR)/ir_%.h $(USER_DIR)/ir_%.cpp $(COMMON_DEPS) $(GTEST_HEADERS) diff --git a/lib/IRremoteESP8266/tools/code_to_raw.cpp b/lib/IRremoteESP8266/tools/code_to_raw.cpp new file mode 100644 index 000000000..7358f16da --- /dev/null +++ b/lib/IRremoteESP8266/tools/code_to_raw.cpp @@ -0,0 +1,148 @@ +// Quick and dirty tool to convert a protocol's (hex) codes to raw timings. +// Copyright 2021 David Conran + +#include +#include +#include +#include +#include +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" + + +void usage_error(char *name) { + std::cerr << "Usage: " << name + << " --protocol PROTOCOL_NAME" + << " --code " + << " [--bits 1-" << kStateSizeMax * 8 << "]" + << " [--timinginfo]" + << std::endl; +} + +int main(int argc, char *argv[]) { + int argv_offset = 1; + int repeats = 0; + uint64_t code = 0; + uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0. + decode_type_t input_type = decode_type_t::UNKNOWN; + bool timinginfo = false; + + // Check the invocation/calling usage. + if (argc < 5 || argc > 8) { + usage_error(argv[0]); + return 1; + } + + if (strncmp("--protocol", argv[argv_offset], 11) == 0) { + argv_offset++; + input_type = strToDecodeType(argv[argv_offset]); + switch (input_type) { + // Unsupported types + case decode_type_t::UNUSED: + case decode_type_t::UNKNOWN: + case decode_type_t::GLOBALCACHE: + case decode_type_t::PRONTO: + case decode_type_t::RAW: + std::cerr << "The protocol specified is not supported by this program." + << std::endl; + return 1; + default: + break; + } + argv_offset++; + } + + uint16_t nbits = IRsend::defaultBits(input_type); + uint16_t stateSize = nbits / 8; + if (strncmp("--code", argv[argv_offset], 7) == 0) { + argv_offset++; + String hexstr = String(argv[argv_offset]); + uint64_t strOffset = 0; + if (hexstr.rfind("0x", 0) || hexstr.rfind("0X", 0)) strOffset = 2; + + // Calculate how many hexadecimal characters there are. + uint64_t hexstrlength = hexstr.length() - strOffset; + + // Ptr to the least significant byte of the resulting state for this + // protocol. + uint8_t *statePtr = &state[stateSize - 1]; + + // Convert the string into a state array of the correct length. + for (uint16_t i = 0; i < hexstrlength; i++) { + // Grab the next least sigificant hexadecimal digit from the string. + uint8_t c = tolower(hexstr[hexstrlength + strOffset - i - 1]); + if (isxdigit(c)) { + if (isdigit(c)) + c -= '0'; + else + c = c - 'a' + 10; + } else { + std::cerr << "Code " << argv[argv_offset] + << " contains non-hexidecimal characters." << std::endl; + return 3; + } + if (i % 2 == 1) { // Odd: Upper half of the byte. + *statePtr += (c << 4); + statePtr--; // Advance up to the next least significant byte of state. + } else { // Even: Lower half of the byte. + *statePtr = c; + } + } + if (!hasACState(input_type)) + code = std::stoull(argv[argv_offset], nullptr, 16); + argv_offset++; + } + + if (argc - argv_offset > 0 && strncmp("--bits", argv[argv_offset], 7) == 0) { + argv_offset++; + nbits = std::stoul(argv[argv_offset], nullptr, 10); + if (nbits == 0 && (nbits <= kStateSizeMax * 8)) { + std::cerr << "Nr. of bits " << argv[argv_offset] + << " is invalid." << std::endl; + return 1; + } + stateSize = nbits / 8; + argv_offset++; + } + + if (argc - argv_offset > 0 && + strncmp("--timinginfo", argv[argv_offset], 13) == 0) { + argv_offset++; + timinginfo = true; + } + + if (argc - argv_offset != 0) { + usage_error(argv[0]); + return 1; + } + + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + + if (hasACState(input_type)) // Is it larger than 64 bits? + irsend.send(input_type, state, stateSize); + else + irsend.send(input_type, code, nbits, repeats); + + irsend.makeDecodeResult(); + irrecv.decode(&irsend.capture); + + std::cout << "Code type: " << irsend.capture.decode_type << " (" + << typeToString(irsend.capture.decode_type) << ")" << std::endl + << "Code bits: " << irsend.capture.bits << std::endl; + if (hasACState(irsend.capture.decode_type)) { + String description = IRAcUtils::resultAcToString(&irsend.capture); + if (description.length()) { + std::cout << "Description: " << description.c_str() << std::endl; + } + } + + std::cout << std::endl << resultToSourceCode(&irsend.capture) << std::endl; + if (timinginfo) std::cout << resultToTimingInfo(&irsend.capture); + + return 0; +} diff --git a/lib/IRremoteESP8266/tools/code_to_raw_test.sh b/lib/IRremoteESP8266/tools/code_to_raw_test.sh new file mode 100644 index 000000000..fd587f2ad --- /dev/null +++ b/lib/IRremoteESP8266/tools/code_to_raw_test.sh @@ -0,0 +1,65 @@ +#! /bin/bash +CODE_TO_RAW=./code_to_raw +if [[ ! -x ${CODE_TO_RAW} ]]; then + echo "'raw_to_code' failed to compile and produce an executable." + exit 1 +fi + +function unittest_success() +{ + COMMAND=$1 + EXPECTED="$2" + echo -n "Testing: \"${COMMAND}\" ..." + OUTPUT="$(${COMMAND})" + STATUS=$? + FAILURE="" + if [[ ${STATUS} -ne 0 ]]; then + FAILURE="Non-Zero Exit status: ${STATUS}. " + fi + if [[ "${OUTPUT}" != "${EXPECTED}" ]]; then + FAILURE="${FAILURE} Unexpected Output: \"${OUTPUT}\" != \"${EXPECTED}\"" + fi + if [[ -z ${FAILURE} ]]; then + echo " ok!" + return 0 + else + echo + echo "FAILED: ${FAILURE}" + return 1 + fi +} + +read -r -d '' OUT << EOM +Code type: 4 (SONY) +Code bits: 12 + +uint16_t rawData[78] = {2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600, 2400, 600, 1200, 600, 1200, 600, 1200, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 1200, 600, 600, 600, 600, 600, 600, 600, 600, 24600 }; // SONY F50 +uint32_t address = 0x1; +uint32_t command = 0x2F; +uint64_t data = 0xF50; +EOM + +unittest_success "${CODE_TO_RAW} --protocol Sony --code 0xf50 --bits 12" "${OUT}" + +read -r -d '' OUT << EOM +Code type: 7 (SAMSUNG) +Code bits: 32 + +uint16_t rawData[68] = {4480, 4480, 560, 1680, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 560, 560, 1680, 560, 1680, 560, 560, 560, 47040 }; // SAMSUNG E0E09966 +uint32_t address = 0x7; +uint32_t command = 0x99; +uint64_t data = 0xE0E09966; +EOM + +unittest_success "${CODE_TO_RAW} --protocol SAMSUNG --code 0xE0E09966" "${OUT}" + +read -r -d '' OUT << xEOMx +Code type: 18 (KELVINATOR) +Code bits: 128 +Description: Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, Quiet: Off, XFan: On, Ion: Off, Light: Off, Swing(H): Off, Swing(V): Off + +uint16_t rawData[280] = {9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 39950, 9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 1530, 680, 39950 }; // KELVINATOR +uint8_t state[16] = {0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0}; +xEOMx + +unittest_success "${CODE_TO_RAW} --protocol KELVINATOR --code 0x190B8050000000E0190B8070000010F0" "${OUT}" diff --git a/lib/IRremoteESP8266/tools/extract_lib_version.sh b/lib/IRremoteESP8266/tools/extract_lib_version.sh new file mode 100644 index 000000000..8bea94334 --- /dev/null +++ b/lib/IRremoteESP8266/tools/extract_lib_version.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e +# Copyright 2021 crankyoldgit +# Extract and constuct the string version of the IRremoteESP8266 Library Version +function getVerNum() +{ + echo $(egrep "^#define\s+_IRREMOTEESP8266_VERSION_$1\s+" \ + src/IRremoteESP8266.h | awk '{print $3;}') +} + +MAJOR=$(getVerNum "MAJOR") +MINOR=$(getVerNum "MINOR") +PATCH=$(getVerNum "PATCH") +LIB_VERSION="${MAJOR}.${MINOR}.${PATCH}" +echo $LIB_VERSION diff --git a/lib/IRremoteESP8266/tools/mkkeywords b/lib/IRremoteESP8266/tools/mkkeywords index e050e4964..34def1332 100644 --- a/lib/IRremoteESP8266/tools/mkkeywords +++ b/lib/IRremoteESP8266/tools/mkkeywords @@ -30,7 +30,8 @@ cat << EndOfTextEndOfTextEndOfText EndOfTextEndOfTextEndOfText -CLASSES=$(egrep -h "^ *((enum|class) |} [a-zA-Z0-9_]+_t;$)" src/*.h | +CLASSES=$(egrep -h \ + "^ *((enum|class|struct) [a-zA-Z0-9_]+|} [a-zA-Z0-9_]+_t;$)" src/*.h | sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sort -u | grep -v "^__") for i in ${CLASSES}; do @@ -45,7 +46,7 @@ cat << EndOfTextEndOfTextEndOfText EndOfTextEndOfTextEndOfText 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" +OURTYPES="match_result_t|stdAc::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)