IRremoteESP8266 library from v2.7.20 to v2.8.0

This commit is contained in:
@cociweb 2021-11-19 23:58:47 +01:00
parent 97ba91ca58
commit 0cbd6cff4a
86 changed files with 9402 additions and 2663 deletions

View File

@ -0,0 +1,3 @@
# Directories & files to ignore from release archives
docs export-ignore
assets export-ignore

View File

@ -71,7 +71,7 @@ Include details about your configuration, circuit and environment:
* Avoid platform-dependent code.
* Use c98 types where possible for better portablity.
* In almost all cases, code & documentation should be peer-reviewed by at least one other contributor.
* The code should pass all the existing testing infrastructure in Travis. e.g. Unit tests, cpplint, and basic compilation.
* The code should pass all the existing testing infrastructure in GitHub Actions. e.g. Unit tests, cpplint, and basic compilation etc.
* State if you have tested this under real conditions if you have, and what other tests you may have carried out.
### Git Commit Messages

View File

@ -18,6 +18,7 @@
- [Mark Kuchel](https://github.com/kuchel77)
- [Christian Nilsson](https://github.com/NiKiZe)
- [Zhongxian Li](https://github.com/siriuslzx)
- [Davide Depau](https://github.com/Depau)
All contributors can be found on the [contributors site](https://github.com/crankyoldgit/IRremoteESP8266/graphs/contributors).

View File

@ -0,0 +1,57 @@
---
name: Problem report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
### Version/revision of the library used
_Typically located in the `library.json` & `src/IRremoteESP8266.h` files in the root directory of the library.
e.g. v2.0.0, or 'master' as at 1st of June, 2017. etc._
### Describe the bug
A clear and concise description of what the bug is.
#### To Reproduce
_What steps did you do, and what did or didn't actually happen? How can we reproduce the issue?_
_e.g._
_1. Initialise the IRsend class._
_2. `IRsend.sendFoobar(0xdeadbeef);`_
_3. Foobar BBQ went into Cow(er)-saving mode and fried me a couple of eggs instead._
#### Example code used
_Include all relevant code snippets or links to the actual code files. Tip: [How to quote your code so it is still readable in the report](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)._
#### Expected behaviour
_What steps did you do and what should it have done? A clear and concise description of what you expected to happen._
_e.g._
_1. Initialise the IRsend class._
_2. `IRsend.sendFoobar(0xdeadbeef);`_
_3. Foobar branded BBQ turns on and cooks me some ribs._
#### Output of raw data from [IRrecvDumpV2.ino](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/IRrecvDumpV2/IRrecvDumpV2.ino) or [V3](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/IRrecvDumpV3/) (if applicable)
_Include some serial text of the raw dumps of what the device saw._
_**Note**: Output from Tasmota is not acceptable. We can't easily use their raw format._
### What brand/model IR demodulator are you using?
_Brand/Model code_ or _None_
_If VS1838b: That is likely your problem. Please buy a better one & try again. Reporting a capture or decode issue when you use one of these is effectively wasting our & your time. See the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions#Help_Im_getting_very_inconsistent_results_when_capturing_an_IR_message_using_a_VS1838b_IR_demodulator)_
### Circuit diagram and hardware used (if applicable)
_Link to an image of the circuit diagram used. Part number of the IR receiver module etc. ESP8266 or ESP32 board type._
### I have followed the steps in the [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions)
_Yes/No._
### Has this library/code previously worked as expected for you?
_Yes/No. If "Yes", which version last worked for you?_
### Other useful information
_More information is always welcome. Be verbose._

View File

@ -1,47 +0,0 @@
_(Please use this template for reporting issues. You can delete what ever is not relevant. Giving us this information will help us help you faster. Please also read the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions) & [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide). Your problem may already have an answer there.)_
### Version/revision of the library used
_Typically located in the `library.json` & `src/IRremoteESP8266.h` files in the root directory of the library.
e.g. v2.0.0, or 'master' as at 1st of June, 2017. etc._
### Expected behavior
_What steps did you do and what should it have done?_
e.g.
1. Initialise the IRsend class.
2. IRsend.sendFoobar(0xdeadbeef);
3. Foobar branded BBQ turns on and cooks me some ribs.
### Actual behavior
_What steps did you do, and what did or didn't actually happen?_
e.g.
1. Initialise the IRsend class.
2. IRsend.sendFoobar(0xdeadbeef);
3. Foobar BBQ went into Cow(er)-saving mode and fried me a couple of eggs instead.
#### Output of raw data from IRrecvDumpV2.ino (if applicable)
_Include some raw dumps of what the device saw._
### What brand/model IR demodulator are you using?
_Brand/Model code_ or _None_
_If VS1838b: That is likely your problem. Please buy a better one & try again. Reporting a capture or decode issue when you use one of these is effectively wasting our & your time. See the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions#Help_Im_getting_very_inconsistent_results_when_capturing_an_IR_message_using_a_VS1838b_IR_demodulator)_
### Steps to reproduce the behavior
_What can we do to (pref. reliably) repeat what is happening?_
#### Example code used
_Include all relevant code snippets or links to the actual code files. Tip: [How to quote your code so it is still readable](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)._
#### Circuit diagram and hardware used (if applicable)
_Link to an image of the circuit diagram used. Part number of the IR receiver module etc. ESP8266 or ESP32 board type._
### I have followed the steps in the [Troubleshooting Guide](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Troubleshooting-Guide) & read the [FAQ](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Frequently-Asked-Questions)
_Yes/No._
### Has this library/code previously worked as expected for you?
_Yes/No. If "Yes", which version last worked for you?_
### Other useful information
_More information is always welcome. Be verbose._

View File

@ -0,0 +1,37 @@
name: Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
Build_Examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v2
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
- name: Set up Python
uses: actions/setup-python@v2
- name: Install PlatformIO
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Build all the examples
env:
PLATFORMIO_BUILD_CACHE_DIR: "../../.pio/buildcache"
run: find . -name platformio.ini -type f | sed 's,/platformio.ini$,,' | xargs --verbose -n 1 pio run --jobs 2 --project-dir

View File

@ -0,0 +1,15 @@
# This is a basic workflow that is triggered on push/PRs to the master branch.
name: Documentation
# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on: [push, pull_request]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
Doxygen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: mattnotmitt/doxygen-action@v1

View File

@ -0,0 +1,28 @@
name: Code Lint
on: [push, pull_request]
jobs:
Linters:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pylint
pip install cpplint
- name: Analysing the code with pylint
run: |
shopt -s nullglob
pylint -d F0001 {src,test,tools}/*.py
- name: Analysing the code with cpplint
run: |
shopt -s nullglob
cpplint --extensions=c,cc,cpp,ino --headers=h,hpp {src,src/locale,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino}

View File

@ -0,0 +1,54 @@
# This is a basic workflow that is triggered on push/PRs to the master branch.
name: Library Linter
# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
arduino-library-manager-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: arduino/arduino-lint-action@v1
with:
library-manager: update
compliance: strict
# Detect case-insensitive file duplication in the same directory.
# This can cause a problem for the Arduino IDE on Windows & Macs.
# See:
# - https://github.com/arduino/Arduino/issues/11441
# - https://github.com/crankyoldgit/IRremoteESP8266/issues/1451
detect-duplicate-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Look for case-insensitive filename collisions.
run: DUPS=$(find . -path '*/.pio' -prune -o -print | sort | uniq -D -i); if [[ -n "${DUPS}" ]]; then echo -e "Duplicates found:\n${DUPS}"; false; fi
version-number-consitent:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check all the version numbers match.
run: |
LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2)
test ${LIB_VERSION} == "$(jq -r .version library.json)"
grep -q "^version=${LIB_VERSION}$" library.properties
examples-have-platformio_ini:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check that every example directory has a platformio.ini file.
run: (status=0; for dir in examples/*; do if [[ ! -f "${dir}/platformio.ini" ]]; then echo "${dir} has no 'platform.ini' file!"; status=1; fi; done; exit ${status})
supported-devices-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check that all files have supported sections.
run: (SUPPORTED_OUTPUT=$(python3 tools/scrape_supported_devices.py --noout --alert 2>&1); if [[ $? -ne 0 || -n "${SUPPORTED_OUTPUT}" ]]; then echo "${SUPPORTED_OUTPUT}"; exit 1; fi)

View File

@ -0,0 +1,23 @@
name: Tests
on: [push, pull_request]
jobs:
Unit_Tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install the Google test suite
env:
MAKEFLAGS: "-j 2"
run: (cd test; make install-googletest)
- name: Build and run the library unit tests.
env:
MAKEFLAGS: "-j 2"
run: (cd test; make run)
- name: Build and run the tools unit tests.
env:
MAKEFLAGS: "-j 2"
run: (cd tools; make all; make run_tests)

View File

@ -22,7 +22,7 @@ examples/**/lib
examples/**/.travis.yml
examples/**/.gitignore
lib/readme.txt
lib/googletest/**/*
lib/googletest/
# GCC pre-compiled headers.
**/*.gch

View File

@ -1,4 +0,0 @@
[submodule "lib/googletest"]
path = lib/googletest
url = https://github.com/google/googletest.git
branch = v1.8.x

View File

@ -1,69 +0,0 @@
language: c
addons:
apt:
packages:
- jq
- doxygen
- graphviz
- python3
- python3-pip
- pylint3
- python3-setuptools
env:
- PLATFORMIO_BUILD_CACHE_DIR="../../.pio/buildcache"
IRRECVDUMP_RE=".*IRrecvDumpV.*"
IRMQTTSERVER_RE=".*IRMQTTServer.*"
MAKEFLAGS="-j 2"
cache:
directories:
- "~/.platformio"
before_install:
- wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py
- python --version && pip --version
- python3 --version && pip3 --version
- sudo pip3 install -U platformio
- pio update
script: echo Running checks
notifications:
email:
on_success: change
on_failure: change
jobs:
include:
- name: "Compile the trivial examples"
script:
# Check that everything compiles but some heavy tasks e.g. IRMQTTServer & IRrecvDumpV2+
# i.e. We are splitting the work load in to parallel tasks.
- find . -regextype egrep -name platformio.ini -type f \! -regex "${IRRECVDUMP_RE}|${IRMQTTSERVER_RE}" | sed 's,/platformio.ini$,,' | xargs --verbose -n 1 pio run --jobs 2 --project-dir
- name: "Compile the IRrecvDumpV2+ examples"
script:
# Check that IRrecvDumpV2+ compiles.
# i.e. We are splitting the work load in to parallel tasks.
- find . -regextype egrep -name platformio.ini -type f -regex "${IRRECVDUMP_RE}" | sed 's,/platformio.ini$,,' | xargs --verbose -n 1 pio run --jobs 2 --project-dir
- name: "Unit tests, Linter, Doc checks, & Compile IRMQTTServer"
script:
# Run all the Tests & check the code linters have no issues, and that
# IRMQTTServer compiles.
# i.e. We are splitting the work load in to parallel tasks.
# Check the version numbers match.
- LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2)
- test ${LIB_VERSION} == "$(jq -r .version library.json)"
- grep -q "^version=${LIB_VERSION}$" library.properties
# Check the tools programs compile.
- (cd tools; make all)
# Check for lint issues.
- shopt -s nullglob
- python cpplint.py --extensions=c,cc,cpp,ino --headers=h,hpp {src,src/locale,test,tools}/*.{h,c,cc,cpp,hpp,ino} examples/*/*.{h,c,cc,cpp,hpp,ino}
- pylint3 -d F0001 {src,test,tools}/*.py
- shopt -u nullglob
# Build and run the unit tests.
- (cd test; make run)
- (cd tools; make run_tests)
# Check that every example directory has a platformio.ini file.
- (status=0; for dir in examples/*; do if [[ ! -f "${dir}/platformio.ini" ]]; then echo "${dir} has no 'platform.ini' file!"; status=1; fi; done; exit ${status})
# Check that doxygen completes without errors or warnings.
- (DOXYGEN_OUTPUT=$(doxygen 2>&1); if [[ $? -ne 0 || -n "${DOXYGEN_OUTPUT}" ]]; then echo "${DOXYGEN_OUTPUT}"; exit 1; fi)
# Check that all files has supported sections.
- (SUPPORTED_OUTPUT=$(python3 tools/scrape_supported_devices.py --noout --alert 2>&1); if [[ $? -ne 0 || -n "${SUPPORTED_OUTPUT}" ]]; then echo "${SUPPORTED_OUTPUT}"; exit 1; fi)
# Check that IRMQTTServer compiles.
- find . -regextype egrep -name platformio.ini -type f -regex "${IRMQTTSERVER_RE}" | sed 's,/platformio.ini$,,' | xargs --verbose -n 1 pio run --jobs 2 --project-dir

View File

@ -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.0 Now Available
Version 2.8.0 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.

View File

@ -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.0 jetzt verfügbar
Version 2.8.0 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.

View File

@ -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.0 disponible
Version 2.8.0 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.

View File

@ -1,5 +1,73 @@
# Release Notes
## _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]**

View File

@ -0,0 +1,3 @@
# Security Policy
You can find this library's <a href="https://crankyoldgit.github.io/IRremoteESP8266/SECURITY">Security Policy</a> and contact procedure at https://crankyoldgit.github.io/IRremoteESP8266/SECURITY

View File

@ -1,14 +1,16 @@
<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: Sat 28 Aug 2021 07:53:10 +0000 --->
Last generated: Fri 19 Nov 2021 00:35:37 +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** | RD1A1 remote<BR>SMVH09B-2A2A3NH ref. 409730 A/C | | - |
| [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<BR>RC04 remote<BR>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<BR>TAC-444 remote<BR>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<BR>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<BR>53NGK009/012 Inverter<BR>619EGX0090E0 A/C<BR>619EGX0120E0 A/C<BR>619EGX0180E0 A/C<BR>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 |
@ -26,11 +28,13 @@
| [Doshisha](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Doshisha.cpp) | **Doshisha** | CZ-S32D LED Light<BR>CZ-S38D LED Light<BR>CZ-S50D LED Light<BR>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<BR>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<BR>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<BR>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<BR>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<BR>YKR-H/102E remote | | Yes |
| [EliteScreens](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_EliteScreens.cpp) | **Elite Screens** | CineTension2 / CineTension3 series<BR>Home2 / Home3 series<BR>Spectrum series<BR>VMAX Plus4 series<BR>VMAX2 / VMAX2 Plus series<BR>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 | | - |
| [Epson](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Epson.cpp) | **Epson** | EN-TW9100W Projector<BR>EX3220 Projector<BR>EX5220 Projector<BR>EX5230 Projector<BR>EX6220 Projector<BR>EX7220 Projector<BR>VS230 Projector<BR>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)<BR>AR-DB1 remote (ARDB1)<BR>AR-DL10 remote (ARDB1)<BR>AR-RAC1E remote (ARRAH2E)<BR>AR-RAE1E remote (ARRAH2E)<BR>AR-RAH1U remote (ARREB1E)<BR>AR-RAH2E remote (ARRAH2E)<BR>AR-REB1E remote (ARREB1E)<BR>AR-REW4E remote (ARREW4E)<BR>AR-RY4 remote (ARRY4)<BR>AST9RSGCW A/C (ARDB1)<BR>ASTB09LBC A/C (ARRY4)<BR>ASU12RLF A/C (ARREB1E)<BR>ASU30C1 A/C (ARDB1)<BR>ASYG09KETA-B A/C (ARREW4E)<BR>ASYG30LFCA A/C (ARRAH2E)<BR>ASYG7LMCA A/C (ARREB1E) | ARDB1<BR>ARJW2<BR>ARRAH2E<BR>ARREB1E<BR>ARREW4E<BR>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)<BR>AR-JW2 remote (ARJW2)<BR>AR-RCE1E remote (ARRAH2E)<BR>ASHG09LLCA A/C (ARRAH2E) | ARDB1<BR>ARJW2<BR>ARRAH2E<BR>ARREB1E<BR>ARREW4E<BR>ARRY4 | Yes |
| [GICable](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_GICable.cpp) | **G.I. Cable** | XRC-200 remote | | - |
@ -39,11 +43,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<BR>YX1FF remote | YAW1F<BR>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<BR>YB1F2 remote | YAW1F<BR>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<BR>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<BR>YB1F2F remote | YAW1F<BR>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<BR>VIR12HP230V1AH A/C<BR>YAA1FBF remote<BR>YAN1F1 remote<BR>YB1F2F remote | YAW1F<BR>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<BR>YBOFB2 remote | YAW1F<BR>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<BR>YAW1F remote | YAW1F<BR>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<BR>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)<BR>HSU07-HEA03 remote (HAIER_AC)<BR>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<BR>YACIFB remote | YAW1F<BR>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)<BR>HSU07-HEA03 remote (HAIER_AC)<BR>V9014557 M47 8D remote (HAIER_AC176)<BR>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)<BR>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)<BR>LT0541-HTA remote (HITACHI_AC1)<BR>PC-LH3B (HITACHI_AC3)<BR>R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1)<BR>RAR-8P2 remote (HITACHI_AC424)<BR>RAS-22NK A/C (HITACHI_AC344)<BR>RAS-35THA6 remote<BR>RAS-AJ25H A/C (HITACHI_AC424)<BR>RF11T1 remote (HITACHI_AC344)<BR>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 +59,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<BR>KSV26HRC A/C<BR>KSV35CRC A/C<BR>KSV35HRC A/C<BR>KSV53HRC A/C<BR>KSV62HRC A/C<BR>KSV70CRC A/C<BR>KSV70HRC A/C<BR>KSV80HRC A/C<BR>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<BR>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)<BR>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)<BR>A4UW30GFA2 A/C (LG2 - AKB74955603 & AKB73757604)<BR>AKB73757604 remote (LG2 - AKB73757604)<BR>AKB74395308 remote (LG2)<BR>AKB74955603 remote (LG2 - AKB74955603)<BR>AKB75215403 remote (LG2)<BR>AMNW09GSJA0 A/C (LG2 - AKB74955603)<BR>AMNW24GTPA1 A/C (LG2 - AKB73757604)<BR>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)<BR>A4UW30GFA2 A/C (LG2 - AKB74955603 & AKB73757604)<BR>AKB73315611 remote (LG2 - AKB74955603)<BR>AKB73757604 remote (LG2 - AKB73757604)<BR>AKB74395308 remote (LG2)<BR>AKB74955603 remote (LG2 - AKB74955603)<BR>AKB75215403 remote (LG2)<BR>AMNW09GSJA0 A/C (LG2 - AKB74955603)<BR>AMNW24GTPA1 A/C (LG2 - AKB73757604)<BR>MS05SQ NW0 A/C (LG2 - AKB74955603)<BR>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<BR>MIR-ITFS-F remote<BR>MIR-ITFS-LF remote<BR>SP-HT remote | | - |
@ -69,7 +75,9 @@
| [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)<BR>RUBO18GMFILCAD A/C (18K BTU) (MIDEA)<BR>RYBO12GMFILCAD A/C (12K BTU) (MIDEA)<BR>UB018GMFILCFHD A/C (12K BTU) (MIDEA)<BR>WS012GMFI22HLD A/C (12K BTU) (MIDEA)<BR>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)<BR>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<BR>MX-CH18CF A/C | KKG29AC1<BR>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<BR>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<BR>Reykir 9000 A/C | KKG29AC1<BR>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)<BR>KM14A 0179213 remote<BR>MS-GK24VA A/C<BR>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)<BR>KPOA remote (MITSUBISHI112)<BR>MLZ-RX5017AS A/C (MITSUBISHI_AC)<BR>MSH-A24WV A/C (MITSUBISHI112)<BR>MSZ-GV2519 A/C (MITSUBISHI_AC)<BR>MSZ-SF25VE3 A/C (MITSUBISHI_AC)<BR>MSZ-ZW4017S A/C (MITSUBISHI_AC)<BR>MUH-A24WV A/C (MITSUBISHI112)<BR>PEAD-RP71JAA Ducted A/C (MITSUBISHI136)<BR>RH151/M21ED6426 remote (MITSUBISHI_AC)<BR>SG153/M21EDF426 remote (MITSUBISHI_AC)<BR>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)<BR>RLA502A700B remote (152 bit)<BR>SRKxxZJ-S A/C (88 bit)<BR>SRKxxZM-S A/C (152 bit)<BR>SRKxxZMXA-S A/C (152 bit) | | Yes |
@ -87,18 +95,21 @@
| [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)<BR>Standard RC-5 (RC5)<BR>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)<BR>AK59-00167A Bluray remote (SAMSUNG36)<BR>AR09FSSDAWKNFA A/C (SAMSUNG_AC)<BR>AR09HSFSBWKN A/C (SAMSUNG_AC)<BR>AR12HSSDBWKNEU A/C (SAMSUNG_AC)<BR>AR12KSFPEWQNET A/C (SAMSUNG_AC)<BR>AR12NXCXAWKXEU A/C (SAMSUNG_AC)<BR>BN59-01178B TV remote (SAMSUNG)<BR>DB63-03556X003 remote<BR>DB93-14195A remote (SAMSUNG_AC)<BR>DB93-16761C remote<BR>HW-J551 Soundbar (SAMSUNG36)<BR>IEC-R03 remote<BR>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)<BR>AK59-00167A Bluray remote (SAMSUNG36)<BR>AR09FSSDAWKNFA A/C (SAMSUNG_AC)<BR>AR09HSFSBWKN A/C (SAMSUNG_AC)<BR>AR12HSSDBWKNEU A/C (SAMSUNG_AC)<BR>AR12KSFPEWQNET A/C (SAMSUNG_AC)<BR>AR12NXCXAWKXEU A/C (SAMSUNG_AC)<BR>AR12TXEAAWKNEU A/C (SAMSUNG_AC)<BR>BN59-01178B TV remote (SAMSUNG)<BR>DB63-03556X003 remote<BR>DB93-14195A remote (SAMSUNG_AC)<BR>DB93-16761C remote<BR>DB96-24901C remote (SAMSUNG_AC)<BR>HW-J551 Soundbar (SAMSUNG36)<BR>IEC-R03 remote<BR>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)<BR>RCS-2HS4E remote (SANYO_AC)<BR>RCS-2S4E remote (SANYO_AC)<BR>SA 8650B - disabled<BR>SAP-K121AHA A/C (SANYO_AC)<BR>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)<BR>AH-AxSAY A/C (A907)<BR>AH-PR13-GL A/C (A903)<BR>AH-XP10NRY A/C (A903)<BR>AY-ZP40KR A/C (A907)<BR>CRMC-820 JBEZ remote (A903)<BR>CRMC-A705 JBEZ remote (A705)<BR>CRMC-A863 JBEZ remote (A903)<BR>CRMC-A903JBEZ remote (A903)<BR>CRMC-A907 JBEZ remote (A907)<BR>LC-52D62U TV | A705<BR>A903<BR>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)<BR>AH-AxSAY A/C (A907)<BR>AH-PR13-GL A/C (A903)<BR>AH-XP10NRY A/C (A903)<BR>AY-ZP40KR A/C (A907)<BR>CRMC-820 JBEZ remote (A903)<BR>CRMC-A705 JBEZ remote (A705)<BR>CRMC-A863 JBEZ remote (A903)<BR>CRMC-A903JBEZ remote (A903)<BR>CRMC-A907 JBEZ remote (A907)<BR>CRMC-A950 JBEZ (A907)<BR>LC-52D62U TV | A705<BR>A903<BR>A907 | Yes |
| [Sherwood](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Sherwood.cpp) | **Sherwood** | RC-138 remote<BR>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<BR>WP-YK8 090218 remote | | - |
| [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **SamHop** | SM3015 Fan Remote Control<BR>SM5021 Encoder chip<BR>SM5032 Decoder chip | | - |
| [Symphony](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Symphony.cpp) | **Satellite Electronic** | ID6 Remote<BR>JY199I Fan driver<BR>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<BR>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) | **[Leberg](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | LBS-TOR07 A/C (TAC09CHSD) | GZ055BE1<BR>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<BR>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)<BR>GZ-055B-E1 remote (GZ055BE1) | GZ055BE1<BR>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<BR>SAC9010QC remote | | Yes |
| [Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teknopoint.cpp) | **Teknopoint** | Allegro SSA-09H A/C<BR>GZ-055B-E1 remote | | - |
@ -127,10 +138,12 @@
## Send & decodable protocols:
- AIRTON
- AIRWELL
- AIWA_RC_T501
- AMCOR
- ARGO
- ARRIS
- BOSE
- CARRIER_AC
- CARRIER_AC40
@ -202,6 +215,7 @@
- RC5X
- RC6
- RCMM
- RHOSS
- SAMSUNG
- SAMSUNG36
- SAMSUNG_AC

View File

@ -261,8 +261,8 @@ const uint16_t kMinUnknownSize = 2 * 10;
#define KEY_RX_GPIO "rx"
// Text for Last Will & Testament status messages.
const char* kLwtOnline = "Online";
const char* kLwtOffline = "Offline";
const char* const kLwtOnline = "Online";
const char* const kLwtOffline = "Offline";
const uint8_t kHostnameLength = 30;
const uint8_t kPortLength = 5; // Largest value of uint16_t is "65535".
@ -285,7 +285,7 @@ const uint16_t kJsonAcStateMaxSize = 1024; // Bytes
// ----------------- End of User Configuration Section -------------------------
// Constants
#define _MY_VERSION_ "v1.5.2"
#define _MY_VERSION_ "v1.6.0"
const uint8_t kRebootTime = 15; // Seconds
const uint8_t kQuickDisplayTime = 2; // Seconds
@ -310,29 +310,29 @@ const int8_t kRxGpios[] = {
// JSON stuff
// Name of the json config file in SPIFFS.
const char* kConfigFile = "/config.json";
const char* kMqttServerKey = "mqtt_server";
const char* kMqttPortKey = "mqtt_port";
const char* kMqttUserKey = "mqtt_user";
const char* kMqttPassKey = "mqtt_pass";
const char* kMqttPrefixKey = "mqtt_prefix";
const char* kHostnameKey = "hostname";
const char* kHttpUserKey = "http_user";
const char* kHttpPassKey = "http_pass";
const char* kCommandDelimiter = ",";
const char* const kConfigFile = "/config.json";
const char* const kMqttServerKey = "mqtt_server";
const char* const kMqttPortKey = "mqtt_port";
const char* const kMqttUserKey = "mqtt_user";
const char* const kMqttPassKey = "mqtt_pass";
const char* const kMqttPrefixKey = "mqtt_prefix";
const char* const kHostnameKey = "hostname";
const char* const kHttpUserKey = "http_user";
const char* const kHttpPassKey = "http_pass";
const char* const kCommandDelimiter = ",";
// URLs
const char* kUrlRoot = "/";
const char* kUrlAdmin = "/admin";
const char* kUrlAircon = "/aircon";
const char* kUrlSendDiscovery = "/send_discovery";
const char* kUrlExamples = "/examples";
const char* kUrlGpio = "/gpio";
const char* kUrlGpioSet = "/gpio/set";
const char* kUrlInfo = "/info";
const char* kUrlReboot = "/quitquitquit";
const char* kUrlWipe = "/reset";
const char* kUrlClearMqtt = "/clear_retained";
const char* const kUrlRoot = "/";
const char* const kUrlAdmin = "/admin";
const char* const kUrlAircon = "/aircon";
const char* const kUrlSendDiscovery = "/send_discovery";
const char* const kUrlExamples = "/examples";
const char* const kUrlGpio = "/gpio";
const char* const kUrlGpioSet = "/gpio/set";
const char* const kUrlInfo = "/info";
const char* const kUrlReboot = "/quitquitquit";
const char* const kUrlWipe = "/reset";
const char* const kUrlClearMqtt = "/clear_retained";
#if MQTT_ENABLE
const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds.
@ -340,7 +340,7 @@ const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000; // mSeconds.
// Default is 5 seconds per IR TX GPIOs (channels) used.
const uint32_t kStatListenPeriodMs = 5 * 1000 * kNrOfIrTxGpios; // mSeconds
const int32_t kMaxPauseMs = 10000; // 10 Seconds.
const char* kSequenceDelimiter = ";";
const char* const kSequenceDelimiter = ";";
const char kPauseChar = 'P';
#if defined(ESP8266)
const uint32_t kChipId = ESP.getChipId();
@ -349,7 +349,7 @@ const uint32_t kChipId = ESP.getChipId();
const uint32_t kChipId = ESP.getEfuseMac(); // Discard the top 16 bits.
#endif // ESP32
const char* kClimateTopics =
static const char kClimateTopics[] PROGMEM =
"(" KEY_PROTOCOL "|" KEY_MODEL "|" KEY_POWER "|" KEY_MODE "|" KEY_TEMP "|"
KEY_FANSPEED "|" KEY_SWINGV "|" KEY_SWINGH "|" KEY_QUIET "|"
KEY_TURBO "|" KEY_LIGHT "|" KEY_BEEP "|" KEY_ECONO "|" KEY_SLEEP "|"
@ -358,7 +358,7 @@ const char* kClimateTopics =
"|" KEY_JSON
#endif // MQTT_CLIMATE_JSON
")<br>";
const char* kMqttTopics[] = {
static const char* const kMqttTopics[] = {
KEY_PROTOCOL, KEY_MODEL, KEY_POWER, KEY_MODE, KEY_TEMP, KEY_FANSPEED,
KEY_SWINGV, KEY_SWINGH, KEY_QUIET, KEY_TURBO, KEY_LIGHT, KEY_BEEP,
KEY_ECONO, KEY_SLEEP, KEY_FILTER, KEY_CLEAN, KEY_CELSIUS, KEY_RESEND,

View File

@ -1,6 +1,6 @@
/*
* Send & receive arbitrary IR codes via a web server or MQTT.
* Copyright David Conran 2016, 2017, 2018, 2019, 2020
* Copyright David Conran 2016-2021
*
* Copyright:
* Code for this has been borrowed from lots of other OpenSource projects &
@ -252,7 +252,8 @@
* - "Middle"
* - "Low"
* - "Lowest"
* power_command_topic: "ir_server/ac/cmnd/power"
* # `power_command_topic` is probably not needed for most HA configurations
* # power_command_topic: "ir_server/ac/cmnd/power"
* mode_command_topic: "ir_server/ac/cmnd/mode"
* mode_state_topic: "ir_server/ac/stat/mode"
* temperature_command_topic: "ir_server/ac/cmnd/temp"
@ -360,6 +361,12 @@
#include <memory>
#include <string>
#ifdef ESP32
#ifdef F
#undef F
#endif // F
#define F(string) string
#endif // ESP32
using irutils::msToString;
#if REPORT_VCC
@ -380,7 +387,7 @@ MDNSResponder mdns;
WiFiClient espClient;
WiFiManager wifiManager;
bool flagSaveWifiConfig = false;
char HttpUsername[kUsernameLength + 1] = "admin"; // Default HTT username.
char HttpUsername[kUsernameLength + 1] = "admin"; // Default HTTP username.
char HttpPassword[kPasswordLength + 1] = ""; // No HTTP password by default.
char Hostname[kHostnameLength + 1] = "ir_server"; // Default hostname.
uint16_t *codeArray;
@ -398,14 +405,14 @@ String lastClimateSource;
IRrecv *irrecv = NULL;
decode_results capture; // Somewhere to store inbound IR messages.
int8_t rx_gpio = kDefaultIrRx;
String lastIrReceived = "None";
String lastIrReceived = FPSTR("None");
uint32_t lastIrReceivedTime = 0;
uint32_t irRecvCounter = 0;
#endif // IR_RX
// Climate stuff
IRac *climate[kNrOfIrTxGpios];
String channel_re = "("; // Will be built later.
String channel_re = FPSTR("("); // Will be built later.
uint16_t chan = 0; // The channel to use for the aircon HTML page.
TimerMs lastClimateIr = TimerMs(); // When we last sent the IR Climate mesg.
@ -416,8 +423,8 @@ bool hasClimateBeenSent = false; // Has the Climate ever been sent?
#if MQTT_ENABLE
PubSubClient mqtt_client(espClient);
String lastMqttCmd = "None";
String lastMqttCmdTopic = "None";
String lastMqttCmd = FPSTR("None");
String lastMqttCmdTopic = FPSTR("None");
uint32_t lastMqttCmdTime = 0;
uint32_t lastConnectedTime = 0;
uint32_t lastDisconnectedTime = 0;
@ -654,7 +661,7 @@ String listOfTxGpios(void) {
if (i) result += ", ";
result += gpioToString(txGpioTable[i]);
if (!found && txGpioTable[i] == getDefaultTxGpio()) {
result += " (default)";
result += F(" (default)");
found = true;
}
}
@ -894,7 +901,7 @@ void handleExamples(void) {
#endif // EXAMPLES_ENABLE
String htmlSelectBool(const String name, const bool def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (uint16_t i = 0; i < 2; i++)
html += htmlOptionItem(IRac::boolToString(i), IRac::boolToString(i),
i == def);
@ -903,7 +910,7 @@ String htmlSelectBool(const String name, const bool def) {
}
String htmlSelectClimateProtocol(const String name, const decode_type_t def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (uint8_t i = 1; i <= decode_type_t::kLastDecodeType; i++) {
if (IRac::isProtocolSupported((decode_type_t)i)) {
html += htmlOptionItem(String(i), typeToString((decode_type_t)i),
@ -915,14 +922,14 @@ String htmlSelectClimateProtocol(const String name, const decode_type_t def) {
}
String htmlSelectModel(const String name, const int16_t def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (int16_t i = -1; i <= 6; i++) {
String num = String(i);
String text;
if (i == -1)
text = F("Default");
else if (i == 0)
text = F("Unknown");
text = kUnknownStr;
else
text = num;
html += htmlOptionItem(num, text, i == def);
@ -933,7 +940,7 @@ String htmlSelectModel(const String name, const int16_t def) {
String htmlSelectUint(const String name, const uint16_t max,
const uint16_t def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (uint16_t i = 0; i < max; i++) {
String num = String(i);
html += htmlOptionItem(num, num, i == def);
@ -944,7 +951,7 @@ String htmlSelectUint(const String name, const uint16_t max,
String htmlSelectGpio(const String name, const int16_t def,
const int8_t list[], const int16_t length) {
String html = ": <select name='" + name + "'>";
String html = F(": <select name='") + name + F("'>");
for (int16_t i = 0; i < length; i++) {
String num = String(list[i]);
html += htmlOptionItem(num, list[i] == kGpioUnused ? F("Unused") : num,
@ -956,7 +963,7 @@ String htmlSelectGpio(const String name, const int16_t def,
}
String htmlSelectMode(const String name, const stdAc::opmode_t def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (int8_t i = -1; i <= (int8_t)stdAc::opmode_t::kLastOpmodeEnum; i++) {
String mode = IRac::opmodeToString((stdAc::opmode_t)i);
html += htmlOptionItem(mode, mode, (stdAc::opmode_t)i == def);
@ -966,7 +973,7 @@ String htmlSelectMode(const String name, const stdAc::opmode_t def) {
}
String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (int8_t i = 0; i <= (int8_t)stdAc::fanspeed_t::kLastFanspeedEnum; i++) {
String speed = IRac::fanspeedToString((stdAc::fanspeed_t)i);
html += htmlOptionItem(speed, speed, (stdAc::fanspeed_t)i == def);
@ -976,7 +983,7 @@ String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def) {
}
String htmlSelectSwingv(const String name, const stdAc::swingv_t def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (int8_t i = -1; i <= (int8_t)stdAc::swingv_t::kLastSwingvEnum; i++) {
String swing = IRac::swingvToString((stdAc::swingv_t)i);
html += htmlOptionItem(swing, swing, (stdAc::swingv_t)i == def);
@ -986,7 +993,7 @@ String htmlSelectSwingv(const String name, const stdAc::swingv_t def) {
}
String htmlSelectSwingh(const String name, const stdAc::swingh_t def) {
String html = "<select name='" + name + "'>";
String html = F("<select name='") + name + F("'>");
for (int8_t i = -1; i <= (int8_t)stdAc::swingh_t::kLastSwinghEnum; i++) {
String swing = IRac::swinghToString((stdAc::swingh_t)i);
html += htmlOptionItem(swing, swing, (stdAc::swingh_t)i == def);
@ -1030,84 +1037,85 @@ void handleAirCon(void) {
String html = htmlHeader(F("Air Conditioner Control"));
html += htmlMenu();
if (kNrOfIrTxGpios > 1) {
html += "<form method='POST' action='/aircon/set'"
html += F("<form method='POST' action='/aircon/set'"
" enctype='multipart/form-data'>"
"<table>"
"<tr><td><b>Climate #</b></td><td>" +
"<tr><td><b>Climate #</b></td><td>") +
htmlSelectUint(KEY_CHANNEL, kNrOfIrTxGpios, chan) +
"<input type='submit' value='Change'>"
F("<input type='submit' value='Change'>"
"</td></tr>"
"</table>"
"</form>"
"<hr>";
"<hr>");
}
if (climate[chan] != NULL) {
html += "<h3>Current Settings</h3>"
html += F("<h3>Current Settings</h3>"
"<form method='POST' action='/aircon/set'"
" enctype='multipart/form-data'>"
"<input type='hidden' name='" KEY_CHANNEL "' value='" + String(chan) +
"'>" +
"<table style='width:33%'>"
"<tr><td>" D_STR_PROTOCOL "</td><td>" +
"<input type='hidden' name='" KEY_CHANNEL "' value='") + String(chan) +
F("'>") +
F("<table style='width:33%'>"
"<tr><td>" D_STR_PROTOCOL "</td><td>") +
htmlSelectClimateProtocol(KEY_PROTOCOL,
climate[chan]->next.protocol) +
"</td></tr>"
"<tr><td>" D_STR_MODEL "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_MODEL "</td><td>") +
htmlSelectModel(KEY_MODEL, climate[chan]->next.model) +
"</td></tr>"
"<tr><td>" D_STR_POWER "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_POWER "</td><td>") +
htmlSelectBool(KEY_POWER, climate[chan]->next.power) +
"</td></tr>"
"<tr><td>" D_STR_MODE "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_MODE "</td><td>") +
htmlSelectMode(KEY_MODE, climate[chan]->next.mode) +
"</td></tr>"
F("</td></tr>"
"<tr><td>" D_STR_TEMP "</td><td>"
"<input type='number' name='" KEY_TEMP "' min='16' max='90' "
"step='0.5' value='" + String(climate[chan]->next.degrees, 1) + "'>"
"step='0.5' value='") + String(climate[chan]->next.degrees, 1) +
F("'>"
"<select name='" KEY_CELSIUS "'>"
"<option value='on'" +
"<option value='on'") +
(climate[chan]->next.celsius ? " selected='selected'" : "") +
">C</option>"
"<option value='off'" +
(!climate[chan]->next.celsius ? " selected='selected'" : "") +
">F</option>"
F(">F</option>"
"</select></td></tr>"
"<tr><td>" D_STR_FAN "</td><td>" +
"<tr><td>" D_STR_FAN "</td><td>") +
htmlSelectFanspeed(KEY_FANSPEED, climate[chan]->next.fanspeed) +
"</td></tr>"
"<tr><td>" D_STR_SWINGV "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_SWINGV "</td><td>") +
htmlSelectSwingv(KEY_SWINGV, climate[chan]->next.swingv) +
"</td></tr>"
"<tr><td>" D_STR_SWINGH "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_SWINGH "</td><td>") +
htmlSelectSwingh(KEY_SWINGH, climate[chan]->next.swingh) +
"</td></tr>"
"<tr><td>" D_STR_QUIET "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_QUIET "</td><td>") +
htmlSelectBool(KEY_QUIET, climate[chan]->next.quiet) +
"</td></tr>"
"<tr><td>" D_STR_TURBO "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_TURBO "</td><td>") +
htmlSelectBool(KEY_TURBO, climate[chan]->next.turbo) +
"</td></tr>"
"<tr><td>" D_STR_ECONO "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_ECONO "</td><td>") +
htmlSelectBool(KEY_ECONO, climate[chan]->next.econo) +
"</td></tr>"
"<tr><td>" D_STR_LIGHT "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_LIGHT "</td><td>") +
htmlSelectBool(KEY_LIGHT, climate[chan]->next.light) +
"</td></tr>"
"<tr><td>" D_STR_FILTER "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_FILTER "</td><td>") +
htmlSelectBool(KEY_FILTER, climate[chan]->next.filter) +
"</td></tr>"
"<tr><td>" D_STR_CLEAN "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_CLEAN "</td><td>") +
htmlSelectBool(KEY_CLEAN, climate[chan]->next.clean) +
"</td></tr>"
"<tr><td>" D_STR_BEEP "</td><td>" +
F("</td></tr>"
"<tr><td>" D_STR_BEEP "</td><td>") +
htmlSelectBool(KEY_BEEP, climate[chan]->next.beep) +
"</td></tr>"
"<tr><td>Force resend</td><td>" +
F("</td></tr>"
"<tr><td>Force resend</td><td>") +
htmlSelectBool(KEY_RESEND, false) +
"</td></tr>"
F("</td></tr>"
"</table>"
"<input type='submit' value='Update & Send'>"
"</form>";
"</form>");
}
html += htmlEnd();
server.send(200, "text/html", html);
@ -1232,124 +1240,127 @@ void handleInfo(void) {
String html = htmlHeader(F("IR MQTT server info"));
html += htmlMenu();
html +=
"<h3>General</h3>"
"<p>Hostname: " + String(Hostname) + "<br>"
"IP address: " + WiFi.localIP().toString() + "<br>"
"MAC address: " + WiFi.macAddress() + "<br>"
"Booted: " + timeSince(1) + "<br>" +
"Version: " _MY_VERSION_ "<br>"
F("<h3>General</h3>"
"<p>Hostname: ") + String(Hostname) + F("<br>"
"IP address: ") + WiFi.localIP().toString() + F("<br>"
"MAC address: ") + WiFi.macAddress() + F("<br>"
"Booted: ") + timeSince(1) + F("<br>") +
F("Version: " _MY_VERSION_ "<br>"
"Built: " __DATE__
" " __TIME__ "<br>"
"Period Offset: " + String(offset) + "us<br>"
"Period Offset: ") + String(offset) + F("us<br>"
"IR Lib Version: " _IRREMOTEESP8266_VERSION_ "<br>"
#if defined(ESP8266)
"ESP8266 Core Version: " + ESP.getCoreVersion() + "<br>"
"Free Sketch Space: " + String(maxSketchSpace() >> 10) + "k<br>"
"ESP8266 Core Version: ") + ESP.getCoreVersion() + F("<br>"
"Free Sketch Space: ") + String(maxSketchSpace() >> 10) + F("k<br>"
#endif // ESP8266
#if defined(ESP32)
"ESP32 SDK Version: " + ESP.getSdkVersion() + "<br>"
"ESP32 SDK Version: ") + ESP.getSdkVersion() + F("<br>"
#endif // ESP32
"Cpu Freq: " + String(ESP.getCpuFreqMHz()) + "MHz<br>"
"Sanity Check: " + String((_sanity == 0) ? "Ok" : "FAILED") + "<br>"
"IR Send GPIO(s): " + listOfTxGpios() + "<br>"
"Cpu Freq: ") + String(ESP.getCpuFreqMHz()) + F("MHz<br>"
"Sanity Check: ") + String((_sanity == 0) ? F("Ok") : F("FAILED")) +
F("<br>"
"IR Send GPIO(s): ") + listOfTxGpios() + F("<br>")
+ irutils::addBoolToString(kInvertTxOutput,
"Inverting GPIO output", false) + "<br>"
"Total send requests: " + String(sendReqCounter) + "<br>"
"Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") +
" <i>(" + timeSince(lastSendTime) + ")</i><br>"
F("Inverting GPIO output"), false) + F("<br>"
"Total send requests: ") + String(sendReqCounter) + F("<br>"
"Last message sent: ") + String(lastSendSucceeded ? F("Ok") : F("FAILED")) +
F(" <i>(") + timeSince(lastSendTime) + F(")</i><br>"
#if IR_RX
"IR Recv GPIO: " + gpioToString(rx_gpio) +
"IR Recv GPIO: ") + gpioToString(rx_gpio) + F(
#if IR_RX_PULLUP
" (pullup)"
#endif // IR_RX_PULLUP
"<br>"
"Total IR Received: " + String(irRecvCounter) + "<br>"
"Last IR Received: " + lastIrReceived +
" <i>(" + timeSince(lastIrReceivedTime) + ")</i><br>"
"Total IR Received: ") + String(irRecvCounter) + F("<br>"
"Last IR Received: ") + lastIrReceived +
F(" <i>(") + timeSince(lastIrReceivedTime) + F(")</i><br>"
#endif // IR_RX
"Duplicate " D_STR_WIFI " networks: " +
String(HIDE_DUPLICATE_NETWORKS ? "Hide" : "Show") + "<br>"
"Duplicate " D_STR_WIFI " networks: ") +
String(HIDE_DUPLICATE_NETWORKS ? F("Hide") : F("Show")) + F("<br>"
"Min " D_STR_WIFI " signal required: "
#ifdef MIN_SIGNAL_STRENGTH
+ String(static_cast<int>(MIN_SIGNAL_STRENGTH)) +
) + // NOLINT(whitespace/parens)
String(static_cast<int>(MIN_SIGNAL_STRENGTH)) + F(
#else // MIN_SIGNAL_STRENGTH
"8"
#endif // MIN_SIGNAL_STRENGTH
"%<br>"
"Serial debugging: "
#if DEBUG
+ String(isSerialGpioUsedByIr() ? D_STR_OFF : D_STR_ON) +
) + // NOLINT(whitespace/parens)
String(isSerialGpioUsedByIr() ? D_STR_OFF : D_STR_ON) + F(
#else // DEBUG
D_STR_OFF
#endif // DEBUG
"<br>"
#if REPORT_VCC
"Vcc: ";
"Vcc: ");
html += vccToString();
html += "V<br>"
html += F("V<br>"
#endif // REPORT_VCC
"</p>"
#if MQTT_ENABLE
"<h4>MQTT Information</h4>"
"<p>Server: " + String(MqttServer) + ":" + String(MqttPort) + " <i>(" +
"<p>Server: ") + String(MqttServer) + ":" + String(MqttPort) + F(" <i>(") +
(mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime)
: "Disconnected " + timeSince(lastConnectedTime)) +
")</i><br>"
"Disconnections: " + String(mqttDisconnectCounter - 1) + "<br>"
"Buffer Size: " + String(mqtt_client.getBufferSize()) + " bytes<br>"
"Client id: " + MqttClientId + "<br>"
"Command topic(s): " + listOfCommandTopics() + "<br>"
"Acknowledgements topic: " + MqttAck + "<br>"
F(")</i><br>"
"Disconnections: ") + String(mqttDisconnectCounter - 1) + F("<br>"
"Buffer Size: ") + String(mqtt_client.getBufferSize()) + F(" bytes<br>"
"Client id: ") + MqttClientId + F("<br>"
"Command topic(s): ") + listOfCommandTopics() + F("<br>"
"Acknowledgements topic: ") + MqttAck + F("<br>"
#if IR_RX
"IR Received topic: " + MqttRecv + "<br>"
"IR Received topic: ") + MqttRecv + F("<br>"
#endif // IR_RX
"Log topic: " + MqttLog + "<br>"
"LWT topic: " + MqttLwt + "<br>"
"QoS: " + String(QOS) + "<br>"
"Log topic: ") + MqttLog + F("<br>"
"LWT topic: ") + MqttLwt + F("<br>"
"QoS: ") + String(QOS) + F("<br>"
// lastMqttCmd* is unescaped untrusted input.
// Avoid any possible HTML/XSS when displaying it.
"Last MQTT command seen: (topic) '" +
"Last MQTT command seen: (topic) '") +
irutils::htmlEscape(lastMqttCmdTopic) +
"' (payload) '" + irutils::htmlEscape(lastMqttCmd) + "' <i>(" +
timeSince(lastMqttCmdTime) + ")</i><br>"
"Total published: " + String(mqttSentCounter) + "<br>"
"Total received: " + String(mqttRecvCounter) + "<br>"
F("' (payload) '") + irutils::htmlEscape(lastMqttCmd) + F("' <i>(") +
timeSince(lastMqttCmdTime) + F(")</i><br>"
"Total published: ") + String(mqttSentCounter) + F("<br>"
"Total received: ") + String(mqttRecvCounter) + F("<br>"
"</p>"
#endif // MQTT_ENABLE
"<h4>Climate Information</h4>"
"<p>"
"IR Send GPIO: " + String(txGpioTable[0]) + "<br>"
"Last update source: " + lastClimateSource + "<br>"
"Total sent: " + String(irClimateCounter) + "<br>"
"Last send: " + String(hasClimateBeenSent ?
"IR Send GPIO: ") + String(txGpioTable[0]) + F("<br>"
"Last update source: ") + lastClimateSource + F("<br>"
"Total sent: ") + String(irClimateCounter) + F("<br>"
"Last send: ") + String(hasClimateBeenSent ?
(String(lastClimateSucceeded ? "Ok" : "FAILED") +
" <i>(" + timeElapsed(lastClimateIr.elapsed()) + ")</i>") :
"<i>Never</i>") + "<br>"
"<i>Never</i>") + F("<br>"
#if MQTT_ENABLE
"State listen period: " + msToString(kStatListenPeriodMs) + "<br>"
"State broadcast period: " + msToString(kBroadcastPeriodMs) + "<br>"
"Last state broadcast: " + (hasBroadcastBeenSent ?
"State listen period: ") + msToString(kStatListenPeriodMs) + F("<br>"
"State broadcast period: ") + msToString(kBroadcastPeriodMs) + F("<br>"
"Last state broadcast: ") + (hasBroadcastBeenSent ?
timeElapsed(lastBroadcast.elapsed()) :
String("<i>Never</i>")) + "<br>"
String(F("<i>Never</i>"))) + F("<br>"
#if MQTT_DISCOVERY_ENABLE
"Last discovery sent: " + (lockMqttBroadcast ?
String("<b>Locked</b>") :
"Last discovery sent: ") + (lockMqttBroadcast ?
String(F("<b>Locked</b>")) :
(hasDiscoveryBeenSent ?
timeElapsed(lastDiscovery.elapsed()) :
String("<i>Never</i>"))) +
"<br>"
"Discovery topic: " + MqttDiscovery + "<br>" +
String(F("<i>Never</i>")))) +
F("<br>"
"Discovery topic: ") + MqttDiscovery + F("<br>") + F(
#endif // MQTT_DISCOVERY_ENABLE
"Command topics: " + MqttClimate + channel_re + '/' + MQTT_CLIMATE_CMND +
'/' + kClimateTopics +
"State topics: " + MqttClimate + channel_re + '/' + MQTT_CLIMATE_STAT +
'/' + kClimateTopics +
"Command topics: ") + MqttClimate + channel_re +
F("/" MQTT_CLIMATE_CMND "/") + FPSTR(kClimateTopics) +
F("State topics: ") + MqttClimate + channel_re +
F("/" MQTT_CLIMATE_STAT "/") + FPSTR(kClimateTopics) + F(
#endif // MQTT_ENABLE
"</p>"
// Page footer
"<hr><p><small><center>"
"<i>(Note: Page will refresh every 60 " D_STR_SECONDS ".)</i>"
"<centre></small></p>";
"<centre></small></p>");
html += addJsReloadUrl(kUrlInfo, 60, false);
html += htmlEnd();
server.send(200, "text/html", html);
@ -1384,14 +1395,14 @@ bool clearMqttSavedStates(const String topic_base) {
for (size_t i = 0; i < sizeof(kMqttTopics) / sizeof(char*); i++) {
// Sending a retained "" message to the topic should clear previous values
// in theory.
String topic = topic_base + channelStr + '/' + F(MQTT_CLIMATE_STAT) +
'/' + String(kMqttTopics[i]);
String topic = topic_base + channelStr + F("/" MQTT_CLIMATE_STAT "/") +
String(kMqttTopics[i]);
success &= mqtt_client.publish(topic.c_str(), "", true);
}
channelStr = '_' + String(channel);
}
String logmesg = "Removing all possible settings saved in MQTT for '" +
topic_base + "' ";
String logmesg = F("Removing all possible settings saved in MQTT for '") +
topic_base + F("' ");
logmesg += success ? F("succeeded") : F("failed");
mqttLog(logmesg.c_str());
return success;
@ -1410,13 +1421,13 @@ void handleClearMqtt(void) {
htmlHeader(F("Clearing saved info from MQTT"),
F("Removing all saved settings for this device from "
"MQTT.")) +
"<p>Device restarting. Try connecting in a few " D_STR_SECONDS ".</p>" +
F("<p>Device restarting. Try connecting in a few " D_STR_SECONDS ".</p>") +
addJsReloadUrl(kUrlRoot, 10, true) +
htmlEnd());
// Do the clearing.
mqttLog("Clearing all saved settings from MQTT.");
mqttLog(PSTR("Clearing all saved settings from MQTT."));
clearMqttSavedStates(MqttClimate);
doRestart("Rebooting...");
doRestart(PSTR("Rebooting..."));
}
#endif // MQTT_ENABLE && MQTT_CLEAR_ENABLE
@ -1438,10 +1449,10 @@ void handleReset(void) {
// Do the reset.
#if MQTT_ENABLE
#if MQTT_CLEAR_ENABLE
mqttLog("Clearing all saved climate settings from MQTT.");
mqttLog(PSTR("Clearing all saved climate settings from MQTT."));
clearMqttSavedStates(MqttClimate);
#endif // MQTT_CLEAR_ENABLE
mqttLog("Wiping all saved config settings.");
mqttLog(PSTR("Wiping all saved config settings."));
#endif // MQTT_ENABLE
if (mountSpiffs()) {
debug("Removing JSON config file");
@ -1451,7 +1462,7 @@ void handleReset(void) {
delay(1000);
debug("Reseting wifiManager's settings.");
wifiManager.resetSettings();
doRestart("Rebooting...");
doRestart(PSTR("Rebooting..."));
}
// Reboot web page
@ -1465,7 +1476,7 @@ void handleReboot() {
#endif
server.send(200, "text/html",
htmlHeader(F("Device restarting.")) +
"<p>Try connecting in a few " D_STR_SECONDS ".</p>" +
F("<p>Try connecting in a few " D_STR_SECONDS ".</p>") +
addJsReloadUrl(kUrlRoot, kRebootTime, true) +
htmlEnd());
doRestart("Reboot requested");
@ -1484,7 +1495,7 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType,
uint8_t state[kStateSizeMax] = {0}; // All array elements are set to 0.
uint16_t stateSize = 0;
if (str.startsWith("0x") || str.startsWith("0X"))
if (str.startsWith(PSTR("0x")) || str.startsWith(PSTR("0X")))
strOffset = 2;
// Calculate how many hexadecimal characters there are.
uint16_t inputLength = str.length() - strOffset;
@ -1643,8 +1654,9 @@ uint16_t * newCodeArray(const uint16_t size) {
// Check we malloc'ed successfully.
if (result == NULL) // malloc failed, so give up.
doRestart(
"FATAL: Can't allocate memory for an array for a new message! "
"Forcing a reboot!", true); // Send to serial only as we are in low mem
PSTR("FATAL: Can't allocate memory for an array for a new message! "
"Forcing a reboot!"),
true); // Send to serial only as we are in low mem
return result;
}
@ -1666,7 +1678,7 @@ bool parseStringAndSendGC(IRsend *irsend, const String str) {
String tmp_str;
// Remove the leading "1:1,1," if present.
if (str.startsWith("1:1,1,"))
if (str.startsWith(PSTR("1:1,1,")))
tmp_str = str.substring(6);
else
tmp_str = str;
@ -2097,7 +2109,7 @@ void setup(void) {
if (isSerialGpioUsedByIr()) Serial.end();
#endif // DEBUG
channel_re.reserve(kNrOfIrTxGpios * 3);
channel_re.reserve(kNrOfIrTxGpios * 3 + 3 + 1);
// Initialise all the IR transmitters.
for (uint8_t i = 0; i < kNrOfIrTxGpios; i++) {
if (txGpioTable[i] == kGpioUnused) {
@ -2195,7 +2207,7 @@ void setup(void) {
if (strlen(HttpPassword)) { // Allow if password is set.
server.on("/update", HTTP_POST, [](){
#if MQTT_ENABLE
mqttLog("Attempting firmware update & reboot");
mqttLog(PSTR("Attempting firmware update & reboot"));
delay(1000);
#endif // MQTT_ENABLE
server.send(200, "text/html",
@ -2287,7 +2299,7 @@ void unsubscribing(const String topic_name) {
void mqttLog(const char* str) {
debug(str);
mqtt_client.publish(MqttLog.c_str(), str);
mqtt_client.publish(MqttLog.c_str(), String(str).c_str());
mqttSentCounter++;
}
@ -2313,7 +2325,7 @@ bool reconnect(void) {
}
if (connected) {
// Once connected, publish an announcement...
mqttLog("(Re)Connected.");
mqttLog(PSTR("(Re)Connected."));
// Update Last Will & Testament to say we are back online.
mqtt_client.publish(MqttLwt.c_str(), kLwtOnline, true);
@ -2364,12 +2376,13 @@ void handleSendMqttDiscovery(void) {
server.send(200, "text/html",
htmlHeader(F("Sending MQTT Discovery message")) +
htmlMenu() +
"<p>The Home Assistant MQTT Discovery message is being sent to topic: " +
MqttDiscovery + ". It will show up in Home Assistant in a few seconds."
F("<p>The Home Assistant MQTT Discovery message is being sent to topic: ")
+ MqttDiscovery +
F(". It will show up in Home Assistant in a few seconds."
"</p>"
"<h3>Warning!</h3>"
"<p>Home Assistant's config for this device is reset each time this is "
" is sent.</p>" +
" is sent.</p>") +
addJsReloadUrl(kUrlRoot, kRebootTime, true) +
htmlEnd());
sendMQTTDiscovery(MqttDiscovery.c_str());
@ -2416,8 +2429,8 @@ void receivingMQTT(String const topic_name, String const callback_str) {
// Or is for a specific ac/climate channel. e.g. "*/ac_[1-9]"
debug(("Checking for channel number in " + topic_name).c_str());
for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) {
if (topic_name.endsWith("_" + String(i)) ||
(i > 0 && topic_name.startsWith(MqttClimate + "_" + String(i)))) {
if (topic_name.endsWith('_' + String(i)) ||
(i > 0 && topic_name.startsWith(MqttClimate + '_' + String(i)))) {
channel = i;
break;
}
@ -2425,8 +2438,8 @@ void receivingMQTT(String const topic_name, String const callback_str) {
debug(("Channel = " + String(channel)).c_str());
// Is it a climate topic?
if (topic_name.startsWith(MqttClimate)) {
String alt_cmnd_topic = MqttClimate + "_" + String(channel) + '/' +
MQTT_CLIMATE_CMND + '/';
String alt_cmnd_topic = MqttClimate + '_' + String(channel) +
F("/" MQTT_CLIMATE_CMND "/");
// Also accept climate commands on the '*_0' channel.
String cmnd_topic = topic_name.startsWith(alt_cmnd_topic) ? alt_cmnd_topic
: MqttClimateCmnd;
@ -2440,7 +2453,7 @@ void receivingMQTT(String const topic_name, String const callback_str) {
if (topic_name.equals(cmnd_topic + KEY_RESEND) &&
callback_str.equalsIgnoreCase(KEY_RESEND)) {
force_resend = true;
mqttLog("Climate resend requested.");
mqttLog(PSTR("Climate resend requested."));
}
if (sendClimate(stat_topic, true, false, force_resend, true,
climate[channel]) && !force_resend)
@ -2548,10 +2561,15 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
void sendMQTTDiscovery(const char *topic) {
if (mqtt_client.publish(
topic, String(
"{"
"\"~\":\"" + MqttClimate + "\","
"\"name\":\"" + MqttHAName + "\","
F("{"
"\"~\":\"") + MqttClimate + F("\","
"\"name\":\"") + MqttHAName + F("\","
#if (!MQTT_CLIMATE_HA_MODE)
// Typically we don't need or use the power command topic if we are using
// our Home Assistant Climate compatiblity mode. It causes odd behaviour
// if both are used.
"\"pow_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_POWER "\","
#endif // !MQTT_CLIMATE_HA_MODE
"\"mode_cmd_t\":\"~/" MQTT_CLIMATE_CMND "/" KEY_MODE "\","
"\"mode_stat_t\":\"~/" MQTT_CLIMATE_STAT "/" KEY_MODE "\","
// I don't know why, but the modes need to be lower case to work with
@ -2571,22 +2589,22 @@ void sendMQTTDiscovery(const char *topic) {
"\"swing_modes\":[\"" D_STR_OFF "\",\"" D_STR_AUTO "\",\"" D_STR_HIGHEST
"\",\"" D_STR_HIGH "\",\"" D_STR_MIDDLE "\",\""
D_STR_LOW "\",\"" D_STR_LOWEST "\"],"
"\"uniq_id\":\"" + MqttUniqueId + "\","
"\"uniq_id\":\"") + MqttUniqueId + F("\","
"\"device\":{"
"\"identifiers\":[\"" + MqttUniqueId + "\"],"
"\"connections\":[[\"mac\",\"" + WiFi.macAddress() + "\"]],"
"\"identifiers\":[\"") + MqttUniqueId + F("\"],"
"\"connections\":[[\"mac\",\"") + WiFi.macAddress() + F("\"]],"
"\"manufacturer\":\"IRremoteESP8266\","
"\"model\":\"IRMQTTServer\","
"\"name\":\"" + Hostname + "\","
"\"name\":\"") + Hostname + F("\","
"\"sw_version\":\"" _MY_VERSION_ "\""
"}"
"}").c_str(), true)) {
mqttLog("MQTT climate discovery successful sent.");
"}")).c_str(), true)) {
mqttLog(PSTR("MQTT climate discovery successful sent."));
hasDiscoveryBeenSent = true;
lastDiscovery.reset();
mqttSentCounter++;
} else {
mqttLog("MQTT climate discovery FAILED to send.");
mqttLog(PSTR("MQTT climate discovery FAILED to send."));
}
}
#endif // MQTT_DISCOVERY_ENABLE
@ -2616,11 +2634,12 @@ void loop(void) {
lastReconnectAttempt = 0;
wasConnected = true;
if (boot) {
mqttLog("IRMQTTServer " _MY_VERSION_ " just booted");
mqttLog(PSTR("IRMQTTServer " _MY_VERSION_ " just booted"));
boot = false;
} else {
mqttLog(String(
"IRMQTTServer just (re)connected to MQTT. Lost connection about "
F("IRMQTTServer just (re)connected to MQTT. "
"Lost connection about ")
+ timeSince(lastConnectedTime)).c_str());
}
lastConnectedTime = now;
@ -2628,7 +2647,7 @@ void loop(void) {
if (lockMqttBroadcast) {
// Attempt to fetch back any Climate state stored in MQTT retained
// messages on the MQTT broker.
mqttLog("Started listening for previous state.");
mqttLog(PSTR("Started listening for previous state."));
for (uint16_t i = 0; i < kNrOfIrTxGpios; i++)
subscribing(genStatTopic(i) + '+');
statListenTime.reset();
@ -2648,10 +2667,10 @@ void loop(void) {
sendClimate(stat_topic, true, false, false,
MQTT_CLIMATE_IR_SEND_ON_RESTART, climate[i]);
lastClimateSource = F("MQTT (via retain)");
mqttLog("The state was recovered from MQTT broker.");
mqttLog(PSTR("The state was recovered from MQTT broker."));
}
}
mqttLog("Finished listening for previous state.");
mqttLog(PSTR("Finished listening for previous state."));
lockMqttBroadcast = false; // Release the lock so we can broadcast again.
}
// Periodically send all of the climate state via MQTT.
@ -2671,17 +2690,17 @@ void loop(void) {
resultToHexidecimal(&capture);
#if REPORT_RAW_UNKNOWNS
if (capture.decode_type == UNKNOWN) {
lastIrReceived += ";";
lastIrReceived += ';';
for (uint16_t i = 1; i < capture.rawlen; i++) {
uint32_t usecs;
for (usecs = capture.rawbuf[i] * kRawTick; usecs > UINT16_MAX;
usecs -= UINT16_MAX) {
lastIrReceived += uint64ToString(UINT16_MAX);
lastIrReceived += ",0,";
lastIrReceived += F(",0,");
}
lastIrReceived += uint64ToString(usecs, 10);
if (i < capture.rawlen - 1)
lastIrReceived += ",";
lastIrReceived += ',';
}
}
#endif // REPORT_RAW_UNKNOWNS
@ -2875,10 +2894,10 @@ void sendJsonState(const stdAc::state_t state, const String topic,
json[KEY_PROTOCOL] = typeToString(state.protocol);
json[KEY_MODEL] = state.model;
json[KEY_POWER] = IRac::boolToString(state.power);
json[KEY_MODE] = IRac::opmodeToString(state.mode);
json[KEY_MODE] = IRac::opmodeToString(state.mode, ha_mode);
// Home Assistant wants mode to be off if power is also off & vice-versa.
if (ha_mode && (state.mode == stdAc::opmode_t::kOff || !state.power)) {
json[KEY_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff);
json[KEY_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff, ha_mode);
json[KEY_POWER] = IRac::boolToString(false);
}
json[KEY_CELSIUS] = IRac::boolToString(state.celsius);
@ -2965,45 +2984,45 @@ void updateClimate(stdAc::state_t *state, const String str,
*state = jsonToState(*state, payload.c_str());
else
#endif // MQTT_CLIMATE_JSON
if (str.equals(prefix + KEY_PROTOCOL)) {
if (str.equals(prefix + F(KEY_PROTOCOL))) {
state->protocol = strToDecodeType(payload.c_str());
} else if (str.equals(prefix + KEY_MODEL)) {
} else if (str.equals(prefix + F(KEY_MODEL))) {
state->model = IRac::strToModel(payload.c_str());
} else if (str.equals(prefix + KEY_POWER)) {
} else if (str.equals(prefix + F(KEY_POWER))) {
state->power = IRac::strToBool(payload.c_str());
#if MQTT_CLIMATE_HA_MODE
if (!state->power) state->mode = stdAc::opmode_t::kOff;
#endif // MQTT_CLIMATE_HA_MODE
} else if (str.equals(prefix + KEY_MODE)) {
} else if (str.equals(prefix + F(KEY_MODE))) {
state->mode = IRac::strToOpmode(payload.c_str());
#if MQTT_CLIMATE_HA_MODE
state->power = (state->mode != stdAc::opmode_t::kOff);
#endif // MQTT_CLIMATE_HA_MODE
} else if (str.equals(prefix + KEY_TEMP)) {
} else if (str.equals(prefix + F(KEY_TEMP))) {
state->degrees = payload.toFloat();
} else if (str.equals(prefix + KEY_FANSPEED)) {
} else if (str.equals(prefix + F(KEY_FANSPEED))) {
state->fanspeed = IRac::strToFanspeed(payload.c_str());
} else if (str.equals(prefix + KEY_SWINGV)) {
} else if (str.equals(prefix + F(KEY_SWINGV))) {
state->swingv = IRac::strToSwingV(payload.c_str());
} else if (str.equals(prefix + KEY_SWINGH)) {
} else if (str.equals(prefix + F(KEY_SWINGH))) {
state->swingh = IRac::strToSwingH(payload.c_str());
} else if (str.equals(prefix + KEY_QUIET)) {
} else if (str.equals(prefix + F(KEY_QUIET))) {
state->quiet = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_TURBO)) {
} else if (str.equals(prefix + F(KEY_TURBO))) {
state->turbo = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_ECONO)) {
} else if (str.equals(prefix + F(KEY_ECONO))) {
state->econo = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_LIGHT)) {
} else if (str.equals(prefix + F(KEY_LIGHT))) {
state->light = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_BEEP)) {
} else if (str.equals(prefix + F(KEY_BEEP))) {
state->beep = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_FILTER)) {
} else if (str.equals(prefix + F(KEY_FILTER))) {
state->filter = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_CLEAN)) {
} else if (str.equals(prefix + F(KEY_CLEAN))) {
state->clean = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_CELSIUS)) {
} else if (str.equals(prefix + F(KEY_CELSIUS))) {
state->celsius = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + KEY_SLEEP)) {
} else if (str.equals(prefix + F(KEY_SLEEP))) {
state->sleep = payload.toInt();
}
}
@ -3024,7 +3043,11 @@ bool sendClimate(const String topic_prefix, const bool retain,
diff = true;
success &= sendInt(topic_prefix + KEY_MODEL, next.model, retain);
}
#ifdef MQTT_CLIMATE_HA_MODE
String mode_str = IRac::opmodeToString(next.mode, MQTT_CLIMATE_HA_MODE);
#else // MQTT_CLIMATE_HA_MODE
String mode_str = IRac::opmodeToString(next.mode);
#endif // MQTT_CLIMATE_HA_MODE
// I don't know why, but the modes need to be lower case to work with
// Home Assistant & Google Home.
mode_str.toLowerCase();

View File

@ -52,6 +52,12 @@ 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)

View File

@ -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)

View File

@ -52,6 +52,7 @@ IRKelonAc KEYWORD1
IRKelvinatorAC KEYWORD1
IRLgAc KEYWORD1
IRMideaAC KEYWORD1
IRMirageAc KEYWORD1
IRMitsubishi112 KEYWORD1
IRMitsubishi136 KEYWORD1
IRMitsubishiAC KEYWORD1
@ -60,6 +61,7 @@ IRMitsubishiHeavy88Ac KEYWORD1
IRNeoclimaAc KEYWORD1
IRPanasonicAc KEYWORD1
IRPanasonicAc32 KEYWORD1
IRRhossAc KEYWORD1
IRSamsungAc KEYWORD1
IRSanyoAc KEYWORD1
IRSanyoAc88 KEYWORD1
@ -85,16 +87,19 @@ 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
state_t KEYWORD1
swingh_t KEYWORD1
swingv_t KEYWORD1
tcl_ac_remote_model_t KEYWORD1
voltas_ac_remote_model_t KEYWORD1
whirlpool_ac_remote_model_t KEYWORD1
@ -107,6 +112,8 @@ _cancelOffTimer KEYWORD2
_cancelOnTimer KEYWORD2
_delayMicroseconds KEYWORD2
_getEconoToggle KEYWORD2
_getOffTimer KEYWORD2
_getOnTimer KEYWORD2
_getTime KEYWORD2
_getTimer KEYWORD2
_isAKB73757604 KEYWORD2
@ -117,6 +124,9 @@ _restoreState KEYWORD2
_sendSony KEYWORD2
_setEconoToggle KEYWORD2
_setMode KEYWORD2
_setOffTimer KEYWORD2
_setOnTimer KEYWORD2
_setSleepTimer KEYWORD2
_setTemp KEYWORD2
_setTime KEYWORD2
_setTimer KEYWORD2
@ -135,6 +145,7 @@ addSwingHToString KEYWORD2
addSwingVToString KEYWORD2
addTempFloatToString KEYWORD2
addTempToString KEYWORD2
addToggleToString KEYWORD2
adjustRepeat KEYWORD2
airwell KEYWORD2
amcor KEYWORD2
@ -189,10 +200,12 @@ daikin2 KEYWORD2
daikin216 KEYWORD2
daikin64 KEYWORD2
decode KEYWORD2
decodeAirton KEYWORD2
decodeAirwell KEYWORD2
decodeAiwaRCT501 KEYWORD2
decodeAmcor KEYWORD2
decodeArgo KEYWORD2
decodeArris KEYWORD2
decodeBose KEYWORD2
decodeCOOLIX KEYWORD2
decodeCarrierAC KEYWORD2
@ -258,6 +271,7 @@ decodePioneer KEYWORD2
decodeRC5 KEYWORD2
decodeRC6 KEYWORD2
decodeRCMM KEYWORD2
decodeRhoss KEYWORD2
decodeSAMSUNG KEYWORD2
decodeSamsung36 KEYWORD2
decodeSamsungAC KEYWORD2
@ -298,6 +312,7 @@ enableIROut KEYWORD2
enableOffTimer KEYWORD2
enableOnTimer KEYWORD2
enableSleepTimer KEYWORD2
encodeArris KEYWORD2
encodeDoshisha KEYWORD2
encodeJVC KEYWORD2
encodeLG KEYWORD2
@ -319,6 +334,7 @@ fahrenheitToCelsius KEYWORD2
fanspeedToString KEYWORD2
fixChecksum KEYWORD2
fixup KEYWORD2
fromCommon KEYWORD2
fujitsu KEYWORD2
get10CHeat KEYWORD2
get3D KEYWORD2
@ -331,6 +347,7 @@ getBufSize KEYWORD2
getButton KEYWORD2
getCelsius KEYWORD2
getClean KEYWORD2
getCleanToggle KEYWORD2
getClock KEYWORD2
getCmd KEYWORD2
getComfort KEYWORD2
@ -367,6 +384,7 @@ getIonFilter KEYWORD2
getLed KEYWORD2
getLight KEYWORD2
getLightToggle KEYWORD2
getLock KEYWORD2
getMax KEYWORD2
getMode KEYWORD2
getMold KEYWORD2
@ -396,9 +414,11 @@ getSectionByte KEYWORD2
getSectionChecksum KEYWORD2
getSensor KEYWORD2
getSensorTemp KEYWORD2
getSensorUpdate KEYWORD2
getSilent KEYWORD2
getSleep KEYWORD2
getSleepTime KEYWORD2
getSleepTimer KEYWORD2
getSleepTimerEnabled KEYWORD2
getSpecial KEYWORD2
getSpeed KEYWORD2
@ -481,6 +501,7 @@ isSwingH KEYWORD2
isSwingV KEYWORD2
isSwingVStep KEYWORD2
isSwingVToggle KEYWORD2
isTcl KEYWORD2
isTimeCommand KEYWORD2
isTimerActive KEYWORD2
isTurboToggle KEYWORD2
@ -509,6 +530,7 @@ matchSpaceRange KEYWORD2
midea KEYWORD2
minRepeats KEYWORD2
minsToString KEYWORD2
mirage KEYWORD2
mitsubishi KEYWORD2
mitsubishi112 KEYWORD2
mitsubishi136 KEYWORD2
@ -532,15 +554,18 @@ 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
sendArgo KEYWORD2
sendArris KEYWORD2
sendBose KEYWORD2
sendCOOLIX KEYWORD2
sendCarrierAC KEYWORD2
@ -621,6 +646,7 @@ sendRC5 KEYWORD2
sendRC6 KEYWORD2
sendRCMM KEYWORD2
sendRaw KEYWORD2
sendRhoss KEYWORD2
sendSAMSUNG KEYWORD2
sendSamsung36 KEYWORD2
sendSamsungAC KEYWORD2
@ -662,6 +688,7 @@ setBreeze KEYWORD2
setButton KEYWORD2
setCelsius KEYWORD2
setClean KEYWORD2
setCleanToggle KEYWORD2
setClock KEYWORD2
setCmd KEYWORD2
setComfort KEYWORD2
@ -697,6 +724,7 @@ setIonFilter KEYWORD2
setLed KEYWORD2
setLight KEYWORD2
setLightToggle KEYWORD2
setLock KEYWORD2
setMax KEYWORD2
setMode KEYWORD2
setModel KEYWORD2
@ -725,6 +753,7 @@ setSave KEYWORD2
setSensor KEYWORD2
setSensorTemp KEYWORD2
setSensorTempRaw KEYWORD2
setSensorUpdate KEYWORD2
setSilent KEYWORD2
setSleep KEYWORD2
setSleepTimer KEYWORD2
@ -791,6 +820,7 @@ teco KEYWORD2
ticksHigh KEYWORD2
ticksLow KEYWORD2
toString KEYWORD2
toggleArrisRelease KEYWORD2
toggleRC5 KEYWORD2
toggleRC6 KEYWORD2
toggleSwingHoriz KEYWORD2
@ -821,6 +851,7 @@ xorBytes KEYWORD2
A705 LITERAL1
A903 LITERAL1
A907 LITERAL1
AIRTON LITERAL1
AIRWELL LITERAL1
AIWA_RC_T501 LITERAL1
AIWA_RC_T501_BITS LITERAL1
@ -857,6 +888,7 @@ ARJW2 LITERAL1
ARRAH2E LITERAL1
ARREB1E LITERAL1
ARREW4E LITERAL1
ARRIS LITERAL1
ARRY4 LITERAL1
BOSE LITERAL1
CARRIER_AC LITERAL1
@ -887,10 +919,12 @@ 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
DECODE_ARGO LITERAL1
DECODE_ARRIS LITERAL1
DECODE_BOSE LITERAL1
DECODE_CARRIER_AC LITERAL1
DECODE_CARRIER_AC40 LITERAL1
@ -961,6 +995,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
@ -1054,6 +1089,7 @@ GREE_SWING_MIDDLE_DOWN LITERAL1
GREE_SWING_MIDDLE_UP LITERAL1
GREE_SWING_UP LITERAL1
GREE_SWING_UP_AUTO LITERAL1
GZ055BE1 LITERAL1
HAIER_AC LITERAL1
HAIER_AC176 LITERAL1
HAIER_AC_AUTO LITERAL1
@ -1063,7 +1099,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
@ -1080,10 +1116,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
@ -1110,8 +1146,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
@ -1144,6 +1178,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
@ -1224,6 +1260,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
@ -1236,10 +1273,12 @@ 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
SEND_ARGO LITERAL1
SEND_ARRIS LITERAL1
SEND_BOSE LITERAL1
SEND_CARRIER_AC LITERAL1
SEND_CARRIER_AC40 LITERAL1
@ -1310,6 +1349,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
@ -1347,6 +1387,7 @@ SONY_15_BITS LITERAL1
SONY_20_BITS LITERAL1
SONY_38K LITERAL1
SYMPHONY LITERAL1
TAC09CHSD LITERAL1
TCL112AC LITERAL1
TECHNIBEL_AC LITERAL1
TECO LITERAL1
@ -1381,6 +1422,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
@ -1390,11 +1433,25 @@ 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
kAirFlowStr LITERAL1
kAirtonBitMark LITERAL1
kAirtonBits LITERAL1
kAirtonDefaultRepeat LITERAL1
kAirtonFreq LITERAL1
kAirtonHdrMark LITERAL1
kAirtonHdrSpace LITERAL1
kAirtonOneSpace LITERAL1
kAirtonZeroSpace LITERAL1
kAirwellAuto LITERAL1
kAirwellBits LITERAL1
kAirwellCool LITERAL1
@ -1420,6 +1477,9 @@ kAiwaRcT501PostBits LITERAL1
kAiwaRcT501PostData LITERAL1
kAiwaRcT501PreBits LITERAL1
kAiwaRcT501PreData LITERAL1
kAkb73757604Str LITERAL1
kAkb74955603Str LITERAL1
kAkb75215403Str LITERAL1
kAllProtocolNamesStr LITERAL1
kAlokaBits LITERAL1
kAlokaLedBlue LITERAL1
@ -1464,6 +1524,7 @@ kAmcorTolerance LITERAL1
kAmcorVentOn LITERAL1
kAmcorZeroMark LITERAL1
kAmcorZeroSpace LITERAL1
kArdb1Str LITERAL1
kArgoAuto LITERAL1
kArgoBitMark LITERAL1
kArgoBits LITERAL1
@ -1497,6 +1558,21 @@ kArgoOneSpace LITERAL1
kArgoStateLength LITERAL1
kArgoTempDelta LITERAL1
kArgoZeroSpace LITERAL1
kArjw2Str LITERAL1
kArrah2eStr LITERAL1
kArreb1eStr LITERAL1
kArrew4eStr LITERAL1
kArrisBits LITERAL1
kArrisChecksumSize LITERAL1
kArrisCommandSize LITERAL1
kArrisGapSpace LITERAL1
kArrisHalfClockPeriod LITERAL1
kArrisHdrMark LITERAL1
kArrisHdrSpace LITERAL1
kArrisOverhead LITERAL1
kArrisReleaseBit LITERAL1
kArrisReleaseToggle LITERAL1
kArry4Str LITERAL1
kAuto LITERAL1
kAutoStr LITERAL1
kAutomaticStr LITERAL1
@ -1558,6 +1634,7 @@ kCelsiusStr LITERAL1
kCentreStr LITERAL1
kChangeStr LITERAL1
kCirculateStr LITERAL1
kCkpStr LITERAL1
kCleanStr LITERAL1
kClockStr LITERAL1
kCodeStr LITERAL1
@ -1567,6 +1644,7 @@ kCommaSpaceStr LITERAL1
kCommandStr LITERAL1
kCool LITERAL1
kCoolStr LITERAL1
kCoolingStr LITERAL1
kCoolixAuto LITERAL1
kCoolixBitMark LITERAL1
kCoolixBitMarkTicks LITERAL1
@ -1866,10 +1944,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
@ -1914,6 +1994,9 @@ kDenonOneSpaceTicks LITERAL1
kDenonTick LITERAL1
kDenonZeroSpace LITERAL1
kDenonZeroSpaceTicks LITERAL1
kDg11j104Str LITERAL1
kDg11j13aStr LITERAL1
kDg11j191Str LITERAL1
kDishBitMark LITERAL1
kDishBitMarkTicks LITERAL1
kDishBits LITERAL1
@ -1930,6 +2013,7 @@ kDishTick LITERAL1
kDishZeroSpace LITERAL1
kDishZeroSpaceTicks LITERAL1
kDisplayTempStr LITERAL1
kDkeStr LITERAL1
kDoshishaBitMark LITERAL1
kDoshishaBits LITERAL1
kDoshishaHdrMark LITERAL1
@ -1939,6 +2023,7 @@ kDoshishaZeroSpace LITERAL1
kDownStr LITERAL1
kDry LITERAL1
kDryStr LITERAL1
kDryingStr LITERAL1
kDutyDefault LITERAL1
kDutyMax LITERAL1
kEcoclimAuto LITERAL1
@ -1992,6 +2077,9 @@ kElectraAcMessageGap LITERAL1
kElectraAcMinRepeat LITERAL1
kElectraAcMinTemp LITERAL1
kElectraAcOneSpace LITERAL1
kElectraAcSensorMaxTemp LITERAL1
kElectraAcSensorMinTemp LITERAL1
kElectraAcSensorTempDelta LITERAL1
kElectraAcStateLength LITERAL1
kElectraAcSwingOff LITERAL1
kElectraAcSwingOn LITERAL1
@ -2008,8 +2096,11 @@ kEyeAutoStr LITERAL1
kEyeStr LITERAL1
kFalseStr LITERAL1
kFan LITERAL1
kFanOnlyNoSpaceStr LITERAL1
kFanOnlyStr LITERAL1
kFanOnlyWithSpaceStr LITERAL1
kFanStr LITERAL1
kFan_OnlyStr LITERAL1
kFastStr LITERAL1
kFilterStr LITERAL1
kFixedStr LITERAL1
@ -2064,6 +2155,7 @@ kFujitsuAcTempOffsetC LITERAL1
kFujitsuAcTempOffsetF LITERAL1
kFujitsuAcTimerMax LITERAL1
kFujitsuAcZeroSpace LITERAL1
kGe6711ar2853mStr LITERAL1
kGicableBitMark LITERAL1
kGicableBits LITERAL1
kGicableHdrMark LITERAL1
@ -2146,6 +2238,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
@ -2155,6 +2254,7 @@ kGreeSwingUp LITERAL1
kGreeSwingUpAuto LITERAL1
kGreeTimerMax LITERAL1
kGreeZeroSpace LITERAL1
kGz055be1Str LITERAL1
kHaierAC176Bits LITERAL1
kHaierAC176StateLength LITERAL1
kHaierACBits LITERAL1
@ -2195,21 +2295,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
@ -2218,26 +2323,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
@ -2377,6 +2491,7 @@ kInaxTick LITERAL1
kInaxZeroSpace LITERAL1
kInsideStr LITERAL1
kIonStr LITERAL1
kJkeStr LITERAL1
kJvcBitMark LITERAL1
kJvcBitMarkTicks LITERAL1
kJvcBits LITERAL1
@ -2445,6 +2560,8 @@ kKelvinatorStateLength LITERAL1
kKelvinatorTick LITERAL1
kKelvinatorZeroSpace LITERAL1
kKelvinatorZeroSpaceTicks LITERAL1
kKkg29ac1Str LITERAL1
kKkg9ac1Str LITERAL1
kLasertagBits LITERAL1
kLasertagDelta LITERAL1
kLasertagExcess LITERAL1
@ -2461,6 +2578,7 @@ kLastSwinghEnum LITERAL1
kLastSwingvEnum LITERAL1
kLeft LITERAL1
kLeftMax LITERAL1
kLeftMaxNoSpaceStr LITERAL1
kLeftMaxStr LITERAL1
kLeftStr LITERAL1
kLegoPfBitMark LITERAL1
@ -2544,7 +2662,9 @@ kLgRptSpace LITERAL1
kLgZeroSpace LITERAL1
kLightStr LITERAL1
kLightToggleStr LITERAL1
kLkeStr LITERAL1
kLoStr LITERAL1
kLockStr LITERAL1
kLoudStr LITERAL1
kLow LITERAL1
kLowNibble LITERAL1
@ -2578,7 +2698,9 @@ kMarkExcess LITERAL1
kMarkState LITERAL1
kMax LITERAL1
kMaxAccurateUsecDelay LITERAL1
kMaxLeftNoSpaceStr LITERAL1
kMaxLeftStr LITERAL1
kMaxRightNoSpaceStr LITERAL1
kMaxRightStr LITERAL1
kMaxStr LITERAL1
kMaxTimeoutMs LITERAL1
@ -2661,6 +2783,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
@ -2954,6 +3104,7 @@ kNikaiOneSpaceTicks LITERAL1
kNikaiTick LITERAL1
kNikaiZeroSpace LITERAL1
kNikaiZeroSpaceTicks LITERAL1
kNkeStr LITERAL1
kNoRepeat LITERAL1
kNoStr LITERAL1
kNowStr LITERAL1
@ -3042,20 +3193,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
@ -3068,6 +3226,7 @@ kPioneerMinGap LITERAL1
kPioneerOneSpace LITERAL1
kPioneerTick LITERAL1
kPioneerZeroSpace LITERAL1
kPkrStr LITERAL1
kPowerButtonStr LITERAL1
kPowerStr LITERAL1
kPowerToggleStr LITERAL1
@ -3145,10 +3304,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
@ -3164,6 +3357,7 @@ kSamsungAcBreezeOn LITERAL1
kSamsungAcCool LITERAL1
kSamsungAcDefaultRepeat LITERAL1
kSamsungAcDry LITERAL1
kSamsungAcEconoOn LITERAL1
kSamsungAcExtendedBits LITERAL1
kSamsungAcExtendedStateLength LITERAL1
kSamsungAcFan LITERAL1
@ -3172,6 +3366,7 @@ kSamsungAcFanAuto2 LITERAL1
kSamsungAcFanHigh LITERAL1
kSamsungAcFanLow LITERAL1
kSamsungAcFanMed LITERAL1
kSamsungAcFanSpecialOff LITERAL1
kSamsungAcFanTurbo LITERAL1
kSamsungAcHdrMark LITERAL1
kSamsungAcHdrSpace LITERAL1
@ -3180,16 +3375,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
@ -3326,8 +3522,15 @@ kSharpAcSpecialTimer LITERAL1
kSharpAcSpecialTimerHalfHour LITERAL1
kSharpAcSpecialTurbo LITERAL1
kSharpAcStateLength LITERAL1
kSharpAcSwingNoToggle LITERAL1
kSharpAcSwingToggle LITERAL1
kSharpAcSwingVCoanda LITERAL1
kSharpAcSwingVHigh LITERAL1
kSharpAcSwingVIgnore LITERAL1
kSharpAcSwingVLast LITERAL1
kSharpAcSwingVLow LITERAL1
kSharpAcSwingVLowest LITERAL1
kSharpAcSwingVMid LITERAL1
kSharpAcSwingVOff LITERAL1
kSharpAcSwingVToggle LITERAL1
kSharpAcTimerHoursMax LITERAL1
kSharpAcTimerHoursOff LITERAL1
kSharpAcTimerIncrement LITERAL1
@ -3397,6 +3600,7 @@ kSymphonyOneMark LITERAL1
kSymphonyOneSpace LITERAL1
kSymphonyZeroMark LITERAL1
kSymphonyZeroSpace LITERAL1
kTac09chsdStr LITERAL1
kTcl112AcAuto LITERAL1
kTcl112AcBitMark LITERAL1
kTcl112AcBits LITERAL1
@ -3408,6 +3612,9 @@ kTcl112AcFanAuto LITERAL1
kTcl112AcFanHigh LITERAL1
kTcl112AcFanLow LITERAL1
kTcl112AcFanMed LITERAL1
kTcl112AcFanMin LITERAL1
kTcl112AcFanNight LITERAL1
kTcl112AcFanQuiet LITERAL1
kTcl112AcGap LITERAL1
kTcl112AcHdrMark LITERAL1
kTcl112AcHdrMarkTolerance LITERAL1
@ -3417,10 +3624,17 @@ kTcl112AcNormal LITERAL1
kTcl112AcOneSpace LITERAL1
kTcl112AcSpecial LITERAL1
kTcl112AcStateLength LITERAL1
kTcl112AcSwingVHigh LITERAL1
kTcl112AcSwingVHighest LITERAL1
kTcl112AcSwingVLow LITERAL1
kTcl112AcSwingVLowest LITERAL1
kTcl112AcSwingVMiddle LITERAL1
kTcl112AcSwingVOff LITERAL1
kTcl112AcSwingVOn LITERAL1
kTcl112AcTempMax LITERAL1
kTcl112AcTempMin LITERAL1
kTcl112AcTimerMax LITERAL1
kTcl112AcTimerResolution LITERAL1
kTcl112AcTolerance LITERAL1
kTcl112AcZeroSpace LITERAL1
kTechnibelAcBitMark LITERAL1
@ -3482,6 +3696,7 @@ kTempDownStr LITERAL1
kTempStr LITERAL1
kTempUpStr LITERAL1
kThreeLetterDayOfWeekStr LITERAL1
kTimeSep LITERAL1
kTimeoutMs LITERAL1
kTimerModeStr LITERAL1
kTimerStr LITERAL1
@ -3617,6 +3832,8 @@ kUnknownThreshold LITERAL1
kUpStr LITERAL1
kUpperStr LITERAL1
kUseDefTol LITERAL1
kV9014557AStr LITERAL1
kV9014557BStr LITERAL1
kVaneStr LITERAL1
kVestelAcAuto LITERAL1
kVestelAcBitMark LITERAL1
@ -3737,6 +3954,8 @@ kXmpRepeatCodeAlt LITERAL1
kXmpSections LITERAL1
kXmpSpaceStep LITERAL1
kXmpWordSize LITERAL1
kYaw1fStr LITERAL1
kYbofbStr LITERAL1
kYesStr LITERAL1
kZepealBits LITERAL1
kZepealCommandOffOn LITERAL1

View File

@ -1,6 +1,6 @@
{
"name": "IRremoteESP8266",
"version": "2.7.20",
"version": "2.8.0",
"keywords": "infrared, ir, remote, esp8266, esp32",
"description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)",
"repository":

View File

@ -1,5 +1,5 @@
name=IRremoteESP8266
version=2.7.20
version=2.8.0
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)

File diff suppressed because it is too large Load Diff

View File

@ -26,10 +26,12 @@
#include "ir_Kelvinator.h"
#include "ir_LG.h"
#include "ir_Midea.h"
#include "ir_Mirage.h"
#include "ir_Mitsubishi.h"
#include "ir_MitsubishiHeavy.h"
#include "ir_Neoclima.h"
#include "ir_Panasonic.h"
#include "ir_Rhoss.h"
#include "ir_Samsung.h"
#include "ir_Sanyo.h"
#include "ir_Sharp.h"
@ -90,7 +92,8 @@ class IRac {
static stdAc::swingh_t strToSwingH(
const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff);
static String boolToString(const bool value);
static String opmodeToString(const stdAc::opmode_t mode);
static String opmodeToString(const stdAc::opmode_t mode,
const bool ha = false);
static String fanspeedToString(const stdAc::fanspeed_t speed);
static String swingvToString(const stdAc::swingv_t swingv);
static String swinghToString(const stdAc::swingh_t swingh);
@ -245,7 +248,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
@ -257,18 +261,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 bool turbo, const bool filter,
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 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
@ -326,6 +332,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,
@ -386,14 +395,21 @@ void electra(IRElectraAc *ac,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh);
#endif // SEND_PANASONIC_AC32
#if SEND_RHOSS
void rhoss(IRRhossAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swing);
#endif // SEND_RHOSS
#if SEND_SAMSUNG_AC
void samsung(IRSamsungAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const 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,
@ -413,11 +429,12 @@ void electra(IRElectraAc *ac,
void sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
const bool on, const bool prev_power, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const bool turbo, const bool light,
const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev,
const bool turbo, const bool light,
const bool filter, const bool clean);
#endif // SEND_SHARP_AC
#if SEND_TCL112AC
void tcl112(IRTcl112Ac *ac,
void tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,

View File

@ -830,8 +830,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;
@ -1036,6 +1035,18 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Bose decode");
if (decodeBose(results, offset)) return true;
#endif // DECODE_BOSE
#if DECODE_ARRIS
DPRINTLN("Attempting Arris decode");
if (decodeArris(results, offset)) return true;
#endif // DECODE_ARRIS
#if DECODE_RHOSS
DPRINTLN("Attempting Rhoss decode");
if (decodeRhoss(results, offset)) return true;
#endif // DECODE_RHOSS
#if DECODE_AIRTON
DPRINTLN("Attempting Airton decode");
if (decodeAirton(results, offset)) return true;
#endif // DECODE_AIRTON
// Typically new protocols are added above this line.
}
#if DECODE_HASH
@ -1811,6 +1822,7 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
const int16_t excess,
const bool MSBfirst,
const bool GEThomas) {
DPRINTLN("DEBUG: Entered matchManchesterData");
uint16_t offset = 0;
uint64_t data = 0;
uint16_t nr_half_periods = 0;
@ -1824,7 +1836,10 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
uint16_t min_remaining = nbits;
// Check if there is enough capture buffer to possibly have the message.
if (remaining < min_remaining) return 0; // Nope, so abort.
if (remaining < min_remaining) {
DPRINTLN("DEBUG: Ran out of capture buffer!");
return 0; // Nope, so abort.
}
// Convert to ticks. Optimisation: Saves on math/extra instructions later.
uint16_t bank = starting_balance / kRawTick;
@ -1847,22 +1862,39 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
while ((offset < remaining || bank) &&
nr_half_periods < expected_half_periods) {
// Get the next entry if we haven't anything existing to process.
DPRINT("DEBUG: Offset = ");
DPRINTLN(offset);
if (!bank) bank = *(data_ptr + offset++);
DPRINT("DEBUG: Bank = ");
DPRINTLN(bank * kRawTick);
// Check if we don't have a short interval.
if (!match(bank, half_period, tolerance, excess)) return 0; // Not valid.
DPRINTLN("DEBUG: Checking for short interval");
if (!match(bank, half_period, tolerance, excess)) {
DPRINTLN("DEBUG: It is. Exiting");
return 0; // Not valid.
}
// We've succeeded in matching half a period, so count it.
nr_half_periods++;
DPRINT("DEBUG: Half Periods = ");
DPRINTLN(nr_half_periods);
// We've now used up our bank, so refill it with the next item, unless we
// are at the end of the capture buffer.
// If we are assume a single half period of "space".
if (offset < remaining)
if (offset < remaining) {
DPRINT("DEBUG: Offset = ");
DPRINTLN(offset);
bank = *(data_ptr + offset++);
else if (offset == remaining)
} else if (offset == remaining) {
bank = raw_half_period;
else
} else {
return 0; // We are out of buffer, so abort!
}
DPRINT("DEBUG: Bank = ");
DPRINTLN(bank * kRawTick);
// Shift the data along and add our new bit.
DPRINT("DEBUG: Adding bit: ");
DPRINTLN((currentBit ? "1" : "0"));
data <<= 1;
data |= currentBit;
@ -1870,10 +1902,12 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr,
if (match(bank, half_period * 2, tolerance, excess)) {
// It is, so flip the bit we need to append, and remove a half_period of
// time from the bank.
DPRINTLN("DEBUG: long interval detected");
currentBit = !currentBit;
bank -= raw_half_period;
} else if (match(bank, half_period, tolerance, excess)) {
// It is a short interval, so eat up all the time and move on.
DPRINTLN("DEBUG: short interval detected");
bank = 0;
} else if (nr_half_periods == expected_half_periods - 1 &&
matchAtLeast(bank, half_period, tolerance, excess)) {

View File

@ -18,7 +18,7 @@
const uint16_t kHeader = 2; // Usual nr. of header entries.
const uint16_t kFooter = 2; // Usual nr. of footer (stop bits) entries.
const uint16_t kStartOffset = 1; // Usual rawbuf entry to start from.
#define MS_TO_USEC(x) (x * 1000U) // Convert milli-Seconds to micro-Seconds.
#define MS_TO_USEC(x) ((x) * 1000U) // Convert milli-Seconds to micro-Seconds.
// Marks tend to be 100us too long, and spaces 100us too short
// when received due to sensor lag.
const uint16_t kMarkExcess = 50;
@ -287,6 +287,10 @@ class IRrecv {
bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kArgoBits, const bool strict = true);
#endif // DECODE_ARGO
#if DECODE_ARRIS
bool decodeArris(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kArrisBits, const bool strict = true);
#endif // DECODE_ARRIS
#if DECODE_SONY
bool decodeSony(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kSonyMinBits,
@ -766,6 +770,15 @@ class IRrecv {
bool decodeBose(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kBoseBits, const bool strict = true);
#endif // DECODE_BOSE
#if DECODE_RHOSS
bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kRhossBits, const bool strict = true);
#endif // DECODE_RHOSS
#if DECODE_AIRTON
bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kAirtonBits,
const bool strict = true);
#endif // DECODE_AIRTON
};
#endif // IRRECV_H_

View File

@ -53,7 +53,7 @@
#endif // UNIT_TEST
// Library Version
#define _IRREMOTEESP8266_VERSION_ "2.7.20"
#define _IRREMOTEESP8266_VERSION_ "2.8.0"
// Set the language & locale for the library. See the `locale` dir for options.
#ifndef _IR_LOCALE_
@ -790,6 +790,27 @@
#define SEND_BOSE _IR_ENABLE_DEFAULT_
#endif // SEND_BOSE
#ifndef DECODE_ARRIS
#define DECODE_ARRIS _IR_ENABLE_DEFAULT_
#endif // DECODE_ARRIS
#ifndef SEND_ARRIS
#define SEND_ARRIS _IR_ENABLE_DEFAULT_
#endif // SEND_ARRIS
#ifndef DECODE_RHOSS
#define DECODE_RHOSS _IR_ENABLE_DEFAULT_
#endif // DECODE_RHOSS
#ifndef SEND_RHOSS
#define SEND_RHOSS _IR_ENABLE_DEFAULT_
#endif // SEND_RHOSS
#ifndef DECODE_AIRTON
#define DECODE_AIRTON _IR_ENABLE_DEFAULT_
#endif // DECODE_AIRTON
#ifndef SEND_AIRTON
#define SEND_AIRTON _IR_ENABLE_DEFAULT_
#endif // SEND_AIRTON
#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 || \
@ -804,7 +825,7 @@
DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \
DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \
DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \
DECODE_SANYO_AC88 || \
DECODE_SANYO_AC88 || DECODE_RHOSS || \
false)
// Add any DECODE to the above if it uses result->state (see kStateSizeMax)
// you might also want to add the protocol to hasACState function
@ -951,14 +972,19 @@ enum decode_type_t {
TROTEC_3550,
SANYO_AC88, // 105
BOSE,
ARRIS,
RHOSS,
AIRTON,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = BOSE,
kLastDecodeType = AIRTON,
};
// 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;
@ -970,6 +996,7 @@ const uint16_t kAmcorDefaultRepeat = kSingleRepeat;
const uint16_t kArgoStateLength = 12;
const uint16_t kArgoBits = kArgoStateLength * 8;
const uint16_t kArgoDefaultRepeat = kNoRepeat;
const uint16_t kArrisBits = 32;
const uint16_t kCoolixBits = 24;
const uint16_t kCoolixDefaultRepeat = kSingleRepeat;
const uint16_t kCarrierAcBits = 32;
@ -1195,6 +1222,9 @@ const uint16_t kMilesTag2ShotBits = 14;
const uint16_t kMilesTag2MsgBits = 24;
const uint16_t kMilesMinRepeat = 0;
const uint16_t kBoseBits = 16;
const uint16_t kRhossStateLength = 12;
const uint16_t kRhossBits = kRhossStateLength * 8;
const uint16_t kRhossDefaultRepeat = 0;
// Legacy defines. (Deprecated)

View File

@ -637,6 +637,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
case LG:
case LG2:
return 28;
case ARRIS:
case CARRIER_AC:
case ELITESCREENS:
case EPSON:
@ -665,6 +666,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
case MIDEA:
case PANASONIC:
return 48;
case AIRTON:
case ECOCLIM:
case MAGIQUEST:
case VESTEL_AC:
@ -738,6 +740,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kNeoclimaBits;
case PANASONIC_AC:
return kPanasonicAcBits;
case RHOSS:
return kRhossBits;
case SAMSUNG_AC:
return kSamsungAcBits;
case SANYO_AC:
@ -781,6 +785,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);
@ -790,7 +799,12 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
case AIWA_RC_T501:
sendAiwaRCT501(data, nbits, min_repeat);
break;
#endif
#endif // SEND_AIWA_RC_T501
#if SEND_ARRIS
case ARRIS:
sendArris(data, nbits, min_repeat);
break;
#endif // SEND_ARRIS
#if SEND_BOSE
case BOSE:
sendBose(data, nbits, min_repeat);
@ -1247,6 +1261,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendPanasonicAC(state, nbytes);
break;
#endif // SEND_PANASONIC_AC
#if SEND_RHOSS
case RHOSS:
sendRhoss(state, nbytes);
break;
#endif // SEND_RHOSS
#if SEND_SAMSUNG_AC
case SAMSUNG_AC:
sendSamsungAC(state, nbytes);

View File

@ -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,
@ -160,6 +172,12 @@ enum sharp_ac_remote_model_t {
A903 = 3, // 820 too
};
/// TCL A/C model numbers
enum tcl_ac_remote_model_t {
TAC09CHSD = 1,
GZ055BE1 = 2,
};
/// Voltas A/C model numbers
enum voltas_ac_remote_model_t {
kVoltasUnknown = 0, // Full Function
@ -737,6 +755,21 @@ class IRsend {
void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits,
const uint16_t repeat = kNoRepeat);
#endif // SEND_BOSE
#if SEND_ARRIS
void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits,
const uint16_t repeat = kNoRepeat);
static uint32_t toggleArrisRelease(const uint32_t data);
static uint32_t encodeArris(const uint32_t command, const bool release);
#endif // SEND_ARRIS
#if SEND_RHOSS
void sendRhoss(const unsigned char data[],
const uint16_t nbytes = kRhossStateLength,
const uint16_t repeat = kRhossDefaultRepeat);
#endif // SEND_RHOSS
#if SEND_AIRTON
void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits,
const uint16_t repeat = kAirtonDefaultRepeat);
#endif // SEND_AIRTON
protected:
#ifdef UNIT_TEST

View File

@ -1,9 +1,10 @@
// Copyright 2019-2020 - David Conran (@crankyoldgit)
// Copyright 2019-2021 - David Conran (@crankyoldgit)
/// @file IRtext.cpp
/// @warning If you add or remove an entry in this file, you should run:
/// '../tools/generate_irtext_h.sh' to rebuild the `IRtext.h` file.
#include "IRtext.h"
#ifndef UNIT_TEST
#include <Arduino.h>
#endif // UNIT_TEST
@ -14,178 +15,263 @@
#define PROGMEM // Pretend we have the PROGMEM macro even if we really don't.
#endif
#ifndef FPSTR
#define FPSTR(X) X // Also pretend we have flash-string helper class cast.
#endif
#define IRTEXT_CONST_BLOB_NAME(NAME)\
NAME ## Blob
#define IRTEXT_CONST_BLOB_DECL(NAME)\
const char IRTEXT_CONST_BLOB_NAME(NAME) [] PROGMEM
#define IRTEXT_CONST_BLOB_PTR(NAME)\
IRTEXT_CONST_PTR(NAME) {\
IRTEXT_CONST_PTR_CAST(IRTEXT_CONST_BLOB_NAME(NAME)) }
#define IRTEXT_CONST_STRING(NAME, VALUE)\
static IRTEXT_CONST_BLOB_DECL(NAME) { VALUE };\
IRTEXT_CONST_PTR(NAME) PROGMEM {\
IRTEXT_CONST_PTR_CAST(&(IRTEXT_CONST_BLOB_NAME(NAME))[0]) }
// Common
const PROGMEM char* kUnknownStr = D_STR_UNKNOWN; ///< "Unknown"
const PROGMEM char* kProtocolStr = D_STR_PROTOCOL; ///< "Protocol"
const PROGMEM char* kPowerStr = D_STR_POWER; ///< "Power"
const PROGMEM char* kOnStr = D_STR_ON; ///< "On"
const PROGMEM char* kOffStr = D_STR_OFF; ///< "Off"
const PROGMEM char* kModeStr = D_STR_MODE; ///< "Mode"
const PROGMEM char* kToggleStr = D_STR_TOGGLE; ///< "Toggle"
const PROGMEM char* kTurboStr = D_STR_TURBO; ///< "Turbo"
const PROGMEM char* kSuperStr = D_STR_SUPER; ///< "Super"
const PROGMEM char* kSleepStr = D_STR_SLEEP; ///< "Sleep"
const PROGMEM char* kLightStr = D_STR_LIGHT; ///< "Light"
const PROGMEM char* kPowerfulStr = D_STR_POWERFUL; ///< "Powerful"
const PROGMEM char* kQuietStr = D_STR_QUIET; ///< "Quiet"
const PROGMEM char* kEconoStr = D_STR_ECONO; ///< "Econo"
const PROGMEM char* kSwingStr = D_STR_SWING; ///< "Swing"
const PROGMEM char* kSwingHStr = D_STR_SWINGH; ///< "SwingH"
const PROGMEM char* kSwingVStr = D_STR_SWINGV; ///< "SwingV"
const PROGMEM char* kBeepStr = D_STR_BEEP; ///< "Beep"
const PROGMEM char* kZoneFollowStr = D_STR_ZONEFOLLOW; ///< "Zone Follow"
const PROGMEM char* kFixedStr = D_STR_FIXED; ///< "Fixed"
const PROGMEM char* kMouldStr = D_STR_MOULD; ///< "Mould"
const PROGMEM char* kCleanStr = D_STR_CLEAN; ///< "Clean"
const PROGMEM char* kPurifyStr = D_STR_PURIFY; ///< "Purify"
const PROGMEM char* kTimerStr = D_STR_TIMER; ///< "Timer"
const PROGMEM char* kOnTimerStr = D_STR_ONTIMER; ///< "On Timer"
const PROGMEM char* kOffTimerStr = D_STR_OFFTIMER; ///< "Off Timer"
const PROGMEM char* kTimerModeStr = D_STR_TIMERMODE; ///< "Timer Mode"
const PROGMEM char* kClockStr = D_STR_CLOCK; ///< "Clock"
const PROGMEM char* kCommandStr = D_STR_COMMAND; ///< "Command"
const PROGMEM char* kXFanStr = D_STR_XFAN; ///< "XFan"
const PROGMEM char* kHealthStr = D_STR_HEALTH; ///< "Health"
const PROGMEM char* kModelStr = D_STR_MODEL; ///< "Model"
const PROGMEM char* kTempStr = D_STR_TEMP; ///< "Temp"
const PROGMEM char* kIFeelStr = D_STR_IFEEL; ///< "IFeel"
const PROGMEM char* kHumidStr = D_STR_HUMID; ///< "Humid"
const PROGMEM char* kSaveStr = D_STR_SAVE; ///< "Save"
const PROGMEM char* kEyeStr = D_STR_EYE; ///< "Eye"
const PROGMEM char* kFollowStr = D_STR_FOLLOW; ///< "Follow"
const PROGMEM char* kIonStr = D_STR_ION; ///< "Ion"
const PROGMEM char* kFreshStr = D_STR_FRESH; ///< "Fresh"
const PROGMEM char* kHoldStr = D_STR_HOLD; ///< "Hold"
const PROGMEM char* kButtonStr = D_STR_BUTTON; ///< "Button"
const PROGMEM char* k8CHeatStr = D_STR_8C_HEAT; ///< "8C Heat"
const PROGMEM char* k10CHeatStr = D_STR_10C_HEAT; ///< "10C Heat"
const PROGMEM char* kNightStr = D_STR_NIGHT; ///< "Night"
const PROGMEM char* kSilentStr = D_STR_SILENT; ///< "Silent"
const PROGMEM char* kFilterStr = D_STR_FILTER; ///< "Filter"
const PROGMEM char* k3DStr = D_STR_3D; ///< "3D"
const PROGMEM char* kCelsiusStr = D_STR_CELSIUS; ///< "Celsius"
const PROGMEM char* kCelsiusFahrenheitStr = D_STR_CELSIUS_FAHRENHEIT; ///<
IRTEXT_CONST_STRING(kUnknownStr, D_STR_UNKNOWN); ///< "Unknown"
IRTEXT_CONST_STRING(kProtocolStr, D_STR_PROTOCOL); ///< "Protocol"
IRTEXT_CONST_STRING(kPowerStr, D_STR_POWER); ///< "Power"
IRTEXT_CONST_STRING(kOnStr, D_STR_ON); ///< "On"
IRTEXT_CONST_STRING(kOffStr, D_STR_OFF); ///< "Off"
IRTEXT_CONST_STRING(k1Str, D_STR_1); ///< "1"
IRTEXT_CONST_STRING(k0Str, D_STR_0); ///< "0"
IRTEXT_CONST_STRING(kModeStr, D_STR_MODE); ///< "Mode"
IRTEXT_CONST_STRING(kToggleStr, D_STR_TOGGLE); ///< "Toggle"
IRTEXT_CONST_STRING(kTurboStr, D_STR_TURBO); ///< "Turbo"
IRTEXT_CONST_STRING(kSuperStr, D_STR_SUPER); ///< "Super"
IRTEXT_CONST_STRING(kSleepStr, D_STR_SLEEP); ///< "Sleep"
IRTEXT_CONST_STRING(kLightStr, D_STR_LIGHT); ///< "Light"
IRTEXT_CONST_STRING(kPowerfulStr, D_STR_POWERFUL); ///< "Powerful"
IRTEXT_CONST_STRING(kQuietStr, D_STR_QUIET); ///< "Quiet"
IRTEXT_CONST_STRING(kEconoStr, D_STR_ECONO); ///< "Econo"
IRTEXT_CONST_STRING(kSwingStr, D_STR_SWING); ///< "Swing"
IRTEXT_CONST_STRING(kSwingHStr, D_STR_SWINGH); ///< "SwingH"
IRTEXT_CONST_STRING(kSwingVStr, D_STR_SWINGV); ///< "SwingV"
IRTEXT_CONST_STRING(kBeepStr, D_STR_BEEP); ///< "Beep"
IRTEXT_CONST_STRING(kZoneFollowStr, D_STR_ZONEFOLLOW); ///< "Zone Follow"
IRTEXT_CONST_STRING(kFixedStr, D_STR_FIXED); ///< "Fixed"
IRTEXT_CONST_STRING(kMouldStr, D_STR_MOULD); ///< "Mould"
IRTEXT_CONST_STRING(kCleanStr, D_STR_CLEAN); ///< "Clean"
IRTEXT_CONST_STRING(kPurifyStr, D_STR_PURIFY); ///< "Purify"
IRTEXT_CONST_STRING(kTimerStr, D_STR_TIMER); ///< "Timer"
IRTEXT_CONST_STRING(kOnTimerStr, D_STR_ONTIMER); ///< "On Timer"
IRTEXT_CONST_STRING(kOffTimerStr, D_STR_OFFTIMER); ///< "Off Timer"
IRTEXT_CONST_STRING(kTimerModeStr, D_STR_TIMERMODE); ///< "Timer Mode"
IRTEXT_CONST_STRING(kClockStr, D_STR_CLOCK); ///< "Clock"
IRTEXT_CONST_STRING(kCommandStr, D_STR_COMMAND); ///< "Command"
IRTEXT_CONST_STRING(kXFanStr, D_STR_XFAN); ///< "XFan"
IRTEXT_CONST_STRING(kHealthStr, D_STR_HEALTH); ///< "Health"
IRTEXT_CONST_STRING(kModelStr, D_STR_MODEL); ///< "Model"
IRTEXT_CONST_STRING(kTempStr, D_STR_TEMP); ///< "Temp"
IRTEXT_CONST_STRING(kIFeelStr, D_STR_IFEEL); ///< "IFeel"
IRTEXT_CONST_STRING(kHumidStr, D_STR_HUMID); ///< "Humid"
IRTEXT_CONST_STRING(kSaveStr, D_STR_SAVE); ///< "Save"
IRTEXT_CONST_STRING(kEyeStr, D_STR_EYE); ///< "Eye"
IRTEXT_CONST_STRING(kFollowStr, D_STR_FOLLOW); ///< "Follow"
IRTEXT_CONST_STRING(kIonStr, D_STR_ION); ///< "Ion"
IRTEXT_CONST_STRING(kFreshStr, D_STR_FRESH); ///< "Fresh"
IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold"
IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button"
IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat"
IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat"
IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night"
IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent"
IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter"
IRTEXT_CONST_STRING(k3DStr, D_STR_3D); ///< "3D"
IRTEXT_CONST_STRING(kCelsiusStr, D_STR_CELSIUS); ///< "Celsius"
IRTEXT_CONST_STRING(kCelsiusFahrenheitStr, D_STR_CELSIUS_FAHRENHEIT); ///<
///< "Celsius/Fahrenheit"
const PROGMEM char* kTempUpStr = D_STR_TEMPUP; ///< "Temp Up"
const PROGMEM char* kTempDownStr = D_STR_TEMPDOWN; ///< "Temp Down"
const PROGMEM char* kStartStr = D_STR_START; ///< "Start"
const PROGMEM char* kStopStr = D_STR_STOP; ///< "Stop"
const PROGMEM char* kMoveStr = D_STR_MOVE; ///< "Move"
const PROGMEM char* kSetStr = D_STR_SET; ///< "Set"
const PROGMEM char* kCancelStr = D_STR_CANCEL; ///< "Cancel"
const PROGMEM char* kUpStr = D_STR_UP; ///< "Up"
const PROGMEM char* kDownStr = D_STR_DOWN; ///< "Down"
const PROGMEM char* kChangeStr = D_STR_CHANGE; ///< "Change"
const PROGMEM char* kComfortStr = D_STR_COMFORT; ///< "Comfort"
const PROGMEM char* kSensorStr = D_STR_SENSOR; ///< "Sensor"
const PROGMEM char* kWeeklyTimerStr = D_STR_WEEKLYTIMER; ///< "WeeklyTimer"
const PROGMEM char* kWifiStr = D_STR_WIFI; ///< "Wifi"
const PROGMEM char* kLastStr = D_STR_LAST; ///< "Last"
const PROGMEM char* kFastStr = D_STR_FAST; ///< "Fast"
const PROGMEM char* kSlowStr = D_STR_SLOW; ///< "Slow"
const PROGMEM char* kAirFlowStr = D_STR_AIRFLOW; ///< "Air Flow"
const PROGMEM char* kStepStr = D_STR_STEP; ///< "Step"
const PROGMEM char* kNAStr = D_STR_NA; ///< "N/A"
const PROGMEM char* kInsideStr = D_STR_INSIDE; ///< "Inside"
const PROGMEM char* kOutsideStr = D_STR_OUTSIDE; ///< "Outside"
const PROGMEM char* kLoudStr = D_STR_LOUD; ///< "Loud"
const PROGMEM char* kLowerStr = D_STR_LOWER; ///< "Lower"
const PROGMEM char* kUpperStr = D_STR_UPPER; ///< "Upper"
const PROGMEM char* kBreezeStr = D_STR_BREEZE; ///< "Breeze"
const PROGMEM char* kCirculateStr = D_STR_CIRCULATE; ///< "Circulate"
const PROGMEM char* kCeilingStr = D_STR_CEILING; ///< "Ceiling"
const PROGMEM char* kWallStr = D_STR_WALL; ///< "Wall"
const PROGMEM char* kRoomStr = D_STR_ROOM; ///< "Room"
const PROGMEM char* k6thSenseStr = D_STR_6THSENSE; ///< "6th Sense"
const PROGMEM char* kTypeStr = D_STR_TYPE; ///< "Type"
const PROGMEM char* kSpecialStr = D_STR_SPECIAL; ///< "Special"
const PROGMEM char* kIdStr = D_STR_ID; ///< "Id" / Device Identifier
const PROGMEM char* kVaneStr = D_STR_VANE; ///< "Vane"
IRTEXT_CONST_STRING(kTempUpStr, D_STR_TEMPUP); ///< "Temp Up"
IRTEXT_CONST_STRING(kTempDownStr, D_STR_TEMPDOWN); ///< "Temp Down"
IRTEXT_CONST_STRING(kStartStr, D_STR_START); ///< "Start"
IRTEXT_CONST_STRING(kStopStr, D_STR_STOP); ///< "Stop"
IRTEXT_CONST_STRING(kMoveStr, D_STR_MOVE); ///< "Move"
IRTEXT_CONST_STRING(kSetStr, D_STR_SET); ///< "Set"
IRTEXT_CONST_STRING(kCancelStr, D_STR_CANCEL); ///< "Cancel"
IRTEXT_CONST_STRING(kUpStr, D_STR_UP); ///< "Up"
IRTEXT_CONST_STRING(kDownStr, D_STR_DOWN); ///< "Down"
IRTEXT_CONST_STRING(kChangeStr, D_STR_CHANGE); ///< "Change"
IRTEXT_CONST_STRING(kComfortStr, D_STR_COMFORT); ///< "Comfort"
IRTEXT_CONST_STRING(kSensorStr, D_STR_SENSOR); ///< "Sensor"
IRTEXT_CONST_STRING(kWeeklyTimerStr, D_STR_WEEKLYTIMER); ///< "WeeklyTimer"
IRTEXT_CONST_STRING(kWifiStr, D_STR_WIFI); ///< "Wifi"
IRTEXT_CONST_STRING(kLastStr, D_STR_LAST); ///< "Last"
IRTEXT_CONST_STRING(kFastStr, D_STR_FAST); ///< "Fast"
IRTEXT_CONST_STRING(kSlowStr, D_STR_SLOW); ///< "Slow"
IRTEXT_CONST_STRING(kAirFlowStr, D_STR_AIRFLOW); ///< "Air Flow"
IRTEXT_CONST_STRING(kStepStr, D_STR_STEP); ///< "Step"
IRTEXT_CONST_STRING(kNAStr, D_STR_NA); ///< "N/A"
IRTEXT_CONST_STRING(kInsideStr, D_STR_INSIDE); ///< "Inside"
IRTEXT_CONST_STRING(kOutsideStr, D_STR_OUTSIDE); ///< "Outside"
IRTEXT_CONST_STRING(kLoudStr, D_STR_LOUD); ///< "Loud"
IRTEXT_CONST_STRING(kLowerStr, D_STR_LOWER); ///< "Lower"
IRTEXT_CONST_STRING(kUpperStr, D_STR_UPPER); ///< "Upper"
IRTEXT_CONST_STRING(kBreezeStr, D_STR_BREEZE); ///< "Breeze"
IRTEXT_CONST_STRING(kCirculateStr, D_STR_CIRCULATE); ///< "Circulate"
IRTEXT_CONST_STRING(kCeilingStr, D_STR_CEILING); ///< "Ceiling"
IRTEXT_CONST_STRING(kWallStr, D_STR_WALL); ///< "Wall"
IRTEXT_CONST_STRING(kRoomStr, D_STR_ROOM); ///< "Room"
IRTEXT_CONST_STRING(k6thSenseStr, D_STR_6THSENSE); ///< "6th Sense"
IRTEXT_CONST_STRING(kTypeStr, D_STR_TYPE); ///< "Type"
IRTEXT_CONST_STRING(kSpecialStr, D_STR_SPECIAL); ///< "Special"
IRTEXT_CONST_STRING(kIdStr, D_STR_ID); ///< "Id" / Device Identifier
IRTEXT_CONST_STRING(kVaneStr, D_STR_VANE); ///< "Vane"
IRTEXT_CONST_STRING(kLockStr, D_STR_LOCK); ///< "Lock"
const PROGMEM char* kAutoStr = D_STR_AUTO; ///< "Auto"
const PROGMEM char* kAutomaticStr = D_STR_AUTOMATIC; ///< "Automatic"
const PROGMEM char* kManualStr = D_STR_MANUAL; ///< "Manual"
const PROGMEM char* kCoolStr = D_STR_COOL; ///< "Cool"
const PROGMEM char* kHeatStr = D_STR_HEAT; ///< "Heat"
const PROGMEM char* kFanStr = D_STR_FAN; ///< "Fan"
const PROGMEM char* kDryStr = D_STR_DRY; ///< "Dry"
const PROGMEM char* kFanOnlyStr = D_STR_FANONLY; ///< "fan_only"
const PROGMEM char* kRecycleStr = D_STR_RECYCLE; ///< "Recycle"
IRTEXT_CONST_STRING(kAutoStr, D_STR_AUTO); ///< "Auto"
IRTEXT_CONST_STRING(kAutomaticStr, D_STR_AUTOMATIC); ///< "Automatic"
IRTEXT_CONST_STRING(kManualStr, D_STR_MANUAL); ///< "Manual"
IRTEXT_CONST_STRING(kCoolStr, D_STR_COOL); ///< "Cool"
IRTEXT_CONST_STRING(kCoolingStr, D_STR_COOLING); ///< "Cooling"
IRTEXT_CONST_STRING(kHeatStr, D_STR_HEAT); ///< "Heat"
IRTEXT_CONST_STRING(kHeatingStr, D_STR_HEATING); ///< "Heating"
IRTEXT_CONST_STRING(kDryStr, D_STR_DRY); ///< "Dry"
IRTEXT_CONST_STRING(kDryingStr, D_STR_DRYING); ///< "Drying"
IRTEXT_CONST_STRING(kDehumidifyStr, D_STR_DEHUMIDIFY); ///< "Dehumidify"
IRTEXT_CONST_STRING(kFanStr, D_STR_FAN); ///< "Fan"
// The following Fans strings with "only" are required to help with
// HomeAssistant & Google Home Climate integration. For compatibility only.
// Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes
IRTEXT_CONST_STRING(kFanOnlyStr, D_STR_FANONLY); ///< "fan-only"
IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (HA/legacy)
IRTEXT_CONST_STRING(kFanOnlyWithSpaceStr, D_STR_FANSPACEONLY); ///< "Fan Only"
IRTEXT_CONST_STRING(kFanOnlyNoSpaceStr, D_STR_FANONLYNOSPACE); ///< "FanOnly"
const PROGMEM char* kMaxStr = D_STR_MAX; ///< "Max"
const PROGMEM char* kMaximumStr = D_STR_MAXIMUM; ///< "Maximum"
const PROGMEM char* kMinStr = D_STR_MIN; ///< "Min"
const PROGMEM char* kMinimumStr = D_STR_MINIMUM; ///< "Minimum"
const PROGMEM char* kMedStr = D_STR_MED; ///< "Med"
const PROGMEM char* kMediumStr = D_STR_MEDIUM; ///< "Medium"
IRTEXT_CONST_STRING(kRecycleStr, D_STR_RECYCLE); ///< "Recycle"
const PROGMEM char* kHighestStr = D_STR_HIGHEST; ///< "Highest"
const PROGMEM char* kHighStr = D_STR_HIGH; ///< "High"
const PROGMEM char* kHiStr = D_STR_HI; ///< "Hi"
const PROGMEM char* kMidStr = D_STR_MID; ///< "Mid"
const PROGMEM char* kMiddleStr = D_STR_MIDDLE; ///< "Middle"
const PROGMEM char* kLowStr = D_STR_LOW; ///< "Low"
const PROGMEM char* kLoStr = D_STR_LO; ///< "Lo"
const PROGMEM char* kLowestStr = D_STR_LOWEST; ///< "Lowest"
const PROGMEM char* kMaxRightStr = D_STR_MAXRIGHT; ///< "Max Right"
const PROGMEM char* kRightMaxStr = D_STR_RIGHTMAX_NOSPACE; ///< "RightMax"
const PROGMEM char* kRightStr = D_STR_RIGHT; ///< "Right"
const PROGMEM char* kLeftStr = D_STR_LEFT; ///< "Left"
const PROGMEM char* kMaxLeftStr = D_STR_MAXLEFT; ///< "Max Left"
const PROGMEM char* kLeftMaxStr = D_STR_LEFTMAX_NOSPACE; ///< "LeftMax"
const PROGMEM char* kWideStr = D_STR_WIDE; ///< "Wide"
const PROGMEM char* kCentreStr = D_STR_CENTRE; ///< "Centre"
const PROGMEM char* kTopStr = D_STR_TOP; ///< "Top"
const PROGMEM char* kBottomStr = D_STR_BOTTOM; ///< "Bottom"
IRTEXT_CONST_STRING(kMaxStr, D_STR_MAX); ///< "Max"
IRTEXT_CONST_STRING(kMaximumStr, D_STR_MAXIMUM); ///< "Maximum"
IRTEXT_CONST_STRING(kMinStr, D_STR_MIN); ///< "Min"
IRTEXT_CONST_STRING(kMinimumStr, D_STR_MINIMUM); ///< "Minimum"
IRTEXT_CONST_STRING(kMedStr, D_STR_MED); ///< "Med"
IRTEXT_CONST_STRING(kMediumStr, D_STR_MEDIUM); ///< "Medium"
IRTEXT_CONST_STRING(kHighestStr, D_STR_HIGHEST); ///< "Highest"
IRTEXT_CONST_STRING(kHighStr, D_STR_HIGH); ///< "High"
IRTEXT_CONST_STRING(kHiStr, D_STR_HI); ///< "Hi"
IRTEXT_CONST_STRING(kMidStr, D_STR_MID); ///< "Mid"
IRTEXT_CONST_STRING(kMiddleStr, D_STR_MIDDLE); ///< "Middle"
IRTEXT_CONST_STRING(kLowStr, D_STR_LOW); ///< "Low"
IRTEXT_CONST_STRING(kLoStr, D_STR_LO); ///< "Lo"
IRTEXT_CONST_STRING(kLowestStr, D_STR_LOWEST); ///< "Lowest"
IRTEXT_CONST_STRING(kMaxRightStr, D_STR_MAXRIGHT); ///< "Max Right"
IRTEXT_CONST_STRING(kMaxRightNoSpaceStr, D_STR_MAXRIGHT_NOSPACE); ///<
///< "MaxRight"
IRTEXT_CONST_STRING(kRightMaxStr, D_STR_RIGHTMAX); ///< "Right Max"
IRTEXT_CONST_STRING(kRightMaxNoSpaceStr, D_STR_RIGHTMAX_NOSPACE); ///<
///< "RightMax"
IRTEXT_CONST_STRING(kRightStr, D_STR_RIGHT); ///< "Right"
IRTEXT_CONST_STRING(kLeftStr, D_STR_LEFT); ///< "Left"
IRTEXT_CONST_STRING(kMaxLeftStr, D_STR_MAXLEFT); ///< "Max Left"
IRTEXT_CONST_STRING(kMaxLeftNoSpaceStr, D_STR_MAXLEFT_NOSPACE); ///< "MaxLeft"
IRTEXT_CONST_STRING(kLeftMaxStr, D_STR_LEFTMAX); ///< "Left Max"
IRTEXT_CONST_STRING(kLeftMaxNoSpaceStr, D_STR_LEFTMAX_NOSPACE); ///< "LeftMax"
IRTEXT_CONST_STRING(kWideStr, D_STR_WIDE); ///< "Wide"
IRTEXT_CONST_STRING(kCentreStr, D_STR_CENTRE); ///< "Centre"
IRTEXT_CONST_STRING(kTopStr, D_STR_TOP); ///< "Top"
IRTEXT_CONST_STRING(kBottomStr, D_STR_BOTTOM); ///< "Bottom"
// Compound words/phrases/descriptions from pre-defined words.
const PROGMEM char* kEconoToggleStr = D_STR_ECONOTOGGLE; ///< "Econo Toggle"
const PROGMEM char* kEyeAutoStr = D_STR_EYEAUTO; ///< "Eye Auto"
const PROGMEM char* kLightToggleStr = D_STR_LIGHTTOGGLE; ///< "Light Toggle"
const PROGMEM char* kOutsideQuietStr = D_STR_OUTSIDEQUIET; ///< "Outside Quiet"
const PROGMEM char* kPowerToggleStr = D_STR_POWERTOGGLE; ///< "Power Toggle"
const PROGMEM char* kPowerButtonStr = D_STR_POWERBUTTON; ///< "Power Button"
const PROGMEM char* kPreviousPowerStr = D_STR_PREVIOUSPOWER; ///<
IRTEXT_CONST_STRING(kEconoToggleStr, D_STR_ECONOTOGGLE); ///< "Econo Toggle"
IRTEXT_CONST_STRING(kEyeAutoStr, D_STR_EYEAUTO); ///< "Eye Auto"
IRTEXT_CONST_STRING(kLightToggleStr, D_STR_LIGHTTOGGLE); ///< "Light Toggle"
///< "Outside Quiet"
IRTEXT_CONST_STRING(kOutsideQuietStr, D_STR_OUTSIDEQUIET);
IRTEXT_CONST_STRING(kPowerToggleStr, D_STR_POWERTOGGLE); ///< "Power Toggle"
IRTEXT_CONST_STRING(kPowerButtonStr, D_STR_POWERBUTTON); ///< "Power Button"
IRTEXT_CONST_STRING(kPreviousPowerStr, D_STR_PREVIOUSPOWER); ///<
///< "Previous Power"
const PROGMEM char* kDisplayTempStr = D_STR_DISPLAYTEMP; ///< "Display Temp"
const PROGMEM char* kSensorTempStr = D_STR_SENSORTEMP; ///< "Sensor Temp"
const PROGMEM char* kSleepTimerStr = D_STR_SLEEP_TIMER; ///< "Sleep Timer"
const PROGMEM char* kSwingVModeStr = D_STR_SWINGVMODE; ///< "Swing(V) Mode"
const PROGMEM char* kSwingVToggleStr = D_STR_SWINGVTOGGLE; ///<
IRTEXT_CONST_STRING(kDisplayTempStr, D_STR_DISPLAYTEMP); ///< "Display Temp"
IRTEXT_CONST_STRING(kSensorTempStr, D_STR_SENSORTEMP); ///< "Sensor Temp"
IRTEXT_CONST_STRING(kSleepTimerStr, D_STR_SLEEP_TIMER); ///< "Sleep Timer"
IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode"
IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///<
///< "Swing(V) Toggle"
const PROGMEM char* kTurboToggleStr = D_STR_TURBOTOGGLE; ///< "Turbo Toggle"
IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle"
// Separators
char kTimeSep = D_CHR_TIME_SEP; ///< ':'
const PROGMEM char* kSpaceLBraceStr = D_STR_SPACELBRACE; ///< " ("
const PROGMEM char* kCommaSpaceStr = D_STR_COMMASPACE; ///< ", "
const PROGMEM char* kColonSpaceStr = D_STR_COLONSPACE; ///< ": "
// Separators & Punctuation
const char kTimeSep = D_CHR_TIME_SEP; ///< ':'
IRTEXT_CONST_STRING(kSpaceLBraceStr, D_STR_SPACELBRACE); ///< " ("
IRTEXT_CONST_STRING(kCommaSpaceStr, D_STR_COMMASPACE); ///< ", "
IRTEXT_CONST_STRING(kColonSpaceStr, D_STR_COLONSPACE); ///< ": "
IRTEXT_CONST_STRING(kDashStr, D_STR_DASH); ///< "-"
// IRutils
// - Time
const PROGMEM char* kDayStr = D_STR_DAY; ///< "Day"
const PROGMEM char* kDaysStr = D_STR_DAYS; ///< "Days"
const PROGMEM char* kHourStr = D_STR_HOUR; ///< "Hour"
const PROGMEM char* kHoursStr = D_STR_HOURS; ///< "Hours"
const PROGMEM char* kMinuteStr = D_STR_MINUTE; ///< "Minute"
const PROGMEM char* kMinutesStr = D_STR_MINUTES; ///< "Minutes"
const PROGMEM char* kSecondStr = D_STR_SECOND; ///< "Second"
const PROGMEM char* kSecondsStr = D_STR_SECONDS; ///< "Seconds"
const PROGMEM char* kNowStr = D_STR_NOW; ///< "Now"
const PROGMEM char* kThreeLetterDayOfWeekStr = D_STR_THREELETTERDAYS; ///<
IRTEXT_CONST_STRING(kDayStr, D_STR_DAY); ///< "Day"
IRTEXT_CONST_STRING(kDaysStr, D_STR_DAYS); ///< "Days"
IRTEXT_CONST_STRING(kHourStr, D_STR_HOUR); ///< "Hour"
IRTEXT_CONST_STRING(kHoursStr, D_STR_HOURS); ///< "Hours"
IRTEXT_CONST_STRING(kMinuteStr, D_STR_MINUTE); ///< "Minute"
IRTEXT_CONST_STRING(kMinutesStr, D_STR_MINUTES); ///< "Minutes"
IRTEXT_CONST_STRING(kSecondStr, D_STR_SECOND); ///< "Second"
IRTEXT_CONST_STRING(kSecondsStr, D_STR_SECONDS); ///< "Seconds"
IRTEXT_CONST_STRING(kNowStr, D_STR_NOW); ///< "Now"
IRTEXT_CONST_STRING(kThreeLetterDayOfWeekStr, D_STR_THREELETTERDAYS); ///<
///< "SunMonTueWedThuFriSat"
const PROGMEM char* kYesStr = D_STR_YES; ///< "Yes"
const PROGMEM char* kNoStr = D_STR_NO; ///< "No"
const PROGMEM char* kTrueStr = D_STR_TRUE; ///< "True"
const PROGMEM char* kFalseStr = D_STR_FALSE; ///< "False"
IRTEXT_CONST_STRING(kYesStr, D_STR_YES); ///< "Yes"
IRTEXT_CONST_STRING(kNoStr, D_STR_NO); ///< "No"
IRTEXT_CONST_STRING(kTrueStr, D_STR_TRUE); ///< "True"
IRTEXT_CONST_STRING(kFalseStr, D_STR_FALSE); ///< "False"
const PROGMEM char* kRepeatStr = D_STR_REPEAT; ///< "Repeat"
const PROGMEM char* kCodeStr = D_STR_CODE; ///< "Code"
const PROGMEM char* kBitsStr = D_STR_BITS; ///< "Bits"
IRTEXT_CONST_STRING(kRepeatStr, D_STR_REPEAT); ///< "Repeat"
IRTEXT_CONST_STRING(kCodeStr, D_STR_CODE); ///< "Code"
IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits"
// Model Names
IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F"
IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB"
IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A"
IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B"
IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A"
IRTEXT_CONST_STRING(kRlt0541htabStr, D_STR_RLT0541HTA_B); ///< "R-LT0541-HTA-B"
IRTEXT_CONST_STRING(kArrah2eStr, D_STR_ARRAH2E); ///< "ARRAH2E"
IRTEXT_CONST_STRING(kArdb1Str, D_STR_ARDB1); ///< "ARDB1"
IRTEXT_CONST_STRING(kArreb1eStr, D_STR_ARREB1E); ///< "ARREB1E"
IRTEXT_CONST_STRING(kArjw2Str, D_STR_ARJW2); ///< "ARJW2"
IRTEXT_CONST_STRING(kArry4Str, D_STR_ARRY4); ///< "ARRY4"
IRTEXT_CONST_STRING(kArrew4eStr, D_STR_ARREW4E); ///< "ARREW4E"
IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///<
///< "GE6711AR2853M"
IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403"
IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603"
IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604"
IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1"
IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1"
IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE"
IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE"
IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE"
IRTEXT_CONST_STRING(kPkrStr, D_STR_PKR); ///< "PKR"
IRTEXT_CONST_STRING(kJkeStr, D_STR_JKE); ///< "JKE"
IRTEXT_CONST_STRING(kCkpStr, D_STR_CKP); ///< "CKP"
IRTEXT_CONST_STRING(kRkrStr, D_STR_RKR); ///< "RKR"
IRTEXT_CONST_STRING(kPanasonicLkeStr, D_STR_PANASONICLKE); ///< "PANASONICLKE"
IRTEXT_CONST_STRING(kPanasonicNkeStr, D_STR_PANASONICNKE); ///< "PANASONICNKE"
IRTEXT_CONST_STRING(kPanasonicDkeStr, D_STR_PANASONICDKE); ///< "PANASONICDKE"
IRTEXT_CONST_STRING(kPanasonicPkrStr, D_STR_PANASONICPKR); ///< "PANASONICPKR"
IRTEXT_CONST_STRING(kPanasonicJkeStr, D_STR_PANASONICJKE); ///< "PANASONICJKE"
IRTEXT_CONST_STRING(kPanasonicCkpStr, D_STR_PANASONICCKP); ///< "PANASONICCKP"
IRTEXT_CONST_STRING(kPanasonicRkrStr, D_STR_PANASONICRKR); ///< "PANASONICRKR"
IRTEXT_CONST_STRING(kA907Str, D_STR_A907); ///< "A907"
IRTEXT_CONST_STRING(kA705Str, D_STR_A705); ///< "A705"
IRTEXT_CONST_STRING(kA903Str, D_STR_A903); ///< "A903"
IRTEXT_CONST_STRING(kTac09chsdStr, D_STR_TAC09CHSD); ///< "TAC09CHSD"
IRTEXT_CONST_STRING(kGz055be1Str, D_STR_GZ055BE1); ///< "GZ055BE1"
IRTEXT_CONST_STRING(k122lzfStr, D_STR_122LZF); ///< "122LZF"
IRTEXT_CONST_STRING(kDg11j13aStr, D_STR_DG11J13A); ///< "DG11J13A"
IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104"
IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191"
// Protocol Names
// Needs to be in decode_type_t order.
const PROGMEM char *kAllProtocolNamesStr =
IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
D_STR_UNUSED "\x0"
D_STR_RC5 "\x0"
D_STR_RC6 "\x0"
@ -293,5 +379,11 @@ const PROGMEM char *kAllProtocolNamesStr =
D_STR_TROTEC_3550 "\x0"
D_STR_SANYO_AC88 "\x0"
D_STR_BOSE "\x0"
D_STR_ARRIS "\x0"
D_STR_RHOSS "\x0"
D_STR_AIRTON "\x0"
///< New protocol strings should be added just above this line.
"\x0"; ///< This string requires double null termination.
"\x0" ///< This string requires double null termination.
};
IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr);

View File

@ -12,158 +12,224 @@
// Constant text to be shared across all object files.
// This means there is only one copy of the character/string/text etc.
extern char kTimeSep;
extern const char* k10CHeatStr;
extern const char* k3DStr;
extern const char* k6thSenseStr;
extern const char* k8CHeatStr;
extern const char* kAirFlowStr;
extern const char *kAllProtocolNamesStr;
extern const char* kAutomaticStr;
extern const char* kAutoStr;
extern const char* kBeepStr;
extern const char* kBitsStr;
extern const char* kBottomStr;
extern const char* kBreezeStr;
extern const char* kButtonStr;
extern const char* kCancelStr;
extern const char* kCeilingStr;
extern const char* kCelsiusFahrenheitStr;
extern const char* kCelsiusStr;
extern const char* kCentreStr;
extern const char* kChangeStr;
extern const char* kCirculateStr;
extern const char* kCleanStr;
extern const char* kClockStr;
extern const char* kCodeStr;
extern const char* kColonSpaceStr;
extern const char* kComfortStr;
extern const char* kCommandStr;
extern const char* kCommaSpaceStr;
extern const char* kCoolStr;
extern const char* kDaysStr;
extern const char* kDayStr;
extern const char* kDisplayTempStr;
extern const char* kDownStr;
extern const char* kDryStr;
extern const char* kEconoStr;
extern const char* kEconoToggleStr;
extern const char* kEyeAutoStr;
extern const char* kEyeStr;
extern const char* kFalseStr;
extern const char* kFanOnlyStr;
extern const char* kFanStr;
extern const char* kFastStr;
extern const char* kFilterStr;
extern const char* kFixedStr;
extern const char* kFollowStr;
extern const char* kFreshStr;
extern const char* kHealthStr;
extern const char* kHeatStr;
extern const char* kHighestStr;
extern const char* kHighStr;
extern const char* kHiStr;
extern const char* kHoldStr;
extern const char* kHoursStr;
extern const char* kHourStr;
extern const char* kHumidStr;
extern const char* kIdStr;
extern const char* kIFeelStr;
extern const char* kInsideStr;
extern const char* kIonStr;
extern const char* kLastStr;
extern const char* kLeftMaxStr;
extern const char* kLeftStr;
extern const char* kLightStr;
extern const char* kLightToggleStr;
extern const char* kLoStr;
extern const char* kLoudStr;
extern const char* kLowerStr;
extern const char* kLowestStr;
extern const char* kLowStr;
extern const char* kManualStr;
extern const char* kMaximumStr;
extern const char* kMaxLeftStr;
extern const char* kMaxRightStr;
extern const char* kMaxStr;
extern const char* kMediumStr;
extern const char* kMedStr;
extern const char* kMiddleStr;
extern const char* kMidStr;
extern const char* kMinimumStr;
extern const char* kMinStr;
extern const char* kMinutesStr;
extern const char* kMinuteStr;
extern const char* kModelStr;
extern const char* kModeStr;
extern const char* kMouldStr;
extern const char* kMoveStr;
extern const char* kNAStr;
extern const char* kNightStr;
extern const char* kNoStr;
extern const char* kNowStr;
extern const char* kOffStr;
extern const char* kOffTimerStr;
extern const char* kOnStr;
extern const char* kOnTimerStr;
extern const char* kOutsideQuietStr;
extern const char* kOutsideStr;
extern const char* kPowerButtonStr;
extern const char* kPowerfulStr;
extern const char* kPowerStr;
extern const char* kPowerToggleStr;
extern const char* kPreviousPowerStr;
extern const char* kProtocolStr;
extern const char* kPurifyStr;
extern const char* kQuietStr;
extern const char* kRecycleStr;
extern const char* kRepeatStr;
extern const char* kRightMaxStr;
extern const char* kRightStr;
extern const char* kRoomStr;
extern const char* kSaveStr;
extern const char* kSecondsStr;
extern const char* kSecondStr;
extern const char* kSensorStr;
extern const char* kSensorTempStr;
extern const char* kSetStr;
extern const char* kSilentStr;
extern const char* kSleepStr;
extern const char* kSleepTimerStr;
extern const char* kSlowStr;
extern const char* kSpaceLBraceStr;
extern const char* kSpecialStr;
extern const char* kStartStr;
extern const char* kStepStr;
extern const char* kStopStr;
extern const char* kSuperStr;
extern const char* kSwingHStr;
extern const char* kSwingStr;
extern const char* kSwingVModeStr;
extern const char* kSwingVStr;
extern const char* kSwingVToggleStr;
extern const char* kTempDownStr;
extern const char* kTempStr;
extern const char* kTempUpStr;
extern const char* kThreeLetterDayOfWeekStr;
extern const char* kTimerModeStr;
extern const char* kTimerStr;
extern const char* kToggleStr;
extern const char* kTopStr;
extern const char* kTrueStr;
extern const char* kTurboStr;
extern const char* kTurboToggleStr;
extern const char* kTypeStr;
extern const char* kUnknownStr;
extern const char* kUpperStr;
extern const char* kUpStr;
extern const char* kVaneStr;
extern const char* kWallStr;
extern const char* kWeeklyTimerStr;
extern const char* kWideStr;
extern const char* kWifiStr;
extern const char* kXFanStr;
extern const char* kYesStr;
extern const char* kZoneFollowStr;
#ifdef ESP8266
class __FlashStringHelper;
#define IRTEXT_CONST_PTR_CAST(PTR)\
reinterpret_cast<const __FlashStringHelper*>(PTR)
#define IRTEXT_CONST_PTR(NAME) const __FlashStringHelper* const NAME
#else // ESP8266
#define IRTEXT_CONST_PTR_CAST(PTR) PTR
#define IRTEXT_CONST_PTR(NAME) const char* const NAME
#endif // ESP8266
extern const char kTimeSep;
extern IRTEXT_CONST_PTR(k0Str);
extern IRTEXT_CONST_PTR(k10CHeatStr);
extern IRTEXT_CONST_PTR(k122lzfStr);
extern IRTEXT_CONST_PTR(k1Str);
extern IRTEXT_CONST_PTR(k3DStr);
extern IRTEXT_CONST_PTR(k6thSenseStr);
extern IRTEXT_CONST_PTR(k8CHeatStr);
extern IRTEXT_CONST_PTR(kA705Str);
extern IRTEXT_CONST_PTR(kA903Str);
extern IRTEXT_CONST_PTR(kA907Str);
extern IRTEXT_CONST_PTR(kAirFlowStr);
extern IRTEXT_CONST_PTR(kAkb73757604Str);
extern IRTEXT_CONST_PTR(kAkb74955603Str);
extern IRTEXT_CONST_PTR(kAkb75215403Str);
extern IRTEXT_CONST_PTR(kArdb1Str);
extern IRTEXT_CONST_PTR(kArjw2Str);
extern IRTEXT_CONST_PTR(kArrah2eStr);
extern IRTEXT_CONST_PTR(kArreb1eStr);
extern IRTEXT_CONST_PTR(kArrew4eStr);
extern IRTEXT_CONST_PTR(kArry4Str);
extern IRTEXT_CONST_PTR(kAutomaticStr);
extern IRTEXT_CONST_PTR(kAutoStr);
extern IRTEXT_CONST_PTR(kBeepStr);
extern IRTEXT_CONST_PTR(kBitsStr);
extern IRTEXT_CONST_PTR(kBottomStr);
extern IRTEXT_CONST_PTR(kBreezeStr);
extern IRTEXT_CONST_PTR(kButtonStr);
extern IRTEXT_CONST_PTR(kCancelStr);
extern IRTEXT_CONST_PTR(kCeilingStr);
extern IRTEXT_CONST_PTR(kCelsiusFahrenheitStr);
extern IRTEXT_CONST_PTR(kCelsiusStr);
extern IRTEXT_CONST_PTR(kCentreStr);
extern IRTEXT_CONST_PTR(kChangeStr);
extern IRTEXT_CONST_PTR(kCirculateStr);
extern IRTEXT_CONST_PTR(kCkpStr);
extern IRTEXT_CONST_PTR(kCleanStr);
extern IRTEXT_CONST_PTR(kClockStr);
extern IRTEXT_CONST_PTR(kCodeStr);
extern IRTEXT_CONST_PTR(kColonSpaceStr);
extern IRTEXT_CONST_PTR(kComfortStr);
extern IRTEXT_CONST_PTR(kCommandStr);
extern IRTEXT_CONST_PTR(kCommaSpaceStr);
extern IRTEXT_CONST_PTR(kCoolingStr);
extern IRTEXT_CONST_PTR(kCoolStr);
extern IRTEXT_CONST_PTR(kDashStr);
extern IRTEXT_CONST_PTR(kDaysStr);
extern IRTEXT_CONST_PTR(kDayStr);
extern IRTEXT_CONST_PTR(kDehumidifyStr);
extern IRTEXT_CONST_PTR(kDg11j104Str);
extern IRTEXT_CONST_PTR(kDg11j13aStr);
extern IRTEXT_CONST_PTR(kDg11j191Str);
extern IRTEXT_CONST_PTR(kDisplayTempStr);
extern IRTEXT_CONST_PTR(kDkeStr);
extern IRTEXT_CONST_PTR(kDownStr);
extern IRTEXT_CONST_PTR(kDryingStr);
extern IRTEXT_CONST_PTR(kDryStr);
extern IRTEXT_CONST_PTR(kEconoStr);
extern IRTEXT_CONST_PTR(kEconoToggleStr);
extern IRTEXT_CONST_PTR(kEyeAutoStr);
extern IRTEXT_CONST_PTR(kEyeStr);
extern IRTEXT_CONST_PTR(kFalseStr);
extern IRTEXT_CONST_PTR(kFanOnlyNoSpaceStr);
extern IRTEXT_CONST_PTR(kFan_OnlyStr);
extern IRTEXT_CONST_PTR(kFanOnlyStr);
extern IRTEXT_CONST_PTR(kFanOnlyWithSpaceStr);
extern IRTEXT_CONST_PTR(kFanStr);
extern IRTEXT_CONST_PTR(kFastStr);
extern IRTEXT_CONST_PTR(kFilterStr);
extern IRTEXT_CONST_PTR(kFixedStr);
extern IRTEXT_CONST_PTR(kFollowStr);
extern IRTEXT_CONST_PTR(kFreshStr);
extern IRTEXT_CONST_PTR(kGe6711ar2853mStr);
extern IRTEXT_CONST_PTR(kGz055be1Str);
extern IRTEXT_CONST_PTR(kHealthStr);
extern IRTEXT_CONST_PTR(kHeatingStr);
extern IRTEXT_CONST_PTR(kHeatStr);
extern IRTEXT_CONST_PTR(kHighestStr);
extern IRTEXT_CONST_PTR(kHighStr);
extern IRTEXT_CONST_PTR(kHiStr);
extern IRTEXT_CONST_PTR(kHoldStr);
extern IRTEXT_CONST_PTR(kHoursStr);
extern IRTEXT_CONST_PTR(kHourStr);
extern IRTEXT_CONST_PTR(kHumidStr);
extern IRTEXT_CONST_PTR(kIdStr);
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);
extern IRTEXT_CONST_PTR(kLeftStr);
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);
extern IRTEXT_CONST_PTR(kLowStr);
extern IRTEXT_CONST_PTR(kManualStr);
extern IRTEXT_CONST_PTR(kMaximumStr);
extern IRTEXT_CONST_PTR(kMaxLeftNoSpaceStr);
extern IRTEXT_CONST_PTR(kMaxLeftStr);
extern IRTEXT_CONST_PTR(kMaxRightNoSpaceStr);
extern IRTEXT_CONST_PTR(kMaxRightStr);
extern IRTEXT_CONST_PTR(kMaxStr);
extern IRTEXT_CONST_PTR(kMediumStr);
extern IRTEXT_CONST_PTR(kMedStr);
extern IRTEXT_CONST_PTR(kMiddleStr);
extern IRTEXT_CONST_PTR(kMidStr);
extern IRTEXT_CONST_PTR(kMinimumStr);
extern IRTEXT_CONST_PTR(kMinStr);
extern IRTEXT_CONST_PTR(kMinutesStr);
extern IRTEXT_CONST_PTR(kMinuteStr);
extern IRTEXT_CONST_PTR(kModelStr);
extern IRTEXT_CONST_PTR(kModeStr);
extern IRTEXT_CONST_PTR(kMouldStr);
extern IRTEXT_CONST_PTR(kMoveStr);
extern IRTEXT_CONST_PTR(kNAStr);
extern IRTEXT_CONST_PTR(kNightStr);
extern IRTEXT_CONST_PTR(kNkeStr);
extern IRTEXT_CONST_PTR(kNoStr);
extern IRTEXT_CONST_PTR(kNowStr);
extern IRTEXT_CONST_PTR(kOffStr);
extern IRTEXT_CONST_PTR(kOffTimerStr);
extern IRTEXT_CONST_PTR(kOnStr);
extern IRTEXT_CONST_PTR(kOnTimerStr);
extern IRTEXT_CONST_PTR(kOutsideQuietStr);
extern IRTEXT_CONST_PTR(kOutsideStr);
extern IRTEXT_CONST_PTR(kPanasonicCkpStr);
extern IRTEXT_CONST_PTR(kPanasonicDkeStr);
extern IRTEXT_CONST_PTR(kPanasonicJkeStr);
extern IRTEXT_CONST_PTR(kPanasonicLkeStr);
extern IRTEXT_CONST_PTR(kPanasonicNkeStr);
extern IRTEXT_CONST_PTR(kPanasonicPkrStr);
extern IRTEXT_CONST_PTR(kPanasonicRkrStr);
extern IRTEXT_CONST_PTR(kPkrStr);
extern IRTEXT_CONST_PTR(kPowerButtonStr);
extern IRTEXT_CONST_PTR(kPowerfulStr);
extern IRTEXT_CONST_PTR(kPowerStr);
extern IRTEXT_CONST_PTR(kPowerToggleStr);
extern IRTEXT_CONST_PTR(kPreviousPowerStr);
extern IRTEXT_CONST_PTR(kProtocolStr);
extern IRTEXT_CONST_PTR(kPurifyStr);
extern IRTEXT_CONST_PTR(kQuietStr);
extern IRTEXT_CONST_PTR(kRecycleStr);
extern IRTEXT_CONST_PTR(kRepeatStr);
extern IRTEXT_CONST_PTR(kRightMaxNoSpaceStr);
extern IRTEXT_CONST_PTR(kRightMaxStr);
extern IRTEXT_CONST_PTR(kRightStr);
extern IRTEXT_CONST_PTR(kRkrStr);
extern IRTEXT_CONST_PTR(kRlt0541htaaStr);
extern IRTEXT_CONST_PTR(kRlt0541htabStr);
extern IRTEXT_CONST_PTR(kRoomStr);
extern IRTEXT_CONST_PTR(kSaveStr);
extern IRTEXT_CONST_PTR(kSecondsStr);
extern IRTEXT_CONST_PTR(kSecondStr);
extern IRTEXT_CONST_PTR(kSensorStr);
extern IRTEXT_CONST_PTR(kSensorTempStr);
extern IRTEXT_CONST_PTR(kSetStr);
extern IRTEXT_CONST_PTR(kSilentStr);
extern IRTEXT_CONST_PTR(kSleepStr);
extern IRTEXT_CONST_PTR(kSleepTimerStr);
extern IRTEXT_CONST_PTR(kSlowStr);
extern IRTEXT_CONST_PTR(kSpaceLBraceStr);
extern IRTEXT_CONST_PTR(kSpecialStr);
extern IRTEXT_CONST_PTR(kStartStr);
extern IRTEXT_CONST_PTR(kStepStr);
extern IRTEXT_CONST_PTR(kStopStr);
extern IRTEXT_CONST_PTR(kSuperStr);
extern IRTEXT_CONST_PTR(kSwingHStr);
extern IRTEXT_CONST_PTR(kSwingStr);
extern IRTEXT_CONST_PTR(kSwingVModeStr);
extern IRTEXT_CONST_PTR(kSwingVStr);
extern IRTEXT_CONST_PTR(kSwingVToggleStr);
extern IRTEXT_CONST_PTR(kTac09chsdStr);
extern IRTEXT_CONST_PTR(kTempDownStr);
extern IRTEXT_CONST_PTR(kTempStr);
extern IRTEXT_CONST_PTR(kTempUpStr);
extern IRTEXT_CONST_PTR(kThreeLetterDayOfWeekStr);
extern IRTEXT_CONST_PTR(kTimerModeStr);
extern IRTEXT_CONST_PTR(kTimerStr);
extern IRTEXT_CONST_PTR(kToggleStr);
extern IRTEXT_CONST_PTR(kTopStr);
extern IRTEXT_CONST_PTR(kTrueStr);
extern IRTEXT_CONST_PTR(kTurboStr);
extern IRTEXT_CONST_PTR(kTurboToggleStr);
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);
extern IRTEXT_CONST_PTR(kWideStr);
extern IRTEXT_CONST_PTR(kWifiStr);
extern IRTEXT_CONST_PTR(kXFanStr);
extern IRTEXT_CONST_PTR(kYaw1fStr);
extern IRTEXT_CONST_PTR(kYbofbStr);
extern IRTEXT_CONST_PTR(kYesStr);
extern IRTEXT_CONST_PTR(kZoneFollowStr);
extern IRTEXT_CONST_PTR(kAllProtocolNamesStr);
#endif // IRTEXT_H_

View File

@ -1,4 +1,4 @@
// Copyright 2017 David Conran
// Copyright 2017-2021 David Conran
#include "IRutils.h"
#ifndef UNIT_TEST
@ -17,6 +17,27 @@
#include "IRsend.h"
#include "IRtext.h"
// On the ESP8266 platform we need to use a set of ..._P functions
// to handle the strings stored in the flash address space.
#ifndef STRCASECMP
#if defined(ESP8266)
#define STRCASECMP(LHS, RHS) \
strcasecmp_P(LHS, reinterpret_cast<const char*>(RHS))
#else // ESP8266
#define STRCASECMP strcasecmp
#endif // ESP8266
#endif // STRCASECMP
#ifndef STRLEN
#if defined(ESP8266)
#define STRLEN(PTR) strlen_P(PTR)
#else // ESP8266
#define STRLEN(PTR) strlen(PTR)
#endif // ESP8266
#endif // STRLEN
#ifndef FPSTR
#define FPSTR(X) X
#endif // FPSTR
/// Reverse the order of the requested least significant nr. of bits.
/// @param[in] input Bit pattern/integer to reverse.
/// @param[in] nbits Nr. of bits to reverse. (LSB -> MSB)
@ -74,7 +95,10 @@ 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) {
return "-" + uint64ToString(-input, base);
// 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);
}
@ -93,20 +117,19 @@ void serialPrintUint64(uint64_t input, uint8_t base) {
/// @param[in] str A C-style string containing a protocol name or number.
/// @return A decode_type_t enum. (decode_type_t::UNKNOWN if no match.)
decode_type_t strToDecodeType(const char * const str) {
const char *ptr = kAllProtocolNamesStr;
uint16_t length = strlen(ptr);
auto *ptr = reinterpret_cast<const char*>(kAllProtocolNamesStr);
uint16_t length = STRLEN(ptr);
for (uint16_t i = 0; length; i++) {
if (!strcasecmp(str, ptr)) return (decode_type_t)i;
if (!STRCASECMP(str, ptr)) return (decode_type_t)i;
ptr += length + 1;
length = strlen(ptr);
length = STRLEN(ptr);
}
// Handle integer values of the type by converting to a string and back again.
decode_type_t result = strToDecodeType(
typeToString((decode_type_t)atoi(str)).c_str());
if (result > 0)
return result;
else
return decode_type_t::UNKNOWN;
}
@ -117,16 +140,20 @@ decode_type_t strToDecodeType(const char * const str) {
String typeToString(const decode_type_t protocol, const bool isRepeat) {
String result = "";
result.reserve(30); // Size of longest protocol name + " (Repeat)"
const char *ptr = kAllProtocolNamesStr;
if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) {
result = kUnknownStr;
} else {
for (uint16_t i = 0; i <= protocol && strlen(ptr); i++) {
auto *ptr = reinterpret_cast<const char*>(kAllProtocolNamesStr);
if (protocol > kLastDecodeType || protocol == decode_type_t::UNKNOWN) {
result = kUnknownStr;
} else {
for (uint16_t i = 0; i <= protocol && STRLEN(ptr); i++) {
if (i == protocol) {
result = ptr;
result = FPSTR(ptr);
break;
}
ptr += strlen(ptr) + 1;
ptr += STRLEN(ptr) + 1;
}
}
}
if (isRepeat) {
@ -175,6 +202,7 @@ bool hasACState(const decode_type_t protocol) {
case MWM:
case NEOCLIMA:
case PANASONIC_AC:
case RHOSS:
case SAMSUNG_AC:
case SANYO_AC:
case SANYO_AC88:
@ -318,7 +346,7 @@ String resultToTimingInfo(const decode_results * const results) {
for (uint16_t i = 1; i < results->rawlen; i++) {
if (i % 2 == 0)
output += '-'; // even
output += kDashStr; // even
else
output += F(" +"); // odd
value = uint64ToString(results->rawbuf[i] * kRawTick);
@ -515,7 +543,18 @@ namespace irutils {
/// @return The resulting String.
String addBoolToString(const bool value, const String label,
const bool precomma) {
return addLabeledString((value ? kOnStr : kOffStr), label, precomma);
return addLabeledString(value ? kOnStr : kOffStr, label, precomma);
}
/// Create a String with a colon separated toggle flag suitable for Humans.
/// e.g. "Light: Toggle", "Light: -"
/// @param[in] toggle The value of the toggle to come after the label.
/// @param[in] label The label to precede the value.
/// @param[in] precomma Should the output string start with ", " or not?
/// @return The resulting String.
String addToggleToString(const bool toggle, const String label,
const bool precomma) {
return addLabeledString(toggle ? kToggleStr : kDashStr, label, precomma);
}
/// Create a String with a colon separated labeled Integer suitable for
@ -547,74 +586,100 @@ 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:
switch (model) {
case fujitsu_ac_remote_model_t::ARRAH2E: return F("ARRAH2E");
case fujitsu_ac_remote_model_t::ARDB1: return F("ARDB1");
case fujitsu_ac_remote_model_t::ARREB1E: return F("ARREB1E");
case fujitsu_ac_remote_model_t::ARJW2: return F("ARJW2");
case fujitsu_ac_remote_model_t::ARRY4: return F("ARRY4");
case fujitsu_ac_remote_model_t::ARREW4E: return F("ARREW4E");
case fujitsu_ac_remote_model_t::ARRAH2E: return kArrah2eStr;
case fujitsu_ac_remote_model_t::ARDB1: return kArdb1Str;
case fujitsu_ac_remote_model_t::ARREB1E: return kArreb1eStr;
case fujitsu_ac_remote_model_t::ARJW2: return kArjw2Str;
case fujitsu_ac_remote_model_t::ARRY4: return kArry4Str;
case fujitsu_ac_remote_model_t::ARREW4E: return kArrew4eStr;
default: return kUnknownStr;
}
break;
case decode_type_t::GREE:
switch (model) {
case gree_ac_remote_model_t::YAW1F: return F("YAW1F");
case gree_ac_remote_model_t::YBOFB: return F("YBOFB");
case gree_ac_remote_model_t::YAW1F: return kYaw1fStr;
case gree_ac_remote_model_t::YBOFB: return kYbofbStr;
default: return kUnknownStr;
}
break;
case decode_type_t::HAIER_AC176:
switch (model) {
case haier_ac176_remote_model_t::V9014557_A:
return kV9014557AStr;
case haier_ac176_remote_model_t::V9014557_B:
return kV9014557BStr;
default:
return kUnknownStr;
}
break;
case decode_type_t::HITACHI_AC1:
switch (model) {
case hitachi_ac1_remote_model_t::R_LT0541_HTA_A:
return F("R-LT0541-HTA-A");
return kRlt0541htaaStr;
case hitachi_ac1_remote_model_t::R_LT0541_HTA_B:
return F("R-LT0541-HTA-B");
default: return kUnknownStr;
return kRlt0541htabStr;
default:
return kUnknownStr;
}
break;
case decode_type_t::LG:
case decode_type_t::LG2:
switch (model) {
case lg_ac_remote_model_t::GE6711AR2853M: return F("GE6711AR2853M");
case lg_ac_remote_model_t::AKB75215403: return F("AKB75215403");
case lg_ac_remote_model_t::AKB74955603: return F("AKB74955603");
case lg_ac_remote_model_t::AKB73757604: return F("AKB73757604");
case lg_ac_remote_model_t::GE6711AR2853M: return kGe6711ar2853mStr;
case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str;
case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str;
case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str;
default: return kUnknownStr;
}
break;
case decode_type_t::SHARP_AC:
case decode_type_t::MIRAGE:
switch (model) {
case sharp_ac_remote_model_t::A907: return F("A907");
case sharp_ac_remote_model_t::A705: return F("A705");
case sharp_ac_remote_model_t::A903: return F("A903");
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 F("LKE");
case panasonic_ac_remote_model_t::kPanasonicNke: return F("NKE");
case panasonic_ac_remote_model_t::kPanasonicDke: return F("DKE");
case panasonic_ac_remote_model_t::kPanasonicJke: return F("JKE");
case panasonic_ac_remote_model_t::kPanasonicCkp: return F("CKP");
case panasonic_ac_remote_model_t::kPanasonicRkr: return F("RKR");
case panasonic_ac_remote_model_t::kPanasonicLke: return kLkeStr;
case panasonic_ac_remote_model_t::kPanasonicNke: return kNkeStr;
case panasonic_ac_remote_model_t::kPanasonicDke: return kDkeStr;
case panasonic_ac_remote_model_t::kPanasonicJke: return kJkeStr;
case panasonic_ac_remote_model_t::kPanasonicCkp: return kCkpStr;
case panasonic_ac_remote_model_t::kPanasonicRkr: return kRkrStr;
default: return kUnknownStr;
}
break;
case decode_type_t::SHARP_AC:
switch (model) {
case sharp_ac_remote_model_t::A907: return kA907Str;
case sharp_ac_remote_model_t::A705: return kA705Str;
case sharp_ac_remote_model_t::A903: return kA903Str;
default: return kUnknownStr;
}
break;
case decode_type_t::TCL112AC:
switch (model) {
case tcl_ac_remote_model_t::TAC09CHSD: return kTac09chsdStr;
case tcl_ac_remote_model_t::GZ055BE1: return kGz055be1Str;
default: return kUnknownStr;
}
break;
case decode_type_t::VOLTAS:
switch (model) {
case voltas_ac_remote_model_t::kVoltas122LZF: return F("122LZF");
case voltas_ac_remote_model_t::kVoltas122LZF: return k122lzfStr;
default: return kUnknownStr;
}
break;
case decode_type_t::WHIRLPOOL_AC:
switch (model) {
case whirlpool_ac_remote_model_t::DG11J13A: return F("DG11J13A");
case whirlpool_ac_remote_model_t::DG11J191: return F("DG11J191");
case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr;
case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str;
default: return kUnknownStr;
}
break;

View File

@ -48,6 +48,8 @@ float fahrenheitToCelsius(const float deg);
namespace irutils {
String addBoolToString(const bool value, const String label,
const bool precomma = true);
String addToggleToString(const bool toggle, const String label,
const bool precomma = true);
String addIntToString(const uint16_t value, const String label,
const bool precomma = true);
String addSignedIntToString(const int16_t value, const String label,
@ -97,10 +99,10 @@ namespace irutils {
bool getBit(const uint64_t data, const uint8_t position,
const uint8_t size = 64);
bool getBit(const uint8_t data, const uint8_t position);
#define GETBIT8(a, b) (a & ((uint8_t)1 << b))
#define GETBIT16(a, b) (a & ((uint16_t)1 << b))
#define GETBIT32(a, b) (a & ((uint32_t)1 << b))
#define GETBIT64(a, b) (a & ((uint64_t)1 << b))
#define GETBIT8(a, b) ((a) & ((uint8_t)1 << (b)))
#define GETBIT16(a, b) ((a) & ((uint16_t)1 << (b)))
#define GETBIT32(a, b) ((a) & ((uint32_t)1 << (b)))
#define GETBIT64(a, b) ((a) & ((uint64_t)1 << (b)))
#define GETBITS8(data, offset, size) \
(((data) & (((uint8_t)UINT8_MAX >> (8 - (size))) << (offset))) >> (offset))
#define GETBITS16(data, offset, size) \

View File

@ -0,0 +1,70 @@
// 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
#include "IRrecv.h"
#include "IRsend.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)
#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;
// Success
results->decode_type = decode_type_t::AIRTON;
results->bits = nbits;
results->command = 0;
results->address = 0;
return true;
}
#endif // DECODE_AIRTON

View File

@ -0,0 +1,123 @@
// Copyright 2021 David Conran
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
/// @file
/// @brief Arris "Manchester code" based protocol.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595
// Supports:
// Brand: Arris, Model: VIP1113M Set-top box
// Brand: Arris, Model: 120A V1.0 A18 remote
const uint8_t kArrisOverhead = 2;
const uint16_t kArrisHalfClockPeriod = 320; // uSeconds
const uint16_t kArrisHdrMark = 8 * kArrisHalfClockPeriod; // uSeconds
const uint16_t kArrisHdrSpace = 6 * kArrisHalfClockPeriod; // uSeconds
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595#issuecomment-913755841
// aka. 77184 uSeconds.
const uint32_t kArrisGapSpace = 102144 - ((8 + 6 + kArrisBits * 2) *
kArrisHalfClockPeriod); // uSeconds
const uint32_t kArrisReleaseToggle = 0x800008;
const uint8_t kArrisChecksumSize = 4;
const uint8_t kArrisCommandSize = 19;
const uint8_t kArrisReleaseBit = kArrisChecksumSize + kArrisCommandSize;
using irutils::sumNibbles;
#if SEND_ARRIS
/// Send an Arris Manchester Code formatted message.
/// Status: STABLE / Confirmed working.
/// @param[in] data The message to be sent.
/// @param[in] nbits The number of bits of the message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595
void IRsend::sendArris(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
enableIROut(38);
for (uint16_t r = 0; r <= repeat; r++) {
// Header (part 1)
mark(kArrisHdrMark);
space(kArrisHdrSpace);
// Header (part 2) + Data + Footer
sendManchester(kArrisHalfClockPeriod * 2, 0, kArrisHalfClockPeriod,
0, kArrisGapSpace, data, nbits);
}
}
/// Flip the toggle button release bits of an Arris message.
/// Used to indicate a change of remote button's state. e.g. Press vs. Release.
/// @param[in] data The existing Arris message.
/// @return A data message suitable for use in sendArris() with the release bits
/// flipped.
uint32_t IRsend::toggleArrisRelease(const uint32_t data) {
return data ^ kArrisReleaseToggle;
}
/// Construct a raw 32-bit Arris message code from the supplied command &
/// release setting.
/// @param[in] command The command code.
/// @param[in] release The button/command action: press (false), release (true)
/// @return A raw 32-bit Arris message code suitable for sendArris() etc.
/// @note Sequence of bits = header + release + command + checksum.
uint32_t IRsend::encodeArris(const uint32_t command, const bool release) {
uint32_t result = 0x10000000;
irutils::setBits(&result, kArrisChecksumSize, kArrisCommandSize, command);
irutils::setBit(&result, kArrisReleaseBit, release);
return result + sumNibbles(result);
}
#endif // SEND_ARRIS
#if DECODE_ARRIS
/// Decode the supplied Arris "Manchester code" message.
/// Status: STABLE / Confirmed 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/1595
bool IRrecv::decodeArris(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (results->rawlen < nbits + kArrisOverhead - offset)
return false; // Too short a message to match.
// Compliance
if (strict && nbits != kArrisBits)
return false; // Doesn't match our protocol defn.
// Header (part 1)
if (!matchMark(results->rawbuf[offset++], kArrisHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kArrisHdrSpace)) return false;
// Header (part 2) + Data
uint64_t data = 0;
if (!matchManchester(results->rawbuf + offset, &data,
results->rawlen - offset, nbits,
kArrisHalfClockPeriod * 2, 0,
kArrisHalfClockPeriod, 0, 0,
false, kUseDefTol, kMarkExcess, true, false))
return false;
// Compliance
if (strict)
// Validate the checksum.
if (GETBITS32(data, 0, kArrisChecksumSize) !=
sumNibbles(data >> kArrisChecksumSize))
return false;
// Success
results->decode_type = decode_type_t::ARRIS;
results->bits = nbits;
results->value = data;
// Set the address as the Release Bit for something useful.
results->address = static_cast<bool>(GETBIT32(data, kArrisReleaseBit));
// The last 4 bits are likely a checksum value, so skip those. Everything else
// after the release bit. e.g. Bits 10-28
results->command = GETBITS32(data, kArrisChecksumSize, kArrisCommandSize);
return true;
}
#endif // DECODE_ARRIS

View File

@ -611,7 +611,11 @@ String IRCoolixAC::toString(void) const {
result += addBoolToString(getZoneFollow(), kZoneFollowStr);
result += addLabeledString(
(getSensorTemp() == kCoolixSensorTempIgnoreCode)
? kOffStr : 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;
}

View File

@ -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
@ -28,6 +28,7 @@ using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addFanToString;
using irutils::addTempToString;
using irutils::addToggleToString;
#if SEND_ELECTRA_AC
/// Send a Electra A/C formatted message.
@ -309,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 {
@ -341,7 +388,8 @@ 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.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);
@ -351,9 +399,15 @@ String IRElectraAc::toString(void) const {
kElectraAcFanMed);
result += addBoolToString(getSwingV(), kSwingVStr);
result += addBoolToString(getSwingH(), kSwingHStr);
result += addLabeledString(getLightToggle() ? kToggleStr : "-", kLightStr);
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;
}

View File

@ -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);

View File

@ -6,6 +6,13 @@
// Supports:
// Brand: Epson, Model: EN-TW9100W Projector
// Brand: Epson, Model: VS230 Projector
// Brand: Epson, Model: VS330 Projector
// Brand: Epson, Model: EX3220 Projector
// Brand: Epson, Model: EX5220 Projector
// Brand: Epson, Model: EX5230 Projector
// Brand: Epson, Model: EX6220 Projector
// Brand: Epson, Model: EX7220 Projector
#define __STDC_LIMIT_MACROS
#include <stdint.h>

View File

@ -21,6 +21,7 @@ using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addFanToString;
using irutils::addTempToString;
using irutils::addToggleToString;
#if SEND_GOODWEATHER
/// Send a Goodweather HVAC formatted message.
@ -346,9 +347,10 @@ String IRGoodweatherAc::toString(void) const {
result += addFanToString(_.Fan, kGoodweatherFanHigh, kGoodweatherFanLow,
kGoodweatherFanAuto, kGoodweatherFanAuto,
kGoodweatherFanMed);
result += addLabeledString(_.Turbo ? kToggleStr : "-", kTurboStr);
result += addLabeledString(_.Light ? kToggleStr : "-", kLightStr);
result += addLabeledString(_.Sleep ? kToggleStr : "-", kSleepStr);
result += addToggleToString(_.Turbo, kTurboStr);
result += addToggleToString(_.Light, kLightStr);
result += addToggleToString(_.Sleep, kSleepStr);
result += addIntToString(_.Swing, kSwingStr);
result += kSpaceLBraceStr;
switch (_.Swing) {

View File

@ -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.
@ -562,6 +557,21 @@ stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) {
}
}
/// 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;
}
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @return The stdAc equivalent of the native settings.
stdAc::state_t IRGreeAC::toCommon(void) {
@ -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;

View File

@ -14,10 +14,15 @@
// Brand: Green, Model: YBOFB2 remote
// Brand: Gree, Model: YAA1FBF remote
// Brand: Gree, Model: YB1F2F remote
// Brand: Gree, Model: YAN1F1 remote
// Brand: Gree, Model: VIR09HP115V1AH A/C
// Brand: Gree, Model: VIR12HP230V1AH A/C
// Brand: Amana, Model: PBC093G00CC A/C
// Brand: Amana, Model: YX1FF remote
// Brand: Cooper & Hunter, Model: YB1F2 remote
// Brand: Cooper & Hunter, Model: CH-S09FTXG A/C
// Brand: Vailland, Model: YACIFB remote
// Brand: Vailland, Model: VAI5-035WNI A/C
#ifndef IR_GREE_H_
#define IR_GREE_H_
@ -60,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;
// Byte 7
uint8_t :4;
uint8_t Sum:4;
uint8_t :2;
uint8_t Econo :1;
uint8_t :1;
uint8_t Sum :4;
};
};
@ -95,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
@ -171,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);
@ -178,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);
@ -185,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[]);

View File

@ -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,9 +416,9 @@ 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;
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.
@ -443,7 +445,7 @@ stdAc::state_t IRHaierAC::toCommon(void) const {
result.econo = false;
result.light = false;
result.clean = false;
result.beep = false;
result.beep = true;
result.clock = -1;
return result;
}
@ -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 {
@ -629,47 +656,118 @@ uint8_t IRHaierAC176::getButton(void) const {
/// Set the operating mode of the A/C.
/// @param[in] mode The desired operating mode.
void IRHaierAC176::setMode(uint8_t mode) {
uint8_t new_mode = mode;
_.Button = kHaierAcYrw02ButtonMode;
switch (mode) {
case kHaierAcYrw02Auto:
case kHaierAcYrw02Cool:
case kHaierAcYrw02Dry:
case kHaierAcYrw02Fan:
// Turbo & Quiet is only available in Cool/Heat mode.
_.Turbo = false;
_.Quiet = false;
// FALL-THRU
case kHaierAcYrw02Cool:
case kHaierAcYrw02Heat:
case kHaierAcYrw02Fan: break;
default: new_mode = kHaierAcYrw02Auto; // Unexpected, default to auto mode.
_.Button = kHaierAcYrw02ButtonMode;
_.Mode = mode;
break;
default:
setMode(kHaierAcYrw02Auto); // Unexpected, default to auto mode.
}
_.Mode = new_mode;
}
/// Get the operating mode setting of the A/C.
/// @return The current operating mode setting.
uint8_t IRHaierAC176::getMode(void) const {
return _.Mode;
}
uint8_t IRHaierAC176::getMode(void) const { return _.Mode; }
/// 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] 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;
/// @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)
if (old_temp == degree) return;
if (_.UseFahrenheit == fahrenheit) {
if (old_temp > degree)
_.Button = kHaierAcYrw02ButtonTempDown;
else
_.Button = kHaierAcYrw02ButtonTempUp;
_.Temp = temp - kHaierAcMinTemp;
} 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.
/// The unit of temperature is specified by UseFahrenheit value.
/// @return The current setting for temperature.
uint8_t IRHaierAC176::getTemp(void) const {
return _.Temp + kHaierAcMinTemp;
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.
@ -681,15 +779,11 @@ void IRHaierAC176::setHealth(const bool on) {
/// Get the Health (filter) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRHaierAC176::getHealth(void) const {
return _.Health;
}
bool IRHaierAC176::getHealth(void) const { return _.Health; }
/// Get the value of the current power setting.
/// @return true, the setting is on. false, the setting is off.
bool IRHaierAC176::getPower(void) const {
return _.Power;
}
bool IRHaierAC176::getPower(void) const { return _.Power; }
/// Change the power setting.
/// @param[in] on true, the setting is on. false, the setting is off.
@ -706,9 +800,7 @@ void IRHaierAC176::off(void) { setPower(false); }
/// Get the Sleep setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRHaierAC176::getSleep(void) const {
return _.Sleep;
}
bool IRHaierAC176::getSleep(void) const { return _.Sleep; }
/// Set the Sleep setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
@ -718,30 +810,42 @@ void IRHaierAC176::setSleep(const bool on) {
}
/// Get the Turbo setting of the A/C.
/// @return The current turbo speed setting.
uint8_t IRHaierAC176::getTurbo(void) const {
return _.Turbo;
}
/// @return The current turbo setting.
bool IRHaierAC176::getTurbo(void) const { return _.Turbo; }
/// Set the Turbo setting of the A/C.
/// @param[in] speed The desired turbo speed setting.
/// @note Valid speeds are kHaierAcYrw02TurboOff, kHaierAcYrw02TurboLow, &
/// kHaierAcYrw02TurboHigh.
void IRHaierAC176::setTurbo(uint8_t speed) {
switch (speed) {
case kHaierAcYrw02TurboOff:
case kHaierAcYrw02TurboLow:
case kHaierAcYrw02TurboHigh:
_.Turbo = speed;
/// @param[in] on The desired turbo setting.
/// @note Turbo & Quiet can't be on at the same time, and only in Heat/Cool mode
void IRHaierAC176::setTurbo(const bool on) {
switch (getMode()) {
case kHaierAcYrw02Cool:
case kHaierAcYrw02Heat:
_.Turbo = on;
_.Button = kHaierAcYrw02ButtonTurbo;
if (on) _.Quiet = false;
}
}
/// Get the Quiet setting of the A/C.
/// @return The current Quiet setting.
bool IRHaierAC176::getQuiet(void) const { return _.Quiet; }
/// Set the Quiet setting of the A/C.
/// @param[in] on The desired Quiet setting.
/// @note Turbo & Quiet can't be on at the same time, and only in Heat/Cool mode
void IRHaierAC176::setQuiet(const bool on) {
switch (getMode()) {
case kHaierAcYrw02Cool:
case kHaierAcYrw02Heat:
_.Quiet = on;
_.Button = kHaierAcYrw02ButtonTurbo;
if (on) _.Turbo = false;
}
}
/// Get the current fan speed setting.
/// @return The current fan speed.
uint8_t IRHaierAC176::getFan(void) const {
return _.Fan;
}
uint8_t IRHaierAC176::getFan(void) const { return _.Fan; }
/// Set the speed of the fan.
/// @param[in] speed The desired setting.
@ -757,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;
}
@ -867,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.
@ -900,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;
}
}
@ -939,37 +1100,52 @@ 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;
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;
}
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @return The stdAc equivalent of the native settings.
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.quiet = false;
result.turbo = false;
result.econo = false;
result.light = false;
result.clean = false;
result.beep = false;
result.beep = true;
result.clock = -1;
return result;
}
@ -978,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;
@ -1005,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;
}
@ -1018,51 +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 += addIntToString(_.Turbo, kTurboStr);
result += addBoolToString(_.Turbo, kTurboStr);
result += addBoolToString(_.Quiet, kQuietStr);
result += addIntToString(_.SwingV, kSwingVStr);
result += kSpaceLBraceStr;
switch (_.Turbo) {
case kHaierAcYrw02TurboOff:
switch (_.SwingV) {
case kHaierAcYrw02SwingVOff:
result += kOffStr;
break;
case kHaierAcYrw02TurboLow:
result += kLowStr;
break;
case kHaierAcYrw02TurboHigh:
result += kHighStr;
break;
default:
result += kUnknownStr;
}
result += ')';
result += addIntToString(_.Swing, kSwingStr);
result += kSpaceLBraceStr;
switch (_.Swing) {
case kHaierAcYrw02SwingOff:
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();
@ -1098,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.
@ -1203,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;
}
@ -1236,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;
}

View File

@ -1,4 +1,4 @@
// Copyright 2018 crankyoldgit
// Copyright 2018-2021 crankyoldgit
/// @file
/// @brief Support for Haier A/C protocols.
/// The specifics of reverse engineering the protocols details:
@ -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,85 +135,54 @@ const uint8_t kHaierAcSleepBit = 0b01000000;
#define HAIER_AC_FAN_MED kHaierAcFanMed
#define HAIER_AC_FAN_HIGH kHaierAcFanHigh
/// Native representation of a Haier YRW02 A/C message.
union HaierYRW02Protocol{
uint8_t raw[kHaierACYRW02StateLength]; ///< The state in native form
struct {
// Byte 0
uint8_t Prefix;
// Byte 1
uint8_t Swing:4;
uint8_t Temp :4; // 16C~30C
// Byte 2
uint8_t :8;
// Byte 3
uint8_t :1;
uint8_t Health:1;
uint8_t :6;
// Byte 4
uint8_t :6;
uint8_t Power:1;
uint8_t :1;
// Byte 5
uint8_t :5;
uint8_t Fan:3;
// Byte 6
uint8_t :6;
uint8_t Turbo:2;
// Byte 7
uint8_t :5;
uint8_t Mode:3;
// Byte 8
uint8_t :7;
uint8_t Sleep:1;
// Byte 9
uint8_t :8;
// Byte 10
uint8_t :8;
// Byte 11
uint8_t :8;
// Byte 12
uint8_t Button:4;
uint8_t :4;
// Byte 13
uint8_t Sum;
};
};
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 kHaierAcYrw02Prefix = 0xA6;
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;
const uint8_t kHaierAcYrw02FanLow = 0b011;
const uint8_t kHaierAcYrw02FanAuto = 0b101; // HAIER_AC176 uses `0` in Fan2
const uint8_t kHaierAcYrw02TurboOff = 0x0;
const uint8_t kHaierAcYrw02TurboHigh = 0x1;
const uint8_t kHaierAcYrw02TurboLow = 0x2;
const uint8_t kHaierAcYrw02Auto = 0b000; // 0
const uint8_t kHaierAcYrw02Cool = 0b001; // 1
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;
@ -224,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;
@ -244,7 +216,8 @@ union HaierAc176Protocol{
uint8_t Fan :3;
// Byte 6
uint8_t OffTimerMins:6;
uint8_t Turbo:2;
uint8_t Turbo :1;
uint8_t Quiet :1;
// Byte 7
uint8_t OnTimerHrs :5;
uint8_t Mode :3;
@ -255,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
@ -295,8 +272,6 @@ union HaierAc176Protocol{
#define HAIER_AC_YRW02_FAN_LOW kHaierAcYrw02FanLow
#define HAIER_AC_YRW02_FAN_AUTO kHaierAcYrw02FanAuto
#define HAIER_AC_YRW02_TURBO_OFF kHaierAcYrw02TurboOff
#define HAIER_AC_YRW02_TURBO_HIGH kHaierAcYrw02TurboHigh
#define HAIER_AC_YRW02_TURBO_LOW kHaierAcYrw02TurboLow
#define HAIER_AC_YRW02_AUTO kHaierAcYrw02Auto
#define HAIER_AC_YRW02_COOL kHaierAcYrw02Cool
#define HAIER_AC_YRW02_DRY kHaierAcYrw02Dry
@ -355,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[]);
@ -400,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);
@ -422,9 +402,18 @@ class IRHaierAC176 {
bool getHealth(void) const;
void setHealth(const bool on);
uint8_t getTurbo(void) const;
void setTurbo(const uint8_t speed);
bool getTurbo(void) const;
void setTurbo(const bool on);
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);
@ -435,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[],
@ -442,9 +434,13 @@ 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;
String toString(void) const;
#ifndef UNIT_TEST

View File

@ -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)

View File

@ -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 <algorithm>
#include <cstring>
#ifndef ARDUINO
#include <string>
#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

View File

@ -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 <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#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_

View File

@ -1166,7 +1166,7 @@ void IRsend::sendMitsubishi112(const unsigned char data[],
}
#endif // SEND_MITSUBISHI112
#if DECODE_MITSUBISHI112 || DECODE_TCL112AC
#if (DECODE_MITSUBISHI112 || DECODE_TCL112AC)
/// Decode the supplied Mitsubishi/TCL 112-bit A/C message.
/// (MITSUBISHI112, TCL112AC)
/// Status: STABLE / Reported as working.
@ -1212,7 +1212,7 @@ bool IRrecv::decodeMitsubishi112(decode_results *results, uint16_t offset,
gap = kMitsubishi112Gap;
}
#endif // DECODE_MITSUBISHI112
#if DECODE_TCL112AC
#if (DECODE_TCL112AC || DECODE_TEKNOPOINT)
if (typeguess == decode_type_t::UNKNOWN && // We didn't match Mitsubishi112
matchMark(results->rawbuf[offset], kTcl112AcHdrMark,
kTcl112AcHdrMarkTolerance, 0)) {
@ -1224,7 +1224,7 @@ bool IRrecv::decodeMitsubishi112(decode_results *results, uint16_t offset,
gap = kTcl112AcGap;
tolerance += kTcl112AcTolerance;
}
#endif // DECODE_TCL112AC
#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT)
if (typeguess == decode_type_t::UNKNOWN) return false; // No header matched.
offset++;

View File

@ -0,0 +1,364 @@
// Copyright 2021 Tom Rosenback
/// @file
/// @brief Support for Rhoss protocols.
#include "ir_Rhoss.h"
#include <algorithm>
#include <cstring>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtext.h"
#include "IRutils.h"
const uint16_t kRhossHdrMark = 3042;
const uint16_t kRhossHdrSpace = 4248;
const uint16_t kRhossBitMark = 648;
const uint16_t kRhossOneSpace = 1545;
const uint16_t kRhossZeroSpace = 457;
const uint32_t kRhossGap = kDefaultMessageGap;
const uint16_t kRhossFreq = 38;
using irutils::addBoolToString;
using irutils::addModeToString;
using irutils::addFanToString;
using irutils::addTempToString;
#if SEND_RHOSS
/// Send a Rhoss HVAC formatted message.
/// Status: STABLE / Reported as working.
/// @param[in] data The message to be sent.
/// @param[in] nbytes The number of bytes of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
void IRsend::sendRhoss(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
// Check if we have enough bytes to send a proper message.
if (nbytes < kRhossStateLength) return;
// We always send a message, even for repeat=0, hence '<= repeat'.
for (uint16_t r = 0; r <= repeat; r++) {
sendGeneric(kRhossHdrMark, kRhossHdrSpace, kRhossBitMark,
kRhossOneSpace, kRhossBitMark, kRhossZeroSpace,
kRhossBitMark, kRhossZeroSpace,
data, nbytes, kRhossFreq, false, 0, kDutyDefault);
mark(kRhossBitMark);
// Gap
space(kRhossGap);
}
}
#endif // SEND_RHOSS
#if DECODE_RHOSS
/// Decode the supplied Rhoss formatted message.
/// Status: STABLE / Known working.
/// @param[in,out] results Ptr to the data to decode & where to store the 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.
bool IRrecv::decodeRhoss(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (strict && nbits != kRhossBits) return false;
if (results->rawlen <= 2 * nbits + kHeader + kFooter - 1 + offset) {
return false; // Can't possibly be a valid Rhoss message.
}
uint16_t used;
// Header + Data Block (96 bits) + Footer
used = matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, kRhossBits,
kRhossHdrMark, kRhossHdrSpace,
kRhossBitMark, kRhossOneSpace,
kRhossBitMark, kRhossZeroSpace,
kRhossBitMark, kRhossZeroSpace,
false, kUseDefTol, kMarkExcess, false);
if (!used) return false;
offset += used;
// Footer (Part 2)
if (!matchMark(results->rawbuf[offset++], kRhossBitMark)) {
return false;
}
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], kRhossGap)) {
return false;
}
if (strict && !IRRhossAc::validChecksum(results->state)) return false;
// Success
results->decode_type = decode_type_t::RHOSS;
results->bits = nbits;
// No need to record the state as we stored it as we decoded it.
// As we use result->state, we don't record value, address, or command as it
// is a union data type.
return true;
}
#endif // DECODE_RHOSS
/// 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?
IRRhossAc::IRRhossAc(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { this->stateReset(); }
/// Set up hardware to be able to send a message.
void IRRhossAc::begin(void) { _irsend.begin(); }
#if SEND_RHOSS
/// Send the current internal state as an IR message.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRRhossAc::send(const uint16_t repeat) {
_irsend.sendRhoss(getRaw(), kRhossStateLength, repeat);
}
#endif // SEND_RHOSS
/// Calculate the checksum for the supplied state.
/// @param[in] state The source state to generate the checksum from.
/// @param[in] length Length of the supplied state to checksum.
/// @return The checksum value.
uint8_t IRRhossAc::calcChecksum(const uint8_t state[], const uint16_t length) {
return sumBytes(state, length - 1);
}
/// Verify the checksum is valid for a given state.
/// @param[in] state The array to verify the checksum of.
/// @param[in] length The size of the state.
/// @return A boolean indicating if it's checksum is valid.
bool IRRhossAc::validChecksum(const uint8_t state[], const uint16_t length) {
return (state[length - 1] == IRRhossAc::calcChecksum(state, length));
}
/// Update the checksum value for the internal state.
void IRRhossAc::checksum(void) {
_.Sum = IRRhossAc::calcChecksum(_.raw, kRhossStateLength);
_.raw[kRhossStateLength - 1] = _.Sum;
}
/// Reset the internals of the object to a known good state.
void IRRhossAc::stateReset(void) {
for (uint8_t i = 1; i < kRhossStateLength; i++) _.raw[i] = 0x0;
_.raw[0] = 0xAA;
_.raw[2] = 0x60;
_.raw[6] = 0x54;
_.Power = kRhossDefaultPower;
_.Fan = kRhossDefaultFan;
_.Mode = kRhossDefaultMode;
_.Swing = kRhossDefaultSwing;
_.Temp = kRhossDefaultTemp - kRhossTempMin;
}
/// Get the raw state of the object, suitable to be sent with the appropriate
/// IRsend object method.
/// @return A PTR to the internal state.
uint8_t* IRRhossAc::getRaw(void) {
checksum(); // Ensure correct bit array before returning
return _.raw;
}
/// Set the raw state of the object.
/// @param[state] state The raw state from the native IR message.
void IRRhossAc::setRaw(const uint8_t state[]) {
std::memcpy(_.raw, state, kRhossStateLength);
}
/// Set the internal state to have the power on.
void IRRhossAc::on(void) { setPower(true); }
/// Set the internal state to have the power off.
void IRRhossAc::off(void) { setPower(false); }
/// Set the internal state to have the desired power.
/// @param[in] on The desired power state.
void IRRhossAc::setPower(const bool on) {
_.Power = (on ? kRhossPowerOn : kRhossPowerOff);
}
/// Get the power setting from the internal state.
/// @return A boolean indicating the power setting.
bool IRRhossAc::getPower(void) const {
return _.Power == kRhossPowerOn;
}
/// Set the temperature.
/// @param[in] degrees The temperature in degrees celsius.
void IRRhossAc::setTemp(const uint8_t degrees) {
uint8_t temp = std::max(kRhossTempMin, degrees);
_.Temp = std::min(kRhossTempMax, temp) - kRhossTempMin;
}
/// Get the current temperature setting.
/// @return Get current setting for temp. in degrees celsius.
uint8_t IRRhossAc::getTemp(void) const {
return _.Temp + kRhossTempMin;
}
/// Set the speed of the fan.
/// @param[in] speed The desired setting.
void IRRhossAc::setFan(const uint8_t speed) {
switch (speed) {
case kRhossFanAuto:
case kRhossFanMin:
case kRhossFanMed:
case kRhossFanMax:
_.Fan = speed;
break;
default:
_.Fan = kRhossFanAuto;
}
}
/// Get the current fan speed setting.
/// @return The current fan speed.
uint8_t IRRhossAc::getFan(void) const {
return _.Fan;
}
/// Set the Vertical Swing mode of the A/C.
/// @param[in] state true, the Swing is on. false, the Swing is off.
void IRRhossAc::setSwing(const bool state) {
_.Swing = state;
}
/// Get the Vertical Swing speed of the A/C.
/// @return The native swing speed setting.
uint8_t IRRhossAc::getSwing(void) const {
return _.Swing;
}
/// Get the current operation mode setting.
/// @return The current operation mode.
uint8_t IRRhossAc::getMode(void) const {
return _.Mode;
}
/// Set the desired operation mode.
/// @param[in] mode The desired operation mode.
void IRRhossAc::setMode(const uint8_t mode) {
switch (mode) {
case kRhossModeFan:
case kRhossModeCool:
case kRhossModeDry:
case kRhossModeHeat:
case kRhossModeAuto:
_.Mode = mode;
return;
default:
_.Mode = kRhossDefaultMode;
break;
}
}
/// 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 IRRhossAc::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
return kRhossModeCool;
case stdAc::opmode_t::kHeat:
return kRhossModeHeat;
case stdAc::opmode_t::kDry:
return kRhossModeDry;
case stdAc::opmode_t::kFan:
return kRhossModeFan;
case stdAc::opmode_t::kAuto:
return kRhossModeAuto;
default:
return kRhossDefaultMode;
}
}
/// 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 IRRhossAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kRhossFanMin;
case stdAc::fanspeed_t::kMedium:
return kRhossFanMed;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kRhossFanMax;
default:
return kRhossDefaultFan;
}
}
/// 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 IRRhossAc::toCommonMode(const uint8_t mode) {
switch (mode) {
case kRhossModeCool: return stdAc::opmode_t::kCool;
case kRhossModeHeat: return stdAc::opmode_t::kHeat;
case kRhossModeDry: return stdAc::opmode_t::kDry;
case kRhossModeFan: return stdAc::opmode_t::kFan;
case kRhossModeAuto: return stdAc::opmode_t::kAuto;
default: return stdAc::opmode_t::kAuto;
}
}
/// Convert a native fan speed into its stdAc equivalent.
/// @param[in] speed The native setting to be converted.
/// @return The stdAc equivalent of the native setting.
stdAc::fanspeed_t IRRhossAc::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kRhossFanMax: return stdAc::fanspeed_t::kMax;
case kRhossFanMed: return stdAc::fanspeed_t::kMedium;
case kRhossFanMin: return stdAc::fanspeed_t::kMin;
case kRhossFanAuto:
default:
return stdAc::fanspeed_t::kAuto;
}
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @return The stdAc equivalent of the native settings.
stdAc::state_t IRRhossAc::toCommon(void) const {
stdAc::state_t result;
result.protocol = decode_type_t::RHOSS;
result.power = getPower();
result.mode = toCommonMode(_.Mode);
result.celsius = true;
result.degrees = _.Temp;
result.fanspeed = toCommonFanSpeed(_.Fan);
result.swingv = _.Swing ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
// Not supported.
result.model = -1;
result.turbo = false;
result.swingh = stdAc::swingh_t::kOff;
result.light = false;
result.filter = false;
result.econo = false;
result.quiet = false;
result.clean = false;
result.beep = false;
result.sleep = -1;
result.clock = -1;
return result;
}
/// Convert the current internal state into a human readable string.
/// @return A human readable string.
String IRRhossAc::toString(void) const {
String result = "";
result.reserve(70); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(getMode(), kRhossModeAuto, kRhossModeCool,
kRhossModeHeat, kRhossModeDry, kRhossModeFan);
result += addTempToString(getTemp());
result += addFanToString(getFan(), kRhossFanMax, kRhossFanMin,
kRhossFanAuto, kRhossFanAuto,
kRhossFanMed);
result += addBoolToString(getSwing(), kSwingVStr);
return result;
}

View File

@ -0,0 +1,145 @@
// Copyright 2021 Tom Rosenback
/// @file
/// @brief Support for Rhoss A/C protocol
// Supports:
// Brand: Rhoss, Model: Idrowall MPCV 20-30-35-40
#ifndef IR_RHOSS_H_
#define IR_RHOSS_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
/// Native representation of a Rhoss A/C message.
union RhossProtocol{
uint8_t raw[kRhossStateLength]; // The state of the IR remote.
struct {
// Byte 0
uint8_t :8; // Typically 0xAA
// Byte 1
uint8_t Temp :4;
uint8_t :4; // Typically 0x0
// Byte 2
uint8_t :8; // Typically 0x60
// Byte 3
uint8_t :8; // Typically 0x0
// Byte 4
uint8_t Fan :2;
uint8_t :2; // Typically 0x0
uint8_t Mode :4;
// Byte 5
uint8_t Swing :1;
uint8_t :5; // Typically 0x0
uint8_t Power :2;
// Byte 6
uint8_t :8; // Typically 0x54
// Byte 7
uint8_t :8; // Typically 0x0
// Byte 8
uint8_t :8; // Typically 0x0
// Byte 9
uint8_t :8; // Typically 0x0
// Byte 10
uint8_t :8; // Typically 0x0
// Byte 11
uint8_t Sum :8;
};
};
// Constants
// Fan Control
const uint8_t kRhossFanAuto = 0b00;
const uint8_t kRhossFanMin = 0b01;
const uint8_t kRhossFanMed = 0b10;
const uint8_t kRhossFanMax = 0b11;
// Modes
const uint8_t kRhossModeHeat = 0b0001;
const uint8_t kRhossModeCool = 0b0010;
const uint8_t kRhossModeDry = 0b0011;
const uint8_t kRhossModeFan = 0b0100;
const uint8_t kRhossModeAuto = 0b0101;
// Temperature
const uint8_t kRhossTempMin = 16; // Celsius
const uint8_t kRhossTempMax = 30; // Celsius
// Power
const uint8_t kRhossPowerOn = 0b10; // 0x2
const uint8_t kRhossPowerOff = 0b01; // 0x1
// Swing
const uint8_t kRhossSwingOn = 0b1; // 0x1
const uint8_t kRhossSwingOff = 0b0; // 0x0
const uint8_t kRhossDefaultFan = kRhossFanAuto;
const uint8_t kRhossDefaultMode = kRhossModeCool;
const uint8_t kRhossDefaultTemp = 21; // Celsius
const bool kRhossDefaultPower = false;
const bool kRhossDefaultSwing = false;
// Classes
/// Class for handling detailed Rhoss A/C messages.
class IRRhossAc {
public:
explicit IRRhossAc(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void stateReset();
#if SEND_RHOSS
void send(const uint16_t repeat = kRhossDefaultRepeat);
/// 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_RHOSS
void begin();
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kRhossStateLength);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kRhossStateLength);
void setPower(const bool state);
bool getPower(void) const;
void on(void);
void off(void);
void setTemp(const uint8_t temp);
uint8_t getTemp(void) const;
void setFan(const uint8_t speed);
uint8_t getFan(void) const;
void setSwing(const bool state);
uint8_t getSwing(void) const;
void setMode(const uint8_t mode);
uint8_t getMode(void) const;
uint8_t* getRaw(void);
void setRaw(const uint8_t state[]);
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void) const;
String toString(void) const;
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
/// @cond IGNORE
IRsendTest _irsend;
/// @endcond
#endif
RhossProtocol _;
void checksum(void);
};
#endif // IR_RHOSS_H_

View File

@ -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)) {

View File

@ -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,41 +44,46 @@
/// 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;
// 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;
// Byte 8
uint8_t Powerful8 :8;
// Byte 9
uint8_t :4;
uint8_t :4; // Sum2Lower
// Byte 9
uint8_t :4; // Sum1Upper
uint8_t Swing :3;
uint8_t :1;
// Byte 10
uint8_t :1;
uint8_t Powerful10 :3;
uint8_t FanSpecial :3; // Powerful, Breeze/WindFree, Econo
uint8_t Display :1;
uint8_t :2;
uint8_t Clean10 :1;
uint8_t CleanToggle10 :1;
// Byte 11
uint8_t Ion :1;
uint8_t Clean11 :1;
uint8_t CleanToggle11 :1;
uint8_t :2;
uint8_t Temp :4;
// Byte 12
@ -84,11 +92,13 @@ union SamsungProtocol{
uint8_t Mode :3;
uint8_t :1;
// Byte 13
uint8_t :2;
uint8_t BeepToggle :1;
uint8_t :1;
uint8_t Beep :1;
uint8_t :6;
uint8_t Power2 :2;
uint8_t :2;
};
struct {
struct { // Extended message map
// 1st Section
// Byte 0
uint8_t :8;
@ -114,15 +124,22 @@ union SamsungProtocol{
uint8_t Sum2Lower :4;
// Byte 9
uint8_t Sum2Upper :4;
uint8_t :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;
// 3rd Section
@ -146,11 +163,6 @@ union SamsungProtocol{
};
// 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_

View File

@ -45,7 +45,9 @@ using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addModelToString;
using irutils::addSwingVToString;
using irutils::addTempToString;
using irutils::addToggleToString;
using irutils::minsToString;
// Also used by Denon protocol
@ -544,24 +546,73 @@ void IRSharpAc::setTurbo(const bool on) {
_.Special = kSharpAcSpecialTurbo;
}
/// Get the Vertical Swing setting of the A/C.
/// @return The position of the Vertical Swing setting.
uint8_t IRSharpAc::getSwingV(void) const { return _.Swing; }
/// Set the Vertical Swing setting of the A/C.
/// @note Some positions may not work on all models.
/// @param[in] position The desired position/setting.
/// @note `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting
/// in Heat mode, it will default to `kSharpAcSwingVLow` otherwise.
/// If you want to set this value in other modes e.g. Cool, you must
/// use `setSwingV`s optional `force` parameter.
/// @param[in] force Do we override the safety checks and just do it?
void IRSharpAc::setSwingV(const uint8_t position, const bool force) {
switch (position) {
case kSharpAcSwingVCoanda:
// Only allowed in Heat mode.
if (!force && getMode() != kSharpAcHeat) {
setSwingV(kSharpAcSwingVLow); // Use the next lowest setting.
return;
}
// FALLTHRU
case kSharpAcSwingVHigh:
case kSharpAcSwingVMid:
case kSharpAcSwingVLow:
case kSharpAcSwingVToggle:
case kSharpAcSwingVOff:
case kSharpAcSwingVLast: // Technically valid, but we don't use it.
// All expected non-positions set the special bits.
_.Special = kSharpAcSpecialSwing;
// FALLTHRU
case kSharpAcSwingVIgnore:
_.Swing = position;
}
}
/// Convert a standard A/C vertical swing into its native setting.
/// @param[in] position A stdAc::swingv_t position to convert.
/// @return The equivalent native horizontal swing position.
uint8_t IRSharpAc::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest:
case stdAc::swingv_t::kHigh: return kSharpAcSwingVHigh;
case stdAc::swingv_t::kMiddle: return kSharpAcSwingVMid;
case stdAc::swingv_t::kLow: return kSharpAcSwingVLow;
case stdAc::swingv_t::kLowest: return kSharpAcSwingVCoanda;
case stdAc::swingv_t::kAuto: return kSharpAcSwingVToggle;
case stdAc::swingv_t::kOff: return kSharpAcSwingVOff;
default: return kSharpAcSwingVIgnore;
}
}
/// Get the (vertical) Swing Toggle setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSharpAc::getSwingToggle(void) const {
return _.Swing == kSharpAcSwingToggle;
return getSwingV() == kSharpAcSwingVToggle;
}
/// Set the (vertical) Swing Toggle setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSharpAc::setSwingToggle(const bool on) {
_.Swing = (on ? kSharpAcSwingToggle : kSharpAcSwingNoToggle);
setSwingV(on ? kSharpAcSwingVToggle : kSharpAcSwingVIgnore);
if (on) _.Special = kSharpAcSpecialSwing;
}
/// Get the Ion (Filter) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSharpAc::getIon(void) const {
return _.Ion;
}
bool IRSharpAc::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.
@ -628,15 +679,11 @@ uint16_t IRSharpAc::getTimerTime(void) const {
/// Is the Timer enabled?
/// @return true, the setting is on. false, the setting is off.
bool IRSharpAc::getTimerEnabled(void) const {
return _.TimerEnabled;
}
bool IRSharpAc::getTimerEnabled(void) const { return _.TimerEnabled; }
/// Get the current timer type.
/// @return true, It's an "On" timer. false, It's an "Off" timer.
bool IRSharpAc::getTimerType(void) const {
return _.TimerType;
}
bool IRSharpAc::getTimerType(void) const { return _.TimerType; }
/// Set or cancel the timer function.
/// @param[in] enable Is the timer to be enabled (true) or canceled(false)?
@ -765,10 +812,34 @@ stdAc::fanspeed_t IRSharpAc::toCommonFanSpeed(const uint8_t speed) const {
}
}
/// Convert a native vertical swing postion to it's common equivalent.
/// @param[in] pos A native position to convert.
/// @param[in] mode What operating mode are we in?
/// @return The common vertical swing position.
stdAc::swingv_t IRSharpAc::toCommonSwingV(const uint8_t pos,
const stdAc::opmode_t mode) const {
switch (pos) {
case kSharpAcSwingVHigh: return stdAc::swingv_t::kHighest;
case kSharpAcSwingVMid: return stdAc::swingv_t::kMiddle;
case kSharpAcSwingVLow: return stdAc::swingv_t::kLow;
case kSharpAcSwingVCoanda: // Coanda has mode dependent positionss
switch (mode) {
case stdAc::opmode_t::kCool: return stdAc::swingv_t::kHighest;
case stdAc::opmode_t::kHeat: return stdAc::swingv_t::kLowest;
default: return stdAc::swingv_t::kOff;
}
case kSharpAcSwingVToggle: return stdAc::swingv_t::kAuto;
default: return stdAc::swingv_t::kOff;
}
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @param[in] prev Ptr to the previous state if required.
/// @return The stdAc equivalent of the native settings.
stdAc::state_t IRSharpAc::toCommon(void) const {
stdAc::state_t IRSharpAc::toCommon(const stdAc::state_t *prev) const {
stdAc::state_t result;
// Start with the previous state if given it.
if (prev != NULL) result = *prev;
result.protocol = decode_type_t::SHARP_AC;
result.model = getModel();
result.power = getPower();
@ -777,8 +848,8 @@ stdAc::state_t IRSharpAc::toCommon(void) const {
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.turbo = getTurbo();
result.swingv = getSwingToggle() ? stdAc::swingv_t::kAuto
: stdAc::swingv_t::kOff;
if (getSwingV() != kSharpAcSwingVIgnore)
result.swingv = toCommonSwingV(getSwingV(), result.mode);
result.filter = _.Ion;
result.econo = getEconoToggle();
result.light = getLightToggle();
@ -797,14 +868,16 @@ stdAc::state_t IRSharpAc::toCommon(void) const {
String IRSharpAc::toString(void) const {
String result = "";
const sharp_ac_remote_model_t model = getModel();
result.reserve(160); // Reserve some heap for the string to reduce fragging.
result.reserve(170); // Reserve some heap for the string to reduce fragging.
result += addModelToString(decode_type_t::SHARP_AC, getModel(), false);
result += addLabeledString(isPowerSpecial() ? "-"
: (getPower() ? kOnStr : kOffStr),
result += addLabeledString(isPowerSpecial() ? String("-")
: String(getPower() ? kOnStr
: kOffStr),
kPowerStr);
const uint8_t mode = _.Mode;
result += addModeToString(
_.Mode,
mode,
// Make the value invalid if the model doesn't support an Auto mode.
(model == sharp_ac_remote_model_t::A907) ? kSharpAcAuto : 255,
kSharpAcCool, kSharpAcHeat, kSharpAcDry, kSharpAcFan);
@ -821,18 +894,37 @@ String IRSharpAc::toString(void) const {
kSharpAcFanAuto, kSharpAcFanAuto,
kSharpAcFanMed);
}
if (getSwingV() == kSharpAcSwingVIgnore) {
result += addIntToString(kSharpAcSwingVIgnore, kSwingVStr);
result += kSpaceLBraceStr;
result += kNAStr;
result += ')';
} else {
result += addSwingVToString(
getSwingV(), 0xFF,
// Coanda means Highest when in Cool mode.
(mode == kSharpAcCool) ? kSharpAcSwingVCoanda : kSharpAcSwingVToggle,
kSharpAcSwingVHigh,
0xFF, // Upper Middle is unused
kSharpAcSwingVMid,
0xFF, // Lower Middle is unused
kSharpAcSwingVLow,
kSharpAcSwingVCoanda,
kSharpAcSwingVOff,
// Below are unused.
kSharpAcSwingVToggle,
0xFF,
0xFF);
}
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getSwingToggle(), kSwingVToggleStr);
result += addBoolToString(_.Ion, kIonStr);
switch (model) {
case sharp_ac_remote_model_t::A705:
case sharp_ac_remote_model_t::A903:
result += addLabeledString(getLightToggle() ? kToggleStr : "-",
kLightStr);
result += addToggleToString(getLightToggle(), kLightStr);
break;
default:
result += addLabeledString(getEconoToggle() ? kToggleStr : "-",
kEconoStr);
result += addToggleToString(getEconoToggle(), kEconoStr);
}
result += addBoolToString(_.Clean, kCleanStr);
if (_.TimerEnabled)

View File

@ -17,6 +17,7 @@
// Brand: Sharp, Model: AY-ZP40KR A/C (A907)
// Brand: Sharp, Model: AH-AxSAY A/C (A907)
// Brand: Sharp, Model: CRMC-A907 JBEZ remote (A907)
// Brand: Sharp, Model: CRMC-A950 JBEZ (A907)
// Brand: Sharp, Model: AH-PR13-GL A/C (A903)
// Brand: Sharp, Model: CRMC-A903JBEZ remote (A903)
// Brand: Sharp, Model: AH-XP10NRY A/C (A903)
@ -121,8 +122,23 @@ const uint8_t kSharpAcTimerHoursMax = 0b1100; // 12
const uint8_t kSharpAcOffTimerType = 0b0;
const uint8_t kSharpAcOnTimerType = 0b1;
const uint8_t kSharpAcSwingToggle = 0b111;
const uint8_t kSharpAcSwingNoToggle = 0b000;
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/discussions/1590#discussioncomment-1260213
const uint8_t kSharpAcSwingVIgnore = 0b000; // Don't change the swing setting.
const uint8_t kSharpAcSwingVHigh = 0b001; // 0° down. Similar to Cool Coanda.
const uint8_t kSharpAcSwingVOff = 0b010; // Stop & Go to last fixed pos.
const uint8_t kSharpAcSwingVMid = 0b011; // 30° down
const uint8_t kSharpAcSwingVLow = 0b100; // 45° down
const uint8_t kSharpAcSwingVLast = 0b101; // Same as kSharpAcSwingVOff.
// Toggles between last fixed pos & either 75° down (Heat) or 0° down (Cool)
// i.e. alternate between last pos <-> 75° down if in Heat mode, AND
// alternate between last pos <-> 0° down if in Cool mode.
// Note: `setSwingV(kSharpAcSwingVLowest)` will only allow the Lowest setting in
// Heat mode, it will default to `kSharpAcSwingVLow` otherwise.
// If you want to set this value in other modes e.g. Cool, you must
// use `setSwingV`s optional `force` parameter.
const uint8_t kSharpAcSwingVLowest = 0b110;
const uint8_t kSharpAcSwingVCoanda = kSharpAcSwingVLowest;
const uint8_t kSharpAcSwingVToggle = 0b111; // Toggle Constant swinging on/off.
const uint8_t kSharpAcSpecialPower = 0x00;
const uint8_t kSharpAcSpecialTurbo = 0x01;
@ -166,6 +182,8 @@ class IRSharpAc {
void setTurbo(const bool on);
bool getSwingToggle(void) const;
void setSwingToggle(const bool on);
uint8_t getSwingV(void) const;
void setSwingV(const uint8_t position, const bool force = false);
bool getIon(void) const;
void setIon(const bool on);
bool getEconoToggle(void) const;
@ -187,9 +205,13 @@ class IRSharpAc {
static uint8_t convertFan(const stdAc::fanspeed_t speed,
const sharp_ac_remote_model_t model =
sharp_ac_remote_model_t::A907);
static uint8_t convertSwingV(const stdAc::swingv_t position);
stdAc::opmode_t toCommonMode(const uint8_t mode) const;
stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const;
stdAc::state_t toCommon(void) const;
stdAc::swingv_t toCommonSwingV(
const uint8_t pos,
const stdAc::opmode_t mode = stdAc::opmode_t::kHeat) const;
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
String toString(void) const;
#ifndef UNIT_TEST

View File

@ -18,6 +18,15 @@
// Brand: Satellite Electronic, Model: ID6 Remote
// Brand: Satellite Electronic, Model: JY199I Fan driver
// Brand: Satellite Electronic, Model: JY199I-L Fan driver
// Brand: SilverCrest, Model: SSVS 85 A1 Fan
// Known Codes:
// SilverCrest SSVS 85 A1 Fan:
// 0x581 - On/Off
// 0x582 - Speed
// 0x584 - Mist
// 0x588 - Timer
// 0x590 - OSC
#include <algorithm>
#include "IRrecv.h"

View File

@ -15,12 +15,18 @@
// Constants
const uint8_t kTcl112AcTimerResolution = 20; // Minutes
const uint16_t kTcl112AcTimerMax = 720; // Minutes (12 hrs)
using irutils::addBoolToString;
using irutils::addFanToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addModelToString;
using irutils::addSwingVToString;
using irutils::addTempFloatToString;
using irutils::minsToString;
#if SEND_TCL112AC
/// Send a TCL 112-bit A/C message.
@ -71,6 +77,8 @@ void IRTcl112Ac::send(const uint16_t repeat) {
_quiet_prev = _quiet;
// Restore the old state.
setRaw(save);
// Make sure it looks like a normal TCL mesg if needed.
if (_.MsgType == kTcl112AcNormal) _.isTcl = true;
}
// Send the normal (type 1) state.
_irsend.sendTcl112Ac(getRaw(), kTcl112AcStateLength, repeat);
@ -109,6 +117,17 @@ bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) {
return (length > 1 && state[length - 1] == calcChecksum(state, length));
}
/// Check the supplied state looks like a TCL112AC message.
/// @param[in] state The array to verify the checksum of.
/// @note Assumes the state is the correct size.
/// @return true, if the state looks like a TCL112AC message. Otherwise, false.
/// @warning This is just a guess.
bool IRTcl112Ac::isTcl(const uint8_t state[]) {
Tcl112Protocol mesg;
std::memcpy(mesg.raw, state, kTcl112AcStateLength);
return (mesg.MsgType != kTcl112AcNormal) || mesg.isTcl;
}
/// Reset the internal state of the emulation. (On, Cool, 24C)
void IRTcl112Ac::stateReset(void) {
// A known good state. (On, Cool, 24C)
@ -121,6 +140,19 @@ void IRTcl112Ac::stateReset(void) {
_quiet_explictly_set = false;
}
/// Get/Detect the model of the A/C.
/// @return The enum of the compatible model.
tcl_ac_remote_model_t IRTcl112Ac::getModel(void) const {
return isTcl(_.raw) ? tcl_ac_remote_model_t::TAC09CHSD
: tcl_ac_remote_model_t::GZ055BE1;
}
/// Set the model of the A/C to emulate.
/// @param[in] model The enum of the appropriate model.
void IRTcl112Ac::setModel(const tcl_ac_remote_model_t model) {
_.isTcl = (model != tcl_ac_remote_model_t::GZ055BE1);
}
/// 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* IRTcl112Ac::getRaw(void) {
@ -203,6 +235,7 @@ float IRTcl112Ac::getTemp(void) const {
void IRTcl112Ac::setFan(const uint8_t speed) {
switch (speed) {
case kTcl112AcFanAuto:
case kTcl112AcFanMin:
case kTcl112AcFanLow:
case kTcl112AcFanMed:
case kTcl112AcFanHigh:
@ -250,14 +283,23 @@ void IRTcl112Ac::setSwingHorizontal(const bool on) { _.SwingH = on; }
bool IRTcl112Ac::getSwingHorizontal(void) const { return _.SwingH; }
/// Set the vertical swing setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRTcl112Ac::setSwingVertical(const bool on) {
_.SwingV = (on ? kTcl112AcSwingVOn : kTcl112AcSwingVOff);
/// @param[in] setting The value of the desired setting.
void IRTcl112Ac::setSwingVertical(const uint8_t setting) {
switch (setting) {
case kTcl112AcSwingVOff:
case kTcl112AcSwingVHighest:
case kTcl112AcSwingVHigh:
case kTcl112AcSwingVMiddle:
case kTcl112AcSwingVLow:
case kTcl112AcSwingVLowest:
case kTcl112AcSwingVOn:
_.SwingV = setting;
}
}
/// Get the vertical swing setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; }
/// @return The current setting.
uint8_t IRTcl112Ac::getSwingVertical(void) const { return _.SwingV; }
/// Set the Turbo setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
@ -291,6 +333,36 @@ bool IRTcl112Ac::getQuiet(const bool def) const {
return _quiet_explictly_set ? _quiet : def;
}
/// Get how long the On Timer is set for, in minutes.
/// @return The time in nr of minutes.
uint16_t IRTcl112Ac::getOnTimer(void) const {
return _.OnTimer * kTcl112AcTimerResolution;
}
/// Set or cancel the On Timer function.
/// @param[in] mins Nr. of minutes the timer is to be set to.
/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off)
void IRTcl112Ac::setOnTimer(const uint16_t mins) {
_.OnTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution;
_.OnTimerEnabled = _.OnTimer > 0;
_.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled;
}
/// Get how long the Off Timer is set for, in minutes.
/// @return The time in nr of minutes.
uint16_t IRTcl112Ac::getOffTimer(void) const {
return _.OffTimer * kTcl112AcTimerResolution;
}
/// Set or cancel the Off Timer function.
/// @param[in] mins Nr. of minutes the timer is to be set to.
/// @note Rounds down to 20 min increments. (max: 720 mins (12h), 0 is Off)
void IRTcl112Ac::setOffTimer(const uint16_t mins) {
_.OffTimer = std::min(mins, kTcl112AcTimerMax) / kTcl112AcTimerResolution;
_.OffTimerEnabled = _.OffTimer > 0;
_.TimerIndicator = _.OnTimerEnabled || _.OffTimerEnabled;
}
/// 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.
@ -309,7 +381,7 @@ uint8_t IRTcl112Ac::convertMode(const stdAc::opmode_t mode) {
/// @return The native equivalent of the enum.
uint8_t IRTcl112Ac::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kMin: return kTcl112AcFanMin;
case stdAc::fanspeed_t::kLow: return kTcl112AcFanLow;
case stdAc::fanspeed_t::kMedium: return kTcl112AcFanMed;
case stdAc::fanspeed_t::kHigh:
@ -331,6 +403,21 @@ stdAc::opmode_t IRTcl112Ac::toCommonMode(const uint8_t mode) {
}
}
/// 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 IRTcl112Ac::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kOff: return kTcl112AcSwingVOff;
case stdAc::swingv_t::kHighest: return kTcl112AcSwingVHighest;
case stdAc::swingv_t::kHigh: return kTcl112AcSwingVHigh;
case stdAc::swingv_t::kMiddle: return kTcl112AcSwingVMiddle;
case stdAc::swingv_t::kLow: return kTcl112AcSwingVLow;
case stdAc::swingv_t::kLowest: return kTcl112AcSwingVLowest;
default: return kTcl112AcSwingVOn;
}
}
/// Convert a native fan speed into its stdAc equivalent.
/// @param[in] spd The native setting to be converted.
/// @return The stdAc equivalent of the native setting.
@ -338,11 +425,21 @@ stdAc::fanspeed_t IRTcl112Ac::toCommonFanSpeed(const uint8_t spd) {
switch (spd) {
case kTcl112AcFanHigh: return stdAc::fanspeed_t::kMax;
case kTcl112AcFanMed: return stdAc::fanspeed_t::kMedium;
case kTcl112AcFanLow: return stdAc::fanspeed_t::kMin;
case kTcl112AcFanLow: return stdAc::fanspeed_t::kLow;
case kTcl112AcFanMin: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
/// Convert a native vertical swing postion to it's common equivalent.
/// @param[in] setting A native position to convert.
/// @return The common vertical swing position.
stdAc::swingv_t IRTcl112Ac::toCommonSwingV(const uint8_t setting) {
switch (setting) {
case kTcl112AcSwingVOff: return stdAc::swingv_t::kOff;
default: return stdAc::swingv_t::kAuto;
}
}
/// Convert the current internal state into its stdAc::state_t equivalent.
/// @param[in] prev Ptr to the previous state if required.
/// @return The stdAc equivalent of the native settings.
@ -351,7 +448,7 @@ stdAc::state_t IRTcl112Ac::toCommon(const stdAc::state_t *prev) const {
// Start with the previous state if given it.
if (prev != NULL) result = *prev;
result.protocol = decode_type_t::TCL112AC;
result.model = -1; // Not supported.
result.model = getModel();
result.quiet = getQuiet(result.quiet);
// The rest only get updated if it is a "normal" message.
if (_.MsgType == kTcl112AcNormal) {
@ -360,7 +457,7 @@ stdAc::state_t IRTcl112Ac::toCommon(const stdAc::state_t *prev) const {
result.celsius = true;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
result.swingv = toCommonSwingV(_.SwingV);
result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
result.turbo = _.Turbo;
result.filter = _.Health;
@ -379,8 +476,10 @@ stdAc::state_t IRTcl112Ac::toCommon(const stdAc::state_t *prev) const {
/// @return A human readable string.
String IRTcl112Ac::toString(void) const {
String result = "";
result.reserve(150); // Reserve some heap for the string to reduce fragging.
result += addIntToString(_.MsgType, D_STR_TYPE, false);
result.reserve(220); // Reserve some heap for the string to reduce fragging.
tcl_ac_remote_model_t model = getModel();
result += addModelToString(decode_type_t::TCL112AC, model, false);
result += addIntToString(_.MsgType, D_STR_TYPE);
switch (_.MsgType) {
case kTcl112AcNormal:
result += addBoolToString(_.Power, kPowerStr);
@ -388,14 +487,32 @@ String IRTcl112Ac::toString(void) const {
kTcl112AcHeat, kTcl112AcDry, kTcl112AcFan);
result += addTempFloatToString(getTemp());
result += addFanToString(_.Fan, kTcl112AcFanHigh, kTcl112AcFanLow,
kTcl112AcFanAuto, kTcl112AcFanAuto,
kTcl112AcFanAuto, kTcl112AcFanMin,
kTcl112AcFanMed);
result += addSwingVToString(_.SwingV, kTcl112AcSwingVOff,
kTcl112AcSwingVHighest,
kTcl112AcSwingVHigh,
0xFF, // unused
kTcl112AcSwingVMiddle,
0xFF, // unused
kTcl112AcSwingVLow,
kTcl112AcSwingVLowest,
kTcl112AcSwingVOff,
kTcl112AcSwingVOn, // Swing
0xFF, 0xFF); // Both Unused
if (model != tcl_ac_remote_model_t::GZ055BE1) {
result += addBoolToString(_.SwingH, kSwingHStr);
result += addBoolToString(_.Econo, kEconoStr);
result += addBoolToString(_.Health, kHealthStr);
result += addBoolToString(_.Turbo, kTurboStr);
result += addBoolToString(_.SwingH, kSwingHStr);
result += addBoolToString(_.SwingV, kSwingVStr);
result += addBoolToString(getLight(), kLightStr);
}
result += addLabeledString(
_.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr,
kOnTimerStr);
result += addLabeledString(
_.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr,
kOffTimerStr);
break;
case kTcl112AcSpecial:
result += addBoolToString(_.Quiet, kQuietStr);

View File

@ -4,8 +4,10 @@
/// @brief Support for TCL protocols.
// Supports:
// Brand: Leberg, Model: LBS-TOR07 A/C
// Brand: TCL, Model: TAC-09CHSD/XA31I A/C
// Brand: Leberg, Model: LBS-TOR07 A/C (TAC09CHSD)
// 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)
#ifndef IR_TCL_H_
#define IR_TCL_H_
@ -25,7 +27,9 @@ union Tcl112Protocol{
uint8_t raw[kTcl112AcStateLength]; ///< The State in IR code form.
struct {
// Byte 0~2
uint8_t pad0[3];
uint8_t :8;
uint8_t :8;
uint8_t :8;
// Byte 3
uint8_t MsgType :2;
uint8_t :6;
@ -34,7 +38,8 @@ union Tcl112Protocol{
// Byte 5
uint8_t :2;
uint8_t Power :1;
uint8_t :2;
uint8_t OffTimerEnabled :1;
uint8_t OnTimerEnabled :1;
uint8_t Quiet :1;
uint8_t Light :1;
uint8_t Econo :1;
@ -49,15 +54,25 @@ union Tcl112Protocol{
// Byte 8
uint8_t Fan :3;
uint8_t SwingV :3;
uint8_t :2;
// Byte 9~11
uint8_t pad1[3];
uint8_t TimerIndicator :1;
uint8_t :1;
// Byte 9
uint8_t :1; // 0
uint8_t OffTimer :6;
uint8_t :1; // 0
// Byte 10
uint8_t :1; // 0
uint8_t OnTimer :6;
uint8_t :1; // 0
// Byte 11
uint8_t :8; // 00000000
// Byte 12
uint8_t :3;
uint8_t SwingH :1;
uint8_t :1;
uint8_t HalfDegree :1;
uint8_t :2;
uint8_t :1;
uint8_t isTcl :1;
// Byte 13
uint8_t Sum :8;
};
@ -81,15 +96,23 @@ const uint8_t kTcl112AcFan = 7;
const uint8_t kTcl112AcAuto = 8;
const uint8_t kTcl112AcFanAuto = 0b000;
const uint8_t kTcl112AcFanMin = 0b001; // Aka. "Night"
const uint8_t kTcl112AcFanLow = 0b010;
const uint8_t kTcl112AcFanMed = 0b011;
const uint8_t kTcl112AcFanHigh = 0b101;
const uint8_t kTcl112AcFanNight = kTcl112AcFanMin;
const uint8_t kTcl112AcFanQuiet = kTcl112AcFanMin;
const float kTcl112AcTempMax = 31.0;
const float kTcl112AcTempMin = 16.0;
const uint8_t kTcl112AcSwingVOn = 0b111;
const uint8_t kTcl112AcSwingVOff = 0b000;
const uint8_t kTcl112AcSwingVHighest = 0b001;
const uint8_t kTcl112AcSwingVHigh = 0b010;
const uint8_t kTcl112AcSwingVMiddle = 0b011;
const uint8_t kTcl112AcSwingVLow = 0b100;
const uint8_t kTcl112AcSwingVLowest = 0b101;
const uint8_t kTcl112AcSwingVOn = 0b111;
// MsgType
const uint8_t kTcl112AcNormal = 0b01;
const uint8_t kTcl112AcSpecial = 0b10;
@ -113,6 +136,8 @@ class IRTcl112Ac {
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kTcl112AcStateLength);
tcl_ac_remote_model_t getModel(void) const;
void setModel(const tcl_ac_remote_model_t model);
void on(void);
void off(void);
void setPower(const bool on);
@ -135,16 +160,23 @@ class IRTcl112Ac {
bool getLight(void) const;
void setSwingHorizontal(const bool on);
bool getSwingHorizontal(void) const;
void setSwingVertical(const bool on);
bool getSwingVertical(void) const;
void setSwingVertical(const uint8_t setting);
uint8_t getSwingVertical(void) const;
void setTurbo(const bool on);
bool getTurbo(void) const;
void setQuiet(const bool on);
bool getQuiet(const bool def = false) const;
uint16_t getOnTimer(void) const;
void setOnTimer(const uint16_t mins);
uint16_t getOffTimer(void) const;
void setOffTimer(const uint16_t mins);
static bool isTcl(const uint8_t state[]);
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);
static stdAc::swingv_t toCommonSwingV(const uint8_t setting);
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
String toString(void) const;
#ifndef UNIT_TEST

View File

@ -29,7 +29,7 @@ using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
using irutils::addToggleToString;
#if SEND_TRANSCOLD
/// Send a Transcold message
@ -391,13 +391,7 @@ String IRTranscoldAc::toString(void) const {
result += addBoolToString(getPower(), kPowerStr, false);
if (!getPower()) return result; // If it's off, there is no other info.
// Special modes.
if (getSwing()) {
result += kCommaSpaceStr;
result += kSwingStr;
result += kColonSpaceStr;
result += kToggleStr;
return result;
}
if (getSwing()) return result + addToggleToString(true, kSwingStr);
result += addModeToString(getMode(), kTranscoldAuto, kTranscoldCool,
kTranscoldHeat, kTranscoldDry, kTranscoldFan);
result += addIntToString(_.Fan, kFanStr);

View File

@ -30,9 +30,15 @@
#ifndef D_STR_ON
#define D_STR_ON "On"
#endif // D_STR_ON
#ifndef D_STR_1
#define D_STR_1 "1"
#endif // D_STR_1
#ifndef D_STR_OFF
#define D_STR_OFF "Off"
#endif // D_STR_OFF
#ifndef D_STR_0
#define D_STR_0 "0"
#endif // D_STR_0
#ifndef D_STR_MODE
#define D_STR_MODE "Mode"
#endif // D_STR_MODE
@ -285,6 +291,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"
@ -298,18 +307,42 @@
#ifndef D_STR_COOL
#define D_STR_COOL "Cool"
#endif // D_STR_COOL
#ifndef D_STR_COOLING
#define D_STR_COOLING "Cooling"
#endif // D_STR_COOLING
#ifndef D_STR_HEAT
#define D_STR_HEAT "Heat"
#endif // D_STR_HEAT
#ifndef D_STR_HEATING
#define D_STR_HEATING "Heating"
#endif // D_STR_HEATING
#ifndef D_STR_FAN
#define D_STR_FAN "Fan"
#endif // D_STR_FAN
#ifndef D_STR_FANONLY
#define D_STR_FANONLY "fan_only"
#define D_STR_FANONLY "fan-only"
#endif // D_STR_FANONLY
#ifndef D_STR_FAN_ONLY
#define D_STR_FAN_ONLY "fan_only"
#endif // D_STR_FAN_ONLY
#ifndef D_STR_ONLY
#define D_STR_ONLY "Only"
#endif // D_STR_ONLY
#ifndef D_STR_FANSPACEONLY
#define D_STR_FANSPACEONLY D_STR_FAN " " D_STR_ONLY
#endif // D_STR_FANSPACEONLY
#ifndef D_STR_FANONLYNOSPACE
#define D_STR_FANONLYNOSPACE D_STR_FAN D_STR_ONLY
#endif // D_STR_FANONLYNOSPACE
#ifndef D_STR_DRY
#define D_STR_DRY "Dry"
#endif // D_STR_DRY
#ifndef D_STR_DRYING
#define D_STR_DRYING "Drying"
#endif // D_STR_DRYING
#ifndef D_STR_DEHUMIDIFY
#define D_STR_DEHUMIDIFY "Dehumidify"
#endif // D_STR_DEHUMIDIFY
#ifndef D_STR_MAX
#define D_STR_MAX "Max"
@ -360,6 +393,12 @@
#ifndef D_STR_MAXRIGHT
#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT // Set `D_STR_MAX` first!
#endif // D_STR_MAXRIGHT
#ifndef D_STR_MAXRIGHT_NOSPACE
#define D_STR_MAXRIGHT_NOSPACE D_STR_MAX D_STR_RIGHT // Set `D_STR_MAX` first!
#endif // D_STR_MAXRIGHT_NOSPACE
#ifndef D_STR_RIGHTMAX
#define D_STR_RIGHTMAX D_STR_RIGHT " " D_STR_MAX // Set `D_STR_MAX` first!
#endif // D_STR_RIGHTMAX
#ifndef D_STR_RIGHTMAX_NOSPACE
#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX // Set `D_STR_MAX` first!
#endif // D_STR_RIGHTMAX_NOSPACE
@ -369,6 +408,12 @@
#ifndef D_STR_MAXLEFT
#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT // Set `D_STR_MAX` first!
#endif // D_STR_MAXLEFT
#ifndef D_STR_MAXLEFT_NOSPACE
#define D_STR_MAXLEFT_NOSPACE D_STR_MAX D_STR_LEFT // Set `D_STR_MAX` first!
#endif // D_STR_MAXLEFT_NOSPACE
#ifndef D_STR_LEFTMAX
#define D_STR_LEFTMAX D_STR_LEFT " " D_STR_MAX // Set `D_STR_MAX` first!
#endif // D_STR_LEFTMAX
#ifndef D_STR_LEFTMAX_NOSPACE
#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX // Set `D_STR_MAX` first!
#endif // D_STR_LEFTMAX_NOSPACE
@ -440,6 +485,9 @@
#ifndef D_STR_COLONSPACE
#define D_STR_COLONSPACE ": "
#endif // D_STR_COLONSPACE
#ifndef D_STR_DASH
#define D_STR_DASH "-"
#endif // D_STR_DASH
#ifndef D_STR_DAY
#define D_STR_DAY "Day"
@ -495,7 +543,135 @@
#define D_STR_BITS "Bits"
#endif // D_STR_BITS
// Model Names
#ifndef D_STR_YAW1F
#define D_STR_YAW1F "YAW1F"
#endif // D_STR_YAW1F
#ifndef D_STR_YBOFB
#define D_STR_YBOFB "YBOFB"
#endif // D_STR_YBOFB
#ifndef D_STR_V9014557_A
#define D_STR_V9014557_A "V9014557-A"
#endif // D_STR_V9014557_A
#ifndef D_STR_V9014557_B
#define D_STR_V9014557_B "V9014557-B"
#endif // D_STR_V9014557_B
#ifndef D_STR_RLT0541HTA_A
#define D_STR_RLT0541HTA_A "R-LT0541-HTA-A"
#endif // D_STR_RLT0541HTA_A
#ifndef D_STR_RLT0541HTA_B
#define D_STR_RLT0541HTA_B "R-LT0541-HTA-B"
#endif // D_STR_RLT0541HTA_B
#ifndef D_STR_ARRAH2E
#define D_STR_ARRAH2E "ARRAH2E"
#endif // D_STR_ARRAH2E
#ifndef D_STR_ARDB1
#define D_STR_ARDB1 "ARDB1"
#endif // D_STR_ARDB1
#ifndef D_STR_ARREB1E
#define D_STR_ARREB1E "ARREB1E"
#endif // D_STR_ARREB1E
#ifndef D_STR_ARJW2
#define D_STR_ARJW2 "ARJW2"
#endif // D_STR_ARJW2
#ifndef D_STR_ARRY4
#define D_STR_ARRY4 "ARRY4"
#endif // D_STR_ARRY4
#ifndef D_STR_ARREW4E
#define D_STR_ARREW4E "ARREW4E"
#endif // D_STR_ARREW4E
#ifndef D_STR_GE6711AR2853M
#define D_STR_GE6711AR2853M "GE6711AR2853M"
#endif // D_STR_GE6711AR2853M
#ifndef D_STR_AKB75215403
#define D_STR_AKB75215403 "AKB75215403"
#endif // D_STR_AKB75215403
#ifndef D_STR_AKB74955603
#define D_STR_AKB74955603 "AKB74955603"
#endif // D_STR_AKB74955603
#ifndef D_STR_AKB73757604
#define D_STR_AKB73757604 "AKB73757604"
#endif // D_STR_AKB73757604
#ifndef D_STR_KKG9AC1
#define D_STR_KKG9AC1 "KKG9AC1"
#endif // D_STR_KKG9AC1
#ifndef D_STR_KKG29AC1
#define D_STR_KKG29AC1 "KKG29AC1"
#endif // D_STR_KKG9AC1
#ifndef D_STR_LKE
#define D_STR_LKE "LKE"
#endif // D_STR_LKE
#ifndef D_STR_NKE
#define D_STR_NKE "NKE"
#endif // D_STR_NKE
#ifndef D_STR_DKE
#define D_STR_DKE "DKE"
#endif // D_STR_DKE
#ifndef D_STR_PKR
#define D_STR_PKR "PKR"
#endif // D_STR_PKR
#ifndef D_STR_JKE
#define D_STR_JKE "JKE"
#endif // D_STR_JKE
#ifndef D_STR_CKP
#define D_STR_CKP "CKP"
#endif // D_STR_CKP
#ifndef D_STR_RKR
#define D_STR_RKR "RKR"
#endif // D_STR_RKR
#ifndef D_STR_PANASONICLKE
#define D_STR_PANASONICLKE "PANASONICLKE"
#endif // D_STR_PANASONICLKE
#ifndef D_STR_PANASONICNKE
#define D_STR_PANASONICNKE "PANASONICNKE"
#endif // D_STR_PANASONICNKE
#ifndef D_STR_PANASONICDKE
#define D_STR_PANASONICDKE "PANASONICDKE"
#endif // D_STR_PANASONICDKE
#ifndef D_STR_PANASONICPKR
#define D_STR_PANASONICPKR "PANASONICPKR"
#endif // D_STR_PANASONICPKR
#ifndef D_STR_PANASONICJKE
#define D_STR_PANASONICJKE "PANASONICJKE"
#endif // D_STR_PANASONICJKE
#ifndef D_STR_PANASONICCKP
#define D_STR_PANASONICCKP "PANASONICCKP"
#endif // D_STR_PANASONICCKP
#ifndef D_STR_PANASONICRKR
#define D_STR_PANASONICRKR "PANASONICRKR"
#endif // D_STR_PANASONICRKR
#ifndef D_STR_A907
#define D_STR_A907 "A907"
#endif // D_STR_A907
#ifndef D_STR_A705
#define D_STR_A705 "A705"
#endif // D_STR_A705
#ifndef D_STR_A903
#define D_STR_A903 "A903"
#endif // D_STR_A903
#ifndef D_STR_TAC09CHSD
#define D_STR_TAC09CHSD "TAC09CHSD"
#endif // D_STR_TAC09CHSD
#ifndef D_STR_GZ055BE1
#define D_STR_GZ055BE1 "GZ055BE1"
#endif // D_STR_GZ055BE1
#ifndef D_STR_122LZF
#define D_STR_122LZF "122LZF"
#endif // D_STR_122LZF
#ifndef D_STR_DG11J13A
#define D_STR_DG11J13A "DG11J13A"
#endif // D_STR_DG11J13A
#ifndef D_STR_DG11J104
#define D_STR_DG11J104 "DG11J104"
#endif // D_STR_DG11J104
#ifndef D_STR_DG11J191
#define D_STR_DG11J191 "DG11J191"
#endif // D_STR_DG11J191
// 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
@ -508,6 +684,9 @@
#ifndef D_STR_ARGO
#define D_STR_ARGO "ARGO"
#endif // D_STR_ARGO
#ifndef D_STR_ARRIS
#define D_STR_ARRIS "ARRIS"
#endif // D_STR_ARRIS
#ifndef D_STR_BOSE
#define D_STR_BOSE "BOSE"
#endif // D_STR_BOSE
@ -733,6 +912,9 @@
#ifndef D_STR_RCMM
#define D_STR_RCMM "RCMM"
#endif // D_STR_RCMM
#ifndef D_STR_RHOSS
#define D_STR_RHOSS "RHOSS"
#endif // D_STR_RHOSS
#ifndef D_STR_SAMSUNG
#define D_STR_SAMSUNG "SAMSUNG"
#endif // D_STR_SAMSUNG

View File

@ -29,6 +29,7 @@
#define D_STR_TIMER "Timer"
#define D_STR_ONTIMER D_STR_TIMER " " D_STR_ON
#define D_STR_OFFTIMER D_STR_TIMER " " D_STR_OFF
#define D_STR_TIMERMODE D_STR_MODE " " D_STR_TIMER
#define D_STR_CLOCK "Relógio"
#define D_STR_COMMAND "Comando"
#define D_STR_HEALTH "Saúde"
@ -84,6 +85,11 @@
#define D_STR_6THSENSE "Sexto sentido"
#define D_STR_ZONEFOLLOW "Acompanhar ambiente"
#define D_STR_FIXED "Fixo"
#define D_STR_TYPE "Tipo"
#define D_STR_SPECIAL "Especial"
#define D_STR_RECYCLE "Reciclar"
#define D_STR_ID "Id"
#define D_STR_VANE "Vane"
#define D_STR_AUTO "Auto"
#define D_STR_AUTOMATIC "Automático"
@ -91,7 +97,11 @@
#define D_STR_COOL "Esfriar"
#define D_STR_HEAT "Aquecer"
#define D_STR_FAN "Ventilar"
#define D_STR_FANONLY "Apenas ventilar"
#define D_STR_FANONLY "Apenas-ventilar"
#define D_STR_FAN_ONLY "Apenas_ventilar"
#define D_STR_ONLY "Apenas"
#define D_STR_FANSPACEONLY D_STR_ONLY " " D_STR_FAN
#define D_STR_FANONLYNOSPACE D_STR_ONLY D_STR_FAN
#define D_STR_DRY "Secar"
#define D_STR_8C_HEAT D_STR_HEAT " 8C"

View File

@ -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_

View File

@ -0,0 +1,189 @@
// Copyright 2021 - Tom Rosenback (@tomrosenback)
// Locale/language file for swedish / Sweden.
// This file will override the default values located in `defaults.h`.
#ifndef LOCALE_SV_SE_H_
#define LOCALE_SV_SE_H_
#define D_STR_UNKNOWN "OKÄND"
#define D_STR_PROTOCOL "Protokoll"
#define D_STR_POWER "Strömläge"
#define D_STR_PREVIOUS "Föregående"
#define D_STR_ON "På"
#define D_STR_OFF "Av"
#define D_STR_MODE "Läge"
#define D_STR_TOGGLE "Växla"
#define D_STR_TURBO "Turbo"
#define D_STR_SUPER "Super"
#define D_STR_SLEEP "Sova"
#define D_STR_LIGHT "Svag"
#define D_STR_POWERFUL "Kraftig"
#define D_STR_QUIET "Tyst"
#define D_STR_ECONO "Eko"
#define D_STR_SWING "Sving"
#define D_STR_SWINGH D_STR_SWING"(H)"
#define D_STR_SWINGV D_STR_SWING"(V)"
#define D_STR_BEEP "Pip"
#define D_STR_MOULD "Forma"
#define D_STR_CLEAN "Rengör"
#define D_STR_PURIFY "Rena"
#define D_STR_TIMER "Timer"
#define D_STR_ONTIMER "På timer"
#define D_STR_OFFTIMER "Av timer"
#define D_STR_TIMERMODE "Timerläge"
#define D_STR_CLOCK "Klocka"
#define D_STR_COMMAND "Kommando"
#define D_STR_XFAN "XFan"
#define D_STR_HEALTH "Hälsa"
#define D_STR_MODEL "Modell"
#define D_STR_TEMP "Temp"
#define D_STR_IFEEL "Känns som"
#define D_STR_HUMID "Humid"
#define D_STR_SAVE "Save"
#define D_STR_EYE "Öga"
#define D_STR_FOLLOW "Följ"
#define D_STR_ION "Ion"
#define D_STR_FRESH "Frisk"
#define D_STR_HOLD "Håll"
#define D_STR_8C_HEAT "8C " D_STR_HEAT
#define D_STR_10C_HEAT "10C " D_STR_HEAT
#define D_STR_BUTTON "Knapp"
#define D_STR_NIGHT "Natt"
#define D_STR_SILENT "Tyst"
#define D_STR_FILTER "Filter"
#define D_STR_3D "3D"
#define D_STR_CELSIUS "Celsius"
#define D_STR_FAHRENHEIT "Fahrenheit"
#define D_STR_CELSIUS_FAHRENHEIT D_STR_CELSIUS "/" D_STR_FAHRENHEIT
#define D_STR_UP "Upp"
#define D_STR_TEMPUP D_STR_TEMP " upp"
#define D_STR_DOWN "Ner"
#define D_STR_TEMPDOWN D_STR_TEMP " ner"
#define D_STR_CHANGE "Ändra"
#define D_STR_START "Starta"
#define D_STR_STOP "Stoppa"
#define D_STR_MOVE "Flytta"
#define D_STR_SET "Ställ in"
#define D_STR_CANCEL "Avbryt"
#define D_STR_COMFORT "Komfort"
#define D_STR_SENSOR "Sensor"
#define D_STR_DISPLAY "Display"
#define D_STR_WEEKLY "Veckovis"
#define D_STR_WEEKLYTIMER D_STR_WEEKLY " timer"
#define D_STR_WIFI "WiFi"
#define D_STR_LAST "Senast"
#define D_STR_FAST "Snabb"
#define D_STR_SLOW "Sakta"
#define D_STR_AIRFLOW "Luftflöde"
#define D_STR_STEP "Steppa"
#define D_STR_NA "N/A"
#define D_STR_INSIDE "Inne"
#define D_STR_OUTSIDE "Ute"
#define D_STR_LOUD "Hög"
#define D_STR_UPPER "Övre"
#define D_STR_LOWER "Nedre"
#define D_STR_BREEZE "Bris"
#define D_STR_CIRCULATE "Cirkulera"
#define D_STR_CEILING "Tak"
#define D_STR_WALL "Vägg"
#define D_STR_ROOM "Rum"
#define D_STR_6THSENSE "6e sinne"
#define D_STR_ZONEFOLLOW "Följ zon"
#define D_STR_FIXED "Fast"
#define D_STR_TYPE "Typ"
#define D_STR_SPECIAL "Speciell"
#define D_STR_RECYCLE "Återvinn"
#define D_STR_ID "Id"
#define D_STR_VANE "Vindflöjel"
#define D_STR_AUTO "Auto"
#define D_STR_AUTOMATIC "Automatisk"
#define D_STR_MANUAL "Manuell"
#define D_STR_COOL "Kyla"
#define D_STR_HEAT "Värme"
#define D_STR_FAN "Fläkt"
#define D_STR_FANONLY "fläkt-enbart"
#define D_STR_FAN_ONLY "fläkt_enbart"
#define D_STR_ONLY "Enbart"
#define D_STR_FANSPACEONLY D_STR_FAN " " D_STR_ONLY
#define D_STR_FANONLYNOSPACE D_STR_FAN D_STR_ONLY
#define D_STR_DRY "Torka"
#define D_STR_MAX "Max"
#define D_STR_MAXIMUM "Maximum"
#define D_STR_MIN "Min"
#define D_STR_MINIMUM "Minimum"
#define D_STR_MED "Med"
#define D_STR_MEDIUM "Medium"
#define D_STR_HIGHEST "Högsta"
#define D_STR_HIGH "Hög"
#define D_STR_HI "Hög"
#define D_STR_MID "Mellan"
#define D_STR_MIDDLE "Mellan"
#define D_STR_LOW "Låg"
#define D_STR_LO "Låg"
#define D_STR_LOWEST "Lägsta"
#define D_STR_RIGHT "Höger"
#define D_STR_MAXRIGHT D_STR_MAX " höger"
#define D_STR_RIGHTMAX_NOSPACE D_STR_MAX D_STR_RIGHT
#define D_STR_LEFT "Vänster"
#define D_STR_MAXLEFT D_STR_MAX " vänster"
#define D_STR_LEFTMAX_NOSPACE D_STR_MAX D_STR_LEFT
#define D_STR_WIDE "Vid"
#define D_STR_CENTRE "Mitten"
#define D_STR_TOP "Topp"
#define D_STR_BOTTOM "Botten"
#define D_STR_ECONOTOGGLE D_STR_TOGGLE " eko"
#define D_STR_EYEAUTO D_STR_AUTO " öga"
#define D_STR_LIGHTTOGGLE D_STR_TOGGLE " svag"
#define D_STR_OUTSIDEQUIET D_STR_QUIET " ute"
#define D_STR_POWERTOGGLE D_STR_TOGGLE " strömläge"
#define D_STR_POWERBUTTON "Strömknapp"
#define D_STR_PREVIOUSPOWER "Föregående strömläge"
#define D_STR_DISPLAYTEMP "Displaytemp"
#define D_STR_SENSORTEMP "Sensortemp"
#define D_STR_SLEEP_TIMER "Sovtimer"
#define D_STR_SWINGVMODE D_STR_SWINGV " läge"
#define D_STR_SWINGVTOGGLE D_STR_TOGGLE " sving(v)"
#define D_STR_TURBOTOGGLE D_STR_TOGGLE " turbo"
// Separatorer
#define D_CHR_TIME_SEP ':'
#define D_STR_SPACELBRACE " ("
#define D_STR_COMMASPACE ", "
#define D_STR_COLONSPACE ": "
#define D_STR_DAY "Dag"
#define D_STR_DAYS D_STR_DAY "ar"
#define D_STR_HOUR "Timme"
#define D_STR_HOURS "Timmar"
#define D_STR_MINUTE "Minut"
#define D_STR_MINUTES D_STR_MINUTE "er"
#define D_STR_SECOND "Sekund"
#define D_STR_SECONDS D_STR_MINUTE "er"
#define D_STR_NOW "Nu"
#define D_STR_THREELETTERDAYS "SönMånTisOnsTorFreLör"
#define D_STR_YES "Ja"
#define D_STR_NO "Nej"
#define D_STR_TRUE "Sant"
#define D_STR_FALSE "Falskt"
#define D_STR_REPEAT "Upprepa"
#define D_STR_CODE "Kod"
#define D_STR_BITS "Bitar"
// IRrecvDumpV2+
#define D_STR_TIMESTAMP "Tidskod"
#define D_STR_LIBRARY "Bibliotek"
#define D_STR_MESGDESC "Meddelande beskr."
#define D_STR_TOLERANCE "Tolerans"
#define D_STR_IRRECVDUMP_STARTUP \
"IRrecvDump har nu startats och väntar på IR signaler på PIN %d"
#define D_WARN_BUFFERFULL \
"VARNING: IR koden är för stor för att rymmas i bufferminnet (>= %d). " \
"Detta resultat är inte pålitligt innan problemet är åtgärdat. " \
"Redigera och öka `kCaptureBufferSize`."
#endif // LOCALE_SV_SE_H_

View File

@ -19,6 +19,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"
@ -560,7 +561,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 +729,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 +743,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 +765,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,17 +793,22 @@ 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: 1 (High), 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,
haier_ac176_remote_model_t::V9014557_A, // Model
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::kOff, // Horizontal swing
true, // Turbo
false, // Quiet
true, // Filter
8 * 60 + 0); // Sleep time
ASSERT_EQ(expected, ac.toString());
@ -816,18 +826,22 @@ 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: 1 (High), 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
true, // Turbo
stdAc::swingh_t::kAuto, // Vertical swing
false, // Turbo
true, // Quiet
true, // Filter
8 * 60 + 0); // Sleep time
ASSERT_EQ(expected, ac.toString());
@ -1125,7 +1139,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();
@ -1163,6 +1192,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);
@ -1252,6 +1354,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);
@ -1493,9 +1651,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();
@ -1505,14 +1664,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));
@ -1529,22 +1692,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) {
@ -1610,7 +1810,7 @@ TEST(TestIRac, Sharp) {
IRrecv capture(kGpioUnused);
char expected[] =
"Model: 1 (A907), Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), "
"Turbo: Off, Swing(V) Toggle: On, Ion: On, Econo: -, Clean: Off";
"Swing(V): 7 (Swing), Turbo: Off, Ion: On, Econo: -, Clean: Off";
ac.begin();
irac.sharp(&ac,
@ -1621,6 +1821,7 @@ TEST(TestIRac, Sharp) {
28, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kAuto, // Vertical swing
stdAc::swingv_t::kOff, // Previous Vertical swing
false, // Turbo
false, // Light
true, // Filter (Ion)
@ -1640,12 +1841,14 @@ TEST(TestIRac, Tcl112) {
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 3 (Medium), "
"Econo: On, Health: On, Turbo: Off, Swing(H): On, Swing(V): Off, "
"Light: On";
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 20C, "
"Fan: 3 (Medium), Swing(V): 0 (Auto), Swing(H): On, "
"Econo: On, Health: On, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off";
ac.begin();
irac.tcl112(&ac,
tcl_ac_remote_model_t::TAC09CHSD, // Model
true, // Power
stdAc::opmode_t::kCool, // Mode
20, // Celsius
@ -1668,6 +1871,7 @@ TEST(TestIRac, Tcl112) {
// Test the quiet mode, which should generate two messages.
ac._irsend.reset();
irac.tcl112(&ac,
tcl_ac_remote_model_t::TAC09CHSD, // Model
true, // Power
stdAc::opmode_t::kCool, // Mode
20, // Celsius
@ -1684,7 +1888,7 @@ TEST(TestIRac, Tcl112) {
ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
ASSERT_EQ(
"Type: 2, Quiet: On",
"Model: 1 (TAC09CHSD), Type: 2, Quiet: On",
IRAcUtils::resultAcToString(&ac._irsend.capture));
// TCL112 uses the Mitsubishi112 decoder.
// Skip first message.
@ -2335,6 +2539,10 @@ TEST(TestIRac, opmodeToString) {
EXPECT_EQ("Auto", IRac::opmodeToString(stdAc::opmode_t::kAuto));
EXPECT_EQ("Cool", IRac::opmodeToString(stdAc::opmode_t::kCool));
EXPECT_EQ("UNKNOWN", IRac::opmodeToString((stdAc::opmode_t)500));
// 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", IRac::opmodeToString(stdAc::opmode_t::kFan)); // Default
}
TEST(TestIRac, fanspeedToString) {

View File

@ -3,6 +3,9 @@
# make [all] - makes everything.
# make TARGET - makes the given target.
# make run - makes everything and runs all the tests.
# make run_tests - run all tests
# make run-% - run specific test file (exclude _test.cpp)
# replace % with given test file, eg run-IRsend
# make clean - removes all files generated by make.
# make install-googletest - install the googletest code suite
@ -45,6 +48,7 @@ clean :
run : all
failed=""; \
for unittest in $(TESTS); do \
echo "RUNNING: $${unittest}"; \
./$${unittest} || failed="$${failed} $${unittest}"; \
done; \
if [ -n "$${failed}" ]; then \
@ -55,6 +59,10 @@ run : all
run_tests : run
run-% : %_test
echo "RUNNING: $*"; \
./$*_test
install-googletest :
rm -rf ../lib/googletest
git clone -b v1.8.x https://github.com/google/googletest.git ../lib/googletest

View File

@ -0,0 +1,62 @@
// Copyright 2021 crankyoldgit
#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);
}
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_FALSE(IRac::isProtocolSupported(decode_type_t::AIRTON));
ASSERT_EQ(kAirtonBits, IRsend::defaultBits(decode_type_t::AIRTON));
ASSERT_EQ(kAirtonDefaultRepeat, IRsend::minRepeats(decode_type_t::AIRTON));
}

View File

@ -0,0 +1,189 @@
// Copyright 2021 David Conran
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
#include "IRsend.h"
#include "IRsend_test.h"
#include "gtest/gtest.h"
// Tests for decodeArris().
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1595
// Data from:
// https://github.com/crankyoldgit/IRremoteESP8266/files/7100289/raw_data.txt
TEST(TestDecodeArris, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
const uint16_t rawData_1[59] = {
2584, 1896, 666, 306, 338, 300, 336, 304, 668, 610, 332, 306, 338, 300,
334, 304, 332, 312, 332, 306, 340, 300, 334, 304, 330, 308, 338, 302, 334,
304, 330, 308, 336, 308, 336, 302, 332, 306, 330, 310, 674, 606, 336, 302,
332, 306, 338, 306, 668, 612, 668, 306, 338, 304, 332, 308, 336, 608,
334}; // UNKNOWN EDF1C0D0
irsend.begin();
irsend.reset();
irsend.sendRaw(rawData_1, 59, 38);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type);
EXPECT_EQ(kArrisBits, irsend.capture.bits);
EXPECT_EQ(0x1000085E, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x85, irsend.capture.command);
irsend.reset();
const uint16_t rawData_2[115] = {
2584, 1898, 664, 308, 338, 302, 332, 306, 668, 614, 330, 308, 336, 302,
332, 306, 340, 304, 330, 310, 336, 304, 332, 306, 338, 300, 334, 304, 330,
308, 336, 302, 332, 312, 334, 306, 330, 308, 336, 302, 670, 610, 332, 306,
330, 310, 336, 308, 674, 606, 664, 312, 334, 306, 338, 302, 334, 612, 330,
5930,
2584, 1898, 664, 308, 336, 302, 332, 306, 666, 614, 338, 300, 336, 304,
332, 310, 674, 610, 332, 334, 312, 328, 308, 332, 304, 336, 310, 330, 306,
332, 302, 314, 330, 336, 308, 330, 306, 334, 640, 612, 330, 308, 336, 302,
332, 312, 672, 608, 672, 608, 672, 304, 330, 614, 330
}; // UNKNOWN E6A77D83
irsend.sendRaw(rawData_2, 115, 38);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type);
EXPECT_EQ(kArrisBits, irsend.capture.bits);
EXPECT_EQ(0x1000085E, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x85, irsend.capture.command);
const uint16_t rawData_3[51] = {
2584, 1896, 666, 308, 338, 328, 306, 332, 640, 612, 332, 336, 310, 300,
334, 304, 678, 606, 336, 330, 306, 334, 310, 300, 334, 304, 332, 308, 338,
302, 334, 310, 672, 304, 332, 614, 668, 612, 330, 336, 638, 620, 670, 610,
670, 304, 330, 310, 336, 610, 672}; // UNKNOWN 4CA048A1
irsend.reset();
irsend.sendRaw(rawData_3, 51, 38);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type);
EXPECT_EQ(kArrisBits, irsend.capture.bits);
EXPECT_EQ(0x1080695D, irsend.capture.value);
EXPECT_EQ(0x1, irsend.capture.address);
EXPECT_EQ(0x695, irsend.capture.command);
}
TEST(TestDecodeArris, SyntheticExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
irsend.reset();
irsend.sendArris(0x1000085E);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type);
EXPECT_EQ(kArrisBits, irsend.capture.bits);
EXPECT_EQ(0x1000085E, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x85, irsend.capture.command);
EXPECT_EQ(
"f38000d50"
// const uint16_t rawData_1[59] = {
// 2584, 1896,
"m2560s1920"
// 666, 306,
"m640s320"
// 338, 300,
"m320s320"
// 336, 304,
"m320s320"
// 668, 610,
"m640s640"
// 332, 306,
"m320s320"
// 338, 300,
"m320s320"
// 334, 304,
"m320s320"
// 332, 312,
"m320s320"
// 332, 306,
"m320s320"
// 340, 300,
"m320s320"
// 334, 304,
"m320s320"
// 330, 308,
"m320s320"
// 338, 302,
"m320s320"
// 334, 304,
"m320s320"
// 330, 308,
"m320s320"
// 336, 308,
"m320s320"
// 336, 302,
"m320s320"
// 332, 306,
"m320s320"
// 330, 310,
"m320s320"
// 674, 606,
"m640s640"
// 336, 302,
"m320s320"
// 332, 306,
"m320s320"
// 338, 306,
"m320s320"
// 668, 612,
"m640s640"
// 668, 306,
"m640s320"
// 338, 304,
"m320s320"
// 332, 308,
"m320s320"
// 336, 608,
"m320s640"
// 334}; // UNKNOWN EDF1C0D0
"m320s77184", irsend.outputStr());
irsend.reset();
irsend.sendArris(0x1080695D);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type);
EXPECT_EQ(kArrisBits, irsend.capture.bits);
EXPECT_EQ(0x1080695D, irsend.capture.value);
EXPECT_EQ(0x1, irsend.capture.address);
EXPECT_EQ(0x695, irsend.capture.command);
}
TEST(TestUtils, Housekeeping) {
ASSERT_EQ("ARRIS", typeToString(decode_type_t::ARRIS));
ASSERT_EQ(decode_type_t::ARRIS, strToDecodeType("ARRIS"));
ASSERT_FALSE(hasACState(decode_type_t::ARRIS));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::ARRIS));
ASSERT_EQ(kArrisBits, IRsend::defaultBits(decode_type_t::ARRIS));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::ARRIS));
}
TEST(TestSendArris, ReleaseToggle) {
EXPECT_EQ(0x10800F5D, IRsend::toggleArrisRelease(0x10000F55));
EXPECT_EQ(0x10000F55, IRsend::toggleArrisRelease(0x10800F5D));
EXPECT_EQ(
0x10800F5D,
IRsend::toggleArrisRelease(IRsend::toggleArrisRelease(0x10800F5D)));
}
TEST(TestSendArris, encodeArris) {
EXPECT_EQ(0x10800F5D, IRsend::encodeArris(0xF5, true));
EXPECT_EQ(0x10000F55, IRsend::encodeArris(0xF5, false));
EXPECT_EQ(0x1080695D, IRsend::encodeArris(0x695, true));
}

View File

@ -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());
}

View File

@ -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();

View File

@ -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));
}

View File

@ -0,0 +1,396 @@
// Copyright 2021 Tom Rosenback
#include "IRac.h"
#include "ir_Rhoss.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
#include "IRsend.h"
#include "IRsend_test.h"
#include "IRutils.h"
#include "gtest/gtest.h"
TEST(TestUtils, Housekeeping) {
ASSERT_EQ("RHOSS", typeToString(decode_type_t::RHOSS));
ASSERT_EQ(decode_type_t::RHOSS, strToDecodeType("RHOSS"));
ASSERT_TRUE(hasACState(decode_type_t::RHOSS));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::RHOSS));
}
// Test sending typical data only.
TEST(TestSendRhoss, SendDataOnly) {
IRsendTest irsend(kGpioUnused);
irsend.begin();
uint8_t expectedState[kRhossStateLength] = {
0xAA, 0x05, 0x60, 0x00, 0x50, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x33 };
irsend.reset();
irsend.sendRhoss(expectedState);
EXPECT_EQ(
"f38000d50"
"m3042s4248"
"m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457m648s1545"
"m648s1545m648s457m648s1545m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s1545m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s1545"
"m648s457m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s1545m648s1545m648s457m648s457m648s1545m648s1545m648s457m648s457"
"m648s457m648"
"s100000",
irsend.outputStr());
}
// Test send typical data with repeats
TEST(TestSendRhoss, SendWithRepeats) {
IRsendTest irsend(kGpioUnused);
irsend.begin();
irsend.reset();
uint8_t expectedState[kRhossStateLength] = {
0xAA, 0x05, 0x60, 0x00, 0x50, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x33 };
irsend.sendRhoss(expectedState, kRhossStateLength, 0); // 0 repeats.
EXPECT_EQ(
"f38000d50"
"m3042s4248"
"m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457m648s1545"
"m648s1545m648s457m648s1545m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s1545m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s1545"
"m648s457m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s1545m648s1545m648s457m648s457m648s1545m648s1545m648s457m648s457"
"m648s457m648"
"s100000",
irsend.outputStr());
irsend.sendRhoss(expectedState, kRhossStateLength, 2); // 2 repeats.
EXPECT_EQ(
"f38000d50"
"m3042s4248"
"m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457m648s1545"
"m648s1545m648s457m648s1545m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s1545m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s1545"
"m648s457m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s1545m648s1545m648s457m648s457m648s1545m648s1545m648s457m648s457"
"m648s457m648"
"s100000"
"m3042s4248"
"m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457m648s1545"
"m648s1545m648s457m648s1545m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s1545m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s1545"
"m648s457m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s1545m648s1545m648s457m648s457m648s1545m648s1545m648s457m648s457"
"m648s457m648"
"s100000"
"m3042s4248"
"m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457m648s1545"
"m648s1545m648s457m648s1545m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s1545m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s1545"
"m648s457m648s457m648s1545m648s457m648s1545m648s457m648s1545m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s457m648s457m648s457m648s457m648s457m648s457m648s457m648s457"
"m648s1545m648s1545m648s457m648s457m648s1545m648s1545m648s457m648s457"
"m648s457m648"
"s100000",
irsend.outputStr());
}
// Test send raw data
TEST(TestSendRhoss, RawData) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
irsend.reset();
// Power on, mode cool, temp 20, fan auto, swing off
const uint16_t rawData[197] = {
3044, 4248,
648, 458, 650, 1540, 646, 458, 650, 1538,
650, 458, 650, 1538, 650, 458, 650, 1540, // byte 0
648, 458, 650, 458, 650, 1540, 646, 484,
624, 456, 650, 456, 650, 456, 650, 456, // byte 1
650, 456, 650, 456, 650, 456, 650, 456,
650, 458, 650, 1540, 650, 1538, 650, 456, // byte 2
650, 456, 650, 456, 650, 456, 650, 458,
650, 456, 650, 456, 650, 456, 650, 458, // byte 3
650, 458, 650, 456, 650, 458, 650, 458,
650, 458, 650, 1538, 650, 458, 650, 458, // byte 4
650, 458, 648, 458, 674, 434, 648, 458,
672, 434, 648, 458, 650, 458, 648, 1540, // byte 5
672, 434, 650, 458, 672, 1518, 644, 488,
622, 1540, 644, 464, 672, 1516, 672, 434, // byte 6
672, 434, 672, 434, 650, 458, 648, 458,
672, 434, 674, 434, 672, 434, 650, 458, // byte 7
672, 434, 648, 458, 650, 458, 672, 434,
672, 436, 648, 458, 648, 456, 650, 458, // byte 8
650, 458, 650, 456, 674, 434, 650, 458,
650, 456, 650, 458, 674, 432, 650, 458, // byte 9
650, 456, 650, 456, 650, 458, 648, 458,
674, 432, 650, 456, 674, 434, 650, 458, // byte 10
650, 458, 650, 1538, 650, 458, 650, 458,
650, 456, 650, 458, 650, 456, 650, 458, // byte 11
650, 456,
650 }; // UNKNOWN 93E7BDB2
irsend.sendRaw(rawData, 197, 38);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(RHOSS, irsend.capture.decode_type);
EXPECT_EQ(kRhossBits, irsend.capture.bits);
uint8_t expected[kRhossStateLength] = {
0xAA, 0x04, 0x60, 0x00, 0x20, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x02 };
EXPECT_STATE_EQ(expected, irsend.capture.state, kRhossBits);
EXPECT_EQ(
"Power: On, Mode: 2 (Cool), Temp: 20C, Fan: 0 (Auto), Swing(V): Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
}
// Test synthetic decode
TEST(TestDecodeRhoss, SyntheticSelfDecode) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(0);
IRRhossAc ac(0);
uint8_t expectedState[kRhossStateLength] = {
0xAA, 0x05, 0x60, 0x00, 0x50, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x33 };
irsend.begin();
irsend.reset();
irsend.sendRhoss(expectedState);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(RHOSS, irsend.capture.decode_type);
EXPECT_EQ(kRhossBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Power: On, Mode: 5 (Auto), Temp: 21C, Fan: 0 (Auto), Swing(V): Off",
ac.toString());
}
// Test strict decoding
TEST(TestDecodeRhoss, StrictDecode) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(0);
IRRhossAc ac(0);
uint8_t expectedState[kRhossStateLength] = {
0xAA, 0x05, 0x60, 0x00, 0x50, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x33 };
irsend.begin();
irsend.reset();
irsend.sendRhoss(expectedState);
irsend.makeDecodeResult();
ASSERT_TRUE(
irrecv.decodeRhoss(&irsend.capture,
kStartOffset, kRhossBits, true));
EXPECT_EQ(RHOSS, irsend.capture.decode_type);
EXPECT_EQ(kRhossBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Power: On, Mode: 5 (Auto), Temp: 21C, Fan: 0 (Auto), Swing(V): Off",
ac.toString());
}
// Tests for IRRhossAc class.
TEST(TestRhossAcClass, Power) {
IRRhossAc ac(0);
ac.begin();
ac.on();
EXPECT_TRUE(ac.getPower());
ac.off();
EXPECT_FALSE(ac.getPower());
ac.setPower(true);
EXPECT_TRUE(ac.getPower());
ac.setPower(false);
EXPECT_FALSE(ac.getPower());
}
TEST(TestRhossAcClass, Temperature) {
IRRhossAc ac(0);
ac.begin();
ac.setTemp(0);
EXPECT_EQ(kRhossTempMin, ac.getTemp());
ac.setTemp(255);
EXPECT_EQ(kRhossTempMax, ac.getTemp());
ac.setTemp(kRhossTempMin);
EXPECT_EQ(kRhossTempMin, ac.getTemp());
ac.setTemp(kRhossTempMax);
EXPECT_EQ(kRhossTempMax, ac.getTemp());
ac.setTemp(kRhossTempMin - 1);
EXPECT_EQ(kRhossTempMin, ac.getTemp());
ac.setTemp(kRhossTempMax + 1);
EXPECT_EQ(kRhossTempMax, 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(29);
EXPECT_EQ(29, ac.getTemp());
}
TEST(TestRhossAcClass, OperatingMode) {
IRRhossAc ac(0);
ac.begin();
ac.setMode(kRhossModeAuto);
EXPECT_EQ(kRhossModeAuto, ac.getMode());
ac.setMode(kRhossModeCool);
EXPECT_EQ(kRhossModeCool, ac.getMode());
ac.setMode(kRhossModeHeat);
EXPECT_EQ(kRhossModeHeat, ac.getMode());
ac.setMode(kRhossModeDry);
EXPECT_EQ(kRhossModeDry, ac.getMode());
ac.setMode(kRhossModeFan);
EXPECT_EQ(kRhossModeFan, ac.getMode());
ac.setMode(kRhossModeAuto + 1);
EXPECT_EQ(kRhossDefaultMode, ac.getMode());
ac.setMode(255);
EXPECT_EQ(kRhossDefaultMode, ac.getMode());
}
TEST(TestRhossAcClass, FanSpeed) {
IRRhossAc ac(0);
ac.begin();
ac.setFan(0);
EXPECT_EQ(kRhossFanAuto, ac.getFan());
ac.setFan(255);
EXPECT_EQ(kRhossFanAuto, ac.getFan());
ac.setFan(kRhossFanMax);
EXPECT_EQ(kRhossFanMax, ac.getFan());
ac.setFan(kRhossFanMax + 1);
EXPECT_EQ(kRhossFanAuto, ac.getFan());
ac.setFan(kRhossFanMax - 1);
EXPECT_EQ(kRhossFanMax - 1, ac.getFan());
ac.setFan(1);
EXPECT_EQ(1, ac.getFan());
ac.setFan(1);
EXPECT_EQ(1, ac.getFan());
ac.setFan(3);
EXPECT_EQ(3, ac.getFan());
}
TEST(TestRhossAcClass, Swing) {
IRRhossAc ac(0);
ac.begin();
ac.setSwing(false);
EXPECT_FALSE(ac.getSwing());
ac.setSwing(true);
EXPECT_TRUE(ac.getSwing());
}
TEST(TestRhossAcClass, Checksums) {
uint8_t state[kRhossStateLength] = {
0xAA, 0x05, 0x60, 0x00, 0x50, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x33 };
ASSERT_EQ(0x33, IRRhossAc::calcChecksum(state));
EXPECT_TRUE(IRRhossAc::validChecksum(state));
// Change the array so the checksum is invalid.
state[0] ^= 0xFF;
EXPECT_FALSE(IRRhossAc::validChecksum(state));
// Restore the previous change, and change another byte.
state[0] ^= 0xFF;
state[4] ^= 0xFF;
EXPECT_FALSE(IRRhossAc::validChecksum(state));
state[4] ^= 0xFF;
EXPECT_TRUE(IRRhossAc::validChecksum(state));
// Additional known good states.
uint8_t knownGood1[kRhossStateLength] = {
0xAA, 0x06, 0x60, 0x00, 0x50, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x34 };
EXPECT_TRUE(IRRhossAc::validChecksum(knownGood1));
ASSERT_EQ(0x34, IRRhossAc::calcChecksum(knownGood1));
uint8_t knownGood2[kRhossStateLength] = {
0xAA, 0x07, 0x60, 0x00, 0x50, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x35 };
EXPECT_TRUE(IRRhossAc::validChecksum(knownGood2));
ASSERT_EQ(0x35, IRRhossAc::calcChecksum(knownGood2));
uint8_t knownGood3[kRhossStateLength] = {
0xAA, 0x07, 0x60, 0x00, 0x53, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x38 };
EXPECT_TRUE(IRRhossAc::validChecksum(knownGood3));
ASSERT_EQ(0x38, IRRhossAc::calcChecksum(knownGood3));
// Validate calculation of checksum,
// same as knownGood3 except for the checksum.
uint8_t knownBad[kRhossStateLength] = {
0xAA, 0x07, 0x60, 0x00, 0x53, 0x80, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00 };
EXPECT_FALSE(IRRhossAc::validChecksum(knownBad));
IRRhossAc ac(0);
ac.setRaw(knownBad);
EXPECT_STATE_EQ(knownGood3, ac.getRaw(), kRhossBits);
}

View File

@ -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();
}

View File

@ -410,8 +410,8 @@ TEST(TestDecodeSharpAc, RealExample) {
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 27C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 27C, Fan: 2 (Auto), "
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@ -561,7 +561,7 @@ TEST(TestSharpAcClass, OperatingMode) {
// Check toString() says Fan rather than Auto.
EXPECT_EQ(
"Model: 2 (A705), Power: Off, Mode: 0 (Fan), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
}
@ -615,8 +615,8 @@ TEST(TestSharpAcClass, ReconstructKnownState) {
EXPECT_STATE_EQ(on_auto_auto, ac.getRaw(), kSharpAcBits);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_auto_28[kSharpAcStateLength] = {
@ -629,8 +629,8 @@ TEST(TestSharpAcClass, ReconstructKnownState) {
ac.setTemp(28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
EXPECT_STATE_EQ(cool_auto_28, ac.getRaw(), kSharpAcBits);
}
@ -647,8 +647,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(off_auto_auto);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: Off, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: Off, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), "
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t on_auto_auto[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0x00, 0x11, 0x20, 0x00, 0x08, 0x80, 0x00, 0xE0,
@ -657,8 +657,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(on_auto_auto);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 0 (Auto), Temp: 15C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_auto_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x22, 0x00, 0x08, 0x80, 0x04, 0xE0,
@ -667,8 +667,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_auto_28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan1_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x42, 0x00, 0x08, 0x80, 0x05, 0xE0,
@ -677,8 +677,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_fan1_28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 4 (Low), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 4 (Low), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan2_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x32, 0x00, 0x08, 0x80, 0x05, 0xE0,
@ -688,7 +688,7 @@ TEST(TestSharpAcClass, KnownStates) {
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan3_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x52, 0x00, 0x08, 0x80, 0x05, 0xE0,
@ -698,7 +698,7 @@ TEST(TestSharpAcClass, KnownStates) {
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 5 (UNKNOWN), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan4_28[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x31, 0x72, 0x00, 0x08, 0x80, 0x05, 0xE0,
@ -707,8 +707,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_fan4_28);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
uint8_t cool_fan4_28_ion_on[kSharpAcStateLength] = {
0xAA, 0x5A, 0xCF, 0x10, 0xCD, 0x61, 0x72, 0x08, 0x08, 0x80, 0x00, 0xE4,
@ -717,8 +717,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(cool_fan4_28_ion_on);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: -, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: On, Econo: -, Clean: Off",
"Power: -, Mode: 2 (Cool), Temp: 28C, Fan: 7 (High), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: On, Econo: -, Clean: Off",
ac.toString());
/* Unsupported / Not yet reverse engineered.
uint8_t cool_fan4_28_eco1[kSharpAcStateLength] = {
@ -735,8 +735,8 @@ TEST(TestSharpAcClass, KnownStates) {
ac.setRaw(dry_auto);
EXPECT_EQ(
"Model: 1 (A907), "
"Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
}
@ -747,6 +747,7 @@ TEST(TestSharpAcClass, toCommon) {
ac.setMode(kSharpAcCool);
ac.setTemp(20);
ac.setFan(kSharpAcFanMax);
ac.setSwingV(kSharpAcSwingVOff);
// Now test it.
ASSERT_EQ(decode_type_t::SHARP_AC, ac.toCommon().protocol);
ASSERT_TRUE(ac.toCommon().power);
@ -755,8 +756,8 @@ TEST(TestSharpAcClass, toCommon) {
ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
ASSERT_EQ(sharp_ac_remote_model_t::A705, ac.toCommon().model);
// Unsupported.
ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
// Unsupported.
ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
ASSERT_FALSE(ac.toCommon().turbo);
ASSERT_FALSE(ac.toCommon().quiet);
@ -868,20 +869,20 @@ TEST(TestSharpAcClass, Turbo) {
EXPECT_EQ(kSharpAcFanMax, ac.getFan());
EXPECT_EQ(
"Model: 3 (A903), "
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Turbo: On, "
"Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), "
"Swing(V): 0 (N/A), Turbo: On, Ion: On, Light: -, Clean: Off",
ac.toString());
ac.setRaw(off_state);
EXPECT_FALSE(ac.getTurbo());
EXPECT_EQ(
"Model: 3 (A903), "
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), "
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
}
TEST(TestSharpAcClass, SwingToggle) {
TEST(TestSharpAcClass, Swings) {
IRSharpAc ac(kGpioUnused);
ac.begin();
@ -906,6 +907,70 @@ TEST(TestSharpAcClass, SwingToggle) {
ac.setRaw(off_state);
EXPECT_FALSE(ac.getSwingToggle());
// Vertical
ac.setSwingV(kSharpAcSwingVToggle);
EXPECT_EQ(kSharpAcSwingVToggle, ac.getSwingV());
EXPECT_TRUE(ac.getSwingToggle());
ac.setSwingV(kSharpAcSwingVHigh);
EXPECT_EQ(kSharpAcSwingVHigh, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setSwingV(0xFF); // Doesn't change if invalid position given.
EXPECT_EQ(kSharpAcSwingVHigh, ac.getSwingV());
ac.setSwingV(kSharpAcSwingVMid);
EXPECT_EQ(kSharpAcSwingVMid, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setSwingV(kSharpAcSwingVLow);
EXPECT_EQ(kSharpAcSwingVLow, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setSwingV(kSharpAcSwingVIgnore);
EXPECT_EQ(kSharpAcSwingVIgnore, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
// Lowest/Coanda only works in Heat mode.
ac.setMode(kSharpAcCool);
ac.setSwingV(kSharpAcSwingVLowest);
EXPECT_EQ(kSharpAcSwingVLow, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
ac.setModel(sharp_ac_remote_model_t::A907); // Model A907 has heat mode.
ac.setMode(kSharpAcHeat);
EXPECT_EQ(kSharpAcHeat, ac.getMode());
ac.setSwingV(kSharpAcSwingVLowest);
EXPECT_EQ(kSharpAcSwingVLowest, ac.getSwingV());
// Check we can force Coanda in Cool mode.
ac.setMode(kSharpAcCool);
ASSERT_EQ(kSharpAcSwingVCoanda, kSharpAcSwingVLowest);
ac.setSwingV(kSharpAcSwingVCoanda, true);
EXPECT_EQ(kSharpAcSwingVCoanda, ac.getSwingV());
EXPECT_FALSE(ac.getSwingToggle());
EXPECT_EQ(kSharpAcCool, ac.getMode());
// Real messages/states
// ref: https://github.com/crankyoldgit/IRremoteESP8266/discussions/1590#discussioncomment-1254748
ac.stateReset();
const uint8_t coanda_heat_on[13] = {
0xAA, 0x5A, 0xCF, 0x10, 0xC8, 0x31, 0x21,
0x0A, 0x0E, 0x80, 0x06, 0xF4, 0x81};
ac.setRaw(coanda_heat_on);
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 1 (Heat), Temp: 23C, Fan: 2 (Auto), "
"Swing(V): 6 (Lowest), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
const uint8_t coanda_cool_on[13] = {
0xAA, 0x5A, 0xCF, 0x10, 0xC7, 0x31, 0x22,
0x0A, 0x0E, 0x80, 0x06, 0xF4, 0x41};
ac.setRaw(coanda_cool_on);
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 2 (Cool), Temp: 22C, Fan: 2 (Auto), "
"Swing(V): 6 (Highest), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
}
TEST(TestSharpAcClass, Ion) {
@ -1005,8 +1070,8 @@ TEST(TestSharpAcClass, Timers) {
EXPECT_TRUE(ac.isPowerSpecial());
EXPECT_EQ(
"Model: 3 (A903), "
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Turbo: Off, "
"Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off, Off Timer: 08:30",
"Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), Swing(V): 0 (N/A), "
"Turbo: Off, Ion: On, Light: -, Clean: Off, Off Timer: 08:30",
ac.toString());
// ref: https://docs.google.com/spreadsheets/d/1otzVFM5_tegrZ4ROCLgQ_jvJaWCDlZs1vC-YuR1FFXM/edit#gid=0&range=E80
@ -1020,7 +1085,7 @@ TEST(TestSharpAcClass, Timers) {
EXPECT_TRUE(ac.isPowerSpecial());
EXPECT_EQ(
"Model: 3 (A903), Power: -, Mode: 2 (Cool), Temp: 21C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off, "
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off, "
"On Timer: 12:00",
ac.toString());
}
@ -1058,13 +1123,13 @@ TEST(TestSharpAcClass, Clean) {
EXPECT_TRUE(ac.getClean());
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: On",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: On",
ac.toString());
ac.setRaw(clean_off_state);
EXPECT_FALSE(ac.getClean());
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
// Try constructing the clean on state.
@ -1084,13 +1149,13 @@ TEST(TestSharpAcClass, Clean) {
ac.setPower(false);
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
// Clean ON
ac.setClean(true);
EXPECT_EQ(
"Model: 1 (A907), Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: On",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: On",
ac.toString());
// Clean OFF (state is identical to `off_msg`).
// i.e. It just clears the clean settings & turns off the device.
@ -1098,25 +1163,25 @@ TEST(TestSharpAcClass, Clean) {
ac.setPower(false, true);
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
// Clean ON
ac.setClean(true);
EXPECT_EQ(
"Model: 1 (A907), Power: On, Mode: 3 (Dry), Temp: 15C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: On",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: On",
ac.toString());
// AC OFF
ac.off();
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
// AC ON (Mode Cool, Temp 25, Ion OFF, Fan 7)
ac.on();
EXPECT_EQ(
"Model: 1 (A907), Power: On, Mode: 2 (Cool), Temp: 25C, Fan: 7 (High), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
}
@ -1126,7 +1191,7 @@ TEST(TestSharpAcClass, Issue1309) {
ac.stateReset();
EXPECT_EQ(
"Model: 1 (A907), Power: Off, Mode: 0 (Auto), Temp: 15C, Fan: 0 (UNKNOWN), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Econo: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Econo: -, Clean: Off",
ac.toString());
const uint8_t issue1309_on[13] = {
@ -1135,7 +1200,7 @@ TEST(TestSharpAcClass, Issue1309) {
ac.setRaw(issue1309_on);
EXPECT_EQ(
"Model: 2 (A705), Power: On, Mode: 2 (Cool), Temp: 16C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
EXPECT_STATE_EQ(issue1309_on, ac.getRaw(), kSharpAcBits);
@ -1147,7 +1212,7 @@ TEST(TestSharpAcClass, Issue1309) {
ac.on();
EXPECT_EQ(
"Model: 2 (A705), Power: On, Mode: 2 (Cool), Temp: 16C, Fan: 2 (Auto), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: Off, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: Off, Light: -, Clean: Off",
ac.toString());
}
@ -1200,13 +1265,13 @@ TEST(TestSharpAcClass, Issue1387Power) {
EXPECT_STATE_EQ(real_off, ac.getRaw(), kSharpAcBits);
EXPECT_EQ(
"Model: 3 (A903), Power: Off, Mode: 2 (Cool), Temp: 27C, Fan: 3 (Low), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
// Create the same off state.
ac.setPower(true, ac.getPower());
EXPECT_STATE_EQ(real_on, ac.getRaw(), kSharpAcBits);
EXPECT_EQ(
"Model: 3 (A903), Power: On, Mode: 2 (Cool), Temp: 27C, Fan: 3 (Low), "
"Turbo: Off, Swing(V) Toggle: Off, Ion: On, Light: -, Clean: Off",
"Swing(V): 0 (N/A), Turbo: Off, Ion: On, Light: -, Clean: Off",
ac.toString());
}

View File

@ -10,8 +10,18 @@
// General housekeeping
TEST(TestTcl112Ac, Housekeeping) {
ASSERT_EQ("TCL112AC", typeToString(TCL112AC));
ASSERT_TRUE(hasACState(TCL112AC));
ASSERT_EQ("TCL112AC", typeToString(decode_type_t::TCL112AC));
ASSERT_EQ(decode_type_t::TCL112AC, strToDecodeType("TCL112AC"));
ASSERT_TRUE(hasACState(decode_type_t::TCL112AC));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::TCL112AC));
ASSERT_EQ(kTcl112AcBits, IRsend::defaultBits(decode_type_t::TCL112AC));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::TCL112AC));
ASSERT_EQ(tcl_ac_remote_model_t::TAC09CHSD, IRac::strToModel("TAC09CHSD"));
ASSERT_EQ(irutils::modelToStr(decode_type_t::TCL112AC,
tcl_ac_remote_model_t::TAC09CHSD), "TAC09CHSD");
ASSERT_EQ(tcl_ac_remote_model_t::GZ055BE1, IRac::strToModel("GZ055BE1"));
ASSERT_EQ(irutils::modelToStr(decode_type_t::TCL112AC,
tcl_ac_remote_model_t::GZ055BE1), "GZ055BE1");
}
// Tests for decodeTcl112Ac().
@ -63,9 +73,10 @@ TEST(TestDecodeTcl112Ac, DecodeRealExample) {
EXPECT_EQ(kTcl112AcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -106,27 +117,31 @@ TEST(TestTcl112AcClass, Temperature) {
IRTcl112Ac ac(kGpioUnused);
ac.setRaw(temp16C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp16point5C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16.5C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16.5C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp19point5C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 19.5C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 19.5C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp31C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setTemp(kTcl112AcTempMin);
@ -206,9 +221,10 @@ TEST(TestTcl112AcClass, OperatingMode) {
0x07, 0x00, 0x00, 0x00, 0x00, 0x80, 0x48};
ac.setRaw(automode);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 8 (Auto), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 8 (Auto), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
}
@ -236,9 +252,10 @@ TEST(TestTcl112AcClass, Power) {
0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB};
ac.setRaw(on);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
const uint8_t off[kTcl112AcStateLength] = {
@ -246,9 +263,10 @@ TEST(TestTcl112AcClass, Power) {
0x07, 0x40, 0x00, 0x00, 0x00, 0x80, 0xCB};
ac.setRaw(off);
EXPECT_EQ(
"Type: 1, Power: Off, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: Off, Mode: 3 (Cool), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
}
@ -263,15 +281,17 @@ TEST(TestTcl112AcClass, Checksum) {
EXPECT_EQ(0xCB, ac.calcChecksum(temp16C));
ac.setRaw(temp16C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
ac.setRaw(temp31C);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
EXPECT_EQ(0xBC, ac.calcChecksum(temp31C));
@ -337,12 +357,22 @@ TEST(TestTcl112AcClass, SwingVertical) {
IRTcl112Ac ac(kGpioUnused);
ac.begin();
ac.setSwingVertical(true);
EXPECT_TRUE(ac.getSwingVertical());
ac.setSwingVertical(false);
EXPECT_EQ(false, ac.getSwingVertical());
ac.setSwingVertical(true);
EXPECT_TRUE(ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVOff);
EXPECT_EQ(kTcl112AcSwingVOff, ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVOn);
EXPECT_EQ(kTcl112AcSwingVOn, ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVHigh);
EXPECT_EQ(kTcl112AcSwingVHigh, ac.getSwingVertical());
ac.setSwingVertical(kTcl112AcSwingVOff);
EXPECT_EQ(kTcl112AcSwingVOff, ac.getSwingVertical());
ac.setSwingVertical(0xFF); // Unused value so shouldn't change from previous.
EXPECT_EQ(kTcl112AcSwingVOff, ac.getSwingVertical());
const uint8_t highest[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x0F, 0x08, 0x00, 0x00, 0x00, 0x00, 0x53};
ac.setRaw(highest);
EXPECT_EQ(kTcl112AcSwingVHighest, ac.getSwingVertical());
}
TEST(TestTcl112AcClass, Turbo) {
@ -405,6 +435,7 @@ TEST(TestTcl112AcClass, Quiet_Mute) {
TEST(TestTcl112AcClass, toCommon) {
IRTcl112Ac ac(kGpioUnused);
ac.setModel(tcl_ac_remote_model_t::TAC09CHSD);
ac.setPower(true);
ac.setMode(kTcl112AcCool);
ac.setTemp(20);
@ -418,7 +449,7 @@ TEST(TestTcl112AcClass, toCommon) {
ac.setQuiet(false);
// Now test it.
ASSERT_EQ(decode_type_t::TCL112AC, 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);
@ -485,9 +516,10 @@ TEST(TestDecodeTcl112Ac, Issue744) {
IRTcl112Ac ac(kGpioUnused);
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 23C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: On",
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 23C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: On, "
"On Timer: Off, Off Timer: Off",
ac.toString());
}
@ -527,7 +559,7 @@ TEST(TestDecodeTcl112Ac, Issue1528) {
EXPECT_EQ(kTcl112AcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Type: 2, Quiet: On",
"Model: 1 (TAC09CHSD), Type: 2, Quiet: On",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -536,7 +568,6 @@ TEST(TestTcl112AcClass, SendingQuiet) {
IRTcl112Ac ac(kGpioUnused);
IRrecv capture(kGpioUnused);
ac.begin();
ac.on();
ac.setTemp(24);
@ -559,16 +590,116 @@ TEST(TestTcl112AcClass, SendingQuiet) {
ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
ASSERT_EQ(
"Type: 2, Quiet: On",
"Model: 1 (TAC09CHSD), Type: 2, Quiet: On",
IRAcUtils::resultAcToString(&ac._irsend.capture));
// Second message.
// TCL112 uses the Mitsubishi112 decoder.
// Skip first message.
EXPECT_TRUE(capture.decodeMitsubishi112(&ac._irsend.capture, 229));
ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
EXPECT_EQ(TCL112AC, ac._irsend.capture.decode_type);
EXPECT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
ASSERT_EQ(
"Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, Fan: 0 (Auto), "
"Econo: Off, Health: Off, Turbo: Off, Swing(H): Off, Swing(V): Off, "
"Light: Off", IRAcUtils::resultAcToString(&ac._irsend.capture));
"Model: 1 (TAC09CHSD), Type: 1, Power: On, Mode: 3 (Cool), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 0 (Auto), Swing(H): Off, "
"Econo: Off, Health: Off, Turbo: Off, Light: Off, "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&ac._irsend.capture));
}
TEST(TestTcl112AcClass, isTcl) {
const uint8_t tcl_temp16C[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x80, 0xCB};
EXPECT_TRUE(IRTcl112Ac::isTcl(tcl_temp16C));
const uint8_t tcl_temp31C[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBC};
EXPECT_TRUE(IRTcl112Ac::isTcl(tcl_temp31C));
const uint8_t issue1528[kTcl112AcStateLength] = {
0x23, 0xCB, 0x26, 0x02, 0x00, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85};
EXPECT_TRUE(IRTcl112Ac::isTcl(issue1528));
// Ref: https://cociweb.info/container/hvac_ir_recapture_2719.log
const uint8_t teknopoint[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03,
0x0F, 0x38, 0x00, 0x00, 0x00, 0x00, 0x83};
EXPECT_FALSE(IRTcl112Ac::isTcl(teknopoint));
}
TEST(TestTcl112AcClass, Timers) {
IRTcl112Ac ac(kGpioUnused);
ac.stateReset();
ac.setOnTimer(0);
ac.setOffTimer(0);
EXPECT_EQ(0, ac.getOnTimer());
EXPECT_EQ(0, ac.getOffTimer());
EXPECT_FALSE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
ac.setOnTimer(7 * 60);
EXPECT_EQ(7 * 60, ac.getOnTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_TRUE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
ac.setOnTimer(0);
EXPECT_EQ(0, ac.getOnTimer());
EXPECT_FALSE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
ac.setOffTimer(13 * 60); // Beyond max.
EXPECT_EQ(12 * 60, ac.getOffTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_TRUE(ac._.OffTimerEnabled);
ac.setOffTimer(0);
EXPECT_EQ(0, ac.getOffTimer());
EXPECT_FALSE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
// Real messages/states
// Per https://github.com/crankyoldgit/IRremoteESP8266/issues/1486#issuecomment-917545485
const uint8_t ontimer_1h[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x34, 0x03,
0x00, 0x78, 0x00, 0x06, 0x00, 0x00, 0xCA};
ac.setRaw(ontimer_1h);
EXPECT_EQ(60, ac.getOnTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_TRUE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
const uint8_t ontimer_4h[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x34, 0x03,
0x00, 0x78, 0x00, 0x18, 0x00, 0x00, 0xDC};
ac.setRaw(ontimer_4h);
EXPECT_EQ(240, ac.getOnTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_TRUE(ac._.OnTimerEnabled);
EXPECT_FALSE(ac._.OffTimerEnabled);
EXPECT_EQ(
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 3 (Cool), Temp: 31C, "
"Fan: 0 (Auto), Swing(V): 7 (Swing), "
"On Timer: 04:00, Off Timer: Off",
ac.toString());
const uint8_t offtimer_2h[14] = {
0x23, 0xCB, 0x26, 0x01, 0x00, 0x2C, 0x08,
0x07, 0x78, 0x0C, 0x00, 0x00, 0x00, 0xD4};
ac.setRaw(offtimer_2h);
EXPECT_EQ(120, ac.getOffTimer());
EXPECT_TRUE(ac._.TimerIndicator);
EXPECT_FALSE(ac._.OnTimerEnabled);
EXPECT_TRUE(ac._.OffTimerEnabled);
EXPECT_EQ(
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 8 (Auto), Temp: 24C, "
"Fan: 0 (Auto), Swing(V): 7 (Swing), "
"On Timer: Off, Off Timer: 02:00",
ac.toString());
}

View File

@ -44,7 +44,9 @@ TEST(TestDecodeTeknopoint, RealExample) {
ASSERT_EQ(kTeknopointBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 1 (Highest), "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
@ -67,7 +69,9 @@ TEST(TestDecodeTeknopoint, SyntheticExample) {
ASSERT_EQ(kTeknopointBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
"Model: 2 (GZ055BE1), Type: 1, Power: On, Mode: 3 (Cool), Temp: 16C, "
"Fan: 0 (Auto), Swing(V): 1 (Highest), "
"On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
EXPECT_EQ(
@ -95,7 +99,7 @@ TEST(TestUtils, Housekeeping) {
ASSERT_EQ("TEKNOPOINT", typeToString(decode_type_t::TEKNOPOINT));
ASSERT_EQ(decode_type_t::TEKNOPOINT, strToDecodeType("TEKNOPOINT"));
ASSERT_TRUE(hasACState(decode_type_t::TEKNOPOINT));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::TEKNOPOINT));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::TEKNOPOINT));
ASSERT_EQ(kTeknopointBits, IRsend::defaultBits(decode_type_t::TEKNOPOINT));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::TEKNOPOINT));
}

View File

@ -1,6 +1,10 @@
# SYNOPSIS:
#
# make [all] - makes everything.
# make TARGET - makes the given target.
# make run_tests - makes everything and runs all test
# make run-% - run specific test file (exclude .py)
# replace % with given test file
# make clean - removes all files generated by make.
# Please tweak the following variable definitions as needed by your
@ -37,6 +41,10 @@ run_tests : all
echo "PASS: \o/ \o/ All unit tests passed. \o/ \o/"; \
fi
run-% : all
echo "RUNNING: $*"; \
python3 ./$*.py;
clean :
rm -f *.o *.pyc gc_decode mode2_decode

View File

@ -85,6 +85,7 @@ class RawIRMessage():
bits = len(binary_str)
rev_binary_str = binary_str[::-1]
rev_num = int(rev_binary_str, 2)
# pylint: disable=C0209
self.output.write("\n Bits: %d\n"
" Hex: %s (MSB first)\n"
" %s (LSB first)\n"
@ -95,22 +96,21 @@ class RawIRMessage():
(bits, ("0x{0:0%dX}" % (bits / 4)).format(num),
("0x{0:0%dX}" % (bits / 4)).format(rev_num), num,
rev_num, binary_str, rev_binary_str))
# pylint: enable=C0209
def add_data_code(self, bin_str, name="", footer=True):
"""Add the common "data" sequence of code to send the bulk of a message."""
# pylint: disable=no-self-use
code = []
nbits = len(bin_str)
code.append(" // Data Section #%d" % self.section_count)
code.append(" // e.g. data = 0x%X, nbits = %d" % (int(bin_str, 2),
nbits))
code.append(" sendData(k%sBitMark, k%sOneSpace, k%sBitMark, "
"k%sZeroSpace, send_data, %d, true);" %
(name, name, name, name, nbits))
code.append(" send_data >>= %d;" % nbits)
code.append(f" // Data Section #{self.section_count}")
code.append(f" // e.g. data = 0x{int(bin_str, 2):X}, nbits = {nbits}")
code.append(f" sendData(k{name}BitMark, k{name}OneSpace, k{name}BitMark,"
f" k{name}ZeroSpace, send_data, {nbits}, true);")
code.append(f" send_data >>= {nbits};")
if footer:
code.append(" // Footer")
code.append(" mark(k%sBitMark);" % name)
code.append(f" mark(k{name}BitMark);")
return code
def add_data_decode_code(self, bin_str, name="", footer=True):
@ -120,21 +120,20 @@ class RawIRMessage():
nbits = len(bin_str)
code.extend([
"",
" // Data Section #%d" % self.section_count,
" // e.g. data_result.data = 0x%X, nbits = %d" % (int(bin_str, 2),
nbits),
" data_result = matchData(&(results->rawbuf[offset]), %s," % nbits,
" k%sBitMark, k%sOneSpace," % (name, name),
" k%sBitMark, k%sZeroSpace);" % (name, name),
f" // Data Section #{self.section_count}",
f" // e.g. data_result.data = 0x{int(bin_str, 2):X}, nbits = {nbits}",
f" data_result = matchData(&(results->rawbuf[offset]), {nbits},",
f" k{name}BitMark, k{name}OneSpace,",
f" k{name}BitMark, k{name}ZeroSpace);",
" offset += data_result.used;",
" if (data_result.success == false) return false; // Fail",
" data <<= %s; // Make room for the new bits of data." % nbits,
f" data <<= {nbits}; // Make room for the new bits of data.",
" data |= data_result.data;"])
if footer:
code.extend([
"",
" // Footer",
" if (!matchMark(results->rawbuf[offset++], k%sBitMark))" % name,
f" if (!matchMark(results->rawbuf[offset++], k{name}BitMark))",
" return false;"])
return code
@ -148,26 +147,28 @@ class RawIRMessage():
ambles = {}
firstmark = ambles.get("firstmark", 0)
firstspace = ambles.get("firstspace", 0)
lastmark = ambles.get("lastmark", "k%sBitMark" % name)
lastmark = ambles.get("lastmark", f"k{name}BitMark")
lastspace = ambles.get("lastspace", "kDefaultMessageGap")
code.append(
" // Data Section #%d" % self.section_count)
code.append(f" // Data Section #{self.section_count}")
if nbits % 8:
code.append(" // DANGER: Nr. of bits is not a multiple of 8. "
"This section won't work!")
code.extend([
" // e.g.",
" // bits = %d; bytes = %d;" % (nbits, nbytes),
f" // bits = {nbits}; bytes = {int(nbytes)};",
# pylint: disable=C0209
" // *(data + pos) = {0x%s};" % (
", 0x".join("%02X" % int(bin_str[i:i + 8], 2)
for i in range(0, len(bin_str), 8))),
" sendGeneric(%s, %s," % (firstmark, firstspace),
" k%sBitMark, k%sOneSpace," % (name, name),
" k%sBitMark, k%sZeroSpace," % (name, name),
" %s, %s," % (lastmark, lastspace),
" data + pos, %d, // Bytes" % nbytes,
" k%sFreq, true, kNoRepeat, kDutyDefault);" % name,
" pos += %d; // Adjust by how many bytes of data we sent" % nbytes])
# pylint: enable=C0209
f" sendGeneric({firstmark}, {firstspace},",
f" k{name}BitMark, k{name}OneSpace,",
f" k{name}BitMark, k{name}ZeroSpace,",
f" {lastmark}, {lastspace},",
f" data + pos, {int(nbytes)}, // Bytes",
f" k{name}Freq, true, kNoRepeat, kDutyDefault);",
f" pos += {int(nbytes)};"
f" // Adjust by how many bytes of data we sent"])
return code
def add_data_byte_decode_code(self, bin_str, name="", ambles=None):
@ -183,36 +184,37 @@ class RawIRMessage():
ambles = {}
firstmark = ambles.get("firstmark", 0)
firstspace = ambles.get("firstspace", 0)
lastmark = ambles.get("lastmark", "k%sBitMark" % name)
lastmark = ambles.get("lastmark", f"k{name}BitMark")
lastspace = ambles.get("lastspace", "kDefaultMessageGap")
code.extend([
"",
" // Data Section #%d" % self.section_count,
f" // Data Section #{self.section_count}",
" // e.g.",
" // bits = %d; bytes = %d;" % (nbits, nbytes),
f" // bits = {nbits}; bytes = {int(nbytes)};",
# pylint: disable=C0209
" // *(results->state + pos) = {0x%s};" % (
", 0x".join("%02X" % int(bin_str[i:i + 8], 2)
for i in range(0, len(bin_str), 8))),
# pylint: enable=C0209
" used = matchGeneric(results->rawbuf + offset, results->state + pos,",
" results->rawlen - offset, %d," % nbits,
" %s, %s," % (firstmark, firstspace),
" k%sBitMark, k%sOneSpace," % (name, name),
" k%sBitMark, k%sZeroSpace," % (name, name),
" %s, %s, true);" % (lastmark, lastspace),
f" results->rawlen - offset, {nbits},",
f" {firstmark}, {firstspace},",
f" k{name}BitMark, k{name}OneSpace,",
f" k{name}BitMark, k{name}ZeroSpace,",
f" {lastmark}, {lastspace}, true);",
" if (used == 0) return false; // We failed to find any data.",
" offset += used; // Adjust for how much of the message we read.",
" pos += %d; // Adjust by how many bytes of data we read" % nbytes])
f" pos += {int(nbytes)};"
" // Adjust by how many bytes of data we read"])
return code
def _calc_values(self):
"""Calculate the values which describe the standard timings
for the protocol."""
if self.verbose:
self.output.write("Potential Mark Candidates:\n"
"%s\n"
"Potential Space Candidates:\n"
"%s\n" % (str(self.marks), str(self.spaces)))
self.output.write(f"Potential Mark Candidates:\n{self.marks}\n"
f"Potential Space Candidates:\n{self.spaces}\n")
# The bit mark is likely to be the smallest mark.
self.bit_mark = self.marks[-1]
if len(self.marks) > 2: # Possible leader mark?
@ -305,8 +307,8 @@ def convert_rawdata(data_str):
results.append(int(timing))
except ValueError as non_numeric:
raise ValueError(
"Raw Data contains a non-numeric value of '%s'." %
timing) from non_numeric
f"Raw Data contains a non-numeric value of '{timing}'."
) from non_numeric
return results
@ -326,35 +328,33 @@ def dump_constants(message, defines, name="", output=sys.stdout):
zero_space = avg_list(message.space_buckets[message.zero_space])
output.write("Guessing key value:\n"
"k%sHdrMark = %d\n"
"k%sHdrSpace = %d\n"
"k%sBitMark = %d\n"
"k%sOneSpace = %d\n"
"k%sZeroSpace = %d\n" % (name, hdr_mark, name, hdr_space,
name, bit_mark, name, one_space,
name, zero_space))
defines.append("const uint16_t k%sHdrMark = %d;" % (name, hdr_mark))
defines.append("const uint16_t k%sBitMark = %d;" % (name, bit_mark))
defines.append("const uint16_t k%sHdrSpace = %d;" % (name, hdr_space))
defines.append("const uint16_t k%sOneSpace = %d;" % (name, one_space))
defines.append("const uint16_t k%sZeroSpace = %d;" % (name, zero_space))
f"k{name}HdrMark = {hdr_mark}\n"
f"k{name}HdrSpace = {hdr_space}\n"
f"k{name}BitMark = {bit_mark}\n"
f"k{name}OneSpace = {one_space}\n"
f"k{name}ZeroSpace = {zero_space}\n")
defines.append(f"const uint16_t k{name}HdrMark = {hdr_mark};")
defines.append(f"const uint16_t k{name}BitMark = {bit_mark};")
defines.append(f"const uint16_t k{name}HdrSpace = {hdr_space};")
defines.append(f"const uint16_t k{name}OneSpace = {one_space};")
defines.append(f"const uint16_t k{name}ZeroSpace = {zero_space};")
if ldr_mark:
output.write("k%sLdrMark = %d\n" % (name, ldr_mark))
defines.append("const uint16_t k%sLdrMark = %d;" % (name, ldr_mark))
output.write(f"k{name}LdrMark = {ldr_mark}\n")
defines.append(f"const uint16_t k{name}LdrMark = {ldr_mark};")
avg_gaps = [avg_list(message.space_buckets[x]) for x in message.gaps]
if len(message.gaps) == 1:
output.write("k%sSpaceGap = %d\n" % (name, avg_gaps[0]))
defines.append("const uint16_t k%sSpaceGap = %d;" % (name, avg_gaps[0]))
output.write(f"k{name}SpaceGap = {avg_gaps[0]}\n")
defines.append(f"const uint16_t k{name}SpaceGap = {avg_gaps[0]};")
else:
count = 0
for gap in avg_gaps:
# We probably (still) have a gap in the protocol.
count = count + 1
output.write("k%sSpaceGap%d = %d\n" % (name, count, gap))
defines.append("const uint16_t k%sSpaceGap%d = %d;" % (name, count, gap))
defines.append("const uint16_t k%sFreq = 38000; "
"// Hz. (Guessing the most common frequency.)" % name)
output.write(f"k{name}SpaceGap{count} = {gap}\n")
defines.append(f"const uint16_t k{name}SpaceGap{count} = {gap};")
defines.append(f"const uint16_t k{name}Freq = 38000; "
"// Hz. (Guessing the most common frequency.)")
def parse_and_report(rawdata_str, margin, gen_code=False, name="",
@ -374,7 +374,7 @@ def parse_and_report(rawdata_str, margin, gen_code=False, name="",
# Parse the input.
rawdata = convert_rawdata(rawdata_str)
output.write("Found %d timing entries.\n" % len(rawdata))
output.write(f"Found {len(rawdata)} timing entries.\n")
message = RawIRMessage(margin, rawdata, output)
output.write("\nGuessing encoding type:\n")
@ -410,17 +410,17 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
code["sendcomhead"].extend([
"",
"#if SEND_%s" % def_name.upper(),
f"#if SEND_{def_name.upper()}",
SAFE64NOTE,
"/// Send a %s formatted message." % name,
f"/// Send a {name} formatted message.",
"/// Status: ALPHA / Untested."])
code["send"].extend([
"/// @param[in] data containing the IR command.",
"/// @param[in] nbits Nr. of bits to send. usually k%sBits" % name,
f"/// @param[in] nbits Nr. of bits to send. usually k{name}Bits",
"/// @param[in] repeat Nr. of times the message is to be repeated.",
"void IRsend::send%s(const uint64_t data, const uint16_t"
" nbits, const uint16_t repeat) {" % def_name,
" enableIROut(k%sFreq);" % name,
f"void IRsend::send{def_name}(const uint64_t data, const uint16_t"
" nbits, const uint16_t repeat) {",
f" enableIROut(k{name}Freq);",
" for (uint16_t r = 0; r <= repeat; r++) {",
" uint64_t send_data = data;"])
code["send64+"].extend([
@ -431,21 +431,21 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
CODEGEN,
"/// @endcode",
"/// @param[in] nbytes Nr. of bytes of data in the array."
" (>=k%sStateLength)" % name,
f" (>=k{name}StateLength)",
"/// @param[in] repeat Nr. of times the message is to be repeated.",
"void IRsend::send%s(const uint8_t data[], const uint16_t nbytes,"
" const uint16_t repeat) {" % def_name,
f"void IRsend::send{def_name}(const uint8_t data[],"
" const uint16_t nbytes, const uint16_t repeat) {",
" for (uint16_t r = 0; r <= repeat; r++) {",
" uint16_t pos = 0;"])
code["sendcomfoot"].extend([
" }",
"}",
"#endif // SEND_%s" % def_name.upper()])
f"#endif // SEND_{def_name.upper()}"])
code["recvcomhead"].extend([
"",
"#if DECODE_%s" % def_name.upper(),
f"#if DECODE_{def_name.upper()}",
SAFE64NOTE,
"/// Decode the supplied %s message." % name,
f"/// Decode the supplied {name} message.",
"/// Status: ALPHA / Untested.",
"/// @param[in,out] results Ptr to the data to decode &"
" where to store the decode",
@ -456,11 +456,11 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
"/// @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::decode%s(decode_results *results, uint16_t offset,"
" const uint16_t nbits, const bool strict) {" % def_name,
" if (results->rawlen < 2 * nbits + k%sOverhead - offset)" % name,
f"bool IRrecv::decode{def_name}(decode_results *results, uint16_t offset,"
" const uint16_t nbits, const bool strict) {",
f" if (results->rawlen < 2 * nbits + k{name}Overhead - offset)",
" return false; // Too short a message to match.",
" if (strict && nbits != k%sBits)" % name,
f" if (strict && nbits != k{name}Bits)",
" return false;",
""])
code["recv"].extend([
@ -472,7 +472,7 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
code["recvcomfoot"].extend([
" return true;",
"}",
"#endif // DECODE_%s" % def_name.upper()])
f"#endif // DECODE_{def_name.upper()}"])
# states are:
# HM: Header/Leader mark
@ -496,24 +496,24 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
code["recv"].extend(message.add_data_decode_code(binary_value, name,
False))
message.section_count = message.section_count + 1
code_info["lastmark"] = "k%s%sdrMark" % (name, mark_type)
code_info["lastmark"] = f"k{name}{mark_type}drMark"
total_bits = total_bits + binary_value
code_info["firstmark"] = "k%s%sdrMark" % (name, mark_type)
code_info["firstmark"] = f"k{name}{mark_type}drMark"
binary_value = add_bit(binary_value, "reset")
output.write("k%s%sdrMark+" % (name, mark_type))
code["send"].extend([" // %seader" % mark_type,
" mark(k%s%sdrMark);" % (name, mark_type)])
output.write(f"k{name}{mark_type}drMark+")
code["send"].extend([f" // {mark_type}eader",
f" mark(k{name}{mark_type}drMark);"])
code["recv"].extend([
"",
" // %seader" % mark_type,
" if (!matchMark(results->rawbuf[offset++], k%s%sdrMark))" % (
name, mark_type),
f" // {mark_type}eader",
" if (!matchMark(results->rawbuf[offset++],"
f" k{name}{mark_type}drMark))",
" return false;"])
# Handle header spaces.
elif message.is_hdr_space(usec) and not message.is_one_space(usec):
if binary64_value:
code_info["lastspace"] = "k%sHdrSpace" % name
code_info["lastspace"] = f"k{name}HdrSpace"
message.section_count = message.section_count - 1
code["send64+"].extend(message.add_data_byte_code(binary64_value, name,
code_info))
@ -529,39 +529,39 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
total_bits = total_bits + binary_value
code["send"].extend(message.add_data_code(binary_value, name))
code["recv"].extend(message.add_data_decode_code(binary_value, name))
code_info["lastspace"] = "k%sHdrSpace" % name
code_info["lastspace"] = f"k{name}HdrSpace"
message.section_count = message.section_count + 1
binary_value = binary64_value = add_bit(binary_value, "reset")
output.write("UNEXPECTED->")
state = "HS"
output.write("k%sHdrSpace+" % name)
code["send"].append(" space(k%sHdrSpace);" % name)
output.write(f"k{name}HdrSpace+")
code["send"].append(f" space(k{name}HdrSpace);")
code["recv"].extend([
" if (!matchSpace(results->rawbuf[offset++], k%sHdrSpace))" % name,
f" if (!matchSpace(results->rawbuf[offset++], k{name}HdrSpace))",
" return false;"])
code_info["firstspace"] = "k%sHdrSpace" % name
code_info["firstspace"] = f"k{name}HdrSpace"
# Handle bit marks.
elif message.is_bit_mark(usec) and count % 2:
if state not in ("HS", "BS"):
output.write("k%sBitMark(UNEXPECTED)" % name)
output.write(f"k{name}BitMark(UNEXPECTED)")
state = "BM"
# Handle "zero" spaces
elif message.is_zero_space(usec):
if state != "BM":
output.write("k%sZeroSpace(UNEXPECTED)" % name)
output.write(f"k{name}ZeroSpace(UNEXPECTED)")
state = "BS"
binary_value = binary64_value = add_bit(binary_value, 0, output)
# Handle "one" spaces
elif message.is_one_space(usec):
if state != "BM":
output.write("k%sOneSpace(UNEXPECTED)" % name)
output.write(f"k{name}OneSpace(UNEXPECTED)")
state = "BS"
binary_value = binary64_value = add_bit(binary_value, 1, output)
elif message.is_gap(usec):
if state != "BM":
output.write("UNEXPECTED->")
output.write("GAP(%d)" % usec)
code_info["lastspace"] = "k%sSpaceGap" % name
output.write(f"GAP({usec})")
code_info["lastspace"] = f"k{name}SpaceGap"
if binary64_value:
code["send64+"].extend(message.add_data_byte_code(binary64_value, name,
code_info))
@ -579,19 +579,19 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
" // Gap"])
code["send"].extend([" // Gap"])
if state == "BM":
code["send"].extend([" mark(k%sBitMark);" % name])
code["send"].extend([f" mark(k{name}BitMark);"])
code["recv"].extend([
" if (!matchMark(results->rawbuf[offset++], k%sBitMark))" % name,
f" if (!matchMark(results->rawbuf[offset++], k{name}BitMark))",
" return false;"])
code["send"].append(" space(k%sSpaceGap);" % name)
code["send"].append(f" space(k{name}SpaceGap);")
code["recv"].extend([
" if (!matchSpace(results->rawbuf[offset++], k%sSpaceGap))" % name,
f" if (!matchSpace(results->rawbuf[offset++], k{name}SpaceGap))",
" return false;"])
total_bits = total_bits + binary_value
binary_value = binary64_value = add_bit(binary_value, "reset")
state = "GS"
else:
output.write("UNKNOWN(%d)" % usec)
output.write(f"UNKNOWN({usec})")
state = "UNK"
count = count + 1
if binary64_value:
@ -611,7 +611,7 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
code["recv"].extend([
"",
" // Success",
" results->decode_type = decode_type_t::%s;" % def_name.upper(),
f" results->decode_type = decode_type_t::{def_name.upper()};",
" results->bits = nbits;",
" results->value = data;",
" results->command = 0;",
@ -619,19 +619,18 @@ def decode_data(message, defines, code, name="", output=sys.stdout):
code["recv64+"].extend([
"",
" // Success",
" results->decode_type = decode_type_t::%s;" % def_name.upper(),
f" results->decode_type = decode_type_t::{def_name.upper()};",
" results->bits = nbits;"])
total_bits = total_bits + binary_value
output.write("\nTotal Nr. of suspected bits: %d\n" % len(total_bits))
defines.append("const uint16_t k%sBits = %d;"
" // Move to IRremoteESP8266.h" % (name, len(total_bits)))
output.write(f"\nTotal Nr. of suspected bits: {len(total_bits)}\n")
defines.append(f"const uint16_t k{name}Bits = {len(total_bits)};"
" // Move to IRremoteESP8266.h")
if len(total_bits) > 64:
defines.append("const uint16_t k%sStateLength = %d;"
" // Move to IRremoteESP8266.h" %
(name, len(total_bits) / 8))
defines.append("const uint16_t k%sOverhead = %d;" %
(name, message.rawlen - 2 * len(total_bits)))
defines.append(f"const uint16_t k{name}StateLength = "
f"{int(len(total_bits) / 8)}; // Move to IRremoteESP8266.h")
defines.append(f"const uint16_t k{name}Overhead = "
f"{message.rawlen - 2 * len(total_bits)};")
return total_bits
@ -645,9 +644,9 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout):
output.write("\nGenerating a VERY rough code outline:\n\n"
"// Copyright 2020 David Conran (crankyoldgit)\n"
"/// @file\n"
"/// @brief Support for %s protocol\n\n"
f"/// @brief Support for {def_name} protocol\n\n"
"// Supports:\n"
"// Brand: %s, Model: TODO add device and remote\n\n"
f"// Brand: {def_name}, Model: TODO add device and remote\n\n"
'#include "IRrecv.h"\n'
'#include "IRsend.h"\n'
'#include "IRutils.h"\n\n'
@ -656,9 +655,9 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout):
"// See https://github.com/crankyoldgit/IRremoteESP8266/wiki/"
"Adding-support-for-a-new-IR-protocol\n"
"// for details of how to include this in the library."
"\n" % (def_name, def_name))
"\n")
for line in defines:
output.write("%s\n" % line)
output.write(f"{line}\n")
if len(bits_str) > 64: # Will it fit in a uint64_t?
output.write("// DANGER: More than 64 bits detected. A uint64_t for "
@ -668,18 +667,21 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout):
for line in code["sendcomhead"] + code["send"] + code["sendcomfoot"]:
if line == SAFE64NOTE:
line = "// Function should be safe up to 64 bits."
output.write("%s\n" % line)
output.write(f"{line}\n")
if len(bits_str) > 64: # Will it fit in a uint64_t?
for line in code["sendcomhead"] + code["send64+"] + code["sendcomfoot"]:
if line == SAFE64NOTE:
line = "// Alternative >64bit function to send %s messages\n" % \
def_name.upper() + "// Function should be safe over 64 bits."
line = (f"// Alternative >64bit function to send {def_name.upper()}"
" messages\n"
"// Function should be safe over 64 bits.")
elif line == CODEGEN:
# pylint: disable=C0209
line = "/// uint8_t data[k%sStateLength] = {0x%s};" % (
name, ", 0x".join("%02X" % int(bits_str[i:i + 8], 2)
for i in range(0, len(bits_str), 8)))
output.write("%s\n" % line)
# pylint: enable=C0209
output.write(f"{line}\n")
if len(bits_str) > 64: # Will it fit in a uint64_t?
output.write("\n// DANGER: More than 64 bits detected. A uint64_t for "
"'data' won't work!")
@ -689,7 +691,7 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout):
for line in code["recvcomhead"] + code["recv"] + code["recvcomfoot"]:
if line == SAFE64NOTE:
line = "// Function should be safe up to 64 bits."
output.write("%s\n" % line)
output.write(f"{line}\n")
# Display the > 64bit version's decode code
if len(bits_str) > 64: # Is it too big for a uint64_t?
@ -699,7 +701,7 @@ def generate_code(defines, code, bits_str, name="", output=sys.stdout):
for line in code["recvcomhead"] + code["recv64+"] + code["recvcomfoot"]:
if line == SAFE64NOTE:
line = "// Function should be safe over 64 bits."
output.write("%s\n" % line)
output.write(f"{line}\n")
def add_rawdata_args(parser):
"""Add the arguments for feeding in the rawdata string(s)."""

View File

@ -24,12 +24,26 @@ cat >${OUTPUT} << EOF
// Constant text to be shared across all object files.
// This means there is only one copy of the character/string/text etc.
#ifdef ESP8266
class __FlashStringHelper;
#define IRTEXT_CONST_PTR_CAST(PTR)\\
reinterpret_cast<const __FlashStringHelper*>(PTR)
#define IRTEXT_CONST_PTR(NAME) const __FlashStringHelper* const NAME
#else // ESP8266
#define IRTEXT_CONST_PTR_CAST(PTR) PTR
#define IRTEXT_CONST_PTR(NAME) const char* const NAME
#endif // ESP8266
EOF
# Parse and output contents of INPUT file.
sed 's/ PROGMEM//' ${INPUT} | egrep "^(const )?char" | cut -f1 -d= |
sed 's/ $/;/;s/^/extern /' | sort -u >> ${OUTPUT}
egrep '^\s{,10}IRTEXT_CONST_STRING\(' ${INPUT} | cut -f2 -d\( | cut -f1 -d, |
sed 's/^/extern IRTEXT_CONST_PTR\(/;s/$/\);/' | sort -u >> ${OUTPUT}
egrep '^\s{,10}IRTEXT_CONST_BLOB_DECL\(' ${INPUT} |
cut -f2 -d\( | cut -f1 -d\) |
sed 's/^/extern IRTEXT_CONST_PTR\(/;s/$/\);/' | sort -u >> ${OUTPUT}
# Footer
cat >> ${OUTPUT} << EOF

View File

@ -31,7 +31,8 @@ cat << EndOfTextEndOfTextEndOfText
EndOfTextEndOfTextEndOfText
CLASSES=$(egrep -h "^ *((enum|class) |} [a-zA-Z0-9_]+_t;$)" src/*.h |
sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sort -u)
sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sort -u |
grep -v "^__")
for i in ${CLASSES}; do
echo -e "${i}\tKEYWORD1"
done | sort -du
@ -59,13 +60,15 @@ cat << EndOfTextEndOfTextEndOfText
#######################################
EndOfTextEndOfTextEndOfText
LITERALS=$(grep "^#define [A-Z]" src/*.cpp src/*.h |
LITERALS=$(grep -h "^#define [A-Z]" src/*.cpp src/*.h |
while read ignore define ignore; do
echo ${define};
done | sort -u |
grep -v [\(\)] | grep -v ^_ | grep -v _\$ | grep -v VIRTUAL)
CONSTS=$(grep "^const " src/*.cpp src/*.h |
sed -E 's/\[.*\] =.*//;s/ =.*//;s/^.* \*?k/k/')
CONSTS=$(grep -h "^const " src/*.cpp src/*.h |
sed -E 's/\[.*\] =.*//;s/ =.*//;s/^.* \*?k/k/';
grep -h "^IRTEXT_CONST_" src/*.cpp src/*.h |
sed -E 's/IRTEXT_CONST_\S+\(//;s/,.*//;s/\).*//')
ENUMS=$(cat src/*.h | while read a b; do
if [[ ${a} == "};" ]]; then
ENUM=0;
@ -76,7 +79,7 @@ ENUMS=$(cat src/*.h | while read a b; do
if [[ ${a} == "enum" ]]; then
ENUM=1;
fi;
done)
done | grep -v "^//")
for i in ${LITERALS} ${CONSTS} ${ENUMS}; do
echo -e "${i}\tLITERAL1"
done | sort -u

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python
"""Convert IRremoteESP8266's Raw data output into Pronto Code."""
#
# Copyright 2020 David Conran
@ -16,7 +16,7 @@ def parse_and_report(rawdata_str, hertz=38000, end_usecs=100000,
# Parse the input.
rawdata = convert_rawdata(rawdata_str)
if verbose:
output.write("Found %d timing entries.\n" % len(rawdata))
output.write(f"Found {len(rawdata)} timing entries.\n")
# Do we need to pad out the rawdata to make it even in length?
if end_usecs > 0 and len(rawdata) % 2 == 1:
@ -26,29 +26,29 @@ def parse_and_report(rawdata_str, hertz=38000, end_usecs=100000,
# Work out the frequency code.
pronto_freq = int(1000000.0 / (hertz * 0.241246))
if verbose:
output.write("Pronto frequency is %X (%d Hz).\n" % (pronto_freq, hertz))
result.append("%04X" % pronto_freq)
output.write(f"Pronto frequency is {pronto_freq:X} ({hertz} Hz).\n")
result.append(f"{pronto_freq:04X}")
period = 1000000.0 / max(1, hertz)
if verbose:
output.write("Pronto period is %f uSecs.\n" % period)
output.write(f"Pronto period is {period} uSecs.\n")
# Add the lengths to the code.
if use_initial:
result.append("%04x" % int(len(rawdata) / 2)) # Initial burst code length
result.append("%04x" % 0) # No Repeat code length
result.append(f"{int(len(rawdata) / 2):04x}") # Initial burst code length
result.append("0000") # No Repeat code length
else:
result.append("%04x" % 0) # No Initial burst code length
result.append("%04x" % int(len(rawdata) / 2)) # Repeat code length
result.append("0000") # No Initial burst code length
result.append(f"{int(len(rawdata) / 2):04x}") # Repeat code length
# Add the data.
if verbose:
output.write("Raw data: %s " % rawdata)
output.write(f"Raw data: {rawdata} ")
for i in rawdata:
result.append("%04x" % int(i / period))
result.append(f"{int(i / period):04x}")
if generate_code:
output.write("uint16_t pronto[%d] = {0x%s};\n" % (len(result),
", 0x".join(result)))
output.write(f"uint16_t pronto[{len(result)}] = "
f"{{0x{', 0x'.join(result)}}};\n")
else:
output.write("Pronto code = '%s'\n" % " ".join(result))
output.write(f"Pronto code = '{' '.join(result)}'\n")
# pylint: enable=too-many-arguments

View File

@ -0,0 +1,426 @@
#!/usr/bin/env python3
"""Generate SupportedProtocols.md by scraping source code files"""
import pathlib
import argparse
import subprocess
from io import StringIO
import sys
import re
import time
CODE_URL = "https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_"
BRAND_MODEL = re.compile(r"""
Brand:\s{1,20} # "Brand:" label followd by between 1 and 20 whitespace chars.
\b(?P<brand>.{1,40})\b # The actual brand of the device, max 40 chars.
\s{0,10}, # Followed by at most 10 whitespace chars, then a comma.
\s{1,20} # The between 1 and 20 whitespace chars.
Model:\s{1,20} # "Model:" label followd by between 1 and 20 whitespace chars.
\b(?P<model>.{1,80}) # The model info of the device, max 80 chars.
\s{0,5}$ # Followed by at most 5 whitespaces before the end of line.
""", re.VERBOSE)
ENUMS = re.compile(r"enum (\w{1,60}) {(.{1,5000}?)};", re.DOTALL)
ENUM_ENTRY = re.compile(r"^\s{1,80}(\w{1,80})", re.MULTILINE)
DECODED_PROTOCOLS = re.compile(r"""
.{0,80} # Ignore upto an 80 char line of whitespace/code etc.
# Now look for code that looks like we are assigning the Protocol type.
# There are two typical styles used:
(?:results->decode_type # The first style.
| # Or
typeguess) # The second style
\s{0,5}=\s{0,5} # The assignment operator and potential whitespace
(?:decode_type_t::)? # The protocol could have an optional type prefix.
(\w{1,40}); # Finally, the last word of code should be the Protocol.
""", re.VERBOSE)
AC_FN = re.compile(r"ir_(.{1,80})\.h")
AC_MODEL_ENUM_RE = re.compile(r"(.{1,40})_ac_remote_model_t")
IRSEND_FN_RE = re.compile(r"IRsend\.h")
ALL_FN = re.compile(r"ir_(.{1,80})\.(h|cpp)")
EXCLUDED_PROTOCOLS = ["UNKNOWN", "UNUSED", "kLastDecodeType", "typeguess"]
EXCLUDED_ACS = ["Magiquest", "NEC"]
def getgitcommittime():
"""Call git to get time of last commit
"""
try:
label = subprocess.check_output(\
["git", "show", "-s", "--format=%ct", "HEAD"]).strip()
return int(label)
except FileNotFoundError as err:
print("Git failed, which is ok, no git binary found?:", err)
return None
except subprocess.SubprocessError as err:
print("Git failed, which is ok, see output, maybe no git checkout?:", err)
return None
def getmarkdownheader():
"""Get the generated header
"""
srctime = getgitcommittime()
# pylint: disable=C0209
return """<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: {} --->""".format(
time.strftime("%a %d %b %Y %H:%M:%S +0000", time.gmtime(srctime)))
# pylint: enable=C0209
def getallprotocols():
"""Return all protocls configured in IRremoteESP8266.h
"""
irremote = ARGS.directory / "IRremoteESP8266.h"
enums = getenums(irremote)["decode_type_t"]
if not enums:
errorexit("Error getting ENUMS from IRremoteESP8266.h")
return enums
def getdecodedprotocols():
"""All protocols that include decoding support"""
ret = set()
for path in ARGS.directory.iterdir():
if path.suffix != ".cpp":
continue
matches = DECODED_PROTOCOLS.finditer(path.open(encoding="utf-8").read())
for match in matches:
protocol = match.group(1)
if protocol not in EXCLUDED_PROTOCOLS:
ret.add(protocol)
return ret
def getallacs():
"""All supported A/C codes"""
ret = {}
for path in ARGS.directory.iterdir():
match = AC_FN.match(path.name)
if match:
acprotocol = match.group(1)
rawmodels = getenums(path)
models = set()
for model in rawmodels:
model = model.upper()
model = model.replace(f"K{acprotocol.upper()}", "")
if model and model not in EXCLUDED_PROTOCOLS:
models.add(model)
if acprotocol in ret:
ret[acprotocol].update(models)
else:
ret[acprotocol] = models
# Parse IRsend.h's enums
match = IRSEND_FN_RE.match(path.name)
if match:
rawmodels = getenums(path)
for acprotocol, acmodels in rawmodels.items():
models = set()
for model in acmodels:
model = model.upper()
model = model.replace(f"K{acprotocol.upper()}", "")
if model and model not in EXCLUDED_PROTOCOLS:
models.add(model)
if acprotocol in ret:
ret[acprotocol].update(models)
else:
ret[acprotocol] = models
return ret
class FnSets():
"""Container for getalldevices"""
def __init__(self):
self.allcodes = {}
self.fnnomatch = set()
self.allhfileprotos = set()
self.fnhmatch = set()
self.fncppmatch = set()
def add(self, supports, path):
"""add the path to correct set based on supports"""
if path.suffix == ".h":
self.allhfileprotos.add(path.stem)
if supports:
if path.suffix == ".h":
self.fnhmatch.add(path.stem)
elif path.suffix == ".cpp":
self.fncppmatch.add(path.stem)
else:
self.fnnomatch.add(path.stem)
def printwarnings(self):
"""print warnings"""
# all protos with support in .cpp file, when there is a .h file
# meaning that the documentation should probably be moved to .h
# in the future, with doxygen, that might change
protosincppwithh = list(self.fncppmatch & self.allhfileprotos)
if protosincppwithh:
protosincppwithh.sort()
print("The following files has supports section in .cpp, expected in .h")
for path in protosincppwithh:
print(f"\t{path}")
protosincppandh = list(self.fncppmatch & self.fnhmatch)
if protosincppandh:
protosincppandh.sort()
print("The following files has supports section in both .h and .cpp")
for path in protosincppandh:
print(f"\t{path}")
nosupports = self.getnosupports()
if nosupports:
nosupports.sort()
print("The following files had no supports section:")
for path in nosupports:
print(f"\t{path}")
return protosincppwithh or protosincppandh or nosupports
def getnosupports(self):
"""get protos without supports sections"""
return list(self.fnnomatch - self.fnhmatch - self.fncppmatch)
def getalldevices():
"""All devices and associated branding and model information (if available)
"""
sets = FnSets()
for path in ARGS.directory.iterdir():
match = ALL_FN.match(path.name)
if not match:
continue
supports = extractsupports(path)
sets.add(supports, path)
protocol = match.group(1)
for brand, model in supports:
protocolbrand = (protocol, brand)
pbset = sets.allcodes.get(protocolbrand, [])
if model in pbset:
print(f"Model {model} is duplicated for {protocol}, {brand}")
sets.allcodes[protocolbrand] = pbset + [model]
for fnprotocol in sets.getnosupports():
sets.allcodes[(fnprotocol[3:], "Unknown")] = []
return sets
def getenums(path):
"""Returns the keys for the first enum type in path
"""
ret = {}
for enums in ENUMS.finditer(path.open(encoding="utf-8").read()):
if enums:
enum_name = AC_MODEL_ENUM_RE.search(enums.group(1))
if enum_name:
enum_name = enum_name.group(1).capitalize()
else:
enum_name = enums.group(1)
ret[enum_name] = set()
for enum in ENUM_ENTRY.finditer(enums.group(2)):
enum = enum.group(1)
if enum in EXCLUDED_PROTOCOLS:
continue
ret[enum_name].add(enum)
return ret
ARGS = None
def initargs():
"""Init the command line arguments"""
global ARGS # pylint: disable=global-statement
parser = argparse.ArgumentParser()
parser.add_argument(
"-n",
"--noout",
help="generate no output data, combine with --alert to only check",
action="store_true",
)
parser.add_argument(
"-s",
"--stdout",
help="output to stdout rather than SupportedProtocols.md",
action="store_true",
)
parser.add_argument("-v",
"--verbose",
help="increase output verbosity",
action="store_true")
parser.add_argument(
"-a",
"--alert",
help="alert if a file does not have a supports section, "
"non zero exit code if issues where found",
action="store_true",
)
parser.add_argument(
"directory",
nargs="?",
help="directory of the source git checkout",
default=None,
)
ARGS = parser.parse_args()
if ARGS.directory is None:
src = pathlib.Path("../src")
if not src.is_dir():
src = pathlib.Path("./src")
else:
src = pathlib.Path(ARGS.directory) / "src"
if not src.is_dir():
errorexit(f"Directory not valid: {src!s}")
ARGS.directory = src
return ARGS
def getmdfile():
"""Resolves SupportedProtocols.md path"""
foutpath = ARGS.directory / "../SupportedProtocols.md"
return foutpath.resolve()
def errorexit(msg):
"""Print an error and exit on critical error"""
sys.stderr.write(f"{msg}\n")
sys.exit(1)
def extractsupports(path):
"""Extract all of the Supports: sections and associated brands and models
"""
supports = []
insupports = False
for line in path.open(encoding="utf-8"):
if not line.startswith("//"):
continue
line = line[2:].strip()
if line == "Supports:":
insupports = True
continue
if insupports:
match = BRAND_MODEL.match(line)
if match:
supports.append((match.group("brand"), match.group("model")))
else:
insupports = False
continue
# search and inform about any legacy formated supports data
elif any(x in line for x in [ \
"seems compatible with",
"be compatible with",
"it working with here"]):
print(f"\t{path.name} Legacy supports format found\n\t\t{line}")
return supports
def makeurl(txt, path):
"""Make a Markup URL from given filename"""
return f"[{txt}]({CODE_URL + path})"
def outputprotocols(fout, protocols):
"""For a given protocol set, sort and output the markdown"""
protocols = list(protocols)
protocols.sort()
for protocol in protocols:
fout.write(f"- {protocol}\n")
def generate(fout):
"""Generate data to fout
return True on any issues (when alert is active)"""
decodedprotocols = getdecodedprotocols()
sendonly = getallprotocols() - decodedprotocols
allacs = getallacs()
sets = getalldevices()
allcodes = sets.allcodes
allbrands = list(allcodes.keys())
allbrands.sort()
fout.write("\n# IR Protocols supported by this library\n\n")
fout.write(
"| Protocol | Brand | Model | A/C Model | Detailed A/C Support |\n")
fout.write("| --- | --- | --- | --- | --- |\n")
for protocolbrand in allbrands:
protocol, brand = protocolbrand
codes = allcodes[protocolbrand]
codes.sort()
acmodels = []
acsupport = "-"
if protocol in allacs:
acmodels = list(allacs[protocol])
acmodels.sort()
brand = makeurl(brand, protocol + ".h")
if protocol not in EXCLUDED_ACS:
acsupport = "Yes"
# pylint: disable=C0209
fout.write("| {} | **{}** | {} | {} | {} |\n".format(
makeurl(protocol, protocol + ".cpp"),
brand,
"<BR>".join(codes).replace("|", "\\|"),
"<BR>".join(acmodels),
acsupport,
))
# pylint: enable=C0209
fout.write("\n\n## Send only protocols:\n\n")
outputprotocols(fout, sendonly)
fout.write("\n\n## Send & decodable protocols:\n\n")
outputprotocols(fout, decodedprotocols)
return ARGS.alert and sets.printwarnings()
def generatenone():
"""No out write
return True on any issues"""
return generate(StringIO())
def generatestdout():
"""Standard out write
return True on any issues"""
fout = sys.stdout
fout.write(getmarkdownheader())
return generate(fout)
def generatefile():
"""File write, extra detection of changes in existing file
return True on any issues, but only if there is changes"""
# get file path
foutpath = getmdfile()
if ARGS.verbose:
print(f"Output path: {foutpath!s}")
# write data to temp memorystream
ftemp = StringIO()
ret = generate(ftemp)
# get old filedata, skipping header
with getmdfile().open("r", encoding="utf-8") as forg:
olddata = forg.readlines()[3:]
# get new data, skip first empty line
ftemp.seek(0)
newdata = ftemp.readlines()[1:]
# if new data is same as old we don't need to write anything
if newdata == olddata:
print("No changes, exit without write")
return False
# write output
with foutpath.open("w", encoding="utf-8") as fout:
fout.write(getmarkdownheader())
fout.write(ftemp.getvalue())
return ret
def main():
"""Default main function
return True on any issues"""
initargs()
if ARGS.verbose:
print(f"Looking for files in: {ARGS.directory.resolve()!s}")
if ARGS.noout:
return generatenone()
if ARGS.stdout:
return generatestdout()
# default file
return generatefile()
if __name__ == "__main__":
sys.exit(1 if main() else 0)

View File

@ -1,6 +1,6 @@
{
"name": "IRremoteESP8266",
"version": "2.7.19",
"version": "2.8.0",
"keywords": "infrared, ir, remote, esp8266, esp32",
"description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)",
"repository":
@ -48,8 +48,6 @@
"frameworks": "arduino",
"platforms": ["espressif8266", "espressif32"],
"build": {
"srcDir": "IRremoteESP8266/src",
"flags": [ "-I$PROJECT_DIR/include", "-includetasmota_options.h" ]