" D_STR_PROTOCOL " | " +
+ "") +
+ F(""
+ "" D_STR_PROTOCOL " | ") +
htmlSelectClimateProtocol(KEY_PROTOCOL,
climate[chan]->next.protocol) +
- " | "
- "" D_STR_MODEL " | " +
+ F(" | "
+ "" D_STR_MODEL " | ") +
htmlSelectModel(KEY_MODEL, climate[chan]->next.model) +
- " | "
- "" D_STR_POWER " | " +
+ F(" | "
+ "" D_STR_POWER " | ") +
htmlSelectBool(KEY_POWER, climate[chan]->next.power) +
- " | "
- "" D_STR_MODE " | " +
+ F(" | "
+ "" D_STR_MODE " | ") +
htmlSelectMode(KEY_MODE, climate[chan]->next.mode) +
- " | "
+ F(""
"" D_STR_TEMP " | "
""
+ "step='0.5' value='") + String(climate[chan]->next.degrees, 1) +
+ F("'>"
" | "
- "" D_STR_FAN " | " +
+ " | " D_STR_FAN " | ") +
htmlSelectFanspeed(KEY_FANSPEED, climate[chan]->next.fanspeed) +
- " | "
- "" D_STR_SWINGV " | " +
+ F(" | "
+ "" D_STR_SWINGV " | ") +
htmlSelectSwingv(KEY_SWINGV, climate[chan]->next.swingv) +
- " | "
- "" D_STR_SWINGH " | " +
+ F(" | "
+ "" D_STR_SWINGH " | ") +
htmlSelectSwingh(KEY_SWINGH, climate[chan]->next.swingh) +
- " | "
- "" D_STR_QUIET " | " +
+ F(" | "
+ "" D_STR_QUIET " | ") +
htmlSelectBool(KEY_QUIET, climate[chan]->next.quiet) +
- " | "
- "" D_STR_TURBO " | " +
+ F(" | "
+ "" D_STR_TURBO " | ") +
htmlSelectBool(KEY_TURBO, climate[chan]->next.turbo) +
- " | "
- "" D_STR_ECONO " | " +
+ F(" | "
+ "" D_STR_ECONO " | ") +
htmlSelectBool(KEY_ECONO, climate[chan]->next.econo) +
- " | "
- "" D_STR_LIGHT " | " +
+ F(" | "
+ "" D_STR_LIGHT " | ") +
htmlSelectBool(KEY_LIGHT, climate[chan]->next.light) +
- " | "
- "" D_STR_FILTER " | " +
+ F(" | "
+ "" D_STR_FILTER " | ") +
htmlSelectBool(KEY_FILTER, climate[chan]->next.filter) +
- " | "
- "" D_STR_CLEAN " | " +
+ F(" | "
+ "" D_STR_CLEAN " | ") +
htmlSelectBool(KEY_CLEAN, climate[chan]->next.clean) +
- " | "
- "" D_STR_BEEP " | " +
+ F(" | "
+ "" D_STR_BEEP " | ") +
htmlSelectBool(KEY_BEEP, climate[chan]->next.beep) +
- " | "
- "Force resend | " +
+ F(" | "
+ "Force resend | ") +
htmlSelectBool(KEY_RESEND, false) +
- " | "
+ F(""
" "
""
- "";
+ "");
}
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 +=
- "General"
- "Hostname: " + String(Hostname) + " "
- "IP address: " + WiFi.localIP().toString() + " "
- "MAC address: " + WiFi.macAddress() + " "
- "Booted: " + timeSince(1) + " " +
- "Version: " _MY_VERSION_ " "
+ F(" General"
+ "Hostname: ") + String(Hostname) + F(" "
+ "IP address: ") + WiFi.localIP().toString() + F(" "
+ "MAC address: ") + WiFi.macAddress() + F(" "
+ "Booted: ") + timeSince(1) + F(" ") +
+ F("Version: " _MY_VERSION_ " "
"Built: " __DATE__
" " __TIME__ " "
- "Period Offset: " + String(offset) + "us "
+ "Period Offset: ") + String(offset) + F("us "
"IR Lib Version: " _IRREMOTEESP8266_VERSION_ " "
#if defined(ESP8266)
- "ESP8266 Core Version: " + ESP.getCoreVersion() + " "
- "Free Sketch Space: " + String(maxSketchSpace() >> 10) + "k "
+ "ESP8266 Core Version: ") + ESP.getCoreVersion() + F(" "
+ "Free Sketch Space: ") + String(maxSketchSpace() >> 10) + F("k "
#endif // ESP8266
#if defined(ESP32)
- "ESP32 SDK Version: " + ESP.getSdkVersion() + " "
+ "ESP32 SDK Version: ") + ESP.getSdkVersion() + F(" "
#endif // ESP32
- "Cpu Freq: " + String(ESP.getCpuFreqMHz()) + "MHz "
- "Sanity Check: " + String((_sanity == 0) ? "Ok" : "FAILED") + " "
- "IR Send GPIO(s): " + listOfTxGpios() + " "
+ "Cpu Freq: ") + String(ESP.getCpuFreqMHz()) + F("MHz "
+ "Sanity Check: ") + String((_sanity == 0) ? F("Ok") : F("FAILED")) +
+ F(" "
+ "IR Send GPIO(s): ") + listOfTxGpios() + F(" ")
+ irutils::addBoolToString(kInvertTxOutput,
- "Inverting GPIO output", false) + " "
- "Total send requests: " + String(sendReqCounter) + " "
- "Last message sent: " + String(lastSendSucceeded ? "Ok" : "FAILED") +
- " (" + timeSince(lastSendTime) + ") "
+ F("Inverting GPIO output"), false) + F(" "
+ "Total send requests: ") + String(sendReqCounter) + F(" "
+ "Last message sent: ") + String(lastSendSucceeded ? F("Ok") : F("FAILED")) +
+ F(" (") + timeSince(lastSendTime) + F(") "
#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
" "
- "Total IR Received: " + String(irRecvCounter) + " "
- "Last IR Received: " + lastIrReceived +
- " (" + timeSince(lastIrReceivedTime) + ") "
+ "Total IR Received: ") + String(irRecvCounter) + F(" "
+ "Last IR Received: ") + lastIrReceived +
+ F(" (") + timeSince(lastIrReceivedTime) + F(") "
#endif // IR_RX
- "Duplicate " D_STR_WIFI " networks: " +
- String(HIDE_DUPLICATE_NETWORKS ? "Hide" : "Show") + " "
+ "Duplicate " D_STR_WIFI " networks: ") +
+ String(HIDE_DUPLICATE_NETWORKS ? F("Hide") : F("Show")) + F(" "
"Min " D_STR_WIFI " signal required: "
#ifdef MIN_SIGNAL_STRENGTH
- + String(static_cast(MIN_SIGNAL_STRENGTH)) +
+ ) + // NOLINT(whitespace/parens)
+ String(static_cast(MIN_SIGNAL_STRENGTH)) + F(
#else // MIN_SIGNAL_STRENGTH
"8"
#endif // MIN_SIGNAL_STRENGTH
"% "
"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
" "
#if REPORT_VCC
- "Vcc: ";
+ "Vcc: ");
html += vccToString();
- html += "V "
+ html += F("V "
#endif // REPORT_VCC
" "
#if MQTT_ENABLE
"MQTT Information"
- "Server: " + String(MqttServer) + ":" + String(MqttPort) + " (" +
+ " Server: ") + String(MqttServer) + ":" + String(MqttPort) + F(" (") +
(mqtt_client.connected() ? "Connected " + timeSince(lastDisconnectedTime)
: "Disconnected " + timeSince(lastConnectedTime)) +
- ") "
- "Disconnections: " + String(mqttDisconnectCounter - 1) + " "
- "Buffer Size: " + String(mqtt_client.getBufferSize()) + " bytes "
- "Client id: " + MqttClientId + " "
- "Command topic(s): " + listOfCommandTopics() + " "
- "Acknowledgements topic: " + MqttAck + " "
+ F(") "
+ "Disconnections: ") + String(mqttDisconnectCounter - 1) + F(" "
+ "Buffer Size: ") + String(mqtt_client.getBufferSize()) + F(" bytes "
+ "Client id: ") + MqttClientId + F(" "
+ "Command topic(s): ") + listOfCommandTopics() + F(" "
+ "Acknowledgements topic: ") + MqttAck + F(" "
#if IR_RX
- "IR Received topic: " + MqttRecv + " "
+ "IR Received topic: ") + MqttRecv + F(" "
#endif // IR_RX
- "Log topic: " + MqttLog + " "
- "LWT topic: " + MqttLwt + " "
- "QoS: " + String(QOS) + " "
+ "Log topic: ") + MqttLog + F(" "
+ "LWT topic: ") + MqttLwt + F(" "
+ "QoS: ") + String(QOS) + F(" "
// 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) + "' (" +
- timeSince(lastMqttCmdTime) + ") "
- "Total published: " + String(mqttSentCounter) + " "
- "Total received: " + String(mqttRecvCounter) + " "
+ F("' (payload) '") + irutils::htmlEscape(lastMqttCmd) + F("' (") +
+ timeSince(lastMqttCmdTime) + F(") "
+ "Total published: ") + String(mqttSentCounter) + F(" "
+ "Total received: ") + String(mqttRecvCounter) + F(" "
" "
#endif // MQTT_ENABLE
"Climate Information"
""
- "IR Send GPIO: " + String(txGpioTable[0]) + " "
- "Last update source: " + lastClimateSource + " "
- "Total sent: " + String(irClimateCounter) + " "
- "Last send: " + String(hasClimateBeenSent ?
+ "IR Send GPIO: ") + String(txGpioTable[0]) + F(" "
+ "Last update source: ") + lastClimateSource + F(" "
+ "Total sent: ") + String(irClimateCounter) + F(" "
+ "Last send: ") + String(hasClimateBeenSent ?
(String(lastClimateSucceeded ? "Ok" : "FAILED") +
" (" + timeElapsed(lastClimateIr.elapsed()) + ")") :
- "Never") + " "
+ "Never") + F(" "
#if MQTT_ENABLE
- "State listen period: " + msToString(kStatListenPeriodMs) + " "
- "State broadcast period: " + msToString(kBroadcastPeriodMs) + " "
- "Last state broadcast: " + (hasBroadcastBeenSent ?
+ "State listen period: ") + msToString(kStatListenPeriodMs) + F(" "
+ "State broadcast period: ") + msToString(kBroadcastPeriodMs) + F(" "
+ "Last state broadcast: ") + (hasBroadcastBeenSent ?
timeElapsed(lastBroadcast.elapsed()) :
- String("Never")) + " "
+ String(F("Never"))) + F(" "
#if MQTT_DISCOVERY_ENABLE
- "Last discovery sent: " + (lockMqttBroadcast ?
- String("Locked") :
+ "Last discovery sent: ") + (lockMqttBroadcast ?
+ String(F("Locked")) :
(hasDiscoveryBeenSent ?
timeElapsed(lastDiscovery.elapsed()) :
- String("Never"))) +
- " "
- "Discovery topic: " + MqttDiscovery + " " +
+ String(F("Never")))) +
+ F(" "
+ "Discovery topic: ") + MqttDiscovery + F(" ") + 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
" "
// Page footer
"
"
"(Note: Page will refresh every 60 " D_STR_SECONDS ".)"
- " ";
+ "");
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.")) +
- "Device restarting. Try connecting in a few " D_STR_SECONDS ". " +
+ F("Device restarting. Try connecting in a few " D_STR_SECONDS ". ") +
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.")) +
- "Try connecting in a few " D_STR_SECONDS ". " +
+ F("Try connecting in a few " D_STR_SECONDS ". ") +
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() +
- "The Home Assistant MQTT Discovery message is being sent to topic: " +
- MqttDiscovery + ". It will show up in Home Assistant in a few seconds."
+ F(" The Home Assistant MQTT Discovery message is being sent to topic: ")
+ + MqttDiscovery +
+ F(". It will show up in Home Assistant in a few seconds."
" "
"Warning!"
"Home Assistant's config for this device is reset each time this is "
- " is sent. " +
+ " is sent.") +
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();
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini
index 0063a1133..b56304f66 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV2/platformio.ini
@@ -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)
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini
index 95a90ac86..e5df92876 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRrecvDumpV3/platformio.ini
@@ -49,5 +49,14 @@ build_flags = -D_IR_LOCALE_=fr-FR ; French
[env:it-IT]
build_flags = -D_IR_LOCALE_=it-IT ; Italian
+[env:pt-BR]
+build_flags = -D_IR_LOCALE_=pt-BR ; Portuguese (Brazilian)
+
+[env:ru-RU]
+build_flags = -D_IR_LOCALE_=ru-RU ; Russian
+
+[env:sv-SE]
+build_flags = -D_IR_LOCALE_=sv-SE ; Swedish
+
[env:zh-CN]
build_flags = -D_IR_LOCALE_=zh-CN ; Chinese (Simplified)
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt
index fefce8d9d..592b5d99b 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.json b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.json
index 5bce2238e..99160ec37 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.json
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.json
@@ -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":
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.properties b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.properties
index e4985e2a2..e7580dd2d 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.properties
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.properties
@@ -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)
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.cpp
index 9b4e10cbd..557e5593e 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.cpp
@@ -36,6 +36,7 @@
#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"
@@ -50,6 +51,17 @@
#include "ir_Voltas.h"
#include "ir_Whirlpool.h"
+// On the ESP8266 platform we need to use a special version of string handling
+// 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(RHS))
+#else // ESP8266
+#define STRCASECMP(LHS, RHS) strcasecmp(LHS, RHS)
+#endif // ESP8266
+#endif // STRCASECMP
+
/// Class constructor
/// @param[in] pin Gpio pin to use when transmitting IR messages.
/// @param[in] inverted true, gpio output defaults to high. false, to low.
@@ -233,7 +245,10 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#endif
#if SEND_MIDEA
case decode_type_t::MIDEA:
-#endif
+#endif // SEND_MIDEA
+#if SEND_MIRAGE
+ case decode_type_t::MIRAGE:
+#endif // SEND_MIRAGE
#if SEND_MITSUBISHI_AC
case decode_type_t::MITSUBISHI_AC:
#endif
@@ -256,6 +271,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_PANASONIC_AC32
case decode_type_t::PANASONIC_AC32:
#endif
+#if SEND_RHOSS
+ case decode_type_t::RHOSS:
+#endif
#if SEND_SAMSUNG_AC
case decode_type_t::SAMSUNG_AC:
#endif
@@ -277,6 +295,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_TECO
case decode_type_t::TECO:
#endif
+#if SEND_TEKNOPOINT
+ case decode_type_t::TEKNOPOINT:
+#endif // SEND_TEKNOPOINT
#if SEND_TOSHIBA_AC
case decode_type_t::TOSHIBA_AC:
#endif
@@ -1018,15 +1039,18 @@ void IRac::goodweather(IRGoodweatherAc *ac,
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] swingh The horizontal swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
+/// @param[in] econo Toggle the device's economical mode.
/// @param[in] light Turn on the LED/Display mode.
/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model,
const bool on, const stdAc::opmode_t mode, const bool celsius,
const float degrees, const stdAc::fanspeed_t fan,
- const stdAc::swingv_t swingv, const bool turbo,
- const bool light, const bool clean, const int16_t sleep) {
+ const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
+ const bool turbo, const bool econo, const bool light,
+ const bool clean, const int16_t sleep) {
ac->begin();
ac->setModel(model);
ac->setPower(on);
@@ -1035,11 +1059,12 @@ void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model,
ac->setFan(ac->convertFan(fan));
ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag.
ac->convertSwingV(swingv));
+ ac->setSwingHorizontal(ac->convertSwingH(swingh));
ac->setLight(light);
ac->setTurbo(turbo);
+ ac->setEcono(econo);
ac->setXFan(clean);
ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off.
- // No Horizontal Swing setting available.
// No Econo setting available.
// No Filter setting available.
// No Beep setting available.
@@ -1068,7 +1093,7 @@ void IRac::haier(IRHaierAC *ac,
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
- ac->setSwing(ac->convertSwingV(swingv));
+ ac->setSwingV(ac->convertSwingV(swingv));
// No Horizontal Swing setting available.
// No Quiet setting available.
// No Turbo setting available.
@@ -1089,26 +1114,35 @@ void IRac::haier(IRHaierAC *ac,
#if SEND_HAIER_AC176
/// Send a Haier 176 bit A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRHaierAC176 object to use.
+/// @param[in] model The A/C model to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
+/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] swingh The horizontal swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
+/// @param[in] quiet Run the device in quiet mode.
/// @param[in] filter Turn on the (ion/pollen/etc) filter mode.
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
-void IRac::haier176(IRHaierAC176 *ac,
+void IRac::haier176(IRHaierAC176 *ac, const haier_ac176_remote_model_t model,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
- const stdAc::swingv_t swingv, const bool turbo,
- const bool filter, const int16_t sleep) {
+ const bool celsius, const float degrees,
+ const stdAc::fanspeed_t fan,
+ const stdAc::swingv_t swingv,
+ const stdAc::swingh_t swingh,
+ const bool turbo, const bool quiet, const bool filter,
+ const int16_t sleep) {
ac->begin();
+ ac->setModel(model);
ac->setMode(ac->convertMode(mode));
+ ac->setUseFahrenheit(!celsius);
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
- ac->setSwing(ac->convertSwingV(swingv));
- // No Horizontal Swing setting available.
- // No Quiet setting available.
+ ac->setSwingV(ac->convertSwingV(swingv));
+ ac->setSwingH(ac->convertSwingH(swingh));
+ ac->setQuiet(quiet);
ac->setTurbo(turbo);
// No Light setting available.
ac->setHealth(filter);
@@ -1125,24 +1159,31 @@ void IRac::haier176(IRHaierAC176 *ac,
/// @param[in, out] ac A Ptr to an IRHaierACYRW02 object to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
+/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] swingh The horizontal swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
+/// @param[in] quiet Run the device in quiet mode.
/// @param[in] filter Turn on the (ion/pollen/etc) filter mode.
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
void IRac::haierYrwo2(IRHaierACYRW02 *ac,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
- const stdAc::swingv_t swingv, const bool turbo,
- const bool filter, const int16_t sleep) {
+ const bool celsius, const float degrees,
+ const stdAc::fanspeed_t fan,
+ const stdAc::swingv_t swingv,
+ const stdAc::swingh_t swingh,
+ const bool turbo, const bool quiet, const bool filter,
+ const int16_t sleep) {
ac->begin();
ac->setMode(ac->convertMode(mode));
+ ac->setUseFahrenheit(!celsius);
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
- ac->setSwing(ac->convertSwingV(swingv));
- // No Horizontal Swing setting available.
- // No Quiet setting available.
+ ac->setSwingV(ac->convertSwingV(swingv));
+ ac->setSwingH(ac->convertSwingH(swingh));
+ ac->setQuiet(quiet);
ac->setTurbo(turbo);
// No Light setting available.
ac->setHealth(filter);
@@ -1450,6 +1491,17 @@ void IRac::midea(IRMideaAC *ac,
}
#endif // SEND_MIDEA
+#if SEND_MIRAGE
+/// Send a Mirage 120-bit A/C message with the supplied settings.
+/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use.
+/// @param[in] state The desired state to send.
+void IRac::mirage(IRMirageAc *ac, const stdAc::state_t state) {
+ ac->begin();
+ ac->fromCommon(state);
+ ac->send();
+}
+#endif // SEND_MIRAGE
+
#if SEND_MITSUBISHI_AC
/// Send a Mitsubishi A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRMitsubishiAC object to use.
@@ -1766,38 +1818,45 @@ void IRac::panasonic32(IRPanasonicAc32 *ac,
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] swingh The horizontal swing setting.
/// @param[in] quiet Run the device in quiet/silent mode.
/// @param[in] turbo Run the device in turbo/powerful mode.
+/// @param[in] econo Run the device in economical mode.
/// @param[in] light Turn on the LED/Display mode.
/// @param[in] filter Turn on the (ion/pollen/etc) filter mode.
-/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc
-/// @param[in] beep Enable/Disable beeps when receiving IR messages.
+/// @param[in] clean Toggle the self-cleaning mode. e.g. Mould, dry filters etc
+/// @param[in] beep Toggle beep setting for receiving IR messages.
+/// @param[in] sleep Nr. of minutes for sleep mode. <= 0 is Off, > 0 is on.
/// @param[in] prevpower The power setting from the previous A/C state.
-/// @param[in] forcepower Do we force send the special power message?
+/// @param[in] prevsleep Nr. of minutes for sleep from the previous A/C state.
+/// @param[in] forceextended Do we force sending the special extended message?
void IRac::samsung(IRSamsungAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees,
- const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
- const bool quiet, const bool turbo, const bool light,
+ const stdAc::fanspeed_t fan,
+ const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
+ const bool quiet, const bool turbo, const bool econo,
+ const bool light,
const bool filter, const bool clean,
- const bool beep, const bool prevpower,
- const bool forcepower) {
+ const bool beep, const int16_t sleep,
+ const bool prevpower, const int16_t prevsleep,
+ const bool forceextended) {
ac->begin();
- ac->stateReset(forcepower, prevpower);
+ ac->stateReset(forceextended || (sleep != prevsleep), prevpower);
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setSwing(swingv != stdAc::swingv_t::kOff);
- // No Horizontal swing setting available.
+ ac->setSwingH(swingh != stdAc::swingh_t::kOff);
ac->setQuiet(quiet);
- ac->setPowerful(turbo);
+ ac->setPowerful(turbo); // FYI, `setEcono(true)` will override this.
ac->setDisplay(light);
- // No Econo setting available.
+ ac->setEcono(econo);
ac->setIon(filter);
- ac->setClean(clean);
- ac->setBeep(beep);
- // No Sleep setting available.
+ ac->setClean(clean); // Toggle
+ ac->setBeep(beep); // Toggle
+ ac->setSleepTimer((sleep <= 0) ? 0 : sleep);
// No Clock setting available.
// Do setMode() again as it can affect fan speed.
ac->setMode(ac->convertMode(mode));
@@ -1893,6 +1952,7 @@ void IRac::sanyo88(IRSanyoAc88 *ac,
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] swingv_prev The previous vertical swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] light Turn on the LED/Display mode.
/// @param[in] filter Turn on the (ion/pollen/etc) filter mode.
@@ -1901,14 +1961,15 @@ void IRac::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 stdAc::swingv_t swingv,
+ const stdAc::swingv_t swingv_prev, const bool turbo,
const bool light, const bool filter, const bool clean) {
ac->begin();
ac->setModel(model);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan, model));
- ac->setSwingToggle(swingv != stdAc::swingv_t::kOff);
+ if (swingv != swingv_prev) ac->setSwingV(ac->convertSwingV(swingv));
// Econo deliberately not used as it cycles through 3 modes uncontrollably.
// ac->setEconoToggle(econo);
ac->setIon(filter);
@@ -1940,6 +2001,7 @@ void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
#if SEND_TCL112AC
/// Send a TCL 112-bit A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRTcl112Ac object to use.
+/// @param[in] model The A/C model to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
@@ -1951,18 +2013,19 @@ void IRac::sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
/// @param[in] light Turn on the LED/Display mode.
/// @param[in] econo Run the device in economical mode.
/// @param[in] filter Turn on the (ion/pollen/etc) filter mode.
-void IRac::tcl112(IRTcl112Ac *ac,
+void IRac::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,
const bool quiet, const bool turbo, const bool light,
const bool econo, const bool filter) {
ac->begin();
+ ac->setModel(model);
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
- ac->setSwingVertical(swingv != stdAc::swingv_t::kOff);
+ ac->setSwingVertical(ac->convertSwingV(swingv));
ac->setSwingHorizontal(swingh != stdAc::swingh_t::kOff);
ac->setQuiet(quiet);
ac->setTurbo(turbo);
@@ -2341,6 +2404,35 @@ void IRac::transcold(IRTranscoldAc *ac,
}
#endif // SEND_TRANSCOLD
+#if SEND_RHOSS
+/// Send an Rhoss A/C message with the supplied settings.
+/// @param[in, out] ac A Ptr to an IRRhossAc object to use.
+/// @param[in] on The power setting.
+/// @param[in] mode The operation mode setting.
+/// @param[in] degrees The temperature setting in degrees.
+/// @param[in] fan The speed setting for the fan.
+/// @param[in] swing The swing setting.
+void IRac::rhoss(IRRhossAc *ac,
+ const bool on, const stdAc::opmode_t mode, const float degrees,
+ const stdAc::fanspeed_t fan, const stdAc::swingv_t swing) {
+ ac->begin();
+ ac->setPower(on);
+ ac->setMode(ac->convertMode(mode));
+ ac->setSwing(swing != stdAc::swingv_t::kOff);
+ ac->setTemp(degrees);
+ ac->setFan(ac->convertFan(fan));
+ // No Quiet setting available.
+ // No Light setting available.
+ // No Filter setting available.
+ // No Turbo setting available.
+ // No Economy setting available.
+ // No Clean setting available.
+ // No Beep setting available.
+ // No Sleep setting available.
+ ac->send();
+}
+#endif // SEND_RHOSS
+
/// Create a new state base on the provided state that has been suitably fixed.
/// @note This is for use with Home Assistant, which requires mode to be off if
/// the power is off.
@@ -2425,11 +2517,20 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired,
case decode_type_t::WHIRLPOOL_AC:
result.power = desired.power ^ prev->power;
break;
+ case decode_type_t::MIRAGE:
+ if (desired.model == mirage_ac_remote_model_t::KKG29AC1)
+ result.light = desired.light ^ prev->light;
+ result.clean = desired.clean ^ prev->clean;
+ break;
case decode_type_t::PANASONIC_AC:
// CKP models use a power mode toggle.
if (desired.model == panasonic_ac_remote_model_t::kPanasonicCkp)
result.power = desired.power ^ prev->power;
break;
+ case decode_type_t::SAMSUNG_AC:
+ result.beep = desired.beep ^ prev->beep;
+ result.clean = desired.clean ^ prev->clean;
+ break;
default:
{};
}
@@ -2494,11 +2595,12 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
// Construct a pointer-safe previous power state incase prev is NULL/NULLPTR.
#if (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC)
const bool prev_power = (prev != NULL) ? prev->power : !send.power;
+ const int16_t prev_sleep = (prev != NULL) ? prev->sleep : -1;
#endif // (SEND_HITACHI_AC1 || SEND_SAMSUNG_AC || SEND_SHARP_AC)
-#if SEND_LG
+#if (SEND_LG || SEND_SHARP_AC)
const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv
: stdAc::swingv_t::kOff;
-#endif // SEND_LG
+#endif // (SEND_LG || SEND_SHARP_AC)
// Per vendor settings & setup.
switch (send.protocol) {
#if SEND_AIRWELL
@@ -2678,8 +2780,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
IRGreeAC ac(_pin, (gree_ac_remote_model_t)send.model, _inverted,
_modulation);
gree(&ac, (gree_ac_remote_model_t)send.model, send.power, send.mode,
- send.celsius, send.degrees, send.fanspeed, send.swingv, send.turbo,
- send.light, send.clean, send.sleep);
+ send.celsius, send.degrees, send.fanspeed, send.swingv, send.swingh,
+ send.turbo, send.econo, send.light, send.clean, send.sleep);
break;
}
#endif // SEND_GREE
@@ -2696,8 +2798,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case HAIER_AC176:
{
IRHaierAC176 ac(_pin, _inverted, _modulation);
- haier176(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.turbo, send.filter, send.sleep);
+ haier176(&ac, (haier_ac176_remote_model_t)send.model, send.power,
+ send.mode, send.celsius, send.degrees, send.fanspeed,
+ send.swingv, send.swingh, send.turbo, send.filter, send.sleep);
break;
}
#endif // SEND_HAIER_AC176
@@ -2705,8 +2808,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case HAIER_AC_YRW02:
{
IRHaierACYRW02 ac(_pin, _inverted, _modulation);
- haierYrwo2(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.turbo, send.filter, send.sleep);
+ haierYrwo2(&ac, send.power, send.mode, send.celsius, send.degrees,
+ send.fanspeed, send.swingv, send.swingh, send.turbo,
+ send.filter, send.sleep);
break;
}
#endif // SEND_HAIER_AC_YRW02
@@ -2792,6 +2896,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
break;
}
#endif // SEND_MIDEA
+#if SEND_MIRAGE
+ case MIRAGE:
+ {
+ IRMirageAc ac(_pin, _inverted, _modulation);
+ mirage(&ac, send);
+ break;
+ }
+#endif // SEND_MIRAGE
#if SEND_MITSUBISHI_AC
case MITSUBISHI_AC:
{
@@ -2866,13 +2978,22 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
break;
}
#endif // SEND_PANASONIC_AC32
+#if SEND_RHOSS
+ case RHOSS:
+ {
+ IRRhossAc ac(_pin, _inverted, _modulation);
+ rhoss(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv);
+ break;
+ }
+#endif // SEND_RHOSS
#if SEND_SAMSUNG_AC
case SAMSUNG_AC:
{
IRSamsungAc ac(_pin, _inverted, _modulation);
samsung(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.quiet, send.turbo, send.light, send.filter, send.clean,
- send.beep, prev_power);
+ send.swingh, send.quiet, send.turbo, send.econo, send.light,
+ send.filter, send.clean, send.beep, send.sleep,
+ prev_power, prev_sleep);
break;
}
#endif // SEND_SAMSUNG_AC
@@ -2899,21 +3020,25 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRSharpAc ac(_pin, _inverted, _modulation);
sharp(&ac, (sharp_ac_remote_model_t)send.model, send.power, prev_power,
- send.mode, degC, send.fanspeed, send.swingv, send.turbo, send.light,
- send.filter, send.clean);
+ send.mode, degC, send.fanspeed, send.swingv, prev_swingv,
+ send.turbo, send.light, send.filter, send.clean);
break;
}
#endif // SEND_SHARP_AC
-#if SEND_TCL112AC
+#if (SEND_TCL112AC || SEND_TEKNOPOINT)
case TCL112AC:
+ case TEKNOPOINT:
{
IRTcl112Ac ac(_pin, _inverted, _modulation);
- tcl112(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.swingh, send.quiet, send.turbo, send.light, send.econo,
- send.filter);
+ tcl_ac_remote_model_t model = (tcl_ac_remote_model_t)send.model;
+ if (send.protocol == decode_type_t::TEKNOPOINT)
+ model = tcl_ac_remote_model_t::GZ055BE1;
+ tcl112(&ac, model, send.power, send.mode,
+ degC, send.fanspeed, send.swingv, send.swingh, send.quiet,
+ send.turbo, send.light, send.econo, send.filter);
break;
}
-#endif // SEND_TCL112AC
+#endif // (SEND_TCL112AC || SEND_TEKNOPOINT)
#if SEND_TECHNIBEL_AC
case TECHNIBEL_AC:
{
@@ -3048,25 +3173,31 @@ bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); }
/// @return The equivalent enum.
stdAc::opmode_t IRac::strToOpmode(const char *str,
const stdAc::opmode_t def) {
- if (!strcasecmp(str, kAutoStr) ||
- !strcasecmp(str, kAutomaticStr))
+ if (!STRCASECMP(str, kAutoStr) ||
+ !STRCASECMP(str, kAutomaticStr))
return stdAc::opmode_t::kAuto;
- else if (!strcasecmp(str, kOffStr) ||
- !strcasecmp(str, kStopStr))
+ else if (!STRCASECMP(str, kOffStr) ||
+ !STRCASECMP(str, kStopStr))
return stdAc::opmode_t::kOff;
- else if (!strcasecmp(str, kCoolStr) ||
- !strcasecmp(str, "COOLING"))
+ else if (!STRCASECMP(str, kCoolStr) ||
+ !STRCASECMP(str, kCoolingStr))
return stdAc::opmode_t::kCool;
- else if (!strcasecmp(str, kHeatStr) ||
- !strcasecmp(str, "HEATING"))
+ else if (!STRCASECMP(str, kHeatStr) ||
+ !STRCASECMP(str, kHeatingStr))
return stdAc::opmode_t::kHeat;
- else if (!strcasecmp(str, kDryStr) ||
- !strcasecmp(str, "DRYING") ||
- !strcasecmp(str, "DEHUMIDIFY"))
+ else if (!STRCASECMP(str, kDryStr) ||
+ !STRCASECMP(str, kDryingStr) ||
+ !STRCASECMP(str, kDehumidifyStr))
return stdAc::opmode_t::kDry;
- else if (!strcasecmp(str, kFanStr) ||
- !strcasecmp(str, "FANONLY") ||
- !strcasecmp(str, kFanOnlyStr))
+ else if (!STRCASECMP(str, kFanStr) ||
+ // 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
+ !STRCASECMP(str, kFanOnlyStr) ||
+ !STRCASECMP(str, kFan_OnlyStr) ||
+ !STRCASECMP(str, kFanOnlyWithSpaceStr) ||
+ !STRCASECMP(str, kFanOnlyNoSpaceStr))
return stdAc::opmode_t::kFan;
else
return def;
@@ -3078,26 +3209,26 @@ stdAc::opmode_t IRac::strToOpmode(const char *str,
/// @return The equivalent enum.
stdAc::fanspeed_t IRac::strToFanspeed(const char *str,
const stdAc::fanspeed_t def) {
- if (!strcasecmp(str, kAutoStr) ||
- !strcasecmp(str, kAutomaticStr))
+ if (!STRCASECMP(str, kAutoStr) ||
+ !STRCASECMP(str, kAutomaticStr))
return stdAc::fanspeed_t::kAuto;
- else if (!strcasecmp(str, kMinStr) ||
- !strcasecmp(str, kMinimumStr) ||
- !strcasecmp(str, kLowestStr))
+ else if (!STRCASECMP(str, kMinStr) ||
+ !STRCASECMP(str, kMinimumStr) ||
+ !STRCASECMP(str, kLowestStr))
return stdAc::fanspeed_t::kMin;
- else if (!strcasecmp(str, kLowStr) ||
- !strcasecmp(str, kLoStr))
+ else if (!STRCASECMP(str, kLowStr) ||
+ !STRCASECMP(str, kLoStr))
return stdAc::fanspeed_t::kLow;
- else if (!strcasecmp(str, kMedStr) ||
- !strcasecmp(str, kMediumStr) ||
- !strcasecmp(str, kMidStr))
+ else if (!STRCASECMP(str, kMedStr) ||
+ !STRCASECMP(str, kMediumStr) ||
+ !STRCASECMP(str, kMidStr))
return stdAc::fanspeed_t::kMedium;
- else if (!strcasecmp(str, kHighStr) ||
- !strcasecmp(str, kHiStr))
+ else if (!STRCASECMP(str, kHighStr) ||
+ !STRCASECMP(str, kHiStr))
return stdAc::fanspeed_t::kHigh;
- else if (!strcasecmp(str, kMaxStr) ||
- !strcasecmp(str, kMaximumStr) ||
- !strcasecmp(str, kHighestStr))
+ else if (!STRCASECMP(str, kMaxStr) ||
+ !STRCASECMP(str, kMaximumStr) ||
+ !STRCASECMP(str, kHighestStr))
return stdAc::fanspeed_t::kMax;
else
return def;
@@ -3109,36 +3240,36 @@ stdAc::fanspeed_t IRac::strToFanspeed(const char *str,
/// @return The equivalent enum.
stdAc::swingv_t IRac::strToSwingV(const char *str,
const stdAc::swingv_t def) {
- if (!strcasecmp(str, kAutoStr) ||
- !strcasecmp(str, kAutomaticStr) ||
- !strcasecmp(str, kOnStr) ||
- !strcasecmp(str, kSwingStr))
+ if (!STRCASECMP(str, kAutoStr) ||
+ !STRCASECMP(str, kAutomaticStr) ||
+ !STRCASECMP(str, kOnStr) ||
+ !STRCASECMP(str, kSwingStr))
return stdAc::swingv_t::kAuto;
- else if (!strcasecmp(str, kOffStr) ||
- !strcasecmp(str, kStopStr))
+ else if (!STRCASECMP(str, kOffStr) ||
+ !STRCASECMP(str, kStopStr))
return stdAc::swingv_t::kOff;
- else if (!strcasecmp(str, kMinStr) ||
- !strcasecmp(str, kMinimumStr) ||
- !strcasecmp(str, kLowestStr) ||
- !strcasecmp(str, kBottomStr) ||
- !strcasecmp(str, kDownStr))
+ else if (!STRCASECMP(str, kMinStr) ||
+ !STRCASECMP(str, kMinimumStr) ||
+ !STRCASECMP(str, kLowestStr) ||
+ !STRCASECMP(str, kBottomStr) ||
+ !STRCASECMP(str, kDownStr))
return stdAc::swingv_t::kLowest;
- else if (!strcasecmp(str, kLowStr))
+ else if (!STRCASECMP(str, kLowStr))
return stdAc::swingv_t::kLow;
- else if (!strcasecmp(str, kMidStr) ||
- !strcasecmp(str, kMiddleStr) ||
- !strcasecmp(str, kMedStr) ||
- !strcasecmp(str, kMediumStr) ||
- !strcasecmp(str, kCentreStr))
+ else if (!STRCASECMP(str, kMidStr) ||
+ !STRCASECMP(str, kMiddleStr) ||
+ !STRCASECMP(str, kMedStr) ||
+ !STRCASECMP(str, kMediumStr) ||
+ !STRCASECMP(str, kCentreStr))
return stdAc::swingv_t::kMiddle;
- else if (!strcasecmp(str, kHighStr) ||
- !strcasecmp(str, kHiStr))
+ else if (!STRCASECMP(str, kHighStr) ||
+ !STRCASECMP(str, kHiStr))
return stdAc::swingv_t::kHigh;
- else if (!strcasecmp(str, kHighestStr) ||
- !strcasecmp(str, kMaxStr) ||
- !strcasecmp(str, kMaximumStr) ||
- !strcasecmp(str, kTopStr) ||
- !strcasecmp(str, kUpStr))
+ else if (!STRCASECMP(str, kHighestStr) ||
+ !STRCASECMP(str, kMaxStr) ||
+ !STRCASECMP(str, kMaximumStr) ||
+ !STRCASECMP(str, kTopStr) ||
+ !STRCASECMP(str, kUpStr))
return stdAc::swingv_t::kHighest;
else
return def;
@@ -3150,34 +3281,34 @@ stdAc::swingv_t IRac::strToSwingV(const char *str,
/// @return The equivalent enum.
stdAc::swingh_t IRac::strToSwingH(const char *str,
const stdAc::swingh_t def) {
- if (!strcasecmp(str, kAutoStr) ||
- !strcasecmp(str, kAutomaticStr) ||
- !strcasecmp(str, kOnStr) || !strcasecmp(str, kSwingStr))
+ if (!STRCASECMP(str, kAutoStr) ||
+ !STRCASECMP(str, kAutomaticStr) ||
+ !STRCASECMP(str, kOnStr) || !STRCASECMP(str, kSwingStr))
return stdAc::swingh_t::kAuto;
- else if (!strcasecmp(str, kOffStr) ||
- !strcasecmp(str, kStopStr))
+ else if (!STRCASECMP(str, kOffStr) ||
+ !STRCASECMP(str, kStopStr))
return stdAc::swingh_t::kOff;
- else if (!strcasecmp(str, kLeftMaxStr) || // "LeftMax"
- !strcasecmp(str, D_STR_LEFT " " D_STR_MAX) || // "Left Max"
- !strcasecmp(str, D_STR_MAX D_STR_LEFT) || // "MaxLeft"
- !strcasecmp(str, kMaxLeftStr)) // "Max Left"
+ else if (!STRCASECMP(str, kLeftMaxNoSpaceStr) || // "LeftMax"
+ !STRCASECMP(str, kLeftMaxStr) || // "Left Max"
+ !STRCASECMP(str, kMaxLeftNoSpaceStr) || // "MaxLeft"
+ !STRCASECMP(str, kMaxLeftStr)) // "Max Left"
return stdAc::swingh_t::kLeftMax;
- else if (!strcasecmp(str, kLeftStr))
+ else if (!STRCASECMP(str, kLeftStr))
return stdAc::swingh_t::kLeft;
- else if (!strcasecmp(str, kMidStr) ||
- !strcasecmp(str, kMiddleStr) ||
- !strcasecmp(str, kMedStr) ||
- !strcasecmp(str, kMediumStr) ||
- !strcasecmp(str, kCentreStr))
+ else if (!STRCASECMP(str, kMidStr) ||
+ !STRCASECMP(str, kMiddleStr) ||
+ !STRCASECMP(str, kMedStr) ||
+ !STRCASECMP(str, kMediumStr) ||
+ !STRCASECMP(str, kCentreStr))
return stdAc::swingh_t::kMiddle;
- else if (!strcasecmp(str, kRightStr))
+ else if (!STRCASECMP(str, kRightStr))
return stdAc::swingh_t::kRight;
- else if (!strcasecmp(str, kRightMaxStr) || // "RightMax"
- !strcasecmp(str, D_STR_RIGHT " " D_STR_MAX) || // "Right Max"
- !strcasecmp(str, D_STR_MAX D_STR_RIGHT) || // "MaxRight"
- !strcasecmp(str, kMaxRightStr)) // "Max Right"
+ else if (!STRCASECMP(str, kRightMaxNoSpaceStr) || // "RightMax"
+ !STRCASECMP(str, kRightMaxStr) || // "Right Max"
+ !STRCASECMP(str, kMaxRightNoSpaceStr) || // "MaxRight"
+ !STRCASECMP(str, kMaxRightStr)) // "Max Right"
return stdAc::swingh_t::kRightMax;
- else if (!strcasecmp(str, kWideStr))
+ else if (!STRCASECMP(str, kWideStr))
return stdAc::swingh_t::kWide;
else
return def;
@@ -3188,59 +3319,86 @@ stdAc::swingh_t IRac::strToSwingH(const char *str,
/// @param[in] str A Ptr to a C-style string to be converted.
/// @param[in] def The enum to return if no conversion was possible.
/// @return The equivalent enum.
+/// @note After adding a new model you should update modelToStr() too.
int16_t IRac::strToModel(const char *str, const int16_t def) {
// Gree
- if (!strcasecmp(str, "YAW1F")) {
+ if (!STRCASECMP(str, kYaw1fStr)) {
return gree_ac_remote_model_t::YAW1F;
- } else if (!strcasecmp(str, "YBOFB")) {
+ } else if (!STRCASECMP(str, kYbofbStr)) {
return gree_ac_remote_model_t::YBOFB;
+ // Haier models
+ } else if (!STRCASECMP(str, kV9014557AStr)) {
+ return haier_ac176_remote_model_t::V9014557_A;
+ } else if (!STRCASECMP(str, kV9014557BStr)) {
+ return haier_ac176_remote_model_t::V9014557_B;
// HitachiAc1 models
- } else if (!strcasecmp(str, "R-LT0541-HTA-A")) {
+ } else if (!STRCASECMP(str, kRlt0541htaaStr)) {
return hitachi_ac1_remote_model_t::R_LT0541_HTA_A;
- } else if (!strcasecmp(str, "R-LT0541-HTA-B")) {
+ } else if (!STRCASECMP(str, kRlt0541htabStr)) {
return hitachi_ac1_remote_model_t::R_LT0541_HTA_B;
// Fujitsu A/C models
- } else if (!strcasecmp(str, "ARRAH2E")) {
+ } else if (!STRCASECMP(str, kArrah2eStr)) {
return fujitsu_ac_remote_model_t::ARRAH2E;
- } else if (!strcasecmp(str, "ARDB1")) {
+ } else if (!STRCASECMP(str, kArdb1Str)) {
return fujitsu_ac_remote_model_t::ARDB1;
- } else if (!strcasecmp(str, "ARREB1E")) {
+ } else if (!STRCASECMP(str, kArreb1eStr)) {
return fujitsu_ac_remote_model_t::ARREB1E;
- } else if (!strcasecmp(str, "ARJW2")) {
+ } else if (!STRCASECMP(str, kArjw2Str)) {
return fujitsu_ac_remote_model_t::ARJW2;
- } else if (!strcasecmp(str, "ARRY4")) {
+ } else if (!STRCASECMP(str, kArry4Str)) {
return fujitsu_ac_remote_model_t::ARRY4;
+ } else if (!STRCASECMP(str, kArrew4eStr)) {
+ return fujitsu_ac_remote_model_t::ARREW4E;
// LG A/C models
- } else if (!strcasecmp(str, "GE6711AR2853M")) {
+ } else if (!STRCASECMP(str, kGe6711ar2853mStr)) {
return lg_ac_remote_model_t::GE6711AR2853M;
- } else if (!strcasecmp(str, "AKB75215403")) {
+ } else if (!STRCASECMP(str, kAkb75215403Str)) {
return lg_ac_remote_model_t::AKB75215403;
- } else if (!strcasecmp(str, "AKB74955603")) {
+ } else if (!STRCASECMP(str, kAkb74955603Str)) {
return lg_ac_remote_model_t::AKB74955603;
- } else if (!strcasecmp(str, "AKB73757604")) {
+ } else if (!STRCASECMP(str, kAkb73757604Str)) {
return lg_ac_remote_model_t::AKB73757604;
// Panasonic A/C families
- } else if (!strcasecmp(str, "LKE") || !strcasecmp(str, "PANASONICLKE")) {
+ } else if (!STRCASECMP(str, kLkeStr) ||
+ !STRCASECMP(str, kPanasonicLkeStr)) {
return panasonic_ac_remote_model_t::kPanasonicLke;
- } else if (!strcasecmp(str, "NKE") || !strcasecmp(str, "PANASONICNKE")) {
+ } else if (!STRCASECMP(str, kNkeStr) ||
+ !STRCASECMP(str, kPanasonicNkeStr)) {
return panasonic_ac_remote_model_t::kPanasonicNke;
- } else if (!strcasecmp(str, "DKE") || !strcasecmp(str, "PANASONICDKE") ||
- !strcasecmp(str, "PKR") || !strcasecmp(str, "PANASONICPKR")) {
+ } else if (!STRCASECMP(str, kDkeStr) ||
+ !STRCASECMP(str, kPanasonicDkeStr) ||
+ !STRCASECMP(str, kPkrStr) ||
+ !STRCASECMP(str, kPanasonicPkrStr)) {
return panasonic_ac_remote_model_t::kPanasonicDke;
- } else if (!strcasecmp(str, "JKE") || !strcasecmp(str, "PANASONICJKE")) {
+ } else if (!STRCASECMP(str, kJkeStr) ||
+ !STRCASECMP(str, kPanasonicJkeStr)) {
return panasonic_ac_remote_model_t::kPanasonicJke;
- } else if (!strcasecmp(str, "CKP") || !strcasecmp(str, "PANASONICCKP")) {
+ } else if (!STRCASECMP(str, kCkpStr) ||
+ !STRCASECMP(str, kPanasonicCkpStr)) {
return panasonic_ac_remote_model_t::kPanasonicCkp;
- } else if (!strcasecmp(str, "RKR") || !strcasecmp(str, "PANASONICRKR")) {
+ } else if (!STRCASECMP(str, kRkrStr) ||
+ !STRCASECMP(str, kPanasonicRkrStr)) {
return panasonic_ac_remote_model_t::kPanasonicRkr;
+ // Sharp A/C Models
+ } else if (!STRCASECMP(str, kA907Str)) {
+ return sharp_ac_remote_model_t::A907;
+ } else if (!STRCASECMP(str, kA705Str)) {
+ return sharp_ac_remote_model_t::A705;
+ } else if (!STRCASECMP(str, kA903Str)) {
+ return sharp_ac_remote_model_t::A903;
+ // TCL A/C Models
+ } else if (!STRCASECMP(str, kTac09chsdStr)) {
+ return tcl_ac_remote_model_t::TAC09CHSD;
+ } else if (!STRCASECMP(str, kGz055be1Str)) {
+ return tcl_ac_remote_model_t::GZ055BE1;
// Voltas A/C models
- } else if (!strcasecmp(str, "122LZF")) {
+ } else if (!STRCASECMP(str, k122lzfStr)) {
return voltas_ac_remote_model_t::kVoltas122LZF;
// Whirlpool A/C models
- } else if (!strcasecmp(str, "DG11J13A") || !strcasecmp(str, "DG11J104") ||
- !strcasecmp(str, "DG11J1-04")) {
+ } else if (!STRCASECMP(str, kDg11j13aStr) ||
+ !STRCASECMP(str, kDg11j104Str)) {
return whirlpool_ac_remote_model_t::DG11J13A;
- } else if (!strcasecmp(str, "DG11J191")) {
+ } else if (!STRCASECMP(str, kDg11j191Str)) {
return whirlpool_ac_remote_model_t::DG11J191;
} else {
int16_t number = atoi(str);
@@ -3256,15 +3414,15 @@ int16_t IRac::strToModel(const char *str, const int16_t def) {
/// @param[in] def The boolean value to return if no conversion was possible.
/// @return The equivalent boolean value.
bool IRac::strToBool(const char *str, const bool def) {
- if (!strcasecmp(str, kOnStr) ||
- !strcasecmp(str, "1") ||
- !strcasecmp(str, kYesStr) ||
- !strcasecmp(str, kTrueStr))
+ if (!STRCASECMP(str, kOnStr) ||
+ !STRCASECMP(str, k1Str) ||
+ !STRCASECMP(str, kYesStr) ||
+ !STRCASECMP(str, kTrueStr))
return true;
- else if (!strcasecmp(str, kOffStr) ||
- !strcasecmp(str, "0") ||
- !strcasecmp(str, kNoStr) ||
- !strcasecmp(str, kFalseStr))
+ else if (!STRCASECMP(str, kOffStr) ||
+ !STRCASECMP(str, k0Str) ||
+ !STRCASECMP(str, kNoStr) ||
+ !STRCASECMP(str, kFalseStr))
return false;
else
return def;
@@ -3279,23 +3437,17 @@ String IRac::boolToString(const bool value) {
/// Convert the supplied operation mode into the appropriate String.
/// @param[in] mode The enum to be converted.
+/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output.
/// @return The equivalent String for the locale.
-String IRac::opmodeToString(const stdAc::opmode_t mode) {
+String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) {
switch (mode) {
- case stdAc::opmode_t::kOff:
- return kOffStr;
- case stdAc::opmode_t::kAuto:
- return kAutoStr;
- case stdAc::opmode_t::kCool:
- return kCoolStr;
- case stdAc::opmode_t::kHeat:
- return kHeatStr;
- case stdAc::opmode_t::kDry:
- return kDryStr;
- case stdAc::opmode_t::kFan:
- return kFanOnlyStr;
- default:
- return kUnknownStr;
+ case stdAc::opmode_t::kOff: return kOffStr;
+ case stdAc::opmode_t::kAuto: return kAutoStr;
+ case stdAc::opmode_t::kCool: return kCoolStr;
+ case stdAc::opmode_t::kHeat: return kHeatStr;
+ case stdAc::opmode_t::kDry: return kDryStr;
+ case stdAc::opmode_t::kFan: return ha ? kFan_OnlyStr : kFanStr;
+ default: return kUnknownStr;
}
}
@@ -3304,20 +3456,13 @@ String IRac::opmodeToString(const stdAc::opmode_t mode) {
/// @return The equivalent String for the locale.
String IRac::fanspeedToString(const stdAc::fanspeed_t speed) {
switch (speed) {
- case stdAc::fanspeed_t::kAuto:
- return kAutoStr;
- case stdAc::fanspeed_t::kMax:
- return kMaxStr;
- case stdAc::fanspeed_t::kHigh:
- return kHighStr;
- case stdAc::fanspeed_t::kMedium:
- return kMediumStr;
- case stdAc::fanspeed_t::kLow:
- return kLowStr;
- case stdAc::fanspeed_t::kMin:
- return kMinStr;
- default:
- return kUnknownStr;
+ case stdAc::fanspeed_t::kAuto: return kAutoStr;
+ case stdAc::fanspeed_t::kMax: return kMaxStr;
+ case stdAc::fanspeed_t::kHigh: return kHighStr;
+ case stdAc::fanspeed_t::kMedium: return kMediumStr;
+ case stdAc::fanspeed_t::kLow: return kLowStr;
+ case stdAc::fanspeed_t::kMin: return kMinStr;
+ default: return kUnknownStr;
}
}
@@ -3326,22 +3471,14 @@ String IRac::fanspeedToString(const stdAc::fanspeed_t speed) {
/// @return The equivalent String for the locale.
String IRac::swingvToString(const stdAc::swingv_t swingv) {
switch (swingv) {
- case stdAc::swingv_t::kOff:
- return kOffStr;
- case stdAc::swingv_t::kAuto:
- return kAutoStr;
- case stdAc::swingv_t::kHighest:
- return kHighestStr;
- case stdAc::swingv_t::kHigh:
- return kHighStr;
- case stdAc::swingv_t::kMiddle:
- return kMiddleStr;
- case stdAc::swingv_t::kLow:
- return kLowStr;
- case stdAc::swingv_t::kLowest:
- return kLowestStr;
- default:
- return kUnknownStr;
+ case stdAc::swingv_t::kOff: return kOffStr;
+ case stdAc::swingv_t::kAuto: return kAutoStr;
+ case stdAc::swingv_t::kHighest: return kHighestStr;
+ case stdAc::swingv_t::kHigh: return kHighStr;
+ case stdAc::swingv_t::kMiddle: return kMiddleStr;
+ case stdAc::swingv_t::kLow: return kLowStr;
+ case stdAc::swingv_t::kLowest: return kLowestStr;
+ default: return kUnknownStr;
}
}
@@ -3350,24 +3487,15 @@ String IRac::swingvToString(const stdAc::swingv_t swingv) {
/// @return The equivalent String for the locale.
String IRac::swinghToString(const stdAc::swingh_t swingh) {
switch (swingh) {
- case stdAc::swingh_t::kOff:
- return kOffStr;
- case stdAc::swingh_t::kAuto:
- return kAutoStr;
- case stdAc::swingh_t::kLeftMax:
- return kLeftMaxStr;
- case stdAc::swingh_t::kLeft:
- return kLeftStr;
- case stdAc::swingh_t::kMiddle:
- return kMiddleStr;
- case stdAc::swingh_t::kRight:
- return kRightStr;
- case stdAc::swingh_t::kRightMax:
- return kRightMaxStr;
- case stdAc::swingh_t::kWide:
- return kWideStr;
- default:
- return kUnknownStr;
+ case stdAc::swingh_t::kOff: return kOffStr;
+ case stdAc::swingh_t::kAuto: return kAutoStr;
+ case stdAc::swingh_t::kLeftMax: return kLeftMaxStr;
+ case stdAc::swingh_t::kLeft: return kLeftStr;
+ case stdAc::swingh_t::kMiddle: return kMiddleStr;
+ case stdAc::swingh_t::kRight: return kRightStr;
+ case stdAc::swingh_t::kRightMax: return kRightMaxStr;
+ case stdAc::swingh_t::kWide: return kWideStr;
+ default: return kUnknownStr;
}
}
@@ -3407,6 +3535,21 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_CARRIER_AC64
+#if DECODE_COOLIX
+ case decode_type_t::COOLIX: {
+ IRCoolixAC ac(kGpioUnused);
+ ac.on();
+ ac.setRaw(result->value); // Coolix uses value instead of state.
+ return ac.toString();
+ }
+#endif // DECODE_COOLIX
+#if DECODE_CORONA_AC
+ case decode_type_t::CORONA_AC: {
+ IRCoronaAc ac(kGpioUnused);
+ ac.setRaw(result->state, result->bits / 8);
+ return ac.toString();
+ }
+#endif // DECODE_CORONA_AC
#if DECODE_DAIKIN
case decode_type_t::DAIKIN: {
IRDaikinESP ac(kGpioUnused);
@@ -3494,6 +3637,69 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_FUJITSU_AC
+#if DECODE_GOODWEATHER
+ case decode_type_t::GOODWEATHER: {
+ IRGoodweatherAc ac(kGpioUnused);
+ ac.setRaw(result->value); // Goodweather uses value instead of state.
+ return ac.toString();
+ }
+#endif // DECODE_GOODWEATHER
+#if DECODE_GREE
+ case decode_type_t::GREE: {
+ IRGreeAC ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_GREE
+#if DECODE_HAIER_AC
+ case decode_type_t::HAIER_AC: {
+ IRHaierAC ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_HAIER_AC
+#if DECODE_HAIER_AC176
+ case decode_type_t::HAIER_AC176: {
+ IRHaierAC176 ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_HAIER_AC176
+#if DECODE_HAIER_AC_YRW02
+ case decode_type_t::HAIER_AC_YRW02: {
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_HAIER_AC_YRW02
+#if DECODE_HITACHI_AC
+ case decode_type_t::HITACHI_AC: {
+ IRHitachiAc ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_HITACHI_AC
+#if DECODE_HITACHI_AC1
+ case decode_type_t::HITACHI_AC1: {
+ IRHitachiAc1 ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_HITACHI_AC1
+#if DECODE_HITACHI_AC344
+ case decode_type_t::HITACHI_AC344: {
+ IRHitachiAc344 ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_HITACHI_AC344
+#if DECODE_HITACHI_AC424
+ case decode_type_t::HITACHI_AC424: {
+ IRHitachiAc424 ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_HITACHI_AC424
#if DECODE_KELON
case decode_type_t::KELON: {
IRKelonAc ac(kGpioUnused);
@@ -3508,6 +3714,28 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_KELVINATOR
+#if DECODE_LG
+ case decode_type_t::LG:
+ case decode_type_t::LG2: {
+ IRLgAc ac(kGpioUnused);
+ ac.setRaw(result->value, result->decode_type); // Use value, not state.
+ return ac.isValidLgAc() ? ac.toString() : "";
+ }
+#endif // DECODE_LG
+#if DECODE_MIDEA
+ case decode_type_t::MIDEA: {
+ IRMideaAC ac(kGpioUnused);
+ ac.setRaw(result->value); // Midea uses value instead of state.
+ return ac.toString();
+ }
+#endif // DECODE_MIDEA
+#if DECODE_MIRAGE
+ case decode_type_t::MIRAGE: {
+ IRMirageAc ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_MIRAGE
#if DECODE_MITSUBISHI_AC
case decode_type_t::MITSUBISHI_AC: {
IRMitsubishiAC ac(kGpioUnused);
@@ -3548,76 +3776,33 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_NEOCLIMA
-#if DECODE_TOSHIBA_AC
- case decode_type_t::TOSHIBA_AC: {
- IRToshibaAC ac(kGpioUnused);
- ac.setRaw(result->state, result->bits / 8);
- return ac.toString();
+#if DECODE_PANASONIC_AC
+ case decode_type_t::PANASONIC_AC: {
+ if (result->bits > kPanasonicAcShortBits) {
+ IRPanasonicAc ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+ return "";
}
-#endif // DECODE_TOSHIBA_AC
-#if DECODE_TROTEC
- case decode_type_t::TROTEC: {
- IRTrotecESP ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
+#endif // DECODE_PANASONIC_AC
+#if DECODE_PANASONIC_AC32
+ case decode_type_t::PANASONIC_AC32: {
+ if (result->bits >= kPanasonicAc32Bits) {
+ IRPanasonicAc32 ac(kGpioUnused);
+ ac.setRaw(result->value); // Uses value instead of state.
+ return ac.toString();
+ }
+ return "";
}
-#endif // DECODE_TROTEC
-#if DECODE_TROTEC_3550
- case decode_type_t::TROTEC_3550: {
- IRTrotec3550 ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_TROTEC_3550
-#if DECODE_TRUMA
- case decode_type_t::TRUMA: {
- IRTrumaAc ac(kGpioUnused);
- ac.setRaw(result->value); // Truma uses value instead of state.
- return ac.toString();
- }
-#endif // DECODE_TRUMA
-#if DECODE_GOODWEATHER
- case decode_type_t::GOODWEATHER: {
- IRGoodweatherAc ac(kGpioUnused);
- ac.setRaw(result->value); // Goodweather uses value instead of state.
- return ac.toString();
- }
-#endif // DECODE_GOODWEATHER
-#if DECODE_GREE
- case decode_type_t::GREE: {
- IRGreeAC ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_GREE
-#if DECODE_MIDEA
- case decode_type_t::MIDEA: {
- IRMideaAC ac(kGpioUnused);
- ac.setRaw(result->value); // Midea uses value instead of state.
- return ac.toString();
- }
-#endif // DECODE_MIDEA
-#if DECODE_HAIER_AC
- case decode_type_t::HAIER_AC: {
- IRHaierAC ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_HAIER_AC
-#if DECODE_HAIER_AC176
- case decode_type_t::HAIER_AC176: {
- IRHaierAC176 ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_HAIER_AC176
-#if DECODE_HAIER_AC_YRW02
- case decode_type_t::HAIER_AC_YRW02: {
- IRHaierACYRW02 ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_HAIER_AC_YRW02
+#endif // DECODE_PANASONIC_AC
+#if DECODE_RHOSS
+ case decode_type_t::RHOSS: {
+ IRRhossAc ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_RHOSS
#if DECODE_SAMSUNG_AC
case decode_type_t::SAMSUNG_AC: {
IRSamsungAc ac(kGpioUnused);
@@ -3646,83 +3831,14 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_SHARP_AC
-#if DECODE_COOLIX
- case decode_type_t::COOLIX: {
- IRCoolixAC ac(kGpioUnused);
- ac.on();
- ac.setRaw(result->value); // Coolix uses value instead of state.
- return ac.toString();
- }
-#endif // DECODE_COOLIX
-#if DECODE_CORONA_AC
- case decode_type_t::CORONA_AC: {
- IRCoronaAc ac(kGpioUnused);
- ac.setRaw(result->state, result->bits / 8);
- return ac.toString();
- }
-#endif // DECODE_CORONA_AC
-#if DECODE_PANASONIC_AC
- case decode_type_t::PANASONIC_AC: {
- if (result->bits > kPanasonicAcShortBits) {
- IRPanasonicAc ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
- return "";
- }
-#endif // DECODE_PANASONIC_AC
-#if DECODE_PANASONIC_AC32
- case decode_type_t::PANASONIC_AC32: {
- if (result->bits >= kPanasonicAc32Bits) {
- IRPanasonicAc32 ac(kGpioUnused);
- ac.setRaw(result->value); // Uses value instead of state.
- return ac.toString();
- }
- return "";
- }
-#endif // DECODE_PANASONIC_AC
-#if DECODE_HITACHI_AC
- case decode_type_t::HITACHI_AC: {
- IRHitachiAc ac(kGpioUnused);
+#if (DECODE_TCL112AC || DECODE_TEKNOPOINT)
+ case decode_type_t::TCL112AC:
+ case decode_type_t::TEKNOPOINT: {
+ IRTcl112Ac ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
-#endif // DECODE_HITACHI_AC
-#if DECODE_HITACHI_AC1
- case decode_type_t::HITACHI_AC1: {
- IRHitachiAc1 ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_HITACHI_AC1
-#if DECODE_HITACHI_AC344
- case decode_type_t::HITACHI_AC344: {
- IRHitachiAc344 ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_HITACHI_AC344
-#if DECODE_HITACHI_AC424
- case decode_type_t::HITACHI_AC424: {
- IRHitachiAc424 ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_HITACHI_AC424
-#if DECODE_WHIRLPOOL_AC
- case decode_type_t::WHIRLPOOL_AC: {
- IRWhirlpoolAc ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_WHIRLPOOL_AC
-#if DECODE_VESTEL_AC
- case decode_type_t::VESTEL_AC: {
- IRVestelAc ac(kGpioUnused);
- ac.setRaw(result->value); // Like Coolix, use value instead of state.
- return ac.toString();
- }
-#endif // DECODE_VESTEL_AC
+#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT)
#if DECODE_TECHNIBEL_AC
case decode_type_t::TECHNIBEL_AC: {
IRTechnibelAc ac(kGpioUnused);
@@ -3730,13 +3846,6 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_TECHNIBEL_AC
-#if DECODE_VOLTAS
- case decode_type_t::VOLTAS: {
- IRVoltas ac(kGpioUnused);
- ac.setRaw(result->state);
- return ac.toString();
- }
-#endif // DECODE_VOLTAS
#if DECODE_TECO
case decode_type_t::TECO: {
IRTecoAc ac(kGpioUnused);
@@ -3744,21 +3853,13 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_TECO
-#if DECODE_TCL112AC
- case decode_type_t::TCL112AC: {
- IRTcl112Ac ac(kGpioUnused);
- ac.setRaw(result->state);
+#if DECODE_TOSHIBA_AC
+ case decode_type_t::TOSHIBA_AC: {
+ IRToshibaAC ac(kGpioUnused);
+ ac.setRaw(result->state, result->bits / 8);
return ac.toString();
}
-#endif // DECODE_TCL112AC
-#if DECODE_LG
- case decode_type_t::LG:
- case decode_type_t::LG2: {
- IRLgAc ac(kGpioUnused);
- ac.setRaw(result->value, result->decode_type); // Use value, not state.
- return ac.isValidLgAc() ? ac.toString() : "";
- }
-#endif // DECODE_LG
+#endif // DECODE_TOSHIBA_AC
#if DECODE_TRANSCOLD
case decode_type_t::TRANSCOLD: {
IRTranscoldAc ac(kGpioUnused);
@@ -3767,6 +3868,48 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_TRANSCOLD
+#if DECODE_TROTEC
+ case decode_type_t::TROTEC: {
+ IRTrotecESP ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_TROTEC
+#if DECODE_TROTEC_3550
+ case decode_type_t::TROTEC_3550: {
+ IRTrotec3550 ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_TROTEC_3550
+#if DECODE_TRUMA
+ case decode_type_t::TRUMA: {
+ IRTrumaAc ac(kGpioUnused);
+ ac.setRaw(result->value); // Truma uses value instead of state.
+ return ac.toString();
+ }
+#endif // DECODE_TRUMA
+#if DECODE_VESTEL_AC
+ case decode_type_t::VESTEL_AC: {
+ IRVestelAc ac(kGpioUnused);
+ ac.setRaw(result->value); // Like Coolix, use value instead of state.
+ return ac.toString();
+ }
+#endif // DECODE_VESTEL_AC
+#if DECODE_VOLTAS
+ case decode_type_t::VOLTAS: {
+ IRVoltas ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_VOLTAS
+#if DECODE_WHIRLPOOL_AC
+ case decode_type_t::WHIRLPOOL_AC: {
+ IRWhirlpoolAc ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_WHIRLPOOL_AC
default:
return "";
}
@@ -3812,6 +3955,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_ARGO
+#if DECODE_CARRIER_AC64
+ case decode_type_t::CARRIER_AC64: {
+ IRCarrierAc64 ac(kGpioUnused);
+ ac.setRaw(decode->value); // Uses value instead of state.
+ *result = ac.toCommon();
+ break;
+ }
+#endif // DECODE_CARRIER_AC64
#if DECODE_COOLIX
case decode_type_t::COOLIX: {
IRCoolixAC ac(kGpioUnused);
@@ -3828,14 +3979,6 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_CARRIER_AC64
-#if DECODE_CARRIER_AC64
- case decode_type_t::CARRIER_AC64: {
- IRCarrierAc64 ac(kGpioUnused);
- ac.setRaw(decode->value); // Uses value instead of state.
- *result = ac.toCommon();
- break;
- }
-#endif // DECODE_CARRIER_AC64
#if DECODE_DAIKIN
case decode_type_t::DAIKIN: {
IRDaikinESP ac(kGpioUnused);
@@ -4042,6 +4185,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_MIDEA
+#if DECODE_MIRAGE
+ case decode_type_t::MIRAGE: {
+ IRMirageAc ac(kGpioUnused);
+ ac.setRaw(decode->state);
+ *result = ac.toCommon();
+ break;
+ }
+#endif // DECODE_MIRAGE
#if DECODE_MITSUBISHI_AC
case decode_type_t::MITSUBISHI_AC: {
IRMitsubishiAC ac(kGpioUnused);
@@ -4108,6 +4259,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_PANASONIC_AC32
+#if DECODE_RHOSS
+ case decode_type_t::RHOSS: {
+ IRRhossAc ac(kGpioUnused);
+ ac.setRaw(decode->state);
+ *result = ac.toCommon();
+ break;
+ }
+#endif // DECODE_RHOSS
#if DECODE_SAMSUNG_AC
case decode_type_t::SAMSUNG_AC: {
IRSamsungAc ac(kGpioUnused);
@@ -4136,18 +4295,22 @@ namespace IRAcUtils {
case decode_type_t::SHARP_AC: {
IRSharpAc ac(kGpioUnused);
ac.setRaw(decode->state);
- *result = ac.toCommon();
- break;
- }
-#endif // DECODE_SHARP_AC
-#if DECODE_TCL112AC
- case decode_type_t::TCL112AC: {
- IRTcl112Ac ac(kGpioUnused);
- ac.setRaw(decode->state);
*result = ac.toCommon(prev);
break;
}
-#endif // DECODE_TCL112AC
+#endif // DECODE_SHARP_AC
+#if (DECODE_TCL112AC || DECODE_TEKNOPOINT)
+ case decode_type_t::TCL112AC:
+ case decode_type_t::TEKNOPOINT: {
+ IRTcl112Ac ac(kGpioUnused);
+ ac.setRaw(decode->state);
+ *result = ac.toCommon(prev);
+ // Teknopoint uses the TCL protocol, but with a different model number.
+ // Just keep the original protocol type ... for now.
+ result->protocol = decode->decode_type;
+ break;
+ }
+#endif // (DECODE_TCL112AC || DECODE_TEKNOPOINT)
#if DECODE_TECHNIBEL_AC
case decode_type_t::TECHNIBEL_AC: {
IRTechnibelAc ac(kGpioUnused);
@@ -4172,6 +4335,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_TOSHIBA_AC
+#if DECODE_TRANSCOLD
+ case decode_type_t::TRANSCOLD: {
+ IRTranscoldAc ac(kGpioUnused);
+ ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state.
+ *result = ac.toCommon(prev);
+ break;
+ }
+#endif // DECODE_TRANSCOLD
#if DECODE_TROTEC
case decode_type_t::TROTEC: {
IRTrotecESP ac(kGpioUnused);
@@ -4220,14 +4391,6 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_WHIRLPOOL_AC
-#if DECODE_TRANSCOLD
- case decode_type_t::TRANSCOLD: {
- IRTranscoldAc ac(kGpioUnused);
- ac.setRaw(decode->value); // TRANSCOLD Uses value instead of state.
- *result = ac.toCommon(prev);
- break;
- }
-#endif // DECODE_TRANSCOLD
default:
return false;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h
index 573e7ae7e..afa4bcee0 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h
@@ -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,
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp
index 9c7519cbd..4970dcf0b 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp
@@ -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)) {
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h
index 1a883d509..f4932b5a6 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h
@@ -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_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h
index 35b45d958..09a3011ce 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h
@@ -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)
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp
index 4814bd431..e20b97045 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp
@@ -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);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h
index c20fc64bc..d55ce0238 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.cpp
index a7bd7de29..a580bff9f 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.cpp
@@ -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
#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);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h
index 55840688d..ee8ea5934 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h
@@ -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(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_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp
index f9892d2a4..d3a83c507 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp
@@ -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(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,21 +117,20 @@ 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(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;
+
+ return decode_type_t::UNKNOWN;
}
/// Convert a protocol type (enum etc) to a human readable string.
@@ -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++) {
- if (i == protocol) {
- result = ptr;
- break;
+ auto *ptr = reinterpret_cast(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 = 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,75 +586,101 @@ 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");
- default: return kUnknownStr;
+ 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");
- default: return kUnknownStr;
+ 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");
- default: return kUnknownStr;
+ 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");
- default: return kUnknownStr;
+ 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");
- default: return kUnknownStr;
+ 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");
- default: return kUnknownStr;
+ 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");
- default: return kUnknownStr;
+ case whirlpool_ac_remote_model_t::DG11J13A: return kDg11j13aStr;
+ case whirlpool_ac_remote_model_t::DG11J191: return kDg11j191Str;
+ default: return kUnknownStr;
}
break;
default: return kUnknownStr;
@@ -690,8 +755,8 @@ namespace irutils {
if (mode == automatic) result += kAutoStr;
else if (mode == cool) result += kCoolStr;
else if (mode == heat) result += kHeatStr;
- else if (mode == dry) result += kDryStr;
- else if (mode == fan) result += kFanStr;
+ else if (mode == dry) result += kDryStr;
+ else if (mode == fan) result += kFanStr;
else
result += kUnknownStr;
return result + ')';
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h
index fdda6d7ae..61fe8b269 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h
@@ -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) \
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Airton.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Airton.cpp
new file mode 100644
index 000000000..507894f37
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Airton.cpp
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Arris.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Arris.cpp
new file mode 100644
index 000000000..5b39808c8
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Arris.cpp
@@ -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(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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp
index 1c3a40a62..f5cacf0e7 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp
@@ -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;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp
index fd70e4bd6..b4d670b88 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018, 2019 David Conran
+// Copyright 2018-2021 David Conran
/// @file
/// @brief Support for Electra A/C protocols.
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp
@@ -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,19 +388,26 @@ stdAc::state_t IRElectraAc::toCommon(void) const {
/// @return A human readable string.
String IRElectraAc::toString(void) const {
String result = "";
- result.reserve(130); // Reserve some heap for the string to reduce fragging.
- result += addBoolToString(_.Power, kPowerStr, false);
- result += addModeToString(_.Mode, kElectraAcAuto, kElectraAcCool,
- kElectraAcHeat, kElectraAcDry, kElectraAcFan);
- result += addTempToString(getTemp());
- result += addFanToString(_.Fan, kElectraAcFanHigh, kElectraAcFanLow,
- kElectraAcFanAuto, kElectraAcFanAuto,
- kElectraAcFanMed);
- result += addBoolToString(getSwingV(), kSwingVStr);
- result += addBoolToString(getSwingH(), kSwingHStr);
- result += addLabeledString(getLightToggle() ? kToggleStr : "-", kLightStr);
- result += addBoolToString(_.Clean, kCleanStr);
- result += addBoolToString(_.Turbo, kTurboStr);
+ result.reserve(160); // Reserve some heap for the string to reduce fragging.
+ if (!_.SensorUpdate) {
+ result += addBoolToString(_.Power, kPowerStr, false);
+ result += addModeToString(_.Mode, kElectraAcAuto, kElectraAcCool,
+ kElectraAcHeat, kElectraAcDry, kElectraAcFan);
+ result += addTempToString(getTemp());
+ result += addFanToString(_.Fan, kElectraAcFanHigh, kElectraAcFanLow,
+ kElectraAcFanAuto, kElectraAcFanAuto,
+ kElectraAcFanMed);
+ result += addBoolToString(getSwingV(), kSwingVStr);
+ result += addBoolToString(getSwingH(), kSwingHStr);
+ result += addToggleToString(getLightToggle(), kLightStr);
+ result += addBoolToString(_.Clean, kCleanStr);
+ result += addBoolToString(_.Turbo, kTurboStr);
+ result += addBoolToString(_.IFeel, kIFeelStr);
+ }
+ if (_.IFeel || _.SensorUpdate) {
+ result += addIntToString(getSensorTemp(), kSensorTempStr, !_.SensorUpdate);
+ result += 'C';
+ }
return result;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h
index 886a92c56..8fd4ee182 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h
@@ -1,4 +1,4 @@
-// Copyright 2019 David Conran
+// Copyright 2019-2021 David Conran
/// @file
/// @brief Support for Electra A/C protocols.
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp
@@ -9,6 +9,10 @@
// Brand: Electra, Model: Classic INV 17 / AXW12DCS A/C
// Brand: Electra, Model: YKR-M/003E remote
// Brand: Frigidaire, Model: FGPC102AB1 A/C
+// Brand: Subtropic, Model: SUB-07HN1_18Y A/C
+// Brand: Subtropic, Model: YKR-H/102E remote
+// Brand: Centek, Model: SCT-65Q09 A/C
+// Brand: Centek, Model: YKR-P/002E remote
#ifndef IR_ELECTRA_H_
#define IR_ELECTRA_H_
@@ -37,7 +41,9 @@ union ElectraProtocol {
uint8_t :5;
uint8_t SwingH :3;
// Byte 3
- uint8_t :8;
+ uint8_t :6;
+ uint8_t SensorUpdate :1;
+ uint8_t :1;
// Byte 4
uint8_t :5;
uint8_t Fan :3;
@@ -46,10 +52,12 @@ union ElectraProtocol {
uint8_t Turbo :1;
uint8_t :1;
// Byte 6
- uint8_t :5;
+ uint8_t :3;
+ uint8_t IFeel :1;
+ uint8_t :1;
uint8_t Mode :3;
// Byte 7
- uint8_t :8;
+ uint8_t SensorTemp :8;
// Byte 8
uint8_t :8;
// Byte 9
@@ -93,6 +101,11 @@ const uint8_t kElectraAcLightToggleMask = 0x11;
// and known OFF values of 0x08 (0b00001000) & 0x05 (0x00000101)
const uint8_t kElectraAcLightToggleOff = 0x08;
+// Re: Byte[7]. Or Delta == 0xA and Temperature are stored in last 6 bits,
+// and bit 7 stores Unknown flag
+const uint8_t kElectraAcSensorTempDelta = 0x4A;
+const uint8_t kElectraAcSensorMinTemp = 0; // 0C
+const uint8_t kElectraAcSensorMaxTemp = 50; // 50C
// Classes
/// Class for handling detailed Electra A/C messages.
@@ -130,6 +143,12 @@ class IRElectraAc {
bool getLightToggle(void) const;
void setTurbo(const bool on);
bool getTurbo(void) const;
+ void setIFeel(const bool on);
+ bool getIFeel(void) const;
+ void setSensorUpdate(const bool on);
+ bool getSensorUpdate(void) const;
+ void setSensorTemp(const uint8_t temp);
+ uint8_t getSensorTemp(void) const;
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kElectraAcStateLength);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Epson.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Epson.cpp
index 4f92704bc..a92beef4f 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Epson.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Epson.cpp
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Goodweather.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Goodweather.cpp
index c86797ad5..1d6918e7f 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Goodweather.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Goodweather.cpp
@@ -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) {
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp
index 1d1371b2d..3c3a8583e 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp
@@ -35,6 +35,7 @@ using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addModelToString;
using irutils::addFanToString;
+using irutils::addSwingHToString;
using irutils::addTempToString;
using irutils::minsToString;
@@ -220,15 +221,11 @@ bool IRGreeAC::getPower(void) const {
/// Set the default temperature units to use.
/// @param[in] on Use Fahrenheit as the units.
/// true is Fahrenheit, false is Celsius.
-void IRGreeAC::setUseFahrenheit(const bool on) {
- _.UseFahrenheit = on;
-}
+void IRGreeAC::setUseFahrenheit(const bool on) { _.UseFahrenheit = on; }
/// Get the default temperature units in use.
/// @return true is Fahrenheit, false is Celsius.
-bool IRGreeAC::getUseFahrenheit(void) const {
- return _.UseFahrenheit;
-}
+bool IRGreeAC::getUseFahrenheit(void) const { return _.UseFahrenheit; }
/// Set the temp. in degrees
/// @param[in] temp Desired temperature in Degrees.
@@ -281,9 +278,7 @@ void IRGreeAC::setFan(const uint8_t speed) {
/// Get the current fan speed setting.
/// @return The current fan speed.
-uint8_t IRGreeAC::getFan(void) const {
- return _.Fan;
-}
+uint8_t IRGreeAC::getFan(void) const { return _.Fan; }
/// Set the operating mode of the A/C.
/// @param[in] new_mode The desired operating mode.
@@ -305,81 +300,63 @@ void IRGreeAC::setMode(const uint8_t new_mode) {
/// Get the operating mode setting of the A/C.
/// @return The current operating mode setting.
-uint8_t IRGreeAC::getMode(void) const {
- return _.Mode;
-}
+uint8_t IRGreeAC::getMode(void) const { return _.Mode; }
/// Set the Light (LED) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRGreeAC::setLight(const bool on) {
- _.Light = on;
-}
+void IRGreeAC::setLight(const bool on) { _.Light = on; }
/// Get the Light (LED) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getLight(void) const {
- return _.Light;
-}
+bool IRGreeAC::getLight(void) const { return _.Light; }
/// Set the IFeel setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRGreeAC::setIFeel(const bool on) {
- _.IFeel = on;
-}
+void IRGreeAC::setIFeel(const bool on) { _.IFeel = on; }
/// Get the IFeel setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getIFeel(void) const {
- return _.IFeel;
-}
+bool IRGreeAC::getIFeel(void) const { return _.IFeel; }
/// Set the Wifi (enabled) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRGreeAC::setWiFi(const bool on) {
- _.WiFi = on;
-}
+void IRGreeAC::setWiFi(const bool on) { _.WiFi = on; }
/// Get the Wifi (enabled) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getWiFi(void) const {
- return _.WiFi;
-}
+bool IRGreeAC::getWiFi(void) const { return _.WiFi; }
/// Set the XFan (Mould) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRGreeAC::setXFan(const bool on) {
- _.Xfan = on;
-}
+void IRGreeAC::setXFan(const bool on) { _.Xfan = on; }
/// Get the XFan (Mould) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getXFan(void) const {
- return _.Xfan;
-}
+bool IRGreeAC::getXFan(void) const { return _.Xfan; }
/// Set the Sleep setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRGreeAC::setSleep(const bool on) {
- _.Sleep = on;
-}
+void IRGreeAC::setSleep(const bool on) { _.Sleep = on; }
/// Get the Sleep setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getSleep(void) const {
- return _.Sleep;
-}
+bool IRGreeAC::getSleep(void) const { return _.Sleep; }
/// Set the Turbo setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRGreeAC::setTurbo(const bool on) {
- _.Turbo = on;
-}
+void IRGreeAC::setTurbo(const bool on) { _.Turbo = on; }
/// Get the Turbo setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getTurbo(void) const {
- return _.Turbo;
-}
+bool IRGreeAC::getTurbo(void) const { return _.Turbo; }
+
+/// Set the Econo setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRGreeAC::setEcono(const bool on) { _.Econo = on; }
+
+/// Get the Econo setting of the A/C.
+/// @return true, the setting is on. false, the setting is off.
+bool IRGreeAC::getEcono(void) const { return _.Econo; }
/// Set the Vertical Swing mode of the A/C.
/// @param[in] automatic Do we use the automatic setting?
@@ -409,32 +386,37 @@ void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) {
new_position = kGreeSwingAuto;
}
}
- _.Swing = new_position;
+ _.SwingV = new_position;
}
/// Get the Vertical Swing Automatic mode setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getSwingVerticalAuto(void) const {
- return _.SwingAuto;
-}
+bool IRGreeAC::getSwingVerticalAuto(void) const { return _.SwingAuto; }
/// Get the Vertical Swing position setting of the A/C.
/// @return The native position/mode.
-uint8_t IRGreeAC::getSwingVerticalPosition(void) const {
- return _.Swing;
+uint8_t IRGreeAC::getSwingVerticalPosition(void) const { return _.SwingV; }
+
+/// Get the Horizontal Swing position setting of the A/C.
+/// @return The native position/mode.
+uint8_t IRGreeAC::getSwingHorizontal(void) const { return _.SwingH; }
+
+/// Set the Horizontal Swing mode of the A/C.
+/// @param[in] position The position/mode to set the vanes to.
+void IRGreeAC::setSwingHorizontal(const uint8_t position) {
+ if (position <= kGreeSwingHMaxRight)
+ _.SwingH = position;
+ else
+ _.SwingH = kGreeSwingHOff;
}
/// Set the timer enable setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRGreeAC::setTimerEnabled(const bool on) {
- _.TimerEnabled = on;
-}
+void IRGreeAC::setTimerEnabled(const bool on) { _.TimerEnabled = on; }
/// Get the timer enabled setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRGreeAC::getTimerEnabled(void) const {
- return _.TimerEnabled;
-}
+bool IRGreeAC::getTimerEnabled(void) const { return _.TimerEnabled; }
/// Get the timer time value from the A/C.
/// @return The number of minutes the timer is set for.
@@ -478,9 +460,7 @@ void IRGreeAC::setDisplayTempSource(const uint8_t mode) {
/// Get the temperature display mode.
/// i.e. Internal, External temperature sensing.
/// @return The current temp source being displayed.
-uint8_t IRGreeAC::getDisplayTempSource(void) const {
- return _.DisplayTemp;
-}
+uint8_t IRGreeAC::getDisplayTempSource(void) const { return _.DisplayTemp; }
/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
@@ -523,6 +503,21 @@ uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) {
}
}
+/// Convert a stdAc::swingh_t enum into it's native setting.
+/// @param[in] swingh The enum to be converted.
+/// @return The native equivalent of the enum.
+uint8_t IRGreeAC::convertSwingH(const stdAc::swingh_t swingh) {
+ switch (swingh) {
+ case stdAc::swingh_t::kAuto: return kGreeSwingHAuto;
+ case stdAc::swingh_t::kLeftMax: return kGreeSwingHMaxLeft;
+ case stdAc::swingh_t::kLeft: return kGreeSwingHLeft;
+ case stdAc::swingh_t::kMiddle: return kGreeSwingHMiddle;
+ case stdAc::swingh_t::kRight: return kGreeSwingHRight;
+ case stdAc::swingh_t::kRightMax: return kGreeSwingHMaxRight;
+ default: return kGreeSwingHOff;
+ }
+}
+
/// Convert a native mode into its stdAc equivalent.
/// @param[in] mode The native setting to be converted.
/// @return The stdAc equivalent of the native setting.
@@ -530,9 +525,9 @@ stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) {
switch (mode) {
case kGreeCool: return stdAc::opmode_t::kCool;
case kGreeHeat: return stdAc::opmode_t::kHeat;
- case kGreeDry: return stdAc::opmode_t::kDry;
- case kGreeFan: return stdAc::opmode_t::kFan;
- default: return stdAc::opmode_t::kAuto;
+ case kGreeDry: return stdAc::opmode_t::kDry;
+ case kGreeFan: return stdAc::opmode_t::kFan;
+ default: return stdAc::opmode_t::kAuto;
}
}
@@ -541,10 +536,10 @@ stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) {
/// @return The stdAc equivalent of the native setting.
stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
- case kGreeFanMax: return stdAc::fanspeed_t::kMax;
+ case kGreeFanMax: return stdAc::fanspeed_t::kMax;
case kGreeFanMax - 1: return stdAc::fanspeed_t::kMedium;
- case kGreeFanMin: return stdAc::fanspeed_t::kMin;
- default: return stdAc::fanspeed_t::kAuto;
+ case kGreeFanMin: return stdAc::fanspeed_t::kMin;
+ default: return stdAc::fanspeed_t::kAuto;
}
}
@@ -553,12 +548,27 @@ stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) {
/// @return The stdAc equivalent of the native setting.
stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) {
switch (pos) {
- case kGreeSwingUp: return stdAc::swingv_t::kHighest;
- case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh;
- case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle;
+ case kGreeSwingUp: return stdAc::swingv_t::kHighest;
+ case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh;
+ case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle;
case kGreeSwingMiddleDown: return stdAc::swingv_t::kLow;
- case kGreeSwingDown: return stdAc::swingv_t::kLowest;
- default: return stdAc::swingv_t::kAuto;
+ case kGreeSwingDown: return stdAc::swingv_t::kLowest;
+ default: return stdAc::swingv_t::kAuto;
+ }
+}
+
+/// Convert a native Horizontal Swing into its stdAc equivalent.
+/// @param[in] pos The native setting to be converted.
+/// @return The stdAc equivalent of the native setting.
+stdAc::swingh_t IRGreeAC::toCommonSwingH(const uint8_t pos) {
+ switch (pos) {
+ case kGreeSwingHAuto: return stdAc::swingh_t::kAuto;
+ case kGreeSwingHMaxLeft: return stdAc::swingh_t::kLeftMax;
+ case kGreeSwingHLeft: return stdAc::swingh_t::kLeft;
+ case kGreeSwingHMiddle: return stdAc::swingh_t::kMiddle;
+ case kGreeSwingHRight: return stdAc::swingh_t::kRight;
+ case kGreeSwingHMaxRight: return stdAc::swingh_t::kRightMax;
+ default: return stdAc::swingh_t::kOff;
}
}
@@ -576,15 +586,15 @@ stdAc::state_t IRGreeAC::toCommon(void) {
if (_.SwingAuto)
result.swingv = stdAc::swingv_t::kAuto;
else
- result.swingv = toCommonSwingV(_.Swing);
+ result.swingv = toCommonSwingV(_.SwingV);
+ result.swingh = toCommonSwingH(_.SwingH);
result.turbo = _.Turbo;
+ result.econo = _.Econo;
result.light = _.Light;
result.clean = _.Xfan;
result.sleep = _.Sleep ? 0 : -1;
// Not supported.
- result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
- result.econo = false;
result.filter = false;
result.beep = false;
result.clock = -1;
@@ -604,6 +614,7 @@ String IRGreeAC::toString(void) {
result += addFanToString(_.Fan, kGreeFanMax, kGreeFanMin, kGreeFanAuto,
kGreeFanAuto, kGreeFanMed);
result += addBoolToString(_.Turbo, kTurboStr);
+ result += addBoolToString(_.Econo, kEconoStr);
result += addBoolToString(_.IFeel, kIFeelStr);
result += addBoolToString(_.WiFi, kWifiStr);
result += addBoolToString(_.Xfan, kXFanStr);
@@ -611,9 +622,9 @@ String IRGreeAC::toString(void) {
result += addBoolToString(_.Sleep, kSleepStr);
result += addLabeledString(_.SwingAuto ? kAutoStr : kManualStr,
kSwingVModeStr);
- result += addIntToString(_.Swing, kSwingVStr);
+ result += addIntToString(_.SwingV, kSwingVStr);
result += kSpaceLBraceStr;
- switch (_.Swing) {
+ switch (_.SwingV) {
case kGreeSwingLastPos:
result += kLastStr;
break;
@@ -623,6 +634,12 @@ String IRGreeAC::toString(void) {
default: result += kUnknownStr;
}
result += ')';
+ result += addSwingHToString(_.SwingH, kGreeSwingHAuto, kGreeSwingHMaxLeft,
+ kGreeSwingHLeft, kGreeSwingHMiddle,
+ kGreeSwingHRight, kGreeSwingHMaxRight,
+ kGreeSwingHOff,
+ // rest are unused.
+ 0xFF, 0xFF, 0xFF, 0xFF);
result += addLabeledString(
_.TimerEnabled ? minsToString(getTimer()) : kOffStr, kTimerStr);
uint8_t src = _.DisplayTemp;
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.h
index 4f89fde5c..be5ac31ce 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.h
@@ -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;
+ 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[]);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.cpp
index a43e9400d..5c9f2a36d 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.cpp
@@ -32,6 +32,8 @@ using irutils::addBoolToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
+using irutils::addModelToString;
+using irutils::addSwingHToString;
using irutils::addFanToString;
using irutils::addTempToString;
using irutils::minsToString;
@@ -322,22 +324,22 @@ void IRHaierAC::setCurrTime(const uint16_t nr_mins) {
}
/// Get the Vertical Swing position setting of the A/C.
-/// @return The native swing mode.
-uint8_t IRHaierAC::getSwing(void) const {
- return _.Swing;
+/// @return The native vertical swing mode.
+uint8_t IRHaierAC::getSwingV(void) const {
+ return _.SwingV;
}
/// Set the Vertical Swing mode of the A/C.
/// @param[in] state The mode to set the vanes to.
-void IRHaierAC::setSwing(const uint8_t state) {
- if (state == _.Swing) return; // Nothing to do.
+void IRHaierAC::setSwingV(const uint8_t state) {
+ if (state == _.SwingV) return; // Nothing to do.
switch (state) {
- case kHaierAcSwingOff:
- case kHaierAcSwingUp:
- case kHaierAcSwingDown:
- case kHaierAcSwingChg:
+ case kHaierAcSwingVOff:
+ case kHaierAcSwingVUp:
+ case kHaierAcSwingVDown:
+ case kHaierAcSwingVChg:
_.Command = kHaierAcCmdSwing;
- _.Swing = state;
+ _.SwingV = state;
break;
}
}
@@ -376,11 +378,11 @@ uint8_t IRHaierAC::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest:
case stdAc::swingv_t::kHigh:
- case stdAc::swingv_t::kMiddle: return kHaierAcSwingUp;
+ case stdAc::swingv_t::kMiddle: return kHaierAcSwingVUp;
case stdAc::swingv_t::kLow:
- case stdAc::swingv_t::kLowest: return kHaierAcSwingDown;
- case stdAc::swingv_t::kOff: return kHaierAcSwingOff;
- default: return kHaierAcSwingChg;
+ case stdAc::swingv_t::kLowest: return kHaierAcSwingVDown;
+ case stdAc::swingv_t::kOff: return kHaierAcSwingVOff;
+ default: return kHaierAcSwingVChg;
}
}
@@ -414,10 +416,10 @@ stdAc::fanspeed_t IRHaierAC::toCommonFanSpeed(const uint8_t speed) {
/// @return The native equivalent of the enum.
stdAc::swingv_t IRHaierAC::toCommonSwingV(const uint8_t pos) {
switch (pos) {
- case kHaierAcSwingUp: return stdAc::swingv_t::kHighest;
- case kHaierAcSwingDown: return stdAc::swingv_t::kLowest;
- case kHaierAcSwingOff: return stdAc::swingv_t::kOff;
- default: return stdAc::swingv_t::kAuto;
+ case kHaierAcSwingVUp: return stdAc::swingv_t::kHighest;
+ case kHaierAcSwingVDown: return stdAc::swingv_t::kLowest;
+ case kHaierAcSwingVOff: return stdAc::swingv_t::kOff;
+ default: return stdAc::swingv_t::kAuto;
}
}
@@ -433,7 +435,7 @@ stdAc::state_t IRHaierAC::toCommon(void) const {
result.celsius = true;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(getFan());
- result.swingv = toCommonSwingV(_.Swing);
+ result.swingv = toCommonSwingV(_.SwingV);
result.filter = _.Health;
result.sleep = _.Sleep ? 0 : -1;
// Not supported.
@@ -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)
- _.Button = kHaierAcYrw02ButtonTempDown;
- else
- _.Button = kHaierAcYrw02ButtonTempUp;
- _.Temp = temp - kHaierAcMinTemp;
+ if (old_temp == degree) return;
+
+ if (_.UseFahrenheit == fahrenheit) {
+ if (old_temp > degree)
+ _.Button = kHaierAcYrw02ButtonTempDown;
+ else
+ _.Button = kHaierAcYrw02ButtonTempUp;
+ } else {
+ _.Button = kHaierAcYrw02ButtonCFAB;
+ }
+ _.UseFahrenheit = fahrenheit;
+
+ uint8_t temp = degree;
+ if (fahrenheit) {
+ if (temp < kHaierAcYrw02MinTempF)
+ temp = kHaierAcYrw02MinTempF;
+ else if (temp > kHaierAcYrw02MaxTempF)
+ temp = kHaierAcYrw02MaxTempF;
+ if (degree >= 77) { temp++; }
+ if (degree >= 79) { temp++; }
+ // See at IRHaierAC176::getTemp() comments for clarification
+ _.ExtraDegreeF = temp % 2;
+ _.Temp = (temp - kHaierAcYrw02MinTempF -_.ExtraDegreeF) >> 1;
+ } else {
+ if (temp < kHaierAcYrw02MinTempC)
+ temp = kHaierAcYrw02MinTempC;
+ else if (temp > kHaierAcYrw02MaxTempC)
+ temp = kHaierAcYrw02MaxTempC;
+ _.Temp = temp - kHaierAcYrw02MinTempC;
+ }
}
/// Get the current temperature setting.
-/// @return The current setting for temp. in degrees celsius.
+/// 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,12 +1100,27 @@ stdAc::fanspeed_t IRHaierAC176::toCommonFanSpeed(const uint8_t speed) {
/// @return The native equivalent of the enum.
stdAc::swingv_t IRHaierAC176::toCommonSwingV(const uint8_t pos) {
switch (pos) {
- case kHaierAcYrw02SwingTop: return stdAc::swingv_t::kHighest;
- case kHaierAcYrw02SwingMiddle: return stdAc::swingv_t::kMiddle;
- case kHaierAcYrw02SwingDown: return stdAc::swingv_t::kLow;
- case kHaierAcYrw02SwingBottom: return stdAc::swingv_t::kLowest;
- case kHaierAcYrw02SwingOff: return stdAc::swingv_t::kOff;
- default: return stdAc::swingv_t::kAuto;
+ case kHaierAcYrw02SwingVTop: return stdAc::swingv_t::kHighest;
+ case kHaierAcYrw02SwingVMiddle: return stdAc::swingv_t::kMiddle;
+ case kHaierAcYrw02SwingVDown: return stdAc::swingv_t::kLow;
+ case kHaierAcYrw02SwingVBottom: return stdAc::swingv_t::kLowest;
+ case kHaierAcYrw02SwingVOff: return stdAc::swingv_t::kOff;
+ default: return stdAc::swingv_t::kAuto;
+ }
+}
+
+/// Convert a stdAc::swingh_t enum into it's native setting.
+/// @param[in] pos The enum to be converted.
+/// @return The native equivalent of the enum.
+stdAc::swingh_t IRHaierAC176::toCommonSwingH(const uint8_t pos) {
+ switch (pos) {
+ case kHaierAcYrw02SwingHMiddle: return stdAc::swingh_t::kMiddle;
+ case kHaierAcYrw02SwingHLeftMax: return stdAc::swingh_t::kLeftMax;
+ case kHaierAcYrw02SwingHLeft: return stdAc::swingh_t::kLeft;
+ case kHaierAcYrw02SwingHRight: return stdAc::swingh_t::kRight;
+ case kHaierAcYrw02SwingHRightMax: return stdAc::swingh_t::kRightMax;
+ case kHaierAcYrw02SwingHAuto: return stdAc::swingh_t::kAuto;
+ default: return stdAc::swingh_t::kOff;
}
}
@@ -953,23 +1129,23 @@ stdAc::swingv_t IRHaierAC176::toCommonSwingV(const uint8_t pos) {
stdAc::state_t IRHaierAC176::toCommon(void) const {
stdAc::state_t result;
result.protocol = decode_type_t::HAIER_AC_YRW02;
- result.model = -1; // No models used.
+ result.model = getModel();
result.power = _.Power;
result.mode = toCommonMode(_.Mode);
- result.celsius = true;
+ result.celsius = !_.UseFahrenheit;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
- result.swingv = toCommonSwingV(_.Swing);
+ result.swingv = toCommonSwingV(_.SwingV);
+ result.swingh = toCommonSwingH(_.SwingH);
result.filter = _.Health;
result.sleep = _.Sleep ? 0 : -1;
+ result.turbo = _.Turbo;
+ result.quiet = _.Quiet;
// Not supported.
- result.swingh = stdAc::swingh_t::kOff;
- result.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;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.h
index 2e287cd12..4e5c8e64c 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Haier.h
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_LG.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_LG.h
index 6010282ca..2bd7dbc1c 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_LG.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_LG.h
@@ -15,6 +15,8 @@
// Brand: LG, Model: AMNW09GSJA0 A/C (LG2 - AKB74955603)
// Brand: LG, Model: AMNW24GTPA1 A/C (LG2 - AKB73757604)
// Brand: LG, Model: AKB73757604 remote (LG2 - AKB73757604)
+// Brand: LG, Model: AKB73315611 remote (LG2 - AKB74955603)
+// Brand: LG, Model: MS05SQ NW0 A/C (LG2 - AKB74955603)
// Brand: General Electric, Model: AG1BH09AW101 Split A/C (LG)
// Brand: General Electric, Model: 6711AR2853M A/C Remote (LG)
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp
index 8a573a8dc..a806937b1 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp
@@ -1,15 +1,35 @@
-// Copyright 2020 David Conran (crankyoldgit)
+// Copyright 2020-2021 David Conran (crankyoldgit)
/// @file
/// @brief Support for Mirage protocol
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1573
-// Supports:
-// Brand: Mirage, Model: VLU series A/C
+#include "ir_Mirage.h"
+#include
+#include
+#ifndef ARDUINO
+#include
+#endif
#include "IRrecv.h"
#include "IRsend.h"
+#include "IRtext.h"
#include "IRutils.h"
+using irutils::addBoolToString;
+using irutils::addFanToString;
+using irutils::addIntToString;
+using irutils::addLabeledString;
+using irutils::addModeToString;
+using irutils::addModelToString;
+using irutils::addSwingHToString;
+using irutils::addSwingVToString;
+using irutils::addTempToString;
+using irutils::addToggleToString;
+using irutils::minsToString;
+using irutils::bcdToUint8;
+using irutils::uint8ToBcd;
+using irutils::sumNibbles;
// Constants
const uint16_t kMirageHdrMark = 8360; ///< uSeconds
@@ -20,6 +40,9 @@ const uint16_t kMirageZeroSpace = 545; ///< uSeconds
const uint32_t kMirageGap = kDefaultMessageGap; ///< uSeconds (just a guess)
const uint16_t kMirageFreq = 38000; ///< Hz. (Just a guess)
+const uint8_t kMirageAcKKG29AC1PowerOn = 0b00; // 0
+const uint8_t kMirageAcKKG29AC1PowerOff = 0b11; // 3
+
#if SEND_MIRAGE
/// Send a Mirage formatted message.
@@ -58,6 +81,8 @@ bool IRrecv::decodeMirage(decode_results *results, uint16_t offset,
kMirageBitMark, kMirageZeroSpace,
kMirageBitMark, kMirageGap, true,
kUseDefTol, kMarkExcess, false)) return false;
+ // Compliance
+ if (strict && !IRMirageAc::validChecksum(results->state)) return false;
// Success
results->decode_type = decode_type_t::MIRAGE;
@@ -67,4 +92,756 @@ bool IRrecv::decodeMirage(decode_results *results, uint16_t offset,
// is a union data type.
return true;
}
+
+// Code to emulate Mirage A/C IR remote control unit.
+
+/// Class constructor
+/// @param[in] pin GPIO to be used when sending.
+/// @param[in] inverted Is the output signal to be inverted?
+/// @param[in] use_modulation Is frequency modulation to be used?
+IRMirageAc::IRMirageAc(const uint16_t pin, const bool inverted,
+ const bool use_modulation)
+ : _irsend(pin, inverted, use_modulation) { stateReset(); }
+
+/// Reset the state of the remote to a known good state/sequence.
+void IRMirageAc::stateReset(void) {
+ // The state of the IR remote in IR code form.
+ static const uint8_t kReset[kMirageStateLength] = {
+ 0x56, 0x6C, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00,
+ 0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x42};
+ setRaw(kReset);
+ _model = mirage_ac_remote_model_t::KKG9AC1;
+}
+
+/// Set up hardware to be able to send a message.
+void IRMirageAc::begin(void) { _irsend.begin(); }
+
+#if SEND_MITSUBISHI_AC
+/// Send the current internal state as an IR message.
+/// @param[in] repeat Nr. of times the message will be repeated.
+void IRMirageAc::send(const uint16_t repeat) {
+ _irsend.sendMirage(getRaw(), kMirageStateLength, repeat);
+ // Reset any toggles after a send.
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ setCleanToggle(false);
+ setLight(false); // For this model (only), Light is a toggle.
+ break;
+ default:
+ break;
+ }
+}
+#endif // SEND_MITSUBISHI_AC
+
+/// Get a PTR to the internal state/code for this protocol.
+/// @return PTR to a code for this protocol based on the current internal state.
+uint8_t *IRMirageAc::getRaw(void) {
+ checksum();
+ return _.raw;
+}
+
+/// Set the internal state from a valid code for this protocol.
+/// @param[in] data A valid code for this protocol.
+void IRMirageAc::setRaw(const uint8_t *data) {
+ std::memcpy(_.raw, data, kMirageStateLength);
+ _model = getModel(true);
+}
+
+/// Guess the Mirage remote model from the supplied state code.
+/// @param[in] state A valid state code for this protocol.
+/// @return The model code.
+/// @note This result isn't perfect. Both protocols can look the same but have
+/// wildly different settings.
+mirage_ac_remote_model_t IRMirageAc::getModel(const uint8_t *state) {
+ Mirage120Protocol p;
+ std::memcpy(p.raw, state, kMirageStateLength);
+ // Check for KKG29AC1 specific settings.
+ if (p.RecycleHeat || p.Filter || p.Sleep_Kkg29ac1 || p.CleanToggle ||
+ p.IFeel || p.OffTimerEnable || p.OnTimerEnable)
+ return mirage_ac_remote_model_t::KKG29AC1;
+ // Check for things specific to KKG9AC1
+ if ((p.Minutes || p.Seconds) || // Is part of the clock set?
+ // Are the timer times set, but not enabled? (enable check filtered above)
+ (p.OffTimerHours || p.OffTimerMins) ||
+ (p.OnTimerHours || p.OnTimerMins))
+ return mirage_ac_remote_model_t::KKG9AC1;
+ // As the above test has a 1 in 3600+ (for 1 second an hour) chance of a false
+ // negative in theory, we are going assume that anything left should be a
+ // KKG29AC1 model.
+ return mirage_ac_remote_model_t::KKG29AC1; // Default.
+}
+
+/// Get the model code of the interal message state.
+/// @param[in] useRaw If set, we try to get the model info from just the state.
+/// @return The model code.
+mirage_ac_remote_model_t IRMirageAc::getModel(const bool useRaw) const {
+ return useRaw ? getModel(_.raw) : _model;
+}
+
+/// Set the model code of the interal message state.
+/// @param[in] model The desired model to use for the settings.
+void IRMirageAc::setModel(const mirage_ac_remote_model_t model) {
+ if (model != _model) { // Only change things if we need to.
+ // Save the old settings.
+ stdAc::state_t state = toCommon();
+ const uint16_t ontimer = getOnTimer();
+ const uint16_t offtimer = getOffTimer();
+ const bool ifeel = getIFeel();
+ const uint8_t sensor = getSensorTemp();
+ // Change the model.
+ state.model = model;
+ // Restore/Convert the settings.
+ fromCommon(state);
+ setOnTimer(ontimer);
+ setOffTimer(offtimer);
+ setIFeel(ifeel);
+ setSensorTemp(sensor);
+ }
+}
+
+/// Calculate and set the checksum values for the internal state.
+void IRMirageAc::checksum(void) { _.Sum = calculateChecksum(_.raw); }
+
+/// Verify the checksum is valid for a given state.
+/// @param[in] data The array to verify the checksum of.
+/// @return true, if the state has a valid checksum. Otherwise, false.
+bool IRMirageAc::validChecksum(const uint8_t *data) {
+ return calculateChecksum(data) == data[kMirageStateLength - 1];
+}
+
+/// Calculate the checksum for a given state.
+/// @param[in] data The value to calc the checksum of.
+/// @return The calculated checksum value.
+uint8_t IRMirageAc::calculateChecksum(const uint8_t *data) {
+ return sumNibbles(data, kMirageStateLength - 1);
+}
+
+/// Set the requested power state of the A/C to on.
+void IRMirageAc::on(void) { setPower(true); }
+
+/// Set the requested power state of the A/C to off.
+void IRMirageAc::off(void) { setPower(false); }
+
+/// Change the power setting.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setPower(bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.Power = on ? kMirageAcKKG29AC1PowerOn : kMirageAcKKG29AC1PowerOff;
+ break;
+ default:
+ // In order to change the power setting, it seems must be less than
+ // kMirageAcPowerOff. kMirageAcPowerOff is larger than half of the
+ // possible value stored in the allocated bit space.
+ // Thus if the value is larger than kMirageAcPowerOff the power is off.
+ // Less than, then power is on.
+ // We can't just aribitarily add or subtract the value (which analysis
+ // indicates is how the power status changes. Very weird, I know!) as that
+ // is not an idempotent action, we must check if the addition or
+ // substraction is needed first. e.g. via getPower()
+ // i.e. If we added or subtracted twice, we would cause a wrap of the
+ // integer and not get the desired result.
+ if (on)
+ _.SwingAndPower -= getPower() ? 0 : kMirageAcPowerOff;
+ else
+ _.SwingAndPower += getPower() ? kMirageAcPowerOff : 0;
+ }
+}
+
+/// Get the value of the current power setting.
+/// @return true, the setting is on. false, the setting is off.
+bool IRMirageAc::getPower(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ return _.Power == kMirageAcKKG29AC1PowerOn;
+ default:
+ return _.SwingAndPower < kMirageAcPowerOff;
+ }
+}
+
+/// Get the operating mode setting of the A/C.
+/// @return The current operating mode setting.
+uint8_t IRMirageAc::getMode(void) const { return _.Mode; }
+
+/// Set the operating mode of the A/C.
+/// @param[in] mode The desired operating mode.
+void IRMirageAc::setMode(const uint8_t mode) {
+ switch (mode) {
+ case kMirageAcCool:
+ case kMirageAcDry:
+ case kMirageAcHeat:
+ case kMirageAcFan:
+ case kMirageAcRecycle:
+ _.Mode = mode;
+ // Reset turbo if we have to.
+ setTurbo(getTurbo());
+ break;
+ default: // Default to cool mode for anything else.
+ setMode(kMirageAcCool);
+ }
+}
+
+/// Set the temperature.
+/// @param[in] degrees The temperature in degrees celsius.
+void IRMirageAc::setTemp(const uint8_t degrees) {
+ // Make sure we have desired temp in the correct range.
+ uint8_t celsius = std::max(degrees, kMirageAcMinTemp);
+ _.Temp = std::min(celsius, kMirageAcMaxTemp) + kMirageAcTempOffset;
+}
+
+/// Get the current temperature setting.
+/// @return The current setting for temp. in degrees celsius.
+uint8_t IRMirageAc::getTemp(void) const { return _.Temp - kMirageAcTempOffset; }
+
+/// Set the speed of the fan.
+/// @param[in] speed The desired setting.
+void IRMirageAc::setFan(const uint8_t speed) {
+ _.Fan = (speed <= kMirageAcFanLow) ? speed : kMirageAcFanAuto;
+}
+
+/// Get the current fan speed setting.
+/// @return The current fan speed/mode.
+uint8_t IRMirageAc::getFan(void) const { return _.Fan; }
+
+/// Change the Turbo setting.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setTurbo(bool on) {
+ const bool value = (on && (getMode() == kMirageAcCool));
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.Turbo_Kkg29ac1 = value;
+ break;
+ default:
+ _.Turbo_Kkg9ac1 = value;
+ }
+}
+
+/// Get the value of the current Turbo setting.
+/// @return true, the setting is on. false, the setting is off.
+bool IRMirageAc::getTurbo(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.Turbo_Kkg29ac1;
+ default: return _.Turbo_Kkg9ac1;
+ }
+}
+
+/// Change the Sleep setting.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setSleep(bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.Sleep_Kkg29ac1 = on;
+ break;
+ default:
+ _.Sleep_Kkg9ac1 = on;
+ }
+}
+
+/// Get the value of the current Sleep setting.
+/// @return true, the setting is on. false, the setting is off.
+bool IRMirageAc::getSleep(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.Sleep_Kkg29ac1;
+ default: return _.Sleep_Kkg9ac1;
+ }
+}
+
+/// Change the Light/Display setting.
+/// @param[in] on true, the setting is on. false, the setting is off.
+/// @note Light is a toggle on the KKG29AC1 model.
+void IRMirageAc::setLight(bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.LightToggle_Kkg29ac1 = on;
+ break;
+ default:
+ _.Light_Kkg9ac1 = on;
+ }
+}
+
+/// Get the value of the current Light/Display setting.
+/// @return true, the setting is on. false, the setting is off.
+/// @note Light is a toggle on the KKG29AC1 model.
+bool IRMirageAc::getLight(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.LightToggle_Kkg29ac1;
+ default: return _.Light_Kkg9ac1;
+ }
+}
+
+/// Get the clock time of the A/C unit.
+/// @return Nr. of seconds past midnight.
+uint32_t IRMirageAc::getClock(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ return 0;
+ default:
+ return ((bcdToUint8(_.Hours) * 60) + bcdToUint8(_.Minutes)) * 60 +
+ bcdToUint8(_.Seconds);
+ }
+}
+
+/// Set the clock time on the A/C unit.
+/// @param[in] nr_of_seconds Nr. of seconds past midnight.
+void IRMirageAc::setClock(const uint32_t nr_of_seconds) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.Minutes = _.Seconds = 0; // No clock setting. Clear it just in case.
+ break;
+ default:
+ uint32_t remaining = std::min(
+ nr_of_seconds, (uint32_t)(24 * 60 * 60 - 1)); // Limit to 23:59:59.
+ _.Seconds = uint8ToBcd(remaining % 60);
+ remaining /= 60;
+ _.Minutes = uint8ToBcd(remaining % 60);
+ remaining /= 60;
+ _.Hours = uint8ToBcd(remaining);
+ }
+}
+
+/// Set the Vertical Swing setting/position of the A/C.
+/// @param[in] position The desired swing setting.
+void IRMirageAc::setSwingV(const uint8_t position) {
+ switch (position) {
+ case kMirageAcSwingVOff:
+ case kMirageAcSwingVLowest:
+ case kMirageAcSwingVLow:
+ case kMirageAcSwingVMiddle:
+ case kMirageAcSwingVHigh:
+ case kMirageAcSwingVHighest:
+ case kMirageAcSwingVAuto:
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.SwingV = (position != kMirageAcSwingVOff);
+ break;
+ default:
+ const bool power = getPower();
+ _.SwingAndPower = position;
+ // Power needs to be reapplied after overwriting SwingAndPower
+ setPower(power);
+ }
+ break;
+ default: // Default to Auto for anything else.
+ setSwingV(kMirageAcSwingVAuto);
+ }
+}
+
+/// Get the Vertical Swing setting/position of the A/C.
+/// @return The desired Vertical Swing setting/position.
+uint8_t IRMirageAc::getSwingV(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ return _.SwingV ? kMirageAcSwingVAuto : kMirageAcSwingVOff;
+ default:
+ return _.SwingAndPower - (getPower() ? 0 : kMirageAcPowerOff);
+ }
+}
+
+/// Set the Horizontal Swing setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setSwingH(const bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.SwingH = on;
+ break;
+ default:
+ break;
+ }
+}
+
+/// Get the Horizontal Swing setting of the A/C.
+/// @return on true, the setting is on. false, the setting is off.
+bool IRMirageAc::getSwingH(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.SwingH;
+ default: return false;
+ }
+}
+
+/// Set the Quiet setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setQuiet(const bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.Quiet = on;
+ break;
+ default:
+ break;
+ }
+}
+
+/// Get the Quiet setting of the A/C.
+/// @return on true, the setting is on. false, the setting is off.
+bool IRMirageAc::getQuiet(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.Quiet;
+ default: return false;
+ }
+}
+
+/// Set the CleanToggle setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setCleanToggle(const bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.CleanToggle = on;
+ break;
+ default:
+ break;
+ }
+}
+
+/// Get the Clean Toggle setting of the A/C.
+/// @return on true, the setting is on. false, the setting is off.
+bool IRMirageAc::getCleanToggle(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.CleanToggle;
+ default: return false;
+ }
+}
+
+/// Set the Filter setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setFilter(const bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.Filter = on;
+ break;
+ default:
+ break;
+ }
+}
+
+/// Get the Filter setting of the A/C.
+/// @return on true, the setting is on. false, the setting is off.
+bool IRMirageAc::getFilter(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.Filter;
+ default: return false;
+ }
+}
+
+/// Set the IFeel setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRMirageAc::setIFeel(const bool on) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.IFeel = on;
+ if (on) {
+ // If no previous sensor temp, default to currently desired temp.
+ if (!_.SensorTemp) _.SensorTemp = getTemp();
+ } else {
+ _.SensorTemp = 0; // When turning it off, clear the Sensor Temp.
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/// Get the IFeel setting of the A/C.
+/// @return on true, the setting is on. false, the setting is off.
+bool IRMirageAc::getIFeel(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1: return _.IFeel;
+ default: return false;
+ }
+}
+
+/// Set the Sensor Temp setting of the A/C's remote.
+/// @param[in] degrees The desired sensor temp. in degrees celsius.
+void IRMirageAc::setSensorTemp(const uint8_t degrees) {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.SensorTemp = std::min(kMirageAcSensorTempMax, degrees) +
+ kMirageAcSensorTempOffset;
+ break;
+ default:
+ break;
+ }
+}
+
+/// Get the Sensor Temp setting of the A/C's remote.
+/// @return The current setting for the sensor temp. in degrees celsius.
+uint16_t IRMirageAc::getSensorTemp(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ return _.SensorTemp - kMirageAcSensorTempOffset;
+ default:
+ return false;
+ }
+}
+
+/// Get the number of minutes the On Timer is currently set for.
+/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use.
+uint16_t IRMirageAc::getOnTimer(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ return _.OnTimerEnable ? _.OnTimerHours * 60 + _.OnTimerMins : 0;
+ default:
+ return 0;
+ }
+}
+
+/// Set the number of minutes for the On Timer.
+/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
+void IRMirageAc::setOnTimer(const uint16_t nr_of_mins) {
+ uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.OnTimerEnable = (mins > 0);
+ _.OnTimerHours = mins / 60;
+ _.OnTimerMins = mins % 60;
+ break;
+ default:
+ break;
+ }
+}
+
+/// Get the number of minutes the Off Timer is currently set for.
+/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use.
+uint16_t IRMirageAc::getOffTimer(void) const {
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ return _.OffTimerEnable ? _.OffTimerHours * 60 + _.OffTimerMins : 0;
+ default:
+ return 0;
+ }
+}
+
+/// Set the number of minutes for the Off Timer.
+/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
+void IRMirageAc::setOffTimer(const uint16_t nr_of_mins) {
+ uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ _.OffTimerEnable = (mins > 0);
+ _.OffTimerHours = mins / 60;
+ _.OffTimerMins = mins % 60;
+ break;
+ default:
+ break;
+ }
+}
+
+/// Convert a native mode into its stdAc equivalent.
+/// @param[in] mode The native setting to be converted.
+/// @return The stdAc equivalent of the native setting.
+stdAc::opmode_t IRMirageAc::toCommonMode(const uint8_t mode) {
+ switch (mode) {
+ case kMirageAcHeat: return stdAc::opmode_t::kHeat;
+ case kMirageAcDry: return stdAc::opmode_t::kDry;
+ case kMirageAcFan: return stdAc::opmode_t::kFan;
+ default: return stdAc::opmode_t::kCool;
+ }
+}
+
+/// Convert a native fan speed into its stdAc equivalent.
+/// @param[in] speed The native setting to be converted.
+/// @param[in] model The model type to use to influence the conversion.
+/// @return The stdAc equivalent of the native setting.
+stdAc::fanspeed_t IRMirageAc::toCommonFanSpeed(const uint8_t speed,
+ const mirage_ac_remote_model_t model) {
+ switch (model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ switch (speed) {
+ case kMirageAcKKG29AC1FanHigh: return stdAc::fanspeed_t::kHigh;
+ case kMirageAcKKG29AC1FanMed: return stdAc::fanspeed_t::kMedium;
+ case kMirageAcKKG29AC1FanLow: return stdAc::fanspeed_t::kLow;
+ default: return stdAc::fanspeed_t::kAuto;
+ }
+ break;
+ default:
+ switch (speed) {
+ case kMirageAcFanHigh: return stdAc::fanspeed_t::kHigh;
+ case kMirageAcFanMed: return stdAc::fanspeed_t::kMedium;
+ case kMirageAcFanLow: return stdAc::fanspeed_t::kLow;
+ default: return stdAc::fanspeed_t::kAuto;
+ }
+ }
+}
+
+/// Convert a stdAc::opmode_t enum into its native mode.
+/// @param[in] mode The enum to be converted.
+/// @return The native equivalent of the enum.
+uint8_t IRMirageAc::convertMode(const stdAc::opmode_t mode) {
+ switch (mode) {
+ case stdAc::opmode_t::kHeat: return kMirageAcHeat;
+ case stdAc::opmode_t::kDry: return kMirageAcDry;
+ case stdAc::opmode_t::kFan: return kMirageAcFan;
+ default: return kMirageAcCool;
+ }
+}
+
+/// Convert a stdAc::fanspeed_t enum into it's native speed.
+/// @param[in] speed The enum to be converted.
+/// @param[in] model The model type to use to influence the conversion.
+/// @return The native equivalent of the enum.
+uint8_t IRMirageAc::convertFan(const stdAc::fanspeed_t speed,
+ const mirage_ac_remote_model_t model) {
+ uint8_t low;
+ uint8_t med;
+ switch (model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ low = kMirageAcKKG29AC1FanLow;
+ med = kMirageAcKKG29AC1FanMed;
+ break;
+ default:
+ low = kMirageAcFanLow;
+ med = kMirageAcFanMed;
+ }
+ switch (speed) {
+ case stdAc::fanspeed_t::kMin:
+ case stdAc::fanspeed_t::kLow: return low;
+ case stdAc::fanspeed_t::kMedium: return med;
+ case stdAc::fanspeed_t::kHigh:
+ case stdAc::fanspeed_t::kMax: return kMirageAcFanHigh;
+ default: return kMirageAcFanAuto;
+ }
+}
+
+/// Convert a stdAc::swingv_t enum into it's native setting.
+/// @param[in] position The enum to be converted.
+/// @return The native equivalent of the enum.
+uint8_t IRMirageAc::convertSwingV(const stdAc::swingv_t position) {
+ switch (position) {
+ case stdAc::swingv_t::kHighest: return kMirageAcSwingVHighest;
+ case stdAc::swingv_t::kHigh: return kMirageAcSwingVHigh;
+ case stdAc::swingv_t::kMiddle: return kMirageAcSwingVMiddle;
+ case stdAc::swingv_t::kLow: return kMirageAcSwingVLow;
+ case stdAc::swingv_t::kLowest: return kMirageAcSwingVLowest;
+ case stdAc::swingv_t::kOff: return kMirageAcSwingVOff;
+ default: return kMirageAcSwingVAuto;
+ }
+}
+
+/// Convert a native vertical swing postion to it's common equivalent.
+/// @param[in] pos A native position to convert.
+/// @return The common vertical swing position.
+stdAc::swingv_t IRMirageAc::toCommonSwingV(const uint8_t pos) {
+ switch (pos) {
+ case kMirageAcSwingVHighest: return stdAc::swingv_t::kHighest;
+ case kMirageAcSwingVHigh: return stdAc::swingv_t::kHigh;
+ case kMirageAcSwingVMiddle: return stdAc::swingv_t::kMiddle;
+ case kMirageAcSwingVLow: return stdAc::swingv_t::kLow;
+ case kMirageAcSwingVLowest: return stdAc::swingv_t::kLowest;
+ case kMirageAcSwingVAuto: return stdAc::swingv_t::kAuto;
+ default: return stdAc::swingv_t::kOff;
+ }
+}
+
+/// Convert the current internal state into its stdAc::state_t equivalent.
+/// @return The stdAc equivalent of the native settings.
+stdAc::state_t IRMirageAc::toCommon(void) const {
+ stdAc::state_t result;
+ result.protocol = decode_type_t::MIRAGE;
+ result.model = _model;
+ result.power = getPower();
+ result.mode = toCommonMode(_.Mode);
+ result.celsius = true;
+ result.degrees = getTemp();
+ result.fanspeed = toCommonFanSpeed(getFan(), _model);
+ result.swingv = toCommonSwingV(getSwingV());
+ result.swingh = getSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
+ result.turbo = getTurbo();
+ result.light = getLight();
+ result.clean = getCleanToggle();
+ result.filter = getFilter();
+ result.sleep = getSleep() ? 0 : -1;
+ result.quiet = getQuiet();
+ result.clock = getClock() / 60;
+ // Not supported.
+ result.econo = false;
+ result.beep = false;
+ return result;
+}
+
+/// Convert & set a stdAc::state_t to its equivalent internal settings.
+/// @param[in] state The desired state in stdAc::state_t form.
+void IRMirageAc::fromCommon(const stdAc::state_t state) {
+ stateReset();
+ _model = (mirage_ac_remote_model_t)state.model; // Set directly to avoid loop
+ setPower(state.power);
+ setTemp(state.celsius ? state.degrees : fahrenheitToCelsius(state.degrees));
+ setMode(convertMode(state.mode));
+ setFan(convertFan(state.fanspeed, _model));
+ setTurbo(state.turbo);
+ setSleep(state.sleep >= 0);
+ setLight(state.light);
+ setSwingV(convertSwingV(state.swingv));
+ setSwingH(state.swingh != stdAc::swingh_t::kOff);
+ setQuiet(state.quiet);
+ setCleanToggle(state.clean);
+ setFilter(state.filter);
+ // setClock() expects seconds, not minutes.
+ setClock((state.clock > 0) ? state.clock * 60 : 0);
+ // Non-common settings.
+ setOnTimer(0);
+ setOffTimer(0);
+ setIFeel(false);
+}
+
+/// Convert the internal state into a human readable string.
+/// @return A string containing the settings in human-readable form.
+String IRMirageAc::toString(void) const {
+ String result = "";
+ result.reserve(240); // Reserve some heap for the string to reduce fragging.
+ result += addModelToString(decode_type_t::MIRAGE, _model, false);
+ result += addBoolToString(getPower(), kPowerStr);
+ result += addModeToString(_.Mode, 0xFF, kMirageAcCool,
+ kMirageAcHeat, kMirageAcDry,
+ kMirageAcFan);
+ result += addTempToString(getTemp());
+ uint8_t fanlow;
+ uint8_t fanmed;
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ fanlow = kMirageAcKKG29AC1FanLow;
+ fanmed = kMirageAcKKG29AC1FanMed;
+ break;
+ default: // e.g. Model KKG9AC1
+ fanlow = kMirageAcFanLow;
+ fanmed = kMirageAcFanMed;
+ }
+ result += addFanToString(_.Fan, kMirageAcFanHigh, fanlow, kMirageAcFanAuto,
+ kMirageAcFanAuto, fanmed);
+ result += addBoolToString(getTurbo(), kTurboStr);
+ result += addBoolToString(getSleep(), kSleepStr);
+ switch (_model) {
+ case mirage_ac_remote_model_t::KKG29AC1:
+ result += addBoolToString(_.Quiet, kQuietStr);
+ result += addToggleToString(getLight(), kLightStr);
+ result += addBoolToString(_.SwingV, kSwingVStr);
+ result += addBoolToString(_.SwingH, kSwingHStr);
+ result += addBoolToString(_.Filter, kFilterStr);
+ result += addToggleToString(_.CleanToggle, kCleanStr);
+ result += addLabeledString(getOnTimer() ? minsToString(getOnTimer())
+ : kOffStr,
+ kOnTimerStr);
+ result += addLabeledString(getOffTimer() ? minsToString(getOffTimer())
+ : kOffStr,
+ kOffTimerStr);
+ result += addBoolToString(_.IFeel, kIFeelStr);
+ if (_.IFeel) {
+ result += addIntToString(getSensorTemp(), kSensorTempStr);
+ result += 'C';
+ }
+ break;
+ default: // e.g. Model KKG9AC1
+ result += addBoolToString(getLight(), kLightStr);
+ result += addSwingVToString(getSwingV(),
+ kMirageAcSwingVAuto,
+ kMirageAcSwingVHighest,
+ kMirageAcSwingVHigh,
+ 0xFF, // Unused.
+ kMirageAcSwingVMiddle,
+ 0xFF, // Unused.
+ kMirageAcSwingVLow,
+ kMirageAcSwingVLowest,
+ kMirageAcSwingVOff,
+ 0xFF, 0xFF, 0xFF); // Unused.
+ result += addLabeledString(minsToString(getClock() / 60), kClockStr);
+ }
+ return result;
+}
#endif // DECODE_MIRAGE
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.h
new file mode 100644
index 000000000..b2b39e759
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.h
@@ -0,0 +1,277 @@
+// Copyright 2020-2021 David Conran (crankyoldgit)
+/// @file
+/// @brief Support for Mirage protocol
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1573
+
+
+// Supports:
+// Brand: Mirage, Model: VLU series A/C
+// Brand: Maxell, Model: MX-CH18CF A/C
+// Brand: Maxell, Model: KKG9A-C1 remote
+// Brand: Tronitechnik, Model: Reykir 9000 A/C
+// Brand: Tronitechnik, Model: KKG29A-C1 remote
+
+#ifndef IR_MIRAGE_H_
+#define IR_MIRAGE_H_
+
+#define __STDC_LIMIT_MACROS
+#include
+#ifndef UNIT_TEST
+#include
+#endif
+#include "IRremoteESP8266.h"
+#include "IRsend.h"
+#ifdef UNIT_TEST
+#include "IRsend_test.h"
+#endif
+
+/// Native representation of a Mirage 120-bit A/C message.
+/// @see https://docs.google.com/spreadsheets/d/1Ucu9mOOIIJoWQjUJq_VCvwgV3EwKaRk8K2AuZgccYEk/edit#gid=0
+union Mirage120Protocol{
+ uint8_t raw[kMirageStateLength]; ///< The state in code form.
+ struct { // Common
+ // Byte 0
+ uint8_t Header :8; // Header. (0x56)
+ // Byte 1
+ uint8_t Temp :8; // Celsius minus 0x5C.
+ // Byte 2
+ uint8_t :8; // Unknown / Unused.
+ // Byte 3
+ uint8_t :8; // Unknown / Unused.
+ // Byte 4
+ uint8_t Fan :2; // Fan Speed.
+ uint8_t :2; // Unknown / Unused.
+ uint8_t Mode :4; // Cool, Heat, Dry, Fan, Recycle
+ // Byte 5
+ uint8_t :8;
+ // Byte 6
+ uint8_t :8;
+ // Byte 7
+ uint8_t :8;
+ // Byte 8
+ uint8_t :8;
+ // Byte 9
+ uint8_t :8;
+ // Byte 10
+ uint8_t :8;
+ // Byte 11
+ uint8_t :8;
+ // Byte 12
+ uint8_t :8;
+ // Byte 13
+ uint8_t :8;
+ // Byte 14
+ uint8_t Sum :8; // Sum of all the previous nibbles.
+ };
+ struct { // KKG9AC1 remote
+ // Byte 0
+ uint8_t :8; // Header
+ // Byte 1
+ uint8_t :8; // Temp
+ // Byte 2
+ uint8_t :8; // Unknown / Unused.
+ // Byte 3
+ uint8_t :3; // Unknown / Unused.
+ uint8_t Light_Kkg9ac1 :1; // Aka. Display. Seems linked to Sleep mode.
+ uint8_t :4; // Unknown / Unused.
+ // Byte 4
+ uint8_t :8; // Fan & Mode
+ // Byte 5
+ uint8_t :1; // Unknown
+ uint8_t SwingAndPower :7;
+ // Byte 6
+ uint8_t :7; // Unknown / Unused.
+ uint8_t Sleep_Kkg9ac1 :1; // Sleep mode on or off.
+ // Byte 7
+ uint8_t :3; // Unknown / Unused.
+ uint8_t Turbo_Kkg9ac1 :1; // Turbo mode on or off. Only works in Cool mode.
+ uint8_t :4; // Unknown / Unused.
+ // Byte 8
+ uint8_t :8; // Unknown / Unused.
+ // Byte 9
+ uint8_t :8; // Unknown / Unused.
+ // Byte 10
+ uint8_t :8; // Unknown / Unused.
+ // Byte 11
+ uint8_t Seconds :8; // Nr. of Seconds in BCD.
+ // Byte 12
+ uint8_t Minutes :8; // Nr. of Minutes in BCD.
+ // Byte 13
+ uint8_t Hours :8; // Nr. of Hours in BCD.
+ // Byte 14
+ uint8_t :8; // Sum
+ };
+ struct { // KKG29A-C1 remote
+ // Byte 0
+ uint8_t :8; // Header
+ // Byte 1
+ uint8_t :8; // Temp
+ // Byte 2
+ uint8_t :8;
+ // Byte 3
+ uint8_t Quiet :1;
+ uint8_t :7;
+ // Byte 4
+ uint8_t :2; // Fan
+ uint8_t OffTimerEnable :1;
+ uint8_t OnTimerEnable :1;
+ uint8_t :3; // Mode
+ uint8_t :1;
+ // Byte 5
+ uint8_t SwingH :1;
+ uint8_t SwingV :1;
+ uint8_t LightToggle_Kkg29ac1 :1; // Aka. Display Toggle.
+ uint8_t :3;
+ uint8_t Power :2;
+ // Byte 6
+ uint8_t :1;
+ uint8_t Filter :1; // Aka. UVC
+ uint8_t :1;
+ uint8_t Sleep_Kkg29ac1 :1; // Sleep mode on or off.
+ uint8_t :2;
+ uint8_t RecycleHeat :1;
+ uint8_t :1;
+ // Byte 7
+ uint8_t SensorTemp :6; // Temperature at the remote
+ uint8_t CleanToggle :1;
+ uint8_t IFeel :1;
+ // Byte 8
+ uint8_t OnTimerHours :5;
+ uint8_t :2;
+ uint8_t Turbo_Kkg29ac1 :1; // Turbo mode on or off.
+ // Byte 9
+ uint8_t OnTimerMins :6;
+ uint8_t :2;
+ // Byte 10
+ uint8_t OffTimerHours :5;
+ uint8_t :3;
+ // Byte 11
+ uint8_t OffTimerMins :6;
+ uint8_t :2;
+ // Byte 12
+ uint8_t :8;
+ // Byte 13
+ uint8_t :8;
+ // Byte 14
+ uint8_t :8; // Sum
+ };
+};
+
+// Constants
+const uint8_t kMirageAcHeat = 0b001; // 1
+const uint8_t kMirageAcCool = 0b010; // 2
+const uint8_t kMirageAcDry = 0b011; // 3
+const uint8_t kMirageAcRecycle = 0b100; // 4
+const uint8_t kMirageAcFan = 0b101; // 5
+
+const uint8_t kMirageAcFanAuto = 0b00; // 0
+const uint8_t kMirageAcFanHigh = 0b01; // 1
+const uint8_t kMirageAcFanMed = 0b10; // 2
+const uint8_t kMirageAcFanLow = 0b11; // 3
+const uint8_t kMirageAcKKG29AC1FanAuto = 0b00; // 0
+const uint8_t kMirageAcKKG29AC1FanHigh = 0b01; // 1
+const uint8_t kMirageAcKKG29AC1FanLow = 0b10; // 2
+const uint8_t kMirageAcKKG29AC1FanMed = 0b11; // 3
+
+const uint8_t kMirageAcMinTemp = 16; // 16C
+const uint8_t kMirageAcMaxTemp = 32; // 32C
+const uint8_t kMirageAcTempOffset = 0x5C;
+const uint8_t kMirageAcSensorTempOffset = 20;
+const uint8_t kMirageAcSensorTempMax = 43; // Celsius
+
+const uint8_t kMirageAcPowerOff = 0x5F;
+const uint8_t kMirageAcSwingVOff = 0b0000; // 0
+const uint8_t kMirageAcSwingVLowest = 0b0011; // 3
+const uint8_t kMirageAcSwingVLow = 0b0101; // 5
+const uint8_t kMirageAcSwingVMiddle = 0b0111; // 7
+const uint8_t kMirageAcSwingVHigh = 0b1001; // 9
+const uint8_t kMirageAcSwingVHighest = 0b1011; // 11
+const uint8_t kMirageAcSwingVAuto = 0b1101; // 13
+
+
+/// Class for handling detailed Mirage 120-bit A/C messages.
+/// @note Inspired and derived from the work done at: https://github.com/r45635/HVAC-IR-Control
+/// @warning Consider this very alpha code. Seems to work, but not validated.
+class IRMirageAc {
+ public:
+ explicit IRMirageAc(const uint16_t pin, const bool inverted = false,
+ const bool use_modulation = true);
+ void stateReset(void);
+#if SEND_MIRAGE
+ void send(const uint16_t repeat = kMirageMinRepeat);
+ /// Run the calibration to calculate uSec timing offsets for this platform.
+ /// @return The uSec timing offset needed per modulation of the IR Led.
+ /// @note This will produce a 65ms IR signal pulse at 38kHz.
+ /// Only ever needs to be run once per object instantiation, if at all.
+ int8_t calibrate(void) { return _irsend.calibrate(); }
+#endif // SEND_MIRAGE
+ void begin(void);
+ void on(void);
+ void off(void);
+ void setPower(const bool on);
+ bool getPower(void) const;
+ void setTemp(const uint8_t degrees);
+ uint8_t getTemp(void) const;
+ void setFan(const uint8_t speed);
+ uint8_t getFan(void) const;
+ void setMode(const uint8_t mode);
+ uint8_t getMode(void) const;
+ uint8_t* getRaw(void);
+ void setRaw(const uint8_t* data);
+ uint32_t getClock(void) const;
+ void setClock(const uint32_t nr_of_seconds);
+ void setTurbo(const bool on);
+ bool getTurbo(void) const;
+ void setLight(const bool on);
+ bool getLight(void) const;
+ void setSleep(const bool on);
+ bool getSleep(void) const;
+ void setSwingV(const uint8_t position);
+ uint8_t getSwingV(void) const;
+ void setSwingH(const bool on);
+ bool getSwingH(void) const;
+ void setQuiet(const bool on);
+ bool getQuiet(void) const;
+ void setCleanToggle(const bool on);
+ bool getCleanToggle(void) const;
+ void setFilter(const bool on);
+ bool getFilter(void) const;
+ void setIFeel(const bool on);
+ bool getIFeel(void) const;
+ void setSensorTemp(const uint8_t degrees);
+ uint16_t getSensorTemp(void) const;
+ uint16_t getOnTimer(void) const;
+ uint16_t getOffTimer(void) const;
+ void setOnTimer(const uint16_t nr_of_mins);
+ void setOffTimer(const uint16_t nr_of_mins);
+ mirage_ac_remote_model_t getModel(const bool useRaw = false) const;
+ void setModel(const mirage_ac_remote_model_t model);
+ static mirage_ac_remote_model_t getModel(const uint8_t *state);
+ static bool validChecksum(const uint8_t* data);
+ static uint8_t calculateChecksum(const uint8_t* data);
+ static uint8_t convertMode(const stdAc::opmode_t mode);
+ static uint8_t convertFan(const stdAc::fanspeed_t speed,
+ const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1);
+ static uint8_t convertSwingV(const stdAc::swingv_t position);
+ static stdAc::opmode_t toCommonMode(const uint8_t mode);
+ static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed,
+ const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1);
+ static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
+ stdAc::state_t toCommon(void) const;
+ void fromCommon(const stdAc::state_t state);
+ String toString(void) const;
+#ifndef UNIT_TEST
+
+ private:
+ IRsend _irsend; ///< Instance of the IR send class
+#else // UNIT_TEST
+ /// @cond IGNORE
+ IRsendTest _irsend; ///< Instance of the testing IR send class
+ /// @endcond
+#endif // UNIT_TEST
+ Mirage120Protocol _;
+ mirage_ac_remote_model_t _model;
+ void checksum(void);
+};
+#endif // IR_MIRAGE_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.cpp
index 03e562ef5..160a882bf 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.cpp
@@ -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++;
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Rhoss.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Rhoss.cpp
new file mode 100644
index 000000000..f906f49be
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Rhoss.cpp
@@ -0,0 +1,364 @@
+// Copyright 2021 Tom Rosenback
+
+/// @file
+/// @brief Support for Rhoss protocols.
+
+#include "ir_Rhoss.h"
+#include
+#include
+#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;
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Rhoss.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Rhoss.h
new file mode 100644
index 000000000..8f66ce738
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Rhoss.h
@@ -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
+#ifndef UNIT_TEST
+#include
+#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_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.cpp
index 9e1ad4669..94cb52bba 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.cpp
@@ -1,5 +1,5 @@
// Copyright 2009 Ken Shirriff
-// Copyright 2017, 2018, 2019 David Conran
+// Copyright 2017-2021 David Conran
/// @file
/// @brief Support for Samsung protocols.
/// Samsung originally added from https://github.com/shirriff/Arduino-IRremote/
@@ -62,12 +62,25 @@ const uint16_t kSamsung36BitMark = 512; /// < uSeconds
const uint16_t kSamsung36OneSpace = 1468; /// < uSeconds
const uint16_t kSamsung36ZeroSpace = 490; /// < uSeconds
+// _.Swing
+const uint8_t kSamsungAcSwingV = 0b010;
+const uint8_t kSamsungAcSwingH = 0b011;
+const uint8_t kSamsungAcSwingBoth = 0b100;
+const uint8_t kSamsungAcSwingOff = 0b111;
+// _.FanSpecial
+const uint8_t kSamsungAcFanSpecialOff = 0b000;
+const uint8_t kSamsungAcPowerfulOn = 0b011;
+const uint8_t kSamsungAcBreezeOn = 0b101;
+const uint8_t kSamsungAcEconoOn = 0b111;
+
using irutils::addBoolToString;
using irutils::addFanToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
+using irutils::addToggleToString;
+using irutils::minsToString;
#if SEND_SAMSUNG
/// Send a 32-bit Samsung formatted message.
@@ -275,17 +288,22 @@ IRSamsungAc::IRSamsungAc(const uint16_t pin, const bool inverted,
}
/// Reset the internal state of the emulation.
-/// @param[in] forcepower A flag indicating if force sending a special power
+/// @param[in] extended A flag indicating if force sending a special extended
/// message with the first `send()` call.
/// @param[in] initialPower Set the initial power state. True, on. False, off.
-void IRSamsungAc::stateReset(const bool forcepower, const bool initialPower) {
+void IRSamsungAc::stateReset(const bool extended, const bool initialPower) {
static const uint8_t kReset[kSamsungAcExtendedStateLength] = {
0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
0x01, 0x02, 0xAE, 0x71, 0x00, 0x15, 0xF0};
std::memcpy(_.raw, kReset, kSamsungAcExtendedStateLength);
- _forcepower = forcepower;
+ _forceextended = extended;
_lastsentpowerstate = initialPower;
setPower(initialPower);
+ _OnTimerEnable = false;
+ _OffTimerEnable = false;
+ _Sleep = false;
+ _lastSleep = false;
+ _OnTimer = _OffTimer = _lastOnTimer = _lastOffTimer = 0;
}
/// Set up hardware to be able to send a message.
@@ -351,30 +369,28 @@ void IRSamsungAc::checksum(void) {
#if SEND_SAMSUNG_AC
/// Send the current internal state as an IR message.
/// @param[in] repeat Nr. of times the message will be repeated.
-/// @param[in] calcchecksum Do we update the checksum before sending?
/// @note Use for most function/mode/settings changes to the unit.
/// i.e. When the device is already running.
-void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) {
- // Do we need to send a the special power on/off message? i.e. An Extended Msg
- if (getPower() != _lastsentpowerstate || _forcepower) { // We do.
- sendExtended(repeat, calcchecksum);
- _forcepower = false; // It has now been sent, so clear the flag if set.
- } else { // No, it's just a normal message.
- if (calcchecksum) checksum();
- _irsend.sendSamsungAC(_.raw, kSamsungAcStateLength, repeat);
- }
+void IRSamsungAc::send(const uint16_t repeat) {
+ // Do we need to send a special (extended) message?
+ if (getPower() != _lastsentpowerstate || _forceextended ||
+ (_lastOnTimer != _OnTimer) || (_lastOffTimer != _OffTimer) ||
+ (_Sleep != _lastSleep)) // We do.
+ sendExtended(repeat);
+ else // No, it's just a normal message.
+ _irsend.sendSamsungAC(getRaw(), kSamsungAcStateLength, repeat);
}
/// Send the extended current internal state as an IR message.
/// @param[in] repeat Nr. of times the message will be repeated.
-/// @param[in] calcchecksum Do we update the checksum before sending?
-/// @note Use this for when you need to power on/off the device.
-/// Samsung A/C requires an extended length message when you want to
-/// change the power operating mode of the A/C unit.
-void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) {
+/// @note Samsung A/C requires an extended length message when you want to
+/// change the power operating mode, Timers, or Sleep setting of the A/C unit.
+void IRSamsungAc::sendExtended(const uint16_t repeat) {
+ _lastsentpowerstate = getPower(); // Remember the last power state sent.
+ _lastOnTimer = _OnTimer;
+ _lastOffTimer = _OffTimer;
static const uint8_t extended_middle_section[kSamsungAcSectionLength] = {
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00};
- if (calcchecksum) checksum();
// Copy/convert the internal state to an extended state by
// copying the second section to the third section, and inserting the extended
// middle (second) section.
@@ -383,13 +399,16 @@ void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) {
kSamsungAcSectionLength);
std::memcpy(_.raw + kSamsungAcSectionLength, extended_middle_section,
kSamsungAcSectionLength);
+ _setOnTimer();
+ _setSleepTimer(); // This also sets any Off Timer if needed too.
// Send it.
- _irsend.sendSamsungAC(_.raw, kSamsungAcExtendedStateLength, repeat);
+ _irsend.sendSamsungAC(getRaw(), kSamsungAcExtendedStateLength, repeat);
// Now revert it by copying the third section over the second section.
std::memcpy(_.raw + kSamsungAcSectionLength,
- _.raw + 2* kSamsungAcSectionLength,
+ _.raw + 2 * kSamsungAcSectionLength,
kSamsungAcSectionLength);
- _lastsentpowerstate = getPower(); // Remember the last power state sent.
+
+ _forceextended = false; // It has now been sent, so clear the flag if set.
}
/// Send the special extended "On" message as the library can't seem to
@@ -434,6 +453,11 @@ void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) {
kSamsungAcExtendedStateLength));
// Shrink the extended state into a normal state.
if (length > kSamsungAcStateLength) {
+ _OnTimerEnable = _.OnTimerEnable;
+ _OffTimerEnable = _.OffTimerEnable;
+ _Sleep = _.Sleep5 && _.Sleep12;
+ _OnTimer = _getOnTimer();
+ _OffTimer = _getOffTimer();
for (uint8_t i = kSamsungAcStateLength; i < length; i++)
_.raw[i - kSamsungAcSectionLength] = _.raw[i];
}
@@ -448,14 +472,13 @@ void IRSamsungAc::off(void) { setPower(false); }
/// Change the power setting.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setPower(const bool on) {
- _.Power1 = !on; // Cleared when on.
- _.Power6 = (on ? 0b11 : 0b00);
+ _.Power1 = _.Power2 = (on ? 0b11 : 0b00);
}
/// Get the value of the current power setting.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getPower(void) const {
- return (_.Power6 == 0b11) && !_.Power1;
+ return _.Power1 == 0b11 && _.Power2 == 0b11;
}
/// Set the temperature.
@@ -524,56 +547,79 @@ uint8_t IRSamsungAc::getFan(void) const {
/// Get the vertical swing setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1.
-/// e.g. 0xAE or 0XAF for swing move.
bool IRSamsungAc::getSwing(void) const {
- return _.Swing == kSamsungAcSwingMove;
+ switch (_.Swing) {
+ case kSamsungAcSwingV:
+ case kSamsungAcSwingBoth: return true;
+ default: return false;
+ }
}
/// Set the vertical swing setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-/// @todo (Hollako) Explain why sometimes the LSB of remote_state[9] is a 1.
-/// e.g. 0xAE or 0XAF for swing move.
void IRSamsungAc::setSwing(const bool on) {
- _.Swing = (on ? kSamsungAcSwingMove : kSamsungAcSwingStop);
+ switch (_.Swing) {
+ case kSamsungAcSwingBoth:
+ case kSamsungAcSwingH:
+ _.Swing = on ? kSamsungAcSwingBoth : kSamsungAcSwingH;
+ break;
+ default:
+ _.Swing = on ? kSamsungAcSwingV : kSamsungAcSwingOff;
+ }
}
-/// Get the Beep setting of the A/C.
+/// Get the horizontal swing setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRSamsungAc::getBeep(void) const {
- return _.Beep;
+bool IRSamsungAc::getSwingH(void) const {
+ switch (_.Swing) {
+ case kSamsungAcSwingH:
+ case kSamsungAcSwingBoth: return true;
+ default: return false;
+ }
}
-/// Set the Beep setting of the A/C.
+/// Set the horizontal swing setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRSamsungAc::setBeep(const bool on) {
- _.Beep = on;
+void IRSamsungAc::setSwingH(const bool on) {
+ switch (_.Swing) {
+ case kSamsungAcSwingV:
+ case kSamsungAcSwingBoth:
+ _.Swing = on ? kSamsungAcSwingBoth : kSamsungAcSwingV;
+ break;
+ default:
+ _.Swing = on ? kSamsungAcSwingH : kSamsungAcSwingOff;
+ }
}
-/// Get the Clean setting of the A/C.
+/// Get the Beep toggle setting of the A/C.
+/// @return true, the setting is on. false, the setting is off.
+bool IRSamsungAc::getBeep(void) const { return _.BeepToggle; }
+
+/// Set the Beep toggle setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRSamsungAc::setBeep(const bool on) { _.BeepToggle = on; }
+
+/// Get the Clean toggle setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getClean(void) const {
- return _.Clean10 && _.Clean11;
+ return _.CleanToggle10 && _.CleanToggle11;
}
-/// Set the Clean setting of the A/C.
+/// Set the Clean toggle setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setClean(const bool on) {
- _.Clean10 = on;
- _.Clean11 = on;
+ _.CleanToggle10 = on;
+ _.CleanToggle11 = on;
}
/// Get the Quiet setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRSamsungAc::getQuiet(void) const {
- return !_.Quiet1 && _.Quiet5;
-}
+bool IRSamsungAc::getQuiet(void) const { return _.Quiet; }
/// Set the Quiet setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setQuiet(const bool on) {
- _.Quiet1 = !on; // Cleared when on.
- _.Quiet5 = on;
+ _.Quiet = on;
if (on) {
// Quiet mode seems to set fan speed to auto.
setFan(kSamsungAcFanAuto);
@@ -584,25 +630,20 @@ void IRSamsungAc::setQuiet(const bool on) {
/// Get the Powerful (Turbo) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getPowerful(void) const {
- return !(_.Powerful8 & kSamsungAcPowerfulMask8) &&
- (_.Powerful10 == kSamsungAcPowerful10On) &&
+ return (_.FanSpecial == kSamsungAcPowerfulOn) &&
(_.Fan == kSamsungAcFanTurbo);
}
/// Set the Powerful (Turbo) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setPowerful(const bool on) {
- uint8_t off_value = getBreeze() ? kSamsungAcBreezeOn : 0b000;
- _.Powerful10 = (on ? kSamsungAcPowerful10On : off_value);
+ uint8_t off_value = (getBreeze() || getEcono()) ? _.FanSpecial
+ : kSamsungAcFanSpecialOff;
+ _.FanSpecial = (on ? kSamsungAcPowerfulOn : off_value);
if (on) {
- _.Powerful8 &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared.
// Powerful mode sets fan speed to Turbo.
setFan(kSamsungAcFanTurbo);
setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet.
- } else {
- _.Powerful8 |= kSamsungAcPowerfulMask8; // Bit needs to be set.
- // Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode
- if (_.Fan == kSamsungAcFanTurbo) setFan(kSamsungAcFanAuto);
}
}
@@ -610,7 +651,7 @@ void IRSamsungAc::setPowerful(const bool on) {
/// @return true, the setting is on. false, the setting is off.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062
bool IRSamsungAc::getBreeze(void) const {
- return (_.Breeze == kSamsungAcBreezeOn) &&
+ return (_.FanSpecial == kSamsungAcBreezeOn) &&
(_.Fan == kSamsungAcFanAuto && !getSwing());
}
@@ -618,36 +659,155 @@ bool IRSamsungAc::getBreeze(void) const {
/// @param[in] on true, the setting is on. false, the setting is off.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062
void IRSamsungAc::setBreeze(const bool on) {
- uint8_t off_value = getPowerful() ? kSamsungAcPowerful10On : 0b000;
- _.Breeze = (on ? kSamsungAcBreezeOn : off_value);
+ const uint8_t off_value = (getPowerful() ||
+ getEcono()) ? _.FanSpecial
+ : kSamsungAcFanSpecialOff;
+ _.FanSpecial = (on ? kSamsungAcBreezeOn : off_value);
if (on) {
setFan(kSamsungAcFanAuto);
setSwing(false);
}
}
+/// Get the current Economy (Eco) setting of the A/C.
+/// @return true, the setting is on. false, the setting is off.
+bool IRSamsungAc::getEcono(void) const {
+ return (_.FanSpecial == kSamsungAcEconoOn) &&
+ (_.Fan == kSamsungAcFanAuto && getSwing());
+}
+
+/// Set the current Economy (Eco) setting of the A/C.
+/// @param[in] on true, the setting is on. false, the setting is off.
+void IRSamsungAc::setEcono(const bool on) {
+ const uint8_t off_value = (getBreeze() ||
+ getPowerful()) ? _.FanSpecial
+ : kSamsungAcFanSpecialOff;
+ _.FanSpecial = (on ? kSamsungAcEconoOn : off_value);
+ if (on) {
+ setFan(kSamsungAcFanAuto);
+ setSwing(true);
+ }
+}
+
/// Get the Display (Light/LED) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRSamsungAc::getDisplay(void) const {
- return _.Display;
-}
+bool IRSamsungAc::getDisplay(void) const { return _.Display; }
/// Set the Display (Light/LED) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRSamsungAc::setDisplay(const bool on) {
- _.Display = on;
-}
+void IRSamsungAc::setDisplay(const bool on) { _.Display = on; }
/// Get the Ion (Filter) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
-bool IRSamsungAc::getIon(void) const {
- return _.Ion;
-}
+bool IRSamsungAc::getIon(void) const { return _.Ion; }
/// Set the Ion (Filter) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
-void IRSamsungAc::setIon(const bool on) {
- _.Ion = on;
+void IRSamsungAc::setIon(const bool on) { _.Ion = on; }
+
+/// Get the On Timer setting of the A/C from a raw extended state.
+/// @return The Nr. of minutes the On Timer is set for.
+uint16_t IRSamsungAc::_getOnTimer(void) const {
+ if (_.OnTimeDay) return 24 * 60;
+ return (_.OnTimeHrs2 * 2 + _.OnTimeHrs1) * 60 + _.OnTimeMins * 10;
+}
+
+/// Set the current On Timer value of the A/C into the raw extended state.
+void IRSamsungAc::_setOnTimer(void) {
+ _.OnTimerEnable = _OnTimerEnable = (_OnTimer > 0);
+ _.OnTimeDay = (_OnTimer >= 24 * 60);
+ if (_.OnTimeDay) {
+ _.OnTimeHrs2 = _.OnTimeHrs1 = _.OnTimeMins = 0;
+ return;
+ }
+ _.OnTimeMins = (_OnTimer % 60) / 10;
+ const uint8_t hours = _OnTimer / 60;
+ _.OnTimeHrs1 = hours & 0b1;
+ _.OnTimeHrs2 = hours >> 1;
+}
+
+/// Get the Off Timer setting of the A/C from a raw extended state.
+/// @return The Nr. of minutes the Off Timer is set for.
+uint16_t IRSamsungAc::_getOffTimer(void) const {
+ if (_.OffTimeDay) return 24 * 60;
+ return (_.OffTimeHrs2 * 2 + _.OffTimeHrs1) * 60 + _.OffTimeMins * 10;
+}
+
+/// Set the current Off Timer value of the A/C into the raw extended state.
+void IRSamsungAc::_setOffTimer(void) {
+ _.OffTimerEnable = _OffTimerEnable = (_OffTimer > 0);
+ _.OffTimeDay = (_OffTimer >= 24 * 60);
+ if (_.OffTimeDay) {
+ _.OffTimeHrs2 = _.OffTimeHrs1 = _.OffTimeMins = 0;
+ return;
+ }
+ _.OffTimeMins = (_OffTimer % 60) / 10;
+ const uint8_t hours = _OffTimer / 60;
+ _.OffTimeHrs1 = hours & 0b1;
+ _.OffTimeHrs2 = hours >> 1;
+}
+
+// Set the current Sleep Timer value of the A/C into the raw extended state.
+void IRSamsungAc::_setSleepTimer(void) {
+ _setOffTimer();
+ // The Sleep mode/timer should only be engaged if an off time has been set.
+ _.Sleep5 = _Sleep && _OffTimerEnable;
+ _.Sleep12 = _.Sleep5;
+}
+
+/// Get the On Timer setting of the A/C.
+/// @return The Nr. of minutes the On Timer is set for.
+uint16_t IRSamsungAc::getOnTimer(void) const { return _OnTimer; }
+
+/// Get the Off Timer setting of the A/C.
+/// @return The Nr. of minutes the Off Timer is set for.
+/// @note Sleep & Off Timer share the same timer.
+uint16_t IRSamsungAc::getOffTimer(void) const {
+ return _Sleep ? 0 : _OffTimer;
+}
+
+/// Get the Sleep Timer setting of the A/C.
+/// @return The Nr. of minutes the Off Timer is set for.
+/// @note Sleep & Off Timer share the same timer.
+uint16_t IRSamsungAc::getSleepTimer(void) const {
+ return _Sleep ? _OffTimer : 0;
+}
+
+#define TIMER_RESOLUTION(mins) \
+ (((std::min((mins), (uint16_t)(24 * 60))) / 10) * 10)
+
+/// Set the On Timer value of the A/C.
+/// @param[in] nr_of_mins The number of minutes the timer should be.
+/// @note The timer time only has a resolution of 10 mins.
+/// @note Setting the On Timer active will cancel the Sleep timer/setting.
+void IRSamsungAc::setOnTimer(const uint16_t nr_of_mins) {
+ // Limit to one day, and round down to nearest 10 min increment.
+ _OnTimer = TIMER_RESOLUTION(nr_of_mins);
+ _OnTimerEnable = _OnTimer > 0;
+ if (_OnTimer) _Sleep = false;
+}
+
+/// Set the Off Timer value of the A/C.
+/// @param[in] nr_of_mins The number of minutes the timer should be.
+/// @note The timer time only has a resolution of 10 mins.
+/// @note Setting the Off Timer active will cancel the Sleep timer/setting.
+void IRSamsungAc::setOffTimer(const uint16_t nr_of_mins) {
+ // Limit to one day, and round down to nearest 10 min increment.
+ _OffTimer = TIMER_RESOLUTION(nr_of_mins);
+ _OffTimerEnable = _OffTimer > 0;
+ if (_OffTimer) _Sleep = false;
+}
+
+/// Set the Sleep Timer value of the A/C.
+/// @param[in] nr_of_mins The number of minutes the timer should be.
+/// @note The timer time only has a resolution of 10 mins.
+/// @note Sleep timer acts as an Off timer, and cancels any On Timer.
+void IRSamsungAc::setSleepTimer(const uint16_t nr_of_mins) {
+ // Limit to one day, and round down to nearest 10 min increment.
+ _OffTimer = TIMER_RESOLUTION(nr_of_mins);
+ if (_OffTimer) setOnTimer(0); // Clear the on timer if set.
+ _Sleep = _OffTimer > 0;
+ _OffTimerEnable = _Sleep;
}
/// Convert a stdAc::opmode_t enum into its native mode.
@@ -714,18 +874,17 @@ stdAc::state_t IRSamsungAc::toCommon(void) const {
result.celsius = true;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
- result.swingv = getSwing() ? stdAc::swingv_t::kAuto :
- stdAc::swingv_t::kOff;
+ result.swingv = getSwing() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
+ result.swingh = getSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
result.quiet = getQuiet();
result.turbo = getPowerful();
+ result.econo = getEcono();
result.clean = getClean();
- result.beep = _.Beep;
+ result.beep = _.BeepToggle;
result.light = _.Display;
result.filter = _.Ion;
+ result.sleep = _Sleep ? getSleepTimer() : -1;
// Not supported.
- result.swingh = stdAc::swingh_t::kOff;
- result.econo = false;
- result.sleep = -1;
result.clock = -1;
return result;
}
@@ -734,7 +893,7 @@ stdAc::state_t IRSamsungAc::toCommon(void) const {
/// @return A human readable string.
String IRSamsungAc::toString(void) const {
String result = "";
- result.reserve(115); // Reserve some heap for the string to reduce fragging.
+ result.reserve(230); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(_.Mode, kSamsungAcAuto, kSamsungAcCool,
kSamsungAcHeat, kSamsungAcDry,
@@ -764,14 +923,21 @@ String IRSamsungAc::toString(void) const {
break;
}
result += ')';
- result += addBoolToString(getSwing(), kSwingStr);
- result += addBoolToString(_.Beep, kBeepStr);
- result += addBoolToString(getClean(), kCleanStr);
+ result += addBoolToString(getSwing(), kSwingVStr);
+ result += addBoolToString(getSwingH(), kSwingHStr);
+ result += addToggleToString(_.BeepToggle, kBeepStr);
+ result += addToggleToString(getClean(), kCleanStr);
result += addBoolToString(getQuiet(), kQuietStr);
result += addBoolToString(getPowerful(), kPowerfulStr);
+ result += addBoolToString(getEcono(), kEconoStr);
result += addBoolToString(getBreeze(), kBreezeStr);
result += addBoolToString(_.Display, kLightStr);
result += addBoolToString(_.Ion, kIonStr);
+ if (_OnTimerEnable)
+ result += addLabeledString(minsToString(_OnTimer), kOnTimerStr);
+ if (_OffTimerEnable)
+ result += addLabeledString(minsToString(_OffTimer),
+ _Sleep ? kSleepTimerStr : kOffTimerStr);
return result;
}
@@ -811,9 +977,6 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t offset,
offset += used;
}
// Compliance
- // Is the signature correct?
- DPRINTLN("DEBUG: Checking signature.");
- if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false;
if (strict) {
// Is the checksum valid?
if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) {
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.h
index bf9215edc..acabb3648 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Samsung.h
@@ -1,4 +1,4 @@
-// Copyright 2018 David Conran
+// Copyright 2018-2021 David Conran
/// @file
/// @brief Support for Samsung protocols.
/// Samsung originally added from https://github.com/shirriff/Arduino-IRremote/
@@ -7,6 +7,7 @@
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062
/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538 (Checksum)
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1277 (Timers)
// Supports:
// Brand: Samsung, Model: UA55H6300 TV (SAMSUNG)
@@ -18,11 +19,13 @@
// Brand: Samsung, Model: AH59-02692E Soundbar remote (SAMSUNG36)
// Brand: Samsung, Model: HW-J551 Soundbar (SAMSUNG36)
// Brand: Samsung, Model: AR09FSSDAWKNFA A/C (SAMSUNG_AC)
+// Brand: Samsung, Model: AR09HSFSBWKN A/C (SAMSUNG_AC)
// Brand: Samsung, Model: AR12KSFPEWQNET A/C (SAMSUNG_AC)
// Brand: Samsung, Model: AR12HSSDBWKNEU A/C (SAMSUNG_AC)
// Brand: Samsung, Model: AR12NXCXAWKXEU A/C (SAMSUNG_AC)
-// Brand: Samsung, Model: AR09HSFSBWKN A/C (SAMSUNG_AC)
+// Brand: Samsung, Model: AR12TXEAAWKNEU A/C (SAMSUNG_AC)
// Brand: Samsung, Model: DB93-14195A remote (SAMSUNG_AC)
+// Brand: Samsung, Model: DB96-24901C remote (SAMSUNG_AC)
#ifndef IR_SAMSUNG_H_
#define IR_SAMSUNG_H_
@@ -41,116 +44,125 @@
/// Native representation of a Samsung A/C message.
union SamsungProtocol{
uint8_t raw[kSamsungAcExtendedStateLength]; ///< State in code form.
- struct {
+ struct { // Standard message map
// Byte 0
- uint8_t :8;
+ uint8_t :8;
// Byte 1
uint8_t :4;
- uint8_t Quiet1 :1;
- uint8_t Power1 :1;
- uint8_t :2;
- // Byte 2~4
- uint8_t pad0[3];
+ uint8_t :4; // Sum1Lower
+ // Byte 2
+ uint8_t :4; // Sum1Upper
+ uint8_t :4;
+ // Byte 3
+ uint8_t :8;
+ // Byte 4
+ uint8_t :8;
// Byte 5
- uint8_t :5;
- uint8_t Quiet5 :1;
+ uint8_t :4;
+ uint8_t Sleep5 :1;
+ uint8_t Quiet :1;
uint8_t :2;
// Byte 6
uint8_t :4;
- uint8_t Power6 :2;
+ uint8_t Power1 :2;
uint8_t :2;
// Byte 7
- uint8_t :8;
+ uint8_t :8;
// Byte 8
- uint8_t Powerful8 :8;
+ uint8_t :4;
+ uint8_t :4; // Sum2Lower
// Byte 9
- uint8_t :4;
- uint8_t Swing :3;
- uint8_t :1;
+ uint8_t :4; // Sum1Upper
+ uint8_t Swing :3;
+ uint8_t :1;
// Byte 10
- uint8_t :1;
- uint8_t Powerful10 :3;
- uint8_t Display :1;
- uint8_t :2;
- uint8_t Clean10 :1;
+ uint8_t :1;
+ uint8_t FanSpecial :3; // Powerful, Breeze/WindFree, Econo
+ uint8_t Display :1;
+ uint8_t :2;
+ uint8_t CleanToggle10 :1;
// Byte 11
- uint8_t Ion :1;
- uint8_t Clean11 :1;
- uint8_t :2;
- uint8_t Temp :4;
+ uint8_t Ion :1;
+ uint8_t CleanToggle11 :1;
+ uint8_t :2;
+ uint8_t Temp :4;
// Byte 12
uint8_t :1;
uint8_t Fan :3;
uint8_t Mode :3;
uint8_t :1;
// Byte 13
- uint8_t :1;
- uint8_t Beep :1;
- uint8_t :6;
+ uint8_t :2;
+ uint8_t BeepToggle :1;
+ uint8_t :1;
+ uint8_t Power2 :2;
+ uint8_t :2;
};
- struct {
+ struct { // Extended message map
// 1st Section
// Byte 0
- uint8_t :8;
+ uint8_t :8;
// Byte 1
- uint8_t :4;
- uint8_t Sum1Lower :4;
+ uint8_t :4;
+ uint8_t Sum1Lower :4;
// Byte 2
- uint8_t Sum1Upper :4;
- uint8_t :4;
+ uint8_t Sum1Upper :4;
+ uint8_t :4;
// Byte 3
- uint8_t :8;
+ uint8_t :8;
// Byte 4
- uint8_t :8;
+ uint8_t :8;
// Byte 5
- uint8_t :8;
+ uint8_t :8;
// Byte 6
- uint8_t :8;
+ uint8_t :8;
// 2nd Section
// Byte 7
- uint8_t :8;
+ uint8_t :8;
// Byte 8
- uint8_t :4;
- uint8_t Sum2Lower :4;
+ uint8_t :4;
+ uint8_t Sum2Lower :4;
// Byte 9
- uint8_t Sum2Upper :4;
- uint8_t :4;
+ uint8_t Sum2Upper :4;
+ uint8_t OffTimeMins :3; // In units of 10's of mins
+ uint8_t OffTimeHrs1 :1; // LSB of the number of hours.
// Byte 10
- uint8_t :1;
- uint8_t Breeze :3; // WindFree
- uint8_t :4;
+ uint8_t OffTimeHrs2 :4; // MSBs of the number of hours.
+ uint8_t OnTimeMins :3; // In units of 10's of mins
+ uint8_t OnTimeHrs1 :1; // LSB of the number of hours.
// Byte 11
- uint8_t :8;
+ uint8_t OnTimeHrs2 :4; // MSBs of the number of hours.
+ uint8_t :4;
// Byte 12
- uint8_t :8;
+ uint8_t OffTimeDay :1;
+ uint8_t OnTimerEnable :1;
+ uint8_t OffTimerEnable :1;
+ uint8_t Sleep12 :1;
+ uint8_t OnTimeDay :1;
+ uint8_t :3;
// Byte 13
- uint8_t :8;
+ uint8_t :8;
// 3rd Section
// Byte 14
- uint8_t :8;
+ uint8_t :8;
// Byte 15
- uint8_t :4;
- uint8_t Sum3Lower :4;
+ uint8_t :4;
+ uint8_t Sum3Lower :4;
// Byte 16
- uint8_t Sum3Upper :4;
- uint8_t :4;
+ uint8_t Sum3Upper :4;
+ uint8_t :4;
// Byte 17
- uint8_t :8;
+ uint8_t :8;
// Byte 18
- uint8_t :8;
+ uint8_t :8;
// Byte 19
- uint8_t :8;
+ uint8_t :8;
// Byte 20
- uint8_t :8;
+ uint8_t :8;
};
};
// Constants
-const uint8_t kSamsungAcPowerfulMask8 = 0b01010000;
-const uint8_t kSamsungAcSwingMove = 0b010;
-const uint8_t kSamsungAcSwingStop = 0b111;
-const uint8_t kSamsungAcPowerful10On = 0b011;
-const uint8_t kSamsungAcBreezeOn = 0b101;
const uint8_t kSamsungAcMinTemp = 16; // C Mask 0b11110000
const uint8_t kSamsungAcMaxTemp = 30; // C Mask 0b11110000
const uint8_t kSamsungAcAutoTemp = 25; // C Mask 0b11110000
@@ -174,12 +186,10 @@ class IRSamsungAc {
public:
explicit IRSamsungAc(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
- void stateReset(const bool forcepower = true, const bool initialPower = true);
+ void stateReset(const bool extended = true, const bool initialPower = true);
#if SEND_SAMSUNG_AC
- void send(const uint16_t repeat = kSamsungAcDefaultRepeat,
- const bool calcchecksum = true);
- void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat,
- const bool calcchecksum = true);
+ void send(const uint16_t repeat = kSamsungAcDefaultRepeat);
+ void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat);
void sendOn(const uint16_t repeat = kSamsungAcDefaultRepeat);
void sendOff(const uint16_t repeat = kSamsungAcDefaultRepeat);
/// Run the calibration to calculate uSec timing offsets for this platform.
@@ -201,6 +211,8 @@ class IRSamsungAc {
uint8_t getMode(void) const;
void setSwing(const bool on);
bool getSwing(void) const;
+ void setSwingH(const bool on);
+ bool getSwingH(void) const;
void setBeep(const bool on);
bool getBeep(void) const;
void setClean(const bool on);
@@ -211,10 +223,18 @@ class IRSamsungAc {
bool getPowerful(void) const;
void setBreeze(const bool on);
bool getBreeze(void) const;
+ void setEcono(const bool on);
+ bool getEcono(void) const;
void setDisplay(const bool on);
bool getDisplay(void) const;
void setIon(const bool on);
bool getIon(void) const;
+ uint16_t getOnTimer(void) const;
+ void setOnTimer(const uint16_t nr_of_mins);
+ uint16_t getOffTimer(void) const;
+ void setOffTimer(const uint16_t nr_of_mins);
+ uint16_t getSleepTimer(void) const;
+ void setSleepTimer(const uint16_t nr_of_mins);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kSamsungAcStateLength);
@@ -238,9 +258,22 @@ class IRSamsungAc {
/// @endcond
#endif // UNIT_TEST
SamsungProtocol _;
- bool _forcepower; ///< Hack to know when we need to send a special power mesg
+ bool _forceextended; ///< Flag to know when we need to send an extended mesg.
bool _lastsentpowerstate;
+ bool _OnTimerEnable;
+ bool _OffTimerEnable;
+ bool _Sleep;
+ bool _lastSleep;
+ uint16_t _OnTimer;
+ uint16_t _OffTimer;
+ uint16_t _lastOnTimer;
+ uint16_t _lastOffTimer;
void checksum(void);
+ uint16_t _getOnTimer(void) const;
+ uint16_t _getOffTimer(void) const;
+ void _setOnTimer(void);
+ void _setOffTimer(void);
+ void _setSleepTimer(void);
};
#endif // IR_SAMSUNG_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.cpp
index 8cbace41e..38f0ac32c 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.cpp
@@ -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)
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.h
index dd6d826ac..b3be534e7 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sharp.h
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Symphony.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Symphony.cpp
index d5fedb29b..b629ef72d 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Symphony.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Symphony.cpp
@@ -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
#include "IRrecv.h"
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.cpp
index cce9d1f4c..a9e8784a3 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.cpp
@@ -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 += addBoolToString(_.Econo, kEconoStr);
- result += addBoolToString(_.Health, kHealthStr);
- result += addBoolToString(_.Turbo, kTurboStr);
- result += addBoolToString(_.SwingH, kSwingHStr);
- result += addBoolToString(_.SwingV, kSwingVStr);
- result += addBoolToString(getLight(), kLightStr);
+ 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(getLight(), kLightStr);
+ }
+ result += addLabeledString(
+ _.OnTimerEnabled ? minsToString(getOnTimer()) : kOffStr,
+ kOnTimerStr);
+ result += addLabeledString(
+ _.OffTimerEnabled ? minsToString(getOffTimer()) : kOffStr,
+ kOffTimerStr);
break;
case kTcl112AcSpecial:
result += addBoolToString(_.Quiet, kQuietStr);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.h
index b9f059627..e1696c42a 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Tcl.h
@@ -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,41 +27,54 @@ 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;
+ uint8_t MsgType :2;
+ uint8_t :6;
// Byte 4
- uint8_t :8;
+ uint8_t :8;
// Byte 5
- uint8_t :2;
- uint8_t Power :1;
- uint8_t :2;
- uint8_t Quiet :1;
- uint8_t Light :1;
- uint8_t Econo :1;
+ uint8_t :2;
+ uint8_t Power :1;
+ uint8_t OffTimerEnabled :1;
+ uint8_t OnTimerEnabled :1;
+ uint8_t Quiet :1;
+ uint8_t Light :1;
+ uint8_t Econo :1;
// Byte 6
- uint8_t Mode :4;
- uint8_t Health :1;
- uint8_t Turbo :1;
- uint8_t :2;
+ uint8_t Mode :4;
+ uint8_t Health :1;
+ uint8_t Turbo :1;
+ uint8_t :2;
// Byte 7
- uint8_t Temp :4;
- uint8_t :4;
+ uint8_t Temp :4;
+ uint8_t :4;
// Byte 8
- uint8_t Fan :3;
- uint8_t SwingV :3;
- uint8_t :2;
- // Byte 9~11
- uint8_t pad1[3];
+ uint8_t Fan :3;
+ uint8_t SwingV :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 :3;
+ uint8_t SwingH :1;
+ uint8_t :1;
+ uint8_t HalfDegree :1;
+ uint8_t :1;
+ uint8_t isTcl :1;
// Byte 13
- uint8_t Sum :8;
+ 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 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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Transcold.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Transcold.cpp
index 066196073..14cc7f8bd 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Transcold.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Transcold.cpp
@@ -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);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h
index b55b87abc..0fcc04791 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/pt-BR.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/pt-BR.h
index 4875dda0d..4b2c929e0 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/pt-BR.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/pt-BR.h
@@ -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"
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/ru-RU.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/ru-RU.h
new file mode 100644
index 000000000..02a892403
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/ru-RU.h
@@ -0,0 +1,152 @@
+// Copyright 2021 - PtilopsisLeucotis (@PtilopsisLeucotis)
+// Locale/language file for Russian / Russia.
+// This file will override the default values located in `defaults.h`.
+#ifndef LOCALE_RU_RU_H_
+#define LOCALE_RU_RU_H_
+
+#define D_STR_UNKNOWN "НЕИЗВЕСТНО"
+#define D_STR_PROTOCOL "Протокол"
+#define D_STR_POWER "Питание"
+#define D_STR_PREVIOUS "Предыдущий"
+#define D_STR_ON "Вкл"
+#define D_STR_OFF "Выкл"
+#define D_STR_MODE "Режим"
+#define D_STR_TOGGLE "Переключить"
+#define D_STR_TURBO "Турбо"
+#define D_STR_SUPER "Супер"
+#define D_STR_SLEEP "Сон"
+#define D_STR_LIGHT "Свет"
+#define D_STR_POWERFUL "Мощный"
+#define D_STR_QUIET "Тихий"
+#define D_STR_ECONO "Экономичный"
+#define D_STR_SWING "Качание"
+#define D_STR_SWINGH D_STR_SWING"(Г)"
+#define D_STR_SWINGV D_STR_SWING"(В)"
+#define D_STR_BEEP "Звук"
+#define D_STR_MOULD "Плесень"
+#define D_STR_CLEAN "Чистый"
+#define D_STR_PURIFY "Очистка"
+#define D_STR_TIMER "Таймер"
+#define D_STR_ONTIMER "Таймер Включения"
+#define D_STR_OFFTIMER "Таймер Выключения"
+#define D_STR_TIMERMODE "Режим Таймера"
+#define D_STR_CLOCK "Часы"
+#define D_STR_COMMAND "Команда"
+#define D_STR_HEALTH "Здоровье"
+#define D_STR_MODEL "Модель"
+#define D_STR_TEMP "Температура"
+#define D_STR_HUMID "Влажность"
+#define D_STR_SAVE "Сохранить"
+#define D_STR_EYE "Глаз"
+#define D_STR_FOLLOW "Следовать"
+#define D_STR_ION "Ион"
+#define D_STR_FRESH "Свежесть"
+#define D_STR_HOLD "Удержать"
+#define D_STR_BUTTON "Кнопка"
+#define D_STR_NIGHT "Ночь"
+#define D_STR_SILENT "Тихий"
+#define D_STR_FILTER "Фильтр"
+#define D_STR_CELSIUS "Цельсий"
+#define D_STR_FAHRENHEIT "Фаренгейт"
+#define D_STR_UP "Выше"
+#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP
+#define D_STR_DOWN "Ниже"
+#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN
+#define D_STR_CHANGE "Изменение"
+#define D_STR_START "Запуск"
+#define D_STR_STOP "Остановка"
+#define D_STR_MOVE "Перемещение"
+#define D_STR_SET "Установка"
+#define D_STR_CANCEL "Отмена"
+#define D_STR_COMFORT "Комфорт"
+#define D_STR_SENSOR "Сенсор"
+#define D_STR_DISPLAY "Дисплей"
+#define D_STR_WEEKLY "Недельный"
+#define D_STR_LAST "Последний"
+#define D_STR_FAST "Быстро"
+#define D_STR_SLOW "Медленно"
+#define D_STR_AIRFLOW "Воздушный Поток"
+#define D_STR_STEP "Шаг"
+#define D_STR_NA "Н/Д"
+#define D_STR_INSIDE "Внутри"
+#define D_STR_OUTSIDE "Снаружи"
+#define D_STR_LOUD "Громко"
+#define D_STR_UPPER "Верхнее"
+#define D_STR_LOWER "Нижнее"
+#define D_STR_BREEZE "Бриз"
+#define D_STR_CIRCULATE "Циркуляция"
+#define D_STR_CEILING "Потолок"
+#define D_STR_WALL "Стена"
+#define D_STR_ROOM "Комната"
+#define D_STR_6THSENSE "6-ое чувство"
+#define D_STR_FIXED "Фиксированный"
+#define D_STR_TYPE "Тип"
+#define D_STR_SPECIAL "Специальный"
+#define D_STR_RECYCLE "Рециркуляция"
+#define D_STR_VANE "Жалюзи"
+#define D_STR_LOCK "Блокировка"
+#define D_STR_AUTO "Авто"
+#define D_STR_AUTOMATIC "Автоматический"
+#define D_STR_MANUAL "Ручной"
+#define D_STR_COOL "Охл"
+#define D_STR_COOLING "Охлаждение"
+#define D_STR_HEAT "Нагр"
+#define D_STR_HEATING "Обогрев"
+#define D_STR_FAN "Вентиляция"
+#define D_STR_ONLY "Только"
+#define D_STR_FANSPACEONLY D_STR_ONLY " " D_STR_FAN
+#define D_STR_FANONLYNOSPACE D_STR_ONLY D_STR_FAN
+#define D_STR_DRY "Сухо"
+#define D_STR_DRYING "Сушка"
+#define D_STR_DEHUMIDIFY "Осушение"
+#define D_STR_MAX "Макс"
+#define D_STR_MAXIMUM "Максимум"
+#define D_STR_MIN "Мин"
+#define D_STR_MINIMUM "Минимум"
+#define D_STR_MED "Сред"
+#define D_STR_MEDIUM "Среднее"
+#define D_STR_HIGHEST "Верхнее"
+#define D_STR_HIGH "Верх"
+#define D_STR_HI "Верх"
+#define D_STR_MID "Сред"
+#define D_STR_MIDDLE "Середина"
+#define D_STR_LOW "Низ"
+#define D_STR_LO "Низ"
+#define D_STR_LOWEST "Нижнее"
+#define D_STR_RIGHT "Право"
+#define D_STR_LEFT "Лево"
+#define D_STR_WIDE "Широкий"
+#define D_STR_CENTRE "Центр"
+#define D_STR_TOP "Наивысший"
+#define D_STR_BOTTOM "Наинизший"
+#define D_STR_DAY "День"
+#define D_STR_DAYS "Дней"
+#define D_STR_HOUR "Час"
+#define D_STR_HOURS "Часов"
+#define D_STR_MINUTE "Минута"
+#define D_STR_MINUTES "Минут"
+#define D_STR_SECOND "Секунда"
+#define D_STR_SECONDS "Секунд"
+#define D_STR_NOW "Сейчас"
+#define D_STR_THREELETTERDAYS "ВскПндВтрСреЧтвПтнСуб"
+#define D_STR_YES "Да"
+#define D_STR_NO "Нет"
+#define D_STR_TRUE "Истина"
+#define D_STR_FALSE "Ложь"
+#define D_STR_REPEAT "Повтор"
+#define D_STR_CODE "Код"
+#define D_STR_BITS "Бит"
+
+// IRrecvDumpV2+
+#define D_STR_TIMESTAMP "Метка Времени"
+#define D_STR_LIBRARY "Библиотека"
+#define D_STR_MESGDESC "Описание Сообщения."
+#define D_STR_TOLERANCE "Допуск"
+#define D_STR_IRRECVDUMP_STARTUP \
+ "IRrecvDump запущен и ождает ИК команды на Входе %d"
+#define D_WARN_BUFFERFULL \
+ "ПРЕДУПРЕЖДЕНИЕ: ИК код слишком велик для буфера (>= %d). " \
+ "Этому результату не следует доверять, пока это не будет исправлено. " \
+ "Исправьте и увеличьте `kCaptureBufferSize`."
+
+#endif // LOCALE_RU_RU_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/sv-SE.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/sv-SE.h
new file mode 100644
index 000000000..b82ec42bc
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/sv-SE.h
@@ -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_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp
index 140e50137..d43cfcd9d 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp
@@ -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,19 +793,24 @@ TEST(TestIRac, Haier176) {
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
const char expected[] =
- "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 23C, "
- "Fan: 2 (Medium), Turbo: 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,
- true, // Power
- stdAc::opmode_t::kCool, // Mode
- 23, // Celsius
- stdAc::fanspeed_t::kMedium, // Fan speed
- stdAc::swingv_t::kHigh, // Vertical swing
- true, // Turbo
- true, // Filter
- 8 * 60 + 0); // Sleep time
+ haier_ac176_remote_model_t::V9014557_A, // Model
+ true, // Power
+ stdAc::opmode_t::kCool, // Mode
+ true, // Celsius
+ 23, // Degrees
+ stdAc::fanspeed_t::kMedium, // Fan speed
+ stdAc::swingv_t::kHigh, // Vertical swing
+ stdAc::swingh_t::kOff, // Horizontal swing
+ true, // Turbo
+ false, // Quiet
+ true, // Filter
+ 8 * 60 + 0); // Sleep time
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
@@ -816,20 +826,24 @@ 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
- stdAc::fanspeed_t::kMedium, // Fan speed
- stdAc::swingv_t::kHigh, // Vertical swing
- true, // Turbo
- true, // Filter
- 8 * 60 + 0); // Sleep time
+ true, // Power
+ stdAc::opmode_t::kCool, // Mode
+ true, // Celsius
+ 23, // Degrees
+ stdAc::fanspeed_t::kMedium, // Fan speed
+ stdAc::swingv_t::kHigh, // Vertical swing
+ stdAc::swingh_t::kAuto, // Vertical swing
+ false, // Turbo
+ true, // Quiet
+ true, // Filter
+ 8 * 60 + 0); // Sleep time
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
@@ -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,23 +1841,25 @@ 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,
- true, // Power
- stdAc::opmode_t::kCool, // Mode
- 20, // Celsius
- stdAc::fanspeed_t::kMedium, // Fan speed
- stdAc::swingv_t::kOff, // Vertical swing
- stdAc::swingh_t::kAuto, // Horizontal swing
- false, // Quiet (aka. Mute)
- false, // Turbo
- true, // Light
- true, // Econo
- true); // Filter (aka. Health)
+ tcl_ac_remote_model_t::TAC09CHSD, // Model
+ true, // Power
+ stdAc::opmode_t::kCool, // Mode
+ 20, // Celsius
+ stdAc::fanspeed_t::kMedium, // Fan speed
+ stdAc::swingv_t::kOff, // Vertical swing
+ stdAc::swingh_t::kAuto, // Horizontal swing
+ false, // Quiet (aka. Mute)
+ false, // Turbo
+ true, // Light
+ true, // Econo
+ true); // Filter (aka. Health)
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
@@ -1668,23 +1871,24 @@ TEST(TestIRac, Tcl112) {
// Test the quiet mode, which should generate two messages.
ac._irsend.reset();
irac.tcl112(&ac,
- true, // Power
- stdAc::opmode_t::kCool, // Mode
- 20, // Celsius
- stdAc::fanspeed_t::kMedium, // Fan speed
- stdAc::swingv_t::kOff, // Vertical swing
- stdAc::swingh_t::kAuto, // Horizontal swing
- true, // Quiet (aka. Mute)
- false, // Turbo
- true, // Light
- true, // Econo
- true); // Filter (aka. Health)
+ tcl_ac_remote_model_t::TAC09CHSD, // Model
+ true, // Power
+ stdAc::opmode_t::kCool, // Mode
+ 20, // Celsius
+ stdAc::fanspeed_t::kMedium, // Fan speed
+ stdAc::swingv_t::kOff, // Vertical swing
+ stdAc::swingh_t::kAuto, // Horizontal swing
+ true, // Quiet (aka. Mute)
+ false, // Turbo
+ true, // Light
+ true, // Econo
+ true); // Filter (aka. Health)
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(TCL112AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kTcl112AcBits, ac._irsend.capture.bits);
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) {
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile
index 132350803..7a193e813 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Airton_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Airton_test.cpp
new file mode 100644
index 000000000..e4e66ee13
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Airton_test.cpp
@@ -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));
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Arris_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Arris_test.cpp
new file mode 100644
index 000000000..2a219b6b2
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Arris_test.cpp
@@ -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));
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Electra_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Electra_test.cpp
index 1fddf537b..9c1dedd09 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Electra_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Electra_test.cpp
@@ -101,7 +101,8 @@ TEST(TestDecodeElectraAC, RealExampleDecode) {
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
- "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off",
+ "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
+ "IFeel: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@@ -235,7 +236,8 @@ TEST(TestIRElectraAcClass, HumanReadable) {
ac.setRaw(on_cool_32C_auto_voff);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 32C, Fan: 5 (Auto), "
- "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off",
+ "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
+ "IFeel: Off",
ac.toString());
uint8_t on_cool_16C_auto_voff[13] = {
0xC3, 0x47, 0xE0, 0x00, 0xA0, 0x00, 0x20,
@@ -243,7 +245,8 @@ TEST(TestIRElectraAcClass, HumanReadable) {
ac.setRaw(on_cool_16C_auto_voff);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 5 (Auto), "
- "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off",
+ "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
+ "IFeel: Off",
ac.toString());
uint8_t on_cool_16C_low_voff[13] = {
0xC3, 0x47, 0xE0, 0x00, 0x60, 0x00, 0x20,
@@ -251,7 +254,8 @@ TEST(TestIRElectraAcClass, HumanReadable) {
ac.setRaw(on_cool_16C_low_voff);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 3 (Low), "
- "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off",
+ "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
+ "IFeel: Off",
ac.toString());
}
@@ -275,7 +279,8 @@ TEST(TestIRElectraAcClass, Clean) {
ac.setRaw(on);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
- "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off",
+ "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, "
+ "IFeel: Off",
ac.toString());
}
@@ -301,7 +306,8 @@ TEST(TestIRElectraAcClass, Turbo) {
ac.setRaw(on);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
- "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: On",
+ "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: On, "
+ "IFeel: Off",
ac.toString());
}
@@ -325,7 +331,8 @@ TEST(TestIRElectraAcClass, LightToggle) {
ac.setRaw(on);
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
- "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off",
+ "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, "
+ "IFeel: Off",
ac.toString());
}
@@ -352,8 +359,76 @@ TEST(TestIRElectraAcClass, ConstructKnownState) {
EXPECT_EQ(
"Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 3 (Low), "
- "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off",
+ "Swing(V): Off, Swing(H): Off, Light: Toggle, Clean: On, Turbo: Off, "
+ "IFeel: Off",
ac.toString());
EXPECT_STATE_EQ(on_cool_24C_fan1_swing_off_turbo_off_clean_on,
ac.getRaw(), kElectraAcBits);
}
+
+TEST(TestIRElectraAcClass, IFeelAndSensor) {
+ IRElectraAc ac(kGpioUnused);
+ ac.stateReset();
+ // Test a real example.
+ const uint8_t ifeel_on[kElectraAcStateLength] = {
+ 0xC3, 0x6F, 0xE0, 0x00, 0xA0, 0x00, 0x28,
+ 0x64, 0x00, 0x20, 0x00, 0x1E, 0x7C};
+ ac.setRaw(ifeel_on);
+ EXPECT_TRUE(ac.getIFeel());
+ EXPECT_EQ(26, ac.getSensorTemp());
+ EXPECT_EQ(
+ "Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 5 (Auto), "
+ "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
+ "IFeel: On, Sensor Temp: 26C",
+ ac.toString());
+
+ ac.stateReset();
+ EXPECT_FALSE(ac.getIFeel());
+ EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp());
+
+ ac.setIFeel(true);
+ EXPECT_TRUE(ac.getIFeel());
+ EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp());
+
+ ac.setSensorTemp(kElectraAcSensorMaxTemp);
+ EXPECT_EQ(kElectraAcSensorMaxTemp, ac.getSensorTemp());
+
+ ac.setSensorTemp(kElectraAcSensorMaxTemp + 1);
+ EXPECT_EQ(kElectraAcSensorMaxTemp, ac.getSensorTemp());
+
+ ac.setIFeel(false);
+ EXPECT_FALSE(ac.getIFeel());
+ EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp());
+ EXPECT_EQ(0, ac._.SensorTemp);
+
+ ac.setIFeel(true);
+ ac.setSensorTemp(kElectraAcSensorMinTemp);
+ EXPECT_TRUE(ac.getIFeel());
+ EXPECT_EQ(kElectraAcSensorMinTemp, ac.getSensorTemp());
+
+ ac.setSensorTemp(26); // Celsius
+ EXPECT_TRUE(ac.getIFeel());
+ EXPECT_EQ(26, ac.getSensorTemp());
+
+ EXPECT_FALSE(ac.getSensorUpdate());
+ ac.setSensorUpdate(true);
+ EXPECT_TRUE(ac.getSensorUpdate());
+ EXPECT_EQ("Sensor Temp: 26C", ac.toString());
+ ac.setSensorUpdate(false);
+ EXPECT_FALSE(ac.getSensorUpdate());
+
+ const uint8_t sensor_update_28C[kElectraAcStateLength] = {
+ 0xC3, 0x9F, 0xE0, 0x40, 0xA0, 0x00, 0x88,
+ 0x66, 0x00, 0x30, 0x00, 0x1E, 0x5E};
+ ac.setRaw(sensor_update_28C);
+ EXPECT_TRUE(ac.getSensorUpdate());
+ EXPECT_EQ(28, ac.getSensorTemp());
+ EXPECT_EQ("Sensor Temp: 28C", ac.toString());
+ ac.setSensorUpdate(false);
+ EXPECT_FALSE(ac.getSensorUpdate());
+ EXPECT_EQ(
+ "Power: On, Mode: 4 (Heat), Temp: 27C, Fan: 5 (Auto), "
+ "Swing(V): Off, Swing(H): Off, Light: -, Clean: Off, Turbo: Off, "
+ "IFeel: On, Sensor Temp: 28C",
+ ac.toString());
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Gree_test.cpp
index 597fd3c09..cb1832f61 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Gree_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Gree_test.cpp
@@ -303,9 +303,10 @@ TEST(TestGreeClass, Temperature) {
EXPECT_EQ(63, ac.getTemp());
EXPECT_EQ(
"Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 63F, Fan: 0 (Auto), "
- "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
- "Swing(V) Mode: Manual, Swing(V): 0 (Last), Timer: Off, "
- "Display Temp: 0 (Off)", ac.toString());
+ "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
+ "Sleep: Off, "
+ "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), "
+ "Timer: Off, Display Temp: 0 (Off)", ac.toString());
}
TEST(TestGreeClass, OperatingMode) {
@@ -385,6 +386,20 @@ TEST(TestGreeClass, Turbo) {
EXPECT_TRUE(ac.getTurbo());
}
+TEST(TestGreeClass, Econo) {
+ IRGreeAC ac(kGpioUnused);
+ ac.begin();
+
+ ac.setEcono(true);
+ EXPECT_TRUE(ac.getEcono());
+
+ ac.setEcono(false);
+ EXPECT_FALSE(ac.getEcono());
+
+ ac.setEcono(true);
+ EXPECT_TRUE(ac.getEcono());
+}
+
TEST(TestGreeClass, IFeel) {
IRGreeAC ac(kGpioUnused);
ac.begin();
@@ -506,6 +521,24 @@ TEST(TestGreeClass, VerticalSwing) {
EXPECT_EQ(kGreeSwingAuto, ac.getSwingVerticalPosition());
}
+TEST(TestGreeClass, HorizontalSwing) {
+ IRGreeAC ac(kGpioUnused);
+ ac.begin();
+
+ ac.setSwingHorizontal(kGreeSwingHAuto);
+ EXPECT_EQ(kGreeSwingHAuto, ac.getSwingHorizontal());
+
+ ac.setSwingHorizontal(kGreeSwingHMiddle);
+ EXPECT_EQ(kGreeSwingHMiddle, ac.getSwingHorizontal());
+
+ ac.setSwingHorizontal(kGreeSwingHMaxRight);
+ EXPECT_EQ(kGreeSwingHMaxRight, ac.getSwingHorizontal());
+
+ // Out of bounds.
+ ac.setSwingHorizontal(kGreeSwingHMaxRight + 1);
+ EXPECT_EQ(kGreeSwingHOff, ac.getSwingHorizontal());
+}
+
TEST(TestGreeClass, SetAndGetRaw) {
IRGreeAC ac(kGpioUnused);
uint8_t initialState[kGreeStateLength] = {0x00, 0x09, 0x20, 0x50,
@@ -543,8 +576,9 @@ TEST(TestGreeClass, HumanReadable) {
EXPECT_EQ(
"Model: 1 (YAW1F), Power: Off, Mode: 0 (Auto), Temp: 25C, Fan: 0 (Auto), "
- "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
- "Swing(V) Mode: Manual, Swing(V): 0 (Last), "
+ "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
+ "Sleep: Off, "
+ "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), "
"Timer: Off, Display Temp: 0 (Off)",
ac.toString());
ac.on();
@@ -562,9 +596,10 @@ TEST(TestGreeClass, HumanReadable) {
ac.setDisplayTempSource(3);
EXPECT_EQ(
"Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 3 (High), "
- "Turbo: On, IFeel: On, WiFi: On, XFan: On, Light: Off, Sleep: On, "
- "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Timer: 12:30, "
- "Display Temp: 3 (Outside)",
+ "Turbo: On, Econo: Off, IFeel: On, WiFi: On, XFan: On, Light: Off, "
+ "Sleep: On, "
+ "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), "
+ "Timer: 12:30, Display Temp: 3 (Outside)",
ac.toString());
}
@@ -623,9 +658,10 @@ TEST(TestDecodeGree, NormalRealExample) {
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 26C, Fan: 1 (Low), "
- "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
- "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Timer: Off, "
- "Display Temp: 3 (Outside)",
+ "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
+ "Sleep: Off, "
+ "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Swing(H): 0 (Off), "
+ "Timer: Off, Display Temp: 3 (Outside)",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@@ -681,8 +717,9 @@ TEST(TestGreeClass, Issue814Power) {
EXPECT_EQ(gree_ac_remote_model_t::YBOFB, ac.getModel());
EXPECT_EQ(
"Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 1 (Low), "
- "Turbo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, Sleep: Off, "
- "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Timer: Off, "
+ "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
+ "Sleep: Off, "
+ "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), Timer: Off, "
"Display Temp: 0 (Off)",
ac.toString());
ac.off();
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Haier_test.cpp
index 8b89b1098..b60a08f23 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Haier_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Haier_test.cpp
@@ -105,597 +105,711 @@ TEST(TestSendHaierAC, SendWithRepeats) {
// Tests for IRHaierAC class.
TEST(TestHaierACClass, Command) {
- IRHaierAC haier(kGpioUnused);
- haier.begin();
+ IRHaierAC ac(kGpioUnused);
+ ac.begin();
- haier.setCommand(kHaierAcCmdOff);
- EXPECT_EQ(kHaierAcCmdOff, haier.getCommand());
- haier.setCommand(kHaierAcCmdOn);
- EXPECT_EQ(kHaierAcCmdOn, haier.getCommand());
- haier.setCommand(kHaierAcCmdMode);
- EXPECT_EQ(kHaierAcCmdMode, haier.getCommand());
- haier.setCommand(kHaierAcCmdFan);
- EXPECT_EQ(kHaierAcCmdFan, haier.getCommand());
- haier.setCommand(kHaierAcCmdTempUp);
- EXPECT_EQ(kHaierAcCmdTempUp, haier.getCommand());
- haier.setCommand(kHaierAcCmdTempDown);
- EXPECT_EQ(kHaierAcCmdTempDown, haier.getCommand());
- haier.setCommand(kHaierAcCmdSleep);
- EXPECT_EQ(kHaierAcCmdSleep, haier.getCommand());
- haier.setCommand(kHaierAcCmdTimerSet);
- EXPECT_EQ(kHaierAcCmdTimerSet, haier.getCommand());
- haier.setCommand(kHaierAcCmdTimerCancel);
- EXPECT_EQ(kHaierAcCmdTimerCancel, haier.getCommand());
- haier.setCommand(kHaierAcCmdHealth);
- EXPECT_EQ(kHaierAcCmdHealth, haier.getCommand());
- haier.setCommand(kHaierAcCmdSwing);
- EXPECT_EQ(kHaierAcCmdSwing, haier.getCommand());
- haier.setCommand(kHaierAcCmdOn);
- EXPECT_EQ(kHaierAcCmdOn, haier.getCommand());
- haier.setCommand(kHaierAcCmdOff);
- EXPECT_EQ(kHaierAcCmdOff, haier.getCommand());
+ ac.setCommand(kHaierAcCmdOff);
+ EXPECT_EQ(kHaierAcCmdOff, ac.getCommand());
+ ac.setCommand(kHaierAcCmdOn);
+ EXPECT_EQ(kHaierAcCmdOn, ac.getCommand());
+ ac.setCommand(kHaierAcCmdMode);
+ EXPECT_EQ(kHaierAcCmdMode, ac.getCommand());
+ ac.setCommand(kHaierAcCmdFan);
+ EXPECT_EQ(kHaierAcCmdFan, ac.getCommand());
+ ac.setCommand(kHaierAcCmdTempUp);
+ EXPECT_EQ(kHaierAcCmdTempUp, ac.getCommand());
+ ac.setCommand(kHaierAcCmdTempDown);
+ EXPECT_EQ(kHaierAcCmdTempDown, ac.getCommand());
+ ac.setCommand(kHaierAcCmdSleep);
+ EXPECT_EQ(kHaierAcCmdSleep, ac.getCommand());
+ ac.setCommand(kHaierAcCmdTimerSet);
+ EXPECT_EQ(kHaierAcCmdTimerSet, ac.getCommand());
+ ac.setCommand(kHaierAcCmdTimerCancel);
+ EXPECT_EQ(kHaierAcCmdTimerCancel, ac.getCommand());
+ ac.setCommand(kHaierAcCmdHealth);
+ EXPECT_EQ(kHaierAcCmdHealth, ac.getCommand());
+ ac.setCommand(kHaierAcCmdSwing);
+ EXPECT_EQ(kHaierAcCmdSwing, ac.getCommand());
+ ac.setCommand(kHaierAcCmdOn);
+ EXPECT_EQ(kHaierAcCmdOn, ac.getCommand());
+ ac.setCommand(kHaierAcCmdOff);
+ EXPECT_EQ(kHaierAcCmdOff, ac.getCommand());
// Test unexpected values.
- haier.setCommand(0b00001110);
- EXPECT_EQ(kHaierAcCmdOff, haier.getCommand());
- haier.setCommand(0b00001111);
- EXPECT_EQ(kHaierAcCmdOff, haier.getCommand());
- haier.setCommand(0b00000100);
- EXPECT_EQ(kHaierAcCmdOff, haier.getCommand());
+ ac.setCommand(0b00001110);
+ EXPECT_EQ(kHaierAcCmdOff, ac.getCommand());
+ ac.setCommand(0b00001111);
+ EXPECT_EQ(kHaierAcCmdOff, ac.getCommand());
+ ac.setCommand(0b00000100);
+ EXPECT_EQ(kHaierAcCmdOff, ac.getCommand());
}
TEST(TestHaierACClass, OperatingMode) {
- IRHaierAC haier(kGpioUnused);
- haier.begin();
+ IRHaierAC ac(kGpioUnused);
+ ac.begin();
- haier.setMode(kHaierAcAuto);
- EXPECT_EQ(kHaierAcAuto, haier.getMode());
+ ac.setMode(kHaierAcAuto);
+ EXPECT_EQ(kHaierAcAuto, ac.getMode());
- haier.setMode(kHaierAcCool);
- EXPECT_EQ(kHaierAcCool, haier.getMode());
+ ac.setMode(kHaierAcCool);
+ EXPECT_EQ(kHaierAcCool, ac.getMode());
- haier.setMode(kHaierAcHeat);
- EXPECT_EQ(kHaierAcHeat, haier.getMode());
+ ac.setMode(kHaierAcHeat);
+ EXPECT_EQ(kHaierAcHeat, ac.getMode());
- haier.setMode(kHaierAcFan);
- EXPECT_EQ(kHaierAcFan, haier.getMode());
+ ac.setMode(kHaierAcFan);
+ EXPECT_EQ(kHaierAcFan, ac.getMode());
- haier.setMode(kHaierAcDry);
- EXPECT_EQ(kHaierAcDry, haier.getMode());
+ ac.setMode(kHaierAcDry);
+ EXPECT_EQ(kHaierAcDry, ac.getMode());
- haier.setMode(kHaierAcAuto - 1);
- EXPECT_EQ(kHaierAcAuto, haier.getMode());
+ ac.setMode(kHaierAcAuto - 1);
+ EXPECT_EQ(kHaierAcAuto, ac.getMode());
- haier.setMode(kHaierAcCool);
- EXPECT_EQ(kHaierAcCool, haier.getMode());
+ ac.setMode(kHaierAcCool);
+ EXPECT_EQ(kHaierAcCool, ac.getMode());
- haier.setMode(kHaierAcFan + 1);
- EXPECT_EQ(kHaierAcAuto, haier.getMode());
+ ac.setMode(kHaierAcFan + 1);
+ EXPECT_EQ(kHaierAcAuto, ac.getMode());
- haier.setMode(255);
- EXPECT_EQ(kHaierAcAuto, haier.getMode());
+ ac.setMode(255);
+ EXPECT_EQ(kHaierAcAuto, ac.getMode());
}
TEST(TestHaierACClass, Temperature) {
- IRHaierAC haier(kGpioUnused);
- haier.begin();
+ IRHaierAC ac(kGpioUnused);
+ ac.begin();
- haier.setTemp(kHaierAcMinTemp);
- EXPECT_EQ(kHaierAcMinTemp, haier.getTemp());
+ ac.setTemp(kHaierAcMinTemp);
+ EXPECT_EQ(kHaierAcMinTemp, ac.getTemp());
- haier.setCommand(kHaierAcCmdOn);
- haier.setTemp(kHaierAcMinTemp + 1);
- EXPECT_EQ(kHaierAcMinTemp + 1, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdTempUp, haier.getCommand());
+ ac.setCommand(kHaierAcCmdOn);
+ ac.setTemp(kHaierAcMinTemp + 1);
+ EXPECT_EQ(kHaierAcMinTemp + 1, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdTempUp, ac.getCommand());
- haier.setTemp(kHaierAcMaxTemp);
- EXPECT_EQ(kHaierAcMaxTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdTempUp, haier.getCommand());
+ ac.setTemp(kHaierAcMaxTemp);
+ EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdTempUp, ac.getCommand());
- haier.setTemp(kHaierAcMinTemp - 1);
- EXPECT_EQ(kHaierAcMinTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdTempDown, haier.getCommand());
+ ac.setTemp(kHaierAcMinTemp - 1);
+ EXPECT_EQ(kHaierAcMinTemp, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdTempDown, ac.getCommand());
- haier.setTemp(kHaierAcMaxTemp + 1);
- EXPECT_EQ(kHaierAcMaxTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdTempUp, haier.getCommand());
+ ac.setTemp(kHaierAcMaxTemp + 1);
+ EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdTempUp, ac.getCommand());
- haier.setTemp(23);
- EXPECT_EQ(23, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdTempDown, haier.getCommand());
- haier.setCommand(kHaierAcCmdOn);
- haier.setTemp(23);
- EXPECT_EQ(23, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdOn, haier.getCommand());
+ ac.setTemp(23);
+ EXPECT_EQ(23, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdTempDown, ac.getCommand());
+ ac.setCommand(kHaierAcCmdOn);
+ ac.setTemp(23);
+ EXPECT_EQ(23, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdOn, ac.getCommand());
- haier.setTemp(0);
- EXPECT_EQ(kHaierAcMinTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdTempDown, haier.getCommand());
+ ac.setTemp(0);
+ EXPECT_EQ(kHaierAcMinTemp, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdTempDown, ac.getCommand());
- haier.setTemp(255);
- EXPECT_EQ(kHaierAcMaxTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcCmdTempUp, haier.getCommand());
+ ac.setTemp(255);
+ EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp());
+ EXPECT_EQ(kHaierAcCmdTempUp, ac.getCommand());
}
TEST(TestHaierACClass, FanSpeed) {
- IRHaierAC haier(kGpioUnused);
- haier.begin();
- haier.setFan(kHaierAcFanLow);
- haier.setCommand(kHaierAcCmdOn);
+ IRHaierAC ac(kGpioUnused);
+ ac.begin();
+ ac.setFan(kHaierAcFanLow);
+ ac.setCommand(kHaierAcCmdOn);
- haier.setFan(kHaierAcFanAuto);
- EXPECT_EQ(kHaierAcFanAuto, haier.getFan());
- EXPECT_EQ(kHaierAcCmdFan, haier.getCommand());
+ ac.setFan(kHaierAcFanAuto);
+ EXPECT_EQ(kHaierAcFanAuto, ac.getFan());
+ EXPECT_EQ(kHaierAcCmdFan, ac.getCommand());
- haier.setFan(kHaierAcFanLow);
- EXPECT_EQ(kHaierAcFanLow, haier.getFan());
- haier.setFan(kHaierAcFanMed);
- EXPECT_EQ(kHaierAcFanMed, haier.getFan());
- haier.setFan(kHaierAcFanHigh);
- EXPECT_EQ(kHaierAcFanHigh, haier.getFan());
+ ac.setFan(kHaierAcFanLow);
+ EXPECT_EQ(kHaierAcFanLow, ac.getFan());
+ ac.setFan(kHaierAcFanMed);
+ EXPECT_EQ(kHaierAcFanMed, ac.getFan());
+ ac.setFan(kHaierAcFanHigh);
+ EXPECT_EQ(kHaierAcFanHigh, ac.getFan());
- haier.setCommand(kHaierAcCmdOn);
- haier.setFan(kHaierAcFanHigh);
- EXPECT_EQ(kHaierAcFanHigh, haier.getFan());
- EXPECT_EQ(kHaierAcCmdOn, haier.getCommand());
+ ac.setCommand(kHaierAcCmdOn);
+ ac.setFan(kHaierAcFanHigh);
+ EXPECT_EQ(kHaierAcFanHigh, ac.getFan());
+ EXPECT_EQ(kHaierAcCmdOn, ac.getCommand());
}
-TEST(TestHaierACClass, Swing) {
- IRHaierAC haier(kGpioUnused);
- haier.begin();
- haier.setFan(kHaierAcFanLow);
- haier.setCommand(kHaierAcCmdOn);
+TEST(TestHaierACClass, SwingV) {
+ IRHaierAC ac(kGpioUnused);
+ ac.begin();
+ ac.setFan(kHaierAcFanLow);
+ ac.setCommand(kHaierAcCmdOn);
- haier.setSwing(kHaierAcSwingOff);
- EXPECT_EQ(kHaierAcSwingOff, haier.getSwing());
+ ac.setSwingV(kHaierAcSwingVOff);
+ EXPECT_EQ(kHaierAcSwingVOff, ac.getSwingV());
- haier.setSwing(kHaierAcSwingUp);
- EXPECT_EQ(kHaierAcSwingUp, haier.getSwing());
- EXPECT_EQ(kHaierAcCmdSwing, haier.getCommand());
+ ac.setSwingV(kHaierAcSwingVUp);
+ EXPECT_EQ(kHaierAcSwingVUp, ac.getSwingV());
+ EXPECT_EQ(kHaierAcCmdSwing, ac.getCommand());
- haier.setSwing(kHaierAcSwingDown);
- EXPECT_EQ(kHaierAcSwingDown, haier.getSwing());
- EXPECT_EQ(kHaierAcCmdSwing, haier.getCommand());
+ ac.setSwingV(kHaierAcSwingVDown);
+ EXPECT_EQ(kHaierAcSwingVDown, ac.getSwingV());
+ EXPECT_EQ(kHaierAcCmdSwing, ac.getCommand());
- haier.setSwing(kHaierAcSwingChg);
- EXPECT_EQ(kHaierAcSwingChg, haier.getSwing());
- EXPECT_EQ(kHaierAcCmdSwing, haier.getCommand());
+ ac.setSwingV(kHaierAcSwingVChg);
+ EXPECT_EQ(kHaierAcSwingVChg, ac.getSwingV());
+ EXPECT_EQ(kHaierAcCmdSwing, ac.getCommand());
}
TEST(TestHaierACClass, CurrentTime) {
- IRHaierAC haier(kGpioUnused);
- haier.begin();
- EXPECT_EQ(0, haier.getCurrTime());
+ IRHaierAC ac(kGpioUnused);
+ ac.begin();
+ EXPECT_EQ(0, ac.getCurrTime());
- haier.setCurrTime(1);
- EXPECT_EQ(1, haier.getCurrTime());
+ ac.setCurrTime(1);
+ EXPECT_EQ(1, ac.getCurrTime());
- haier.setCurrTime(60);
- EXPECT_EQ(60, haier.getCurrTime());
+ ac.setCurrTime(60);
+ EXPECT_EQ(60, ac.getCurrTime());
- haier.setCurrTime(61);
- EXPECT_EQ(61, haier.getCurrTime());
+ ac.setCurrTime(61);
+ EXPECT_EQ(61, ac.getCurrTime());
- haier.setCurrTime(18 * 60 + 34); // 18:34
- EXPECT_EQ(1114, haier.getCurrTime());
+ ac.setCurrTime(18 * 60 + 34); // 18:34
+ EXPECT_EQ(1114, ac.getCurrTime());
- haier.setCurrTime(23 * 60 + 59); // 23:59
- EXPECT_EQ(kHaierAcMaxTime, haier.getCurrTime()); // 23:59
+ ac.setCurrTime(23 * 60 + 59); // 23:59
+ EXPECT_EQ(kHaierAcMaxTime, ac.getCurrTime()); // 23:59
- haier.setCurrTime(23 * 60 + 59 + 1); // 24:00
- EXPECT_EQ(kHaierAcMaxTime, haier.getCurrTime()); // 23:59
+ ac.setCurrTime(23 * 60 + 59 + 1); // 24:00
+ EXPECT_EQ(kHaierAcMaxTime, ac.getCurrTime()); // 23:59
- haier.setCurrTime(UINT16_MAX);
- EXPECT_EQ(kHaierAcMaxTime, haier.getCurrTime()); // 23:59
+ ac.setCurrTime(UINT16_MAX);
+ EXPECT_EQ(kHaierAcMaxTime, ac.getCurrTime()); // 23:59
}
TEST(TestHaierACClass, Timers) {
- IRHaierAC haier(kGpioUnused);
- haier.begin();
+ IRHaierAC ac(kGpioUnused);
+ ac.begin();
- haier.setCommand(kHaierAcCmdOn);
+ ac.setCommand(kHaierAcCmdOn);
// Off by default.
- EXPECT_GT(0, haier.getOnTimer());
- EXPECT_GT(0, haier.getOffTimer());
- EXPECT_EQ(kHaierAcCmdOn, haier.getCommand());
+ EXPECT_GT(0, ac.getOnTimer());
+ EXPECT_GT(0, ac.getOffTimer());
+ EXPECT_EQ(kHaierAcCmdOn, ac.getCommand());
// On Timer.
- haier.setOnTimer(6 * 60); // 6am
- EXPECT_EQ(6 * 60, haier.getOnTimer()); // 6am
- EXPECT_GT(0, haier.getOffTimer());
- EXPECT_EQ(kHaierAcCmdTimerSet, haier.getCommand());
+ ac.setOnTimer(6 * 60); // 6am
+ EXPECT_EQ(6 * 60, ac.getOnTimer()); // 6am
+ EXPECT_GT(0, ac.getOffTimer());
+ EXPECT_EQ(kHaierAcCmdTimerSet, ac.getCommand());
- haier.setCommand(kHaierAcCmdOn);
- EXPECT_EQ(6 * 60, haier.getOnTimer()); // 6am
- EXPECT_GT(0, haier.getOffTimer());
- EXPECT_EQ(kHaierAcCmdOn, haier.getCommand());
+ ac.setCommand(kHaierAcCmdOn);
+ EXPECT_EQ(6 * 60, ac.getOnTimer()); // 6am
+ EXPECT_GT(0, ac.getOffTimer());
+ EXPECT_EQ(kHaierAcCmdOn, ac.getCommand());
- haier.cancelTimers();
- EXPECT_GT(0, haier.getOnTimer());
- EXPECT_GT(0, haier.getOffTimer());
- EXPECT_EQ(kHaierAcCmdTimerCancel, haier.getCommand());
+ ac.cancelTimers();
+ EXPECT_GT(0, ac.getOnTimer());
+ EXPECT_GT(0, ac.getOffTimer());
+ EXPECT_EQ(kHaierAcCmdTimerCancel, ac.getCommand());
// Off Timer.
- haier.setOffTimer(18 * 60 + 30); // 6:30pm
- EXPECT_GT(0, haier.getOnTimer());
- EXPECT_EQ(18 * 60 + 30, haier.getOffTimer()); // 6:30pm
- EXPECT_EQ(kHaierAcCmdTimerSet, haier.getCommand());
+ ac.setOffTimer(18 * 60 + 30); // 6:30pm
+ EXPECT_GT(0, ac.getOnTimer());
+ EXPECT_EQ(18 * 60 + 30, ac.getOffTimer()); // 6:30pm
+ EXPECT_EQ(kHaierAcCmdTimerSet, ac.getCommand());
- haier.setCommand(kHaierAcCmdOn);
- EXPECT_GT(0, haier.getOnTimer());
- EXPECT_EQ(18 * 60 + 30, haier.getOffTimer()); // 6:30pm
- EXPECT_EQ(kHaierAcCmdOn, haier.getCommand());
+ ac.setCommand(kHaierAcCmdOn);
+ EXPECT_GT(0, ac.getOnTimer());
+ EXPECT_EQ(18 * 60 + 30, ac.getOffTimer()); // 6:30pm
+ EXPECT_EQ(kHaierAcCmdOn, ac.getCommand());
- haier.cancelTimers();
- EXPECT_GT(0, haier.getOnTimer());
- EXPECT_GT(0, haier.getOffTimer());
- EXPECT_EQ(kHaierAcCmdTimerCancel, haier.getCommand());
+ ac.cancelTimers();
+ EXPECT_GT(0, ac.getOnTimer());
+ EXPECT_GT(0, ac.getOffTimer());
+ EXPECT_EQ(kHaierAcCmdTimerCancel, ac.getCommand());
// Both Timers.
- haier.setOnTimer(6 * 60); // 6am
- EXPECT_EQ(kHaierAcCmdTimerSet, haier.getCommand());
- haier.setOffTimer(18 * 60 + 30); // 6:30pm
- EXPECT_EQ(kHaierAcCmdTimerSet, haier.getCommand());
- EXPECT_EQ(6 * 60, haier.getOnTimer()); // 6am
- EXPECT_EQ(18 * 60 + 30, haier.getOffTimer()); // 6:30pm
+ ac.setOnTimer(6 * 60); // 6am
+ EXPECT_EQ(kHaierAcCmdTimerSet, ac.getCommand());
+ ac.setOffTimer(18 * 60 + 30); // 6:30pm
+ EXPECT_EQ(kHaierAcCmdTimerSet, ac.getCommand());
+ EXPECT_EQ(6 * 60, ac.getOnTimer()); // 6am
+ EXPECT_EQ(18 * 60 + 30, ac.getOffTimer()); // 6:30pm
- haier.cancelTimers();
- EXPECT_GT(0, haier.getOnTimer());
- EXPECT_GT(0, haier.getOffTimer());
- EXPECT_EQ(kHaierAcCmdTimerCancel, haier.getCommand());
+ ac.cancelTimers();
+ EXPECT_GT(0, ac.getOnTimer());
+ EXPECT_GT(0, ac.getOffTimer());
+ EXPECT_EQ(kHaierAcCmdTimerCancel, ac.getCommand());
}
TEST(TestHaierACClass, MessageConstuction) {
- IRHaierAC haier(kGpioUnused);
+ IRHaierAC ac(kGpioUnused);
EXPECT_EQ(
"Command: 1 (On), Mode: 0 (Auto), Temp: 25C, Fan: 1 (Low), "
- "Swing: 0 (Off), Sleep: Off, Health: Off, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: Off, "
"Clock: 00:00, On Timer: Off, Off Timer: Off",
- haier.toString());
- haier.setMode(kHaierAcCool);
- haier.setTemp(21);
- haier.setFan(kHaierAcFanHigh);
+ ac.toString());
+ ac.setMode(kHaierAcCool);
+ ac.setTemp(21);
+ ac.setFan(kHaierAcFanHigh);
EXPECT_EQ(
"Command: 3 (Fan), Mode: 1 (Cool), Temp: 21C, Fan: 3 (High), "
- "Swing: 0 (Off), Sleep: Off, Health: Off, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: Off, "
"Clock: 00:00, On Timer: Off, Off Timer: Off",
- haier.toString());
- haier.setSwing(kHaierAcSwingChg);
- haier.setHealth(true);
- haier.setSleep(true);
- haier.setCurrTime(615); // 10:15am
+ ac.toString());
+ ac.setSwingV(kHaierAcSwingVChg);
+ ac.setHealth(true);
+ ac.setSleep(true);
+ ac.setCurrTime(615); // 10:15am
EXPECT_EQ(
"Command: 8 (Sleep), Mode: 1 (Cool), Temp: 21C, Fan: 3 (High), "
- "Swing: 3 (Change), Sleep: On, Health: On, "
+ "Swing(V): 3 (Change), Sleep: On, Health: On, "
"Clock: 10:15, On Timer: Off, Off Timer: Off",
- haier.toString());
- haier.setOnTimer(800); // 1:20pm
- haier.setOffTimer(1125); // 6:45pm
- haier.setCommand(kHaierAcCmdOn);
+ ac.toString());
+ ac.setOnTimer(800); // 1:20pm
+ ac.setOffTimer(1125); // 6:45pm
+ ac.setCommand(kHaierAcCmdOn);
EXPECT_EQ(
"Command: 1 (On), Mode: 1 (Cool), Temp: 21C, Fan: 3 (High), "
- "Swing: 3 (Change), Sleep: On, Health: On, "
+ "Swing(V): 3 (Change), Sleep: On, Health: On, "
"Clock: 10:15, On Timer: 13:20, Off Timer: 18:45",
- haier.toString());
+ ac.toString());
// Now change a few already set things.
- haier.setMode(kHaierAcHeat);
+ ac.setMode(kHaierAcHeat);
EXPECT_EQ(
"Command: 2 (Mode), Mode: 3 (Heat), Temp: 21C, Fan: 3 (High), "
- "Swing: 3 (Change), Sleep: On, Health: On, "
+ "Swing(V): 3 (Change), Sleep: On, Health: On, "
"Clock: 10:15, On Timer: 13:20, Off Timer: 18:45",
- haier.toString());
+ ac.toString());
- haier.setTemp(25);
+ ac.setTemp(25);
EXPECT_EQ(
"Command: 6 (Temp Up), Mode: 3 (Heat), Temp: 25C, Fan: 3 (High), "
- "Swing: 3 (Change), Sleep: On, Health: On, "
+ "Swing(V): 3 (Change), Sleep: On, Health: On, "
"Clock: 10:15, On Timer: 13:20, Off Timer: 18:45",
- haier.toString());
+ ac.toString());
uint8_t expectedState[kHaierACStateLength] = {0xA5, 0x96, 0xEA, 0xCF, 0x32,
0x6D, 0x6D, 0x54, 0x54};
- EXPECT_STATE_EQ(expectedState, haier.getRaw(), kHaierACBits);
+ EXPECT_STATE_EQ(expectedState, ac.getRaw(), kHaierACBits);
// Check that the checksum is valid.
- EXPECT_TRUE(IRHaierAC::validChecksum(haier.getRaw()));
+ EXPECT_TRUE(IRHaierAC::validChecksum(ac.getRaw()));
// Now load up some random data.
uint8_t randomState[kHaierACStateLength] = {0x52, 0x49, 0x50, 0x20, 0x54,
0x61, 0x6C, 0x69, 0x61};
EXPECT_FALSE(IRHaierAC::validChecksum(randomState));
- haier.setRaw(randomState);
+ ac.setRaw(randomState);
EXPECT_EQ(
"Command: 9 (Timer Set), Mode: 3 (Heat), Temp: 20C, Fan: 3 (High), "
- "Swing: 1 (Up), Sleep: On, Health: Off, "
+ "Swing(V): 1 (Up), Sleep: On, Health: Off, "
"Clock: 16:32, On Timer: Off, Off Timer: Off",
- haier.toString());
+ ac.toString());
// getRaw() should correct the checksum.
- EXPECT_TRUE(IRHaierAC::validChecksum(haier.getRaw()));
+ EXPECT_TRUE(IRHaierAC::validChecksum(ac.getRaw()));
}
// Tests for the IRHaierACYRW02 class.
TEST(TestHaierACYRW02Class, Button) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setButton(kHaierAcYrw02ButtonPower);
- EXPECT_EQ(kHaierAcYrw02ButtonPower, haier.getButton());
- haier.setButton(kHaierAcYrw02ButtonMode);
- EXPECT_EQ(kHaierAcYrw02ButtonMode, haier.getButton());
- haier.setButton(kHaierAcYrw02ButtonSleep);
- EXPECT_EQ(kHaierAcYrw02ButtonSleep, haier.getButton());
- haier.setButton(kHaierAcYrw02ButtonFan);
+ ac.setButton(kHaierAcYrw02ButtonPower);
+ EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
+ ac.setButton(kHaierAcYrw02ButtonMode);
+ EXPECT_EQ(kHaierAcYrw02ButtonMode, ac.getButton());
+ ac.setButton(kHaierAcYrw02ButtonSleep);
+ EXPECT_EQ(kHaierAcYrw02ButtonSleep, ac.getButton());
+ ac.setButton(kHaierAcYrw02ButtonFan);
// Test unexpected values.
- haier.setButton(0xFF);
- EXPECT_EQ(kHaierAcYrw02ButtonFan, haier.getButton());
- haier.setButton(0x10);
- EXPECT_EQ(kHaierAcYrw02ButtonFan, haier.getButton());
+ ac.setButton(0xFF);
+ EXPECT_EQ(kHaierAcYrw02ButtonFan, ac.getButton());
+ ac.setButton(0x10);
+ EXPECT_EQ(kHaierAcYrw02ButtonFan, ac.getButton());
}
TEST(TestHaierACYRW02Class, OperatingMode) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setButton(kHaierAcYrw02ButtonPower);
- haier.setMode(kHaierAcYrw02Auto);
- EXPECT_EQ(kHaierAcYrw02Auto, haier.getMode());
- EXPECT_EQ(kHaierAcYrw02ButtonMode, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonPower);
+ ac.setMode(kHaierAcYrw02Auto);
+ EXPECT_EQ(kHaierAcYrw02Auto, ac.getMode());
+ EXPECT_EQ(kHaierAcYrw02ButtonMode, ac.getButton());
- haier.setMode(kHaierAcYrw02Cool);
- EXPECT_EQ(kHaierAcYrw02Cool, haier.getMode());
+ ac.setMode(kHaierAcYrw02Cool);
+ EXPECT_EQ(kHaierAcYrw02Cool, ac.getMode());
- haier.setMode(kHaierAcYrw02Heat);
- EXPECT_EQ(kHaierAcYrw02Heat, haier.getMode());
+ ac.setMode(kHaierAcYrw02Heat);
+ EXPECT_EQ(kHaierAcYrw02Heat, ac.getMode());
- haier.setMode(kHaierAcYrw02Fan);
- EXPECT_EQ(kHaierAcYrw02Fan, haier.getMode());
+ ac.setMode(kHaierAcYrw02Fan);
+ EXPECT_EQ(kHaierAcYrw02Fan, ac.getMode());
- haier.setMode(kHaierAcYrw02Dry);
- EXPECT_EQ(kHaierAcYrw02Dry, haier.getMode());
+ ac.setMode(kHaierAcYrw02Dry);
+ EXPECT_EQ(kHaierAcYrw02Dry, ac.getMode());
- haier.setMode(kHaierAcYrw02Auto - 1);
- EXPECT_EQ(kHaierAcYrw02Auto, haier.getMode());
+ ac.setMode(kHaierAcYrw02Auto - 1);
+ EXPECT_EQ(kHaierAcYrw02Auto, ac.getMode());
- haier.setMode(kHaierAcYrw02Cool);
- EXPECT_EQ(kHaierAcYrw02Cool, haier.getMode());
+ ac.setMode(kHaierAcYrw02Cool);
+ EXPECT_EQ(kHaierAcYrw02Cool, ac.getMode());
- haier.setMode(kHaierAcYrw02Fan + 1);
- EXPECT_EQ(kHaierAcYrw02Auto, haier.getMode());
+ ac.setMode(kHaierAcYrw02Fan + 1);
+ EXPECT_EQ(kHaierAcYrw02Auto, ac.getMode());
- haier.setMode(255);
- EXPECT_EQ(kHaierAcYrw02Auto, haier.getMode());
+ ac.setMode(255);
+ EXPECT_EQ(kHaierAcYrw02Auto, ac.getMode());
}
TEST(TestHaierACYRW02Class, Temperature) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setTemp(kHaierAcMinTemp);
- EXPECT_EQ(kHaierAcMinTemp, haier.getTemp());
+ ac.setTemp(kHaierAcYrw02MinTempC);
+ EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp());
- haier.setButton(kHaierAcYrw02ButtonPower);
- haier.setTemp(kHaierAcMinTemp + 1);
- EXPECT_EQ(kHaierAcMinTemp + 1, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonTempUp, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonPower);
+ ac.setTemp(kHaierAcYrw02MinTempC + 1);
+ EXPECT_EQ(kHaierAcYrw02MinTempC + 1, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
- haier.setTemp(kHaierAcMaxTemp);
- EXPECT_EQ(kHaierAcMaxTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonTempUp, haier.getButton());
+ ac.setTemp(kHaierAcYrw02MaxTempC);
+ EXPECT_EQ(kHaierAcYrw02MaxTempC, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
- haier.setTemp(kHaierAcMinTemp - 1);
- EXPECT_EQ(kHaierAcMinTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonTempDown, haier.getButton());
+ ac.setTemp(kHaierAcYrw02MinTempC - 1);
+ EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
- haier.setTemp(kHaierAcMaxTemp + 1);
- EXPECT_EQ(kHaierAcMaxTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonTempUp, haier.getButton());
+ ac.setTemp(kHaierAcYrw02MaxTempC + 1);
+ EXPECT_EQ(kHaierAcYrw02MaxTempC, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
- haier.setTemp(23);
- EXPECT_EQ(23, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonTempDown, haier.getButton());
- haier.setButton(kHaierAcYrw02ButtonPower);
- haier.setTemp(23);
- EXPECT_EQ(23, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonPower, haier.getButton());
+ ac.setTemp(23);
+ EXPECT_EQ(23, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
+ ac.setButton(kHaierAcYrw02ButtonPower);
+ ac.setTemp(23);
+ EXPECT_EQ(23, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
- haier.setTemp(0);
- EXPECT_EQ(kHaierAcMinTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonTempDown, haier.getButton());
+ ac.setTemp(kHaierAcYrw02MinTempF, true);
+ EXPECT_EQ(kHaierAcYrw02MinTempF, ac.getTemp());
- haier.setTemp(255);
- EXPECT_EQ(kHaierAcMaxTemp, haier.getTemp());
- EXPECT_EQ(kHaierAcYrw02ButtonTempUp, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonPower);
+ ac.setTemp(kHaierAcYrw02MinTempF + 1, true);
+ EXPECT_EQ(kHaierAcYrw02MinTempF + 1, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
+
+ ac.setTemp(kHaierAcYrw02MaxTempF, true);
+ EXPECT_EQ(kHaierAcYrw02MaxTempF, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
+
+ ac.setTemp(kHaierAcYrw02MinTempF - 1, true);
+ EXPECT_EQ(kHaierAcYrw02MinTempF, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
+
+ ac.setTemp(kHaierAcYrw02MaxTempF + 1, true);
+ EXPECT_EQ(kHaierAcYrw02MaxTempF, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
+
+ ac.setTemp(66, true);
+ EXPECT_EQ(66, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
+ ac.setButton(kHaierAcYrw02ButtonPower);
+ ac.setTemp(66, true);
+ EXPECT_EQ(66, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
+
+ // Test specific cases for converting to Fahrenheit
+ ac.setTemp(76, true);
+ EXPECT_EQ(76, ac.getTemp());
+ ac.setTemp(77, true);
+ EXPECT_EQ(77, ac.getTemp());
+ ac.setTemp(78, true);
+ EXPECT_EQ(78, ac.getTemp());
+
+ ac.setTemp(24);
+ EXPECT_EQ(kHaierAcYrw02ButtonCFAB, ac.getButton());
+
+ ac.setTemp(0);
+ EXPECT_EQ(kHaierAcYrw02MinTempC, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempDown, ac.getButton());
+
+ ac.setTemp(255);
+ EXPECT_EQ(kHaierAcMaxTemp, ac.getTemp());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
}
TEST(TestHaierACYRW02Class, HealthMode) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setHealth(true);
- EXPECT_TRUE(haier.getHealth());
- EXPECT_EQ(kHaierAcYrw02ButtonHealth, haier.getButton());
+ ac.setHealth(true);
+ EXPECT_TRUE(ac.getHealth());
+ EXPECT_EQ(kHaierAcYrw02ButtonHealth, ac.getButton());
- haier.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setHealth(false);
- EXPECT_FALSE(haier.getHealth());
- EXPECT_EQ(kHaierAcYrw02ButtonHealth, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setHealth(false);
+ EXPECT_FALSE(ac.getHealth());
+ EXPECT_EQ(kHaierAcYrw02ButtonHealth, ac.getButton());
- haier.setHealth(true);
- EXPECT_TRUE(haier.getHealth());
- EXPECT_EQ(kHaierAcYrw02ButtonHealth, haier.getButton());
+ ac.setHealth(true);
+ EXPECT_TRUE(ac.getHealth());
+ EXPECT_EQ(kHaierAcYrw02ButtonHealth, ac.getButton());
}
TEST(TestHaierACYRW02Class, Power) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setPower(true);
- EXPECT_TRUE(haier.getPower());
- EXPECT_EQ(kHaierAcYrw02ButtonPower, haier.getButton());
+ ac.setPower(true);
+ EXPECT_TRUE(ac.getPower());
+ EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
- haier.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setPower(false);
- EXPECT_FALSE(haier.getPower());
- EXPECT_EQ(kHaierAcYrw02ButtonPower, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setPower(false);
+ EXPECT_FALSE(ac.getPower());
+ EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
- haier.setPower(true);
- EXPECT_TRUE(haier.getPower());
- EXPECT_EQ(kHaierAcYrw02ButtonPower, haier.getButton());
+ ac.setPower(true);
+ EXPECT_TRUE(ac.getPower());
+ EXPECT_EQ(kHaierAcYrw02ButtonPower, ac.getButton());
- haier.off();
- EXPECT_FALSE(haier.getPower());
- haier.on();
- EXPECT_TRUE(haier.getPower());
+ ac.off();
+ EXPECT_FALSE(ac.getPower());
+ ac.on();
+ EXPECT_TRUE(ac.getPower());
}
TEST(TestHaierACYRW02Class, SleepMode) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setSleep(true);
- EXPECT_TRUE(haier.getSleep());
- EXPECT_EQ(kHaierAcYrw02ButtonSleep, haier.getButton());
+ ac.setSleep(true);
+ EXPECT_TRUE(ac.getSleep());
+ EXPECT_EQ(kHaierAcYrw02ButtonSleep, ac.getButton());
- haier.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setSleep(false);
- EXPECT_FALSE(haier.getSleep());
- EXPECT_EQ(kHaierAcYrw02ButtonSleep, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setSleep(false);
+ EXPECT_FALSE(ac.getSleep());
+ EXPECT_EQ(kHaierAcYrw02ButtonSleep, ac.getButton());
- haier.setSleep(true);
- EXPECT_TRUE(haier.getSleep());
- EXPECT_EQ(kHaierAcYrw02ButtonSleep, haier.getButton());
+ ac.setSleep(true);
+ EXPECT_TRUE(ac.getSleep());
+ EXPECT_EQ(kHaierAcYrw02ButtonSleep, ac.getButton());
}
-TEST(TestHaierACYRW02Class, TurboMode) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+TEST(TestHaierACYRW02Class, TurboAndQuiet) {
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setTurbo(kHaierAcYrw02TurboOff);
- EXPECT_EQ(kHaierAcYrw02TurboOff, haier.getTurbo());
- EXPECT_EQ(kHaierAcYrw02ButtonTurbo, haier.getButton());
+ ac.setMode(kHaierAcYrw02Cool); // Turbo & Quiet is allowed in this mode.
+ ac.setTurbo(false);
+ ac.setQuiet(false);
+ EXPECT_FALSE(ac.getTurbo());
+ EXPECT_FALSE(ac.getQuiet());
+ EXPECT_EQ(kHaierAcYrw02ButtonTurbo, ac.getButton());
- haier.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setTurbo(kHaierAcYrw02TurboLow);
- EXPECT_EQ(kHaierAcYrw02TurboLow, haier.getTurbo());
- EXPECT_EQ(kHaierAcYrw02ButtonTurbo, haier.getButton());
+ ac.setTurbo(true);
+ EXPECT_TRUE(ac.getTurbo());
+ EXPECT_FALSE(ac.getQuiet());
+ EXPECT_EQ(kHaierAcYrw02ButtonTurbo, ac.getButton());
- haier.setTurbo(kHaierAcYrw02TurboHigh);
- EXPECT_EQ(kHaierAcYrw02TurboHigh, haier.getTurbo());
- EXPECT_EQ(kHaierAcYrw02ButtonTurbo, haier.getButton());
+ ac.setQuiet(true);
+ EXPECT_FALSE(ac.getTurbo());
+ EXPECT_TRUE(ac.getQuiet());
+ EXPECT_EQ(kHaierAcYrw02ButtonTurbo, ac.getButton());
- haier.setTurbo(kHaierAcYrw02TurboOff);
- EXPECT_EQ(kHaierAcYrw02TurboOff, haier.getTurbo());
- EXPECT_EQ(kHaierAcYrw02ButtonTurbo, haier.getButton());
+ ac.setTurbo(false);
+ ac.setQuiet(false);
+ EXPECT_FALSE(ac.getTurbo());
+ EXPECT_FALSE(ac.getQuiet());
+ EXPECT_EQ(kHaierAcYrw02ButtonTurbo, ac.getButton());
+
+ ac.setMode(kHaierAcYrw02Auto); // Turbo & Quiet is not allowed in this mode.
+ EXPECT_FALSE(ac.getTurbo());
+ EXPECT_FALSE(ac.getQuiet());
+ ac.setTurbo(true);
+ EXPECT_FALSE(ac.getTurbo());
+ EXPECT_NE(kHaierAcYrw02ButtonTurbo, ac.getButton());
+ ac.setQuiet(true);
+ EXPECT_FALSE(ac.getQuiet());
+ EXPECT_NE(kHaierAcYrw02ButtonTurbo, ac.getButton());
}
TEST(TestHaierACYRW02Class, Fan) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setFan(kHaierAcYrw02FanAuto);
- EXPECT_EQ(kHaierAcYrw02FanAuto, haier.getFan());
- EXPECT_EQ(kHaierAcYrw02ButtonFan, haier.getButton());
+ ac.setFan(kHaierAcYrw02FanAuto);
+ EXPECT_EQ(kHaierAcYrw02FanAuto, ac.getFan());
+ EXPECT_EQ(kHaierAcYrw02ButtonFan, ac.getButton());
- haier.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setFan(kHaierAcYrw02FanLow);
- EXPECT_EQ(kHaierAcYrw02FanLow, haier.getFan());
- EXPECT_EQ(kHaierAcYrw02ButtonFan, haier.getButton());
+ ac.setFan(kHaierAcYrw02FanLow);
+ EXPECT_EQ(kHaierAcYrw02FanLow, ac.getFan());
+ EXPECT_EQ(kHaierAcYrw02ButtonFan, ac.getButton());
- haier.setFan(kHaierAcYrw02FanHigh);
- EXPECT_EQ(kHaierAcYrw02FanHigh, haier.getFan());
- EXPECT_EQ(kHaierAcYrw02ButtonFan, haier.getButton());
+ ac.setFan(kHaierAcYrw02FanHigh);
+ EXPECT_EQ(kHaierAcYrw02FanHigh, ac.getFan());
+ EXPECT_EQ(kHaierAcYrw02ButtonFan, ac.getButton());
- haier.setFan(kHaierAcYrw02FanMed);
- EXPECT_EQ(kHaierAcYrw02FanMed, haier.getFan());
- EXPECT_EQ(kHaierAcYrw02ButtonFan, haier.getButton());
+ ac.setFan(kHaierAcYrw02FanMed);
+ EXPECT_EQ(kHaierAcYrw02FanMed, ac.getFan());
+ EXPECT_EQ(kHaierAcYrw02ButtonFan, ac.getButton());
// Test unexpected values.
- haier.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setFan(0x00);
- EXPECT_EQ(kHaierAcYrw02FanMed, haier.getFan());
- EXPECT_EQ(kHaierAcYrw02ButtonTempUp, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setFan(0x00);
+ EXPECT_EQ(kHaierAcYrw02FanMed, ac.getFan());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
}
-TEST(TestHaierACYRW02Class, Swing) {
- IRHaierACYRW02 haier(kGpioUnused);
- haier.begin();
+TEST(TestHaierACYRW02Class, SwingV) {
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
- haier.setSwing(kHaierAcYrw02SwingOff);
- EXPECT_EQ(kHaierAcYrw02SwingOff, haier.getSwing());
- EXPECT_EQ(kHaierAcYrw02ButtonSwing, haier.getButton());
+ ac.setSwingV(kHaierAcYrw02SwingVOff);
+ EXPECT_EQ(kHaierAcYrw02SwingVOff, ac.getSwingV());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton());
- haier.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setSwing(kHaierAcYrw02SwingAuto);
- EXPECT_EQ(kHaierAcYrw02SwingAuto, haier.getSwing());
- EXPECT_EQ(kHaierAcYrw02ButtonSwing, haier.getButton());
+ ac.setSwingV(kHaierAcYrw02SwingVAuto);
+ EXPECT_EQ(kHaierAcYrw02SwingVAuto, ac.getSwingV());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton());
- haier.setSwing(kHaierAcYrw02SwingTop);
- EXPECT_EQ(kHaierAcYrw02SwingTop, haier.getSwing());
- EXPECT_EQ(kHaierAcYrw02ButtonSwing, haier.getButton());
+ ac.setSwingV(kHaierAcYrw02SwingVTop);
+ EXPECT_EQ(kHaierAcYrw02SwingVTop, ac.getSwingV());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton());
- haier.setSwing(kHaierAcYrw02SwingDown);
- EXPECT_EQ(kHaierAcYrw02SwingDown, haier.getSwing());
- EXPECT_EQ(kHaierAcYrw02ButtonSwing, haier.getButton());
+ ac.setSwingV(kHaierAcYrw02SwingVDown);
+ EXPECT_EQ(kHaierAcYrw02SwingVDown, ac.getSwingV());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton());
// Test unexpected values.
- haier.setButton(kHaierAcYrw02ButtonTempUp);
- haier.setSwing(0xFF);
- EXPECT_EQ(kHaierAcYrw02SwingDown, haier.getSwing());
- EXPECT_EQ(kHaierAcYrw02ButtonTempUp, haier.getButton());
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setSwingV(0xFF);
+ EXPECT_EQ(kHaierAcYrw02SwingVDown, ac.getSwingV());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
// Test the mode-dependant positions.
- haier.setMode(kHaierAcYrw02Auto);
- haier.setSwing(kHaierAcYrw02SwingMiddle);
- EXPECT_EQ(kHaierAcYrw02SwingMiddle, haier.getSwing());
- EXPECT_EQ(kHaierAcYrw02ButtonSwing, haier.getButton());
- haier.setMode(kHaierAcYrw02Heat);
- haier.setSwing(kHaierAcYrw02SwingMiddle);
- EXPECT_EQ(kHaierAcYrw02SwingBottom, haier.getSwing());
- haier.setSwing(kHaierAcYrw02SwingAuto);
- EXPECT_EQ(kHaierAcYrw02SwingAuto, haier.getSwing());
- haier.setSwing(kHaierAcYrw02SwingBottom);
- EXPECT_EQ(kHaierAcYrw02SwingBottom, haier.getSwing());
- haier.setMode(kHaierAcYrw02Cool);
- haier.setSwing(kHaierAcYrw02SwingBottom);
- EXPECT_EQ(kHaierAcYrw02SwingMiddle, haier.getSwing());
+ ac.setMode(kHaierAcYrw02Auto);
+ ac.setSwingV(kHaierAcYrw02SwingVMiddle);
+ EXPECT_EQ(kHaierAcYrw02SwingVMiddle, ac.getSwingV());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingV, ac.getButton());
+ ac.setMode(kHaierAcYrw02Heat);
+ ac.setSwingV(kHaierAcYrw02SwingVMiddle);
+ EXPECT_EQ(kHaierAcYrw02SwingVBottom, ac.getSwingV());
+ ac.setSwingV(kHaierAcYrw02SwingVAuto);
+ EXPECT_EQ(kHaierAcYrw02SwingVAuto, ac.getSwingV());
+ ac.setSwingV(kHaierAcYrw02SwingVBottom);
+ EXPECT_EQ(kHaierAcYrw02SwingVBottom, ac.getSwingV());
+ ac.setMode(kHaierAcYrw02Cool);
+ ac.setSwingV(kHaierAcYrw02SwingVBottom);
+ EXPECT_EQ(kHaierAcYrw02SwingVMiddle, ac.getSwingV());
+}
+
+TEST(TestHaierACYRW02Class, SwingH) {
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
+
+ ac.setSwingH(kHaierAcYrw02SwingVOff);
+ EXPECT_EQ(kHaierAcYrw02SwingHMiddle, ac.getSwingH());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton());
+
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+
+ ac.setSwingH(kHaierAcYrw02SwingHLeftMax);
+ EXPECT_EQ(kHaierAcYrw02SwingHLeftMax, ac.getSwingH());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton());
+
+ ac.setSwingH(kHaierAcYrw02SwingHLeft);
+ EXPECT_EQ(kHaierAcYrw02SwingHLeft, ac.getSwingH());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton());
+
+ ac.setSwingH(kHaierAcYrw02SwingHRight);
+ EXPECT_EQ(kHaierAcYrw02SwingHRight, ac.getSwingH());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton());
+
+ ac.setSwingH(kHaierAcYrw02SwingHRightMax);
+ EXPECT_EQ(kHaierAcYrw02SwingHRightMax, ac.getSwingH());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton());
+
+ ac.setSwingH(kHaierAcYrw02SwingHAuto);
+ EXPECT_EQ(kHaierAcYrw02SwingHAuto, ac.getSwingH());
+ EXPECT_EQ(kHaierAcYrw02ButtonSwingH, ac.getButton());
+
+ // Test unexpected values.
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setSwingH(0xFF);
+ EXPECT_EQ(kHaierAcYrw02SwingHAuto, ac.getSwingH());
+ EXPECT_EQ(kHaierAcYrw02ButtonTempUp, ac.getButton());
+}
+
+TEST(TestHaierACYRW02Class, Lock) {
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.begin();
+
+ ac.setLock(true);
+ EXPECT_TRUE(ac.getLock());
+ EXPECT_EQ(kHaierAcYrw02ButtonLock, ac.getButton());
+
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setLock(false);
+ EXPECT_FALSE(ac.getLock());
+ EXPECT_EQ(kHaierAcYrw02ButtonLock, ac.getButton());
+
+ ac.setLock(true);
+ EXPECT_TRUE(ac.getLock());
+ EXPECT_EQ(kHaierAcYrw02ButtonLock, ac.getButton());
}
TEST(TestHaierACYRW02Class, MessageConstuction) {
- IRHaierACYRW02 haier(kGpioUnused);
+ IRHaierACYRW02 ac(kGpioUnused);
EXPECT_EQ(
- "Power: On, Button: 5 (Power), Mode: 0 (Auto), Temp: 25C,"
- " Fan: 5 (Auto), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off,"
- " Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
- haier.setMode(kHaierAcYrw02Cool);
- haier.setTemp(21);
- haier.setFan(kHaierAcYrw02FanHigh);
+ "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), "
+ "Mode: 0 (Auto), Temp: 25C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: On, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
+ ac.setMode(kHaierAcYrw02Cool);
+ ac.setTemp(21);
+ ac.setFan(kHaierAcYrw02FanHigh);
EXPECT_EQ(
- "Power: On, Button: 4 (Fan), Mode: 1 (Cool), Temp: 21C,"
- " Fan: 1 (High), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off,"
- " Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: On, Button: 4 (Fan), "
+ "Mode: 1 (Cool), Temp: 21C, Fan: 1 (High), Turbo: Off, Quiet: Off, "
+ "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: On, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
- haier.setSwing(kHaierAcYrw02SwingMiddle);
- haier.setHealth(false);
- haier.setSleep(true);
- haier.setTurbo(kHaierAcYrw02TurboHigh);
+ ac.setTemp(75, true);
+ ac.setSwingV(kHaierAcYrw02SwingVMiddle);
+ ac.setHealth(false);
+ ac.setSleep(true);
+ ac.setTurbo(true);
EXPECT_EQ(
- "Power: On, Button: 8 (Turbo), Mode: 1 (Cool), Temp: 21C, "
- "Fan: 1 (High), Turbo: 1 (High), Swing: 2 (Middle), "
- "Sleep: On, Health: Off, "
- "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: On, Button: 8 (Turbo), "
+ "Mode: 1 (Cool), Temp: 75F, Fan: 1 (High), Turbo: On, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: On, Health: Off, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
}
// Decode "real" state messages.
@@ -704,55 +818,60 @@ TEST(TestHaierACYRW02Class, RealStates) {
0xA6, 0xE1, 0x00, 0x00, 0x40, 0x20, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x6E};
- IRHaierACYRW02 haier(kGpioUnused);
- haier.setRaw(expectedState1);
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.setRaw(expectedState1);
EXPECT_EQ(
- "Power: On, Button: 7 (Health), Mode: 4 (Heat), Temp: 30C, "
- "Fan: 1 (High), Turbo: 0 (Off), Swing: 1 (Highest), Sleep: Off, "
- "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: On, Button: 7 (Health), "
+ "Mode: 4 (Heat), Temp: 30C, Fan: 1 (High), Turbo: Off, Quiet: Off, "
+ "Swing(V): 1 (Highest), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
uint8_t expectedState2[kHaierACYRW02StateLength] = {
0xA6, 0xE0, 0x00, 0x00, 0x00, 0x20, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x75};
- haier.setRaw(expectedState2);
+ ac.setRaw(expectedState2);
EXPECT_EQ(
- "Power: Off, Button: 5 (Power), Mode: 4 (Heat), Temp: 30C, "
- "Fan: 1 (High), Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off, "
- "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: Off, Button: 5 (Power), "
+ "Mode: 4 (Heat), Temp: 30C, Fan: 1 (High), Turbo: Off, Quiet: Off, "
+ "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
uint8_t expectedState3[kHaierACYRW02StateLength] = {
0xA6, 0x02, 0x00, 0x02, 0x40, 0x20, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2B};
- haier.setRaw(expectedState3);
+ ac.setRaw(expectedState3);
EXPECT_EQ(
- "Power: On, Button: 1 (Temp Down), Mode: 1 (Cool), Temp: 16C, "
- "Fan: 1 (High), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: On, Button: 1 (Temp Down), "
+ "Mode: 1 (Cool), Temp: 16C, Fan: 1 (High), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: On, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
- // cool 25, health, fan auto, swing auto, sleep on
+ // cool 25, health, fan auto, vertical swing auto, sleep on
uint8_t expectedState4[kHaierACYRW02StateLength] = {
0xA6, 0x9C, 0x00, 0x02, 0x40, 0xA8, 0x00,
0x20, 0x80, 0x00, 0x00, 0x00, 0x0B, 0xD7};
- haier.setRaw(expectedState4);
+ ac.setRaw(expectedState4);
EXPECT_EQ(
- "Power: On, Button: 11 (Sleep), Mode: 1 (Cool), Temp: 25C, "
- "Fan: 5 (Auto), Turbo: 0 (Off), Swing: 12 (Auto), Sleep: On, "
- "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: On, Button: 11 (Sleep), "
+ "Mode: 1 (Cool), Temp: 25C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 12 (Auto), Swing(H): 0 (Middle), Sleep: On, Health: On, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
- // cool 25, health, fan 3, swing auto, sleep on
+ // cool 25, health, fan 3, vertical swing auto, sleep on
uint8_t expectedState5[kHaierACYRW02StateLength] = {
0xA6, 0x9C, 0x00, 0x02, 0x40, 0x27, 0x36,
0x20, 0x80, 0x00, 0x00, 0x00, 0x04, 0x85};
- haier.setRaw(expectedState5);
+ ac.setRaw(expectedState5);
EXPECT_EQ(
- "Power: On, Button: 4 (Fan), Mode: 1 (Cool), Temp: 25C, "
- "Fan: 1 (High), Turbo: 0 (Off), Swing: 12 (Auto), Sleep: On, "
- "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: On, Button: 4 (Fan), "
+ "Mode: 1 (Cool), Temp: 25C, Fan: 1 (High), Turbo: Off, Quiet: Off, "
+ "Swing(V): 12 (Auto), Swing(H): 0 (Middle), Sleep: On, Health: On, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
}
// Tests for decodeHaierAC().
@@ -823,7 +942,7 @@ TEST(TestDecodeHaierAC, RealExample1) {
EXPECT_EQ(
"Command: 1 (On), Mode: 1 (Cool), Temp: 16C, Fan: 1 (Low), "
- "Swing: 0 (Off), Sleep: Off, Health: Off, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: Off, "
"Clock: 00:01, On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
@@ -864,13 +983,13 @@ TEST(TestDecodeHaierAC, RealExample2) {
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
- IRHaierAC haier(kGpioUnused);
- haier.setRaw(irsend.capture.state);
+ IRHaierAC ac(kGpioUnused);
+ ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Command: 6 (Temp Up), Mode: 1 (Cool), Temp: 22C, Fan: 1 (Low), "
- "Swing: 0 (Off), Sleep: Off, Health: Off, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: Off, "
"Clock: 00:01, On Timer: Off, Off Timer: Off",
- haier.toString());
+ ac.toString());
}
// Decode a "real" example message.
@@ -907,13 +1026,13 @@ TEST(TestDecodeHaierAC, RealExample3) {
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
- IRHaierAC haier(kGpioUnused);
- haier.setRaw(irsend.capture.state);
+ IRHaierAC ac(kGpioUnused);
+ ac.setRaw(irsend.capture.state);
EXPECT_EQ(
"Command: 12 (Health), Mode: 1 (Cool), Temp: 30C, Fan: 1 (Low), "
- "Swing: 0 (Off), Sleep: Off, Health: On, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: On, "
"Clock: 00:09, On Timer: Off, Off Timer: Off",
- haier.toString());
+ ac.toString());
}
// Decode normal "synthetic" messages.
@@ -977,13 +1096,14 @@ TEST(TestDecodeHaierAC_YRW02, RealExample) {
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
- IRHaierACYRW02 haier(kGpioUnused);
- haier.setRaw(irsend.capture.state);
+ IRHaierACYRW02 ac(kGpioUnused);
+ ac.setRaw(irsend.capture.state);
EXPECT_EQ(
- "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 17C, "
- "Fan: 1 (High), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: On, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
- haier.toString());
+ "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), "
+ "Mode: 1 (Cool), Temp: 17C, Fan: 1 (High), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: On, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
}
// Default state of the remote needed to include hidden data.
@@ -998,7 +1118,7 @@ TEST(TestHaierAcIssues, Issue668) {
ac._irsend.reset();
char expected_on[] =
"Command: 1 (On), Mode: 1 (Cool), Temp: 25C, Fan: 1 (Low), "
- "Swing: 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, "
"On Timer: Off, Off Timer: Off";
// State taken from real capture:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/668#issuecomment-483531895
@@ -1036,7 +1156,7 @@ TEST(TestHaierAcIssues, Issue668) {
ac._irsend.reset();
char expected_temp_plus_one[] =
"Command: 6 (Temp Up), Mode: 1 (Cool), Temp: 26C, Fan: 1 (Low), "
- "Swing: 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, "
"On Timer: Off, Off Timer: Off";
// State taken from real capture:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/668#issuecomment-483531895
@@ -1060,7 +1180,7 @@ TEST(TestHaierAcIssues, Issue668) {
ac._irsend.reset();
char expected_temp_minus_one[] =
"Command: 7 (Temp Down), Mode: 1 (Cool), Temp: 25C, Fan: 1 (Low), "
- "Swing: 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, "
+ "Swing(V): 0 (Off), Sleep: Off, Health: Off, Clock: 00:00, "
"On Timer: Off, Off Timer: Off";
ASSERT_EQ(26, ac.getTemp());
ac.setTemp(ac.getTemp() - 1);
@@ -1081,7 +1201,7 @@ TEST(TestHaierACClass, toCommon) {
ac.setMode(kHaierAcCool);
ac.setTemp(20);
ac.setFan(kHaierAcFanHigh);
- ac.setSwing(kHaierAcSwingChg);
+ ac.setSwingV(kHaierAcSwingVChg);
ac.setHealth(true);
ac.setSleep(true);
// Now test it.
@@ -1102,7 +1222,7 @@ TEST(TestHaierACClass, toCommon) {
ASSERT_FALSE(ac.toCommon().quiet);
ASSERT_FALSE(ac.toCommon().econo);
ASSERT_FALSE(ac.toCommon().clean);
- ASSERT_FALSE(ac.toCommon().beep);
+ ASSERT_TRUE(ac.toCommon().beep);
ASSERT_EQ(-1, ac.toCommon().clock);
}
@@ -1112,12 +1232,14 @@ TEST(TestHaierACYRW02Class, toCommon) {
ac.setMode(kHaierAcYrw02Cool);
ac.setTemp(20);
ac.setFan(kHaierAcYrw02FanHigh);
- ac.setSwing(kHaierAcYrw02SwingTop);
+ ac.setSwingV(kHaierAcYrw02SwingVTop);
+ ac.setSwingH(kHaierAcYrw02SwingHRightMax);
ac.setHealth(true);
ac.setSleep(true);
+ ac.setTurbo(true);
// Now test it.
ASSERT_EQ(decode_type_t::HAIER_AC_YRW02, ac.toCommon().protocol);
- ASSERT_EQ(-1, ac.toCommon().model);
+ ASSERT_EQ(1, ac.toCommon().model);
ASSERT_TRUE(ac.toCommon().power);
ASSERT_TRUE(ac.toCommon().celsius);
ASSERT_EQ(20, ac.toCommon().degrees);
@@ -1125,15 +1247,15 @@ TEST(TestHaierACYRW02Class, toCommon) {
ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv);
+ ASSERT_EQ(stdAc::swingh_t::kRightMax, ac.toCommon().swingh);
ASSERT_EQ(0, ac.toCommon().sleep);
- // Unsupported.
- ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
- ASSERT_FALSE(ac.toCommon().turbo);
- ASSERT_FALSE(ac.toCommon().light);
+ ASSERT_TRUE(ac.toCommon().turbo);
ASSERT_FALSE(ac.toCommon().quiet);
+ // Unsupported.
+ ASSERT_FALSE(ac.toCommon().light);
ASSERT_FALSE(ac.toCommon().econo);
ASSERT_FALSE(ac.toCommon().clean);
- ASSERT_FALSE(ac.toCommon().beep);
+ ASSERT_TRUE(ac.toCommon().beep);
ASSERT_EQ(-1, ac.toCommon().clock);
}
@@ -1204,9 +1326,10 @@ TEST(TestDecodeHaierAC176, SyntheticDecode) {
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
- "Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 24C, Fan: 5 (Auto), "
- "Turbo: 0 (Off), Swing: 6 (UNKNOWN), Sleep: Off, Health: Off, "
- "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
+ "Model: 1 (V9014557-A), Power: On, Button: 5 (Power), "
+ "Mode: 1 (Cool), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 6 (UNKNOWN), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t result, prev;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev));
@@ -1245,9 +1368,10 @@ TEST(TestHaierAC176Class, BuildKnownState) {
ac.setFan(kHaierAcYrw02FanHigh);
EXPECT_TRUE(ac.validChecksum(ac.getRaw()));
EXPECT_EQ(
- "Power: On, Button: 4 (Fan), Mode: 4 (Heat), Temp: 24C, Fan: 1 (High), "
- "Turbo: 0 (Off), Swing: 0 (Off), Sleep: Off, Health: On, "
- "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
+ "Model: 1 (V9014557-A), Power: On, Button: 4 (Fan), "
+ "Mode: 4 (Heat), Temp: 24C, Fan: 1 (High), Turbo: Off, Quiet: Off, "
+ "Swing(V): 0 (Off), Swing(H): 0 (Middle), Sleep: Off, Health: On, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
ac.toString());
/* Disabled pending:
https://github.com/crankyoldgit/IRremoteESP8266/issues/1480#issuecomment-885636790
@@ -1323,56 +1447,91 @@ TEST(TestHaierAC176Class, Timers) {
// Real data.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1480#issuecomment-894804106
- const uint8_t timer30m[22] = {
+ const uint8_t timer30m[kHaierAC176StateLength] = {
0xA6, 0x82, 0x00, 0x40, 0x00, 0xA0, 0x00, 0x00, 0x1E, 0x00, 0x00,
0x00, 0x10, 0x36, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7};
- const uint8_t timeroff[22] = {
+ const uint8_t timeroff[kHaierAC176StateLength] = {
0xA6, 0x82, 0x00, 0x00, 0x40, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x18, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7};
// https://docs.google.com/spreadsheets/d/1wdOVS08wgK2pEP7hTZLYMmrQ9FZVmLpZF2HjNQaVxlU/edit#gid=0&range=A65
- const uint8_t timeroffthenon[22] = {
+ const uint8_t timeroffthenon[kHaierAC176StateLength] = {
0xA6, 0x82, 0x00, 0xA0, 0x40, 0xA0, 0x1E, 0x08, 0x00, 0x00, 0x00,
0x00, 0x10, 0xDE, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7};
ac.setRaw(timer30m);
EXPECT_EQ(kHaierAcYrw02OnTimer, ac.getTimerMode());
EXPECT_EQ(
- "Power: Off, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, "
- "Fan: 5 (Auto), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: Off, Timer Mode: 2 (On), On Timer: 00:30, Off Timer: Off",
+ "Model: 1 (V9014557-A), Power: Off, Button: 16 (Timer), "
+ "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 2 (On), On Timer: 00:30, Off Timer: Off, Lock: Off",
ac.toString());
ac.setRaw(timeroff);
EXPECT_EQ(kHaierAcYrw02NoTimers, ac.getTimerMode());
EXPECT_EQ(
- "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, "
- "Fan: 5 (Auto), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
+ "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), "
+ "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
ac.toString());
ac.setRaw(timeroffthenon);
EXPECT_EQ(
- "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, "
- "Fan: 5 (Auto), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: Off, Timer Mode: 5 (Off-On), On Timer: 08:00, Off Timer: 00:30",
+ "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), "
+ "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 5 (Off-On), On Timer: 08:00, Off Timer: 00:30, Lock: Off",
ac.toString());
ac.setTimerMode(kHaierAcYrw02OnThenOffTimer);
EXPECT_EQ(
- "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, "
- "Fan: 5 (Auto), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: Off, Timer Mode: 4 (On-Off), On Timer: 08:00, Off Timer: 00:30",
+ "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), "
+ "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 4 (On-Off), On Timer: 08:00, Off Timer: 00:30, Lock: Off",
ac.toString());
ac.setTimerMode(kHaierAcYrw02OffTimer);
EXPECT_EQ(0, ac.getOnTimer());
EXPECT_EQ(30, ac.getOffTimer());
EXPECT_EQ(
- "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, "
- "Fan: 5 (Auto), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: Off, Timer Mode: 1 (Off), On Timer: Off, Off Timer: 00:30",
+ "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), "
+ "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 1 (Off), On Timer: Off, Off Timer: 00:30, Lock: Off",
ac.toString());
ac.setTimerMode(kHaierAcYrw02NoTimers);
EXPECT_EQ(0, ac.getOnTimer());
EXPECT_EQ(0, ac.getOffTimer());
EXPECT_EQ(
- "Power: On, Button: 0 (Temp Up), Mode: 0 (Auto), Temp: 24C, "
- "Fan: 5 (Auto), Turbo: 0 (Off), Swing: 2 (Middle), Sleep: Off, "
- "Health: Off, Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off",
+ "Model: 1 (V9014557-A), Power: On, Button: 16 (Timer), "
+ "Mode: 0 (Auto), Temp: 24C, Fan: 5 (Auto), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
+ ac.toString());
+}
+
+TEST(TestHaierAC176Class, Models) {
+ IRHaierAC176 ac(kGpioUnused);
+ ac.begin();
+ EXPECT_EQ(haier_ac176_remote_model_t::V9014557_A, ac.getModel());
+
+ ac.setButton(kHaierAcYrw02ButtonTempUp);
+ ac.setModel(haier_ac176_remote_model_t::V9014557_B);
+ EXPECT_EQ(haier_ac176_remote_model_t::V9014557_B, ac.getModel());
+ EXPECT_EQ(kHaierAcYrw02ButtonCFAB, ac.getButton());
+
+ ac.setButton(kHaierAcYrw02ButtonTempDown);
+ ac.setModel(haier_ac176_remote_model_t::V9014557_A);
+ EXPECT_EQ(haier_ac176_remote_model_t::V9014557_A, ac.getModel());
+ EXPECT_EQ(kHaierAcYrw02ButtonCFAB, ac.getButton());
+
+ // Real data.
+ const uint8_t setmodelb[kHaierAC176StateLength] = {
+ 0x59, 0x82, 0x00, 0x00, 0x40, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x1A, 0x55, 0xB7, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x77};
+ ac.setRaw(setmodelb);
+ EXPECT_EQ(haier_ac176_remote_model_t::V9014557_B, ac.getModel());
+ EXPECT_EQ(
+ "Model: 2 (V9014557-B), Power: On, Button: 26 (Celsius/Fahrenheit), "
+ "Mode: 6 (Fan), Temp: 24C, Fan: 3 (Low), Turbo: Off, Quiet: Off, "
+ "Swing(V): 2 (Middle), Swing(H): 0 (Middle), Sleep: Off, Health: Off, "
+ "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
ac.toString());
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Mirage_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Mirage_test.cpp
index 2530afc5c..ad08918e2 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Mirage_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Mirage_test.cpp
@@ -1,5 +1,6 @@
-// Copyright 2020 David Conran
+// Copyright 2020-2021 David Conran
+#include "ir_Mirage.h"
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
@@ -11,7 +12,7 @@ TEST(TestUtils, Housekeeping) {
ASSERT_EQ("MIRAGE", typeToString(decode_type_t::MIRAGE));
ASSERT_EQ(decode_type_t::MIRAGE, strToDecodeType("MIRAGE"));
ASSERT_TRUE(hasACState(decode_type_t::MIRAGE));
- ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::MIRAGE));
+ ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::MIRAGE));
ASSERT_EQ(kMirageBits, IRsend::defaultBits(decode_type_t::MIRAGE));
ASSERT_EQ(kMirageMinRepeat, IRsend::minRepeats(decode_type_t::MIRAGE));
}
@@ -55,7 +56,9 @@ TEST(TestDecodeMirage, RealExample) {
ASSERT_EQ(kMirageBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
- "",
+ "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
+ "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
+ "Swing(V): 0 (Off), Clock: 14:16",
IRAcUtils::resultAcToString(&irsend.capture));
}
@@ -70,13 +73,14 @@ TEST(TestDecodeMirage, SyntheticExample) {
irsend.sendMirage(expected);
irsend.makeDecodeResult();
- ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::MIRAGE, irsend.capture.decode_type);
ASSERT_EQ(kMirageBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
- "",
+ "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
+ "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
+ "Swing(V): 0 (Off), Clock: 14:16",
IRAcUtils::resultAcToString(&irsend.capture));
}
@@ -119,6 +123,469 @@ TEST(TestDecodeMirage, RealExampleWithDodgyHardwareCapture) {
ASSERT_EQ(kMirageBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
- "",
+ "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
+ "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
+ "Swing(V): 0 (Off), Clock: 14:16",
IRAcUtils::resultAcToString(&irsend.capture));
}
+
+TEST(TestMirageAcClass, Power) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
+ ac.on();
+ EXPECT_TRUE(ac.getPower());
+ ac.on();
+ EXPECT_TRUE(ac.getPower());
+
+ ac.off();
+ EXPECT_FALSE(ac.getPower());
+ ac.off();
+ EXPECT_FALSE(ac.getPower());
+
+ ac.setPower(true);
+ EXPECT_TRUE(ac.getPower());
+
+ ac.setPower(false);
+ EXPECT_FALSE(ac.getPower());
+
+ const uint8_t on[kMirageStateLength] = {
+ 0x56, 0x75, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x16, 0x14, 0x26};
+ ac.setRaw(on);
+ EXPECT_TRUE(ac.getPower());
+ const uint8_t off[kMirageStateLength] = {
+ 0x56, 0x6C, 0x00, 0x00, 0x21, 0xD8, 0x00, 0x00,
+ 0x0C, 0x00, 0x0C, 0x2C, 0x23, 0x01, 0x61};
+ ac.setRaw(off);
+ EXPECT_FALSE(ac.getPower());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+ ac.on();
+ EXPECT_TRUE(ac.getPower());
+ ac.off();
+ EXPECT_FALSE(ac.getPower());
+ ac.setPower(true);
+ EXPECT_TRUE(ac.getPower());
+ ac.setPower(false);
+ EXPECT_FALSE(ac.getPower());
+}
+
+TEST(TestMirageAcClass, OperatingMode) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setMode(kMirageAcCool);
+ EXPECT_EQ(kMirageAcCool, ac.getMode());
+ ac.setMode(kMirageAcHeat);
+ EXPECT_EQ(kMirageAcHeat, ac.getMode());
+ ac.setMode(kMirageAcDry);
+ EXPECT_EQ(kMirageAcDry, ac.getMode());
+ ac.setMode(kMirageAcFan);
+ EXPECT_EQ(kMirageAcFan, ac.getMode());
+ ac.setMode(kMirageAcRecycle);
+ EXPECT_EQ(kMirageAcRecycle, ac.getMode());
+ ac.setMode(255);
+ EXPECT_EQ(kMirageAcCool, ac.getMode());
+}
+
+TEST(TestMirageAcClass, HumanReadable) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ // Tests for the KKG9AC1 model.
+ EXPECT_EQ(
+ "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 16C, "
+ "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
+ "Swing(V): 13 (Auto), Clock: 00:00",
+ ac.toString());
+ // Ref: https://docs.google.com/spreadsheets/d/1Ucu9mOOIIJoWQjUJq_VCvwgV3EwKaRk8K2AuZgccYEk/edit#gid=0&range=C7
+ // 0x56710000201A00000C000C26010041
+ const uint8_t cool_21c_auto[kMirageStateLength] = {
+ 0x56, 0x71, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00,
+ 0x0C, 0x00, 0x0C, 0x26, 0x01, 0x00, 0x41};
+ ac.setRaw(cool_21c_auto);
+ EXPECT_EQ(
+ "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 21C, "
+ "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
+ "Swing(V): 13 (Auto), Clock: 00:01",
+ ac.toString());
+
+ const uint8_t SyntheticExample[kMirageStateLength] = {
+ 0x56, 0x75, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x16, 0x14, 0x26};
+ ac.setRaw(SyntheticExample);
+ EXPECT_EQ(
+ "Model: 1 (KKG9AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
+ "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Light: Off, "
+ "Swing(V): 0 (Off), Clock: 14:16",
+ ac.toString());
+
+ // Tests for the KKG29AC1 model.
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+ EXPECT_EQ(
+ "Model: 2 (KKG29AC1), Power: On, Mode: 2 (Cool), Temp: 25C, "
+ "Fan: 0 (Auto), Turbo: Off, Sleep: Off, Quiet: Off, Light: -, "
+ "Swing(V): Off, Swing(H): Off, "
+ "Filter: Off, Clean: -, On Timer: Off, Off Timer: Off, "
+ "IFeel: Off",
+ ac.toString());
+}
+
+TEST(TestMirageAcClass, Temperature) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setTemp(0);
+ EXPECT_EQ(kMirageAcMinTemp, ac.getTemp());
+
+ ac.setTemp(255);
+ EXPECT_EQ(kMirageAcMaxTemp, ac.getTemp());
+
+ ac.setTemp(kMirageAcMinTemp);
+ EXPECT_EQ(kMirageAcMinTemp, ac.getTemp());
+
+ ac.setTemp(kMirageAcMaxTemp);
+ EXPECT_EQ(kMirageAcMaxTemp, ac.getTemp());
+
+ ac.setTemp(kMirageAcMinTemp - 1);
+ EXPECT_EQ(kMirageAcMinTemp, ac.getTemp());
+
+ ac.setTemp(kMirageAcMaxTemp + 1);
+ EXPECT_EQ(kMirageAcMaxTemp, ac.getTemp());
+
+ ac.setTemp(17);
+ EXPECT_EQ(17, ac.getTemp());
+
+ ac.setTemp(21);
+ EXPECT_EQ(21, ac.getTemp());
+
+ ac.setTemp(25);
+ EXPECT_EQ(25, ac.getTemp());
+
+ ac.setTemp(30);
+ EXPECT_EQ(30, ac.getTemp());
+}
+
+TEST(TestMirageAcClass, FanSpeed) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setFan(kMirageAcFanAuto);
+ EXPECT_EQ(kMirageAcFanAuto, ac.getFan());
+ ac.setFan(kMirageAcFanLow);
+ EXPECT_EQ(kMirageAcFanLow, ac.getFan());
+ ac.setFan(kMirageAcFanMed);
+ EXPECT_EQ(kMirageAcFanMed, ac.getFan());
+ ac.setFan(kMirageAcFanHigh);
+ EXPECT_EQ(kMirageAcFanHigh, ac.getFan());
+
+ ac.setFan(255);
+ EXPECT_EQ(kMirageAcFanAuto, ac.getFan());
+}
+
+TEST(TestMirageAcClass, Turbo) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
+ ac.setTurbo(true);
+ EXPECT_TRUE(ac.getTurbo());
+ ac.setTurbo(false);
+ EXPECT_FALSE(ac.getTurbo());
+ ac.setTurbo(true);
+ EXPECT_TRUE(ac.getTurbo());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+ ac.setTurbo(true);
+ EXPECT_TRUE(ac.getTurbo());
+ ac.setTurbo(false);
+ EXPECT_FALSE(ac.getTurbo());
+ ac.setTurbo(true);
+ EXPECT_TRUE(ac.getTurbo());
+}
+
+TEST(TestMirageAcClass, Light) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
+ ac.setLight(true);
+ EXPECT_TRUE(ac.getLight());
+ ac.setLight(false);
+ EXPECT_FALSE(ac.getLight());
+ ac.setLight(true);
+ EXPECT_TRUE(ac.getLight());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+ ac.setLight(true);
+ EXPECT_TRUE(ac.getLight());
+ ac.setLight(false);
+ EXPECT_FALSE(ac.getLight());
+ ac.setLight(true);
+ EXPECT_TRUE(ac.getLight());
+}
+
+TEST(TestMirageAcClass, Sleep) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
+ ac.setSleep(true);
+ EXPECT_TRUE(ac.getSleep());
+ ac.setSleep(false);
+ EXPECT_FALSE(ac.getSleep());
+ ac.setSleep(true);
+ EXPECT_TRUE(ac.getSleep());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+ ac.setSleep(true);
+ EXPECT_TRUE(ac.getSleep());
+ ac.setSleep(false);
+ EXPECT_FALSE(ac.getSleep());
+ ac.setSleep(true);
+ EXPECT_TRUE(ac.getSleep());
+}
+
+TEST(TestMirageAcClass, Clock) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // This model supports time.
+ ac.setClock(0);
+ EXPECT_EQ(0, ac.getClock());
+ ac.setClock(12 * 60 * 60 + 30 * 60 + 59); // aka. 12:30:59
+ EXPECT_EQ(12 * 60 * 60 + 30 * 60 + 59, ac.getClock());
+ ac.setClock(23 * 60 * 60 + 59 * 60 + 59); // aka. 23:59:59
+ EXPECT_EQ(23 * 60 * 60 + 59 * 60 + 59, ac.getClock());
+ ac.setClock(24 * 60 * 60); // aka. 24:00:00
+ EXPECT_EQ(23 * 60 * 60 + 59 * 60 + 59, ac.getClock()); // aka. 23:59:59
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // This model has no clock.
+ EXPECT_EQ(0, ac.getClock());
+ ac.setClock(12 * 60 * 60 + 30 * 60 + 59); // aka. 12:30:59
+ EXPECT_EQ(0, ac.getClock());
+}
+
+TEST(TestMirageAcClass, Checksums) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ const uint8_t SyntheticExample[kMirageStateLength] = {
+ 0x56, 0x75, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x16, 0x14, 0x26};
+ EXPECT_TRUE(IRMirageAc::validChecksum(SyntheticExample));
+ EXPECT_EQ(0x26, IRMirageAc::calculateChecksum(SyntheticExample));
+}
+
+TEST(TestMirageAcClass, SwingV) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ // Set the model to one with full swingv support.
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
+
+ ac.setSwingV(kMirageAcSwingVAuto);
+ EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
+
+ ac.setSwingV(kMirageAcSwingVHigh);
+ EXPECT_EQ(kMirageAcSwingVHigh, ac.getSwingV());
+
+ ac.setSwingV(0xFF);
+ EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
+
+ ac.setSwingV(kMirageAcSwingVLowest);
+ EXPECT_EQ(kMirageAcSwingVLowest, ac.getSwingV());
+
+ ac.setSwingV(kMirageAcSwingVLowest - 1);
+ EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
+
+ // Set the model to one with limited swingv support.
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+
+ ac.setSwingV(kMirageAcSwingVAuto);
+ EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
+ ac.setSwingV(kMirageAcSwingVOff);
+ EXPECT_EQ(kMirageAcSwingVOff, ac.getSwingV());
+ ac.setSwingV(kMirageAcSwingVHigh);
+ EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
+ ac.setSwingV(0xFF);
+ EXPECT_EQ(kMirageAcSwingVAuto, ac.getSwingV());
+ ac.setSwingV(kMirageAcSwingVOff);
+ EXPECT_EQ(kMirageAcSwingVOff, ac.getSwingV());
+}
+
+TEST(TestMirageAcClass, SwingH) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
+ ac.setSwingH(true);
+ EXPECT_FALSE(ac.getSwingH());
+ ac.setSwingH(false);
+ EXPECT_FALSE(ac.getSwingH());
+ ac.setSwingH(true);
+ EXPECT_FALSE(ac.getSwingH());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+ ac.setSwingH(true);
+ EXPECT_TRUE(ac.getSwingH());
+ ac.setSwingH(false);
+ EXPECT_FALSE(ac.getSwingH());
+ ac.setSwingH(true);
+ EXPECT_TRUE(ac.getSwingH());
+}
+
+TEST(TestMirageAcClass, Filter) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No Support
+ ac.setFilter(true);
+ EXPECT_FALSE(ac.getFilter());
+ ac.setFilter(false);
+ EXPECT_FALSE(ac.getFilter());
+ ac.setFilter(true);
+ EXPECT_FALSE(ac.getFilter());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported
+ ac.setFilter(true);
+ EXPECT_TRUE(ac.getFilter());
+ ac.setFilter(false);
+ EXPECT_FALSE(ac.getFilter());
+ ac.setFilter(true);
+ EXPECT_TRUE(ac.getFilter());
+}
+
+TEST(TestMirageAcClass, Quiet) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No Support
+ ac.setQuiet(true);
+ EXPECT_FALSE(ac.getQuiet());
+ ac.setQuiet(false);
+ EXPECT_FALSE(ac.getQuiet());
+ ac.setQuiet(true);
+ EXPECT_FALSE(ac.getQuiet());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported
+ ac.setQuiet(true);
+ EXPECT_TRUE(ac.getQuiet());
+ ac.setQuiet(false);
+ EXPECT_FALSE(ac.getQuiet());
+ ac.setQuiet(true);
+ EXPECT_TRUE(ac.getQuiet());
+}
+
+TEST(TestMirageAcClass, CleanToggle) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1);
+ ac.setCleanToggle(true);
+ EXPECT_FALSE(ac.getCleanToggle());
+ ac.setCleanToggle(false);
+ EXPECT_FALSE(ac.getCleanToggle());
+ ac.setCleanToggle(true);
+ EXPECT_FALSE(ac.getCleanToggle());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1);
+ ac.setCleanToggle(true);
+ EXPECT_TRUE(ac.getCleanToggle());
+ ac.setCleanToggle(false);
+ EXPECT_FALSE(ac.getCleanToggle());
+ ac.setCleanToggle(true);
+ EXPECT_TRUE(ac.getCleanToggle());
+ ac.send(); // Should be reset when sent.
+ EXPECT_FALSE(ac.getCleanToggle());
+}
+
+TEST(TestMirageAcClass, Timers) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No timer support
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+ ac.setOnTimer(12 * 60 + 37); // 12:37
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+ ac.setOffTimer(17 * 60 + 5); // 17:05
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Timer supported
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOnTimer(12 * 60 + 37); // 12:37
+ EXPECT_EQ(12 * 60 + 37, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOffTimer(17 * 60 + 5); // 17:05
+ EXPECT_EQ(17 * 60 + 5, ac.getOffTimer());
+ EXPECT_EQ(12 * 60 + 37, ac.getOnTimer());
+ ac.setOnTimer(0); // Off/Disabled
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(17 * 60 + 5, ac.getOffTimer());
+ ac.setOffTimer(0); // Off/Disabled
+ EXPECT_EQ(0, ac.getOffTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+
+ ac.setOnTimer(12 * 60 + 37); // 12:37
+ ac.setOffTimer(17 * 60 + 5); // 17:05
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No timer support
+ EXPECT_EQ(0, ac.getOffTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+}
+
+TEST(TestMirageAcClass, IFeelAndSensorTemp) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+
+ ac.setModel(mirage_ac_remote_model_t::KKG9AC1); // No support
+ EXPECT_FALSE(ac.getIFeel());
+ EXPECT_EQ(0, ac.getSensorTemp());
+ ac.setIFeel(true);
+ EXPECT_FALSE(ac.getIFeel());
+ EXPECT_EQ(0, ac.getSensorTemp());
+ ac.setSensorTemp(20); // 20C
+ EXPECT_FALSE(ac.getIFeel());
+ EXPECT_EQ(0, ac.getSensorTemp());
+
+ ac.setModel(mirage_ac_remote_model_t::KKG29AC1); // Supported
+ EXPECT_FALSE(ac.getIFeel());
+ EXPECT_EQ(0, ac.getSensorTemp());
+ ac.setIFeel(true);
+ EXPECT_TRUE(ac.getIFeel());
+ EXPECT_EQ(0, ac.getSensorTemp());
+ ac.setSensorTemp(25); // 25C
+ EXPECT_TRUE(ac.getIFeel());
+ EXPECT_EQ(25, ac.getSensorTemp());
+ ac.setIFeel(false);
+ EXPECT_FALSE(ac.getIFeel());
+}
+
+TEST(TestMirageAcClass, getModel) {
+ IRMirageAc ac(kGpioUnused);
+ ac.begin();
+ const uint8_t KKG9AC1[kMirageStateLength] = {
+ 0x56, 0x6C, 0x00, 0x00, 0x20, 0xD8, 0x00, 0x00,
+ 0x0C, 0x32, 0x0B, 0x00, 0x32, 0x0F, 0x64};
+ EXPECT_EQ(mirage_ac_remote_model_t::KKG9AC1, IRMirageAc::getModel(KKG9AC1));
+
+ // https://github.com/crankyoldgit/IRremoteESP8266/issues/1573#issuecomment-955722044
+ const uint8_t KKG29AC1[kMirageStateLength] = {
+ 0x56, 0x74, 0x00, 0x00, 0x12, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D};
+ EXPECT_EQ(mirage_ac_remote_model_t::KKG29AC1, IRMirageAc::getModel(KKG29AC1));
+
+ // https://github.com/crankyoldgit/IRremoteESP8266/issues/1573#issuecomment-962362540
+ const uint8_t KKG29AC1_2[kMirageStateLength] = {
+ 0x56, 0x72, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19};
+ EXPECT_EQ(mirage_ac_remote_model_t::KKG29AC1,
+ IRMirageAc::getModel(KKG29AC1_2));
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Rhoss_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Rhoss_test.cpp
new file mode 100644
index 000000000..855b9a48c
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Rhoss_test.cpp
@@ -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);
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Samsung_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Samsung_test.cpp
index a4e0503cd..061067421 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Samsung_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Samsung_test.cpp
@@ -426,16 +426,37 @@ TEST(TestIRSamsungAcClass, SetAndGetPower) {
TEST(TestIRSamsungAcClass, SetAndGetSwing) {
IRSamsungAc ac(kGpioUnused);
+
+ // Vertical
ac.setSwing(true);
EXPECT_TRUE(ac.getSwing());
+ EXPECT_FALSE(ac.getSwingH());
ac.setSwing(false);
EXPECT_FALSE(ac.getSwing());
+ EXPECT_FALSE(ac.getSwingH());
ac.setSwing(true);
EXPECT_TRUE(ac.getSwing());
+ EXPECT_FALSE(ac.getSwingH());
+ // Horizontal
+ ac.setSwingH(true);
+ EXPECT_TRUE(ac.getSwing());
+ EXPECT_TRUE(ac.getSwingH());
+ ac.setSwingH(false);
+ EXPECT_TRUE(ac.getSwing());
+ EXPECT_FALSE(ac.getSwingH());
+ ac.setSwingH(true);
+ EXPECT_TRUE(ac.getSwing());
+ EXPECT_TRUE(ac.getSwingH());
+
+ ac.setSwing(false);
+ EXPECT_FALSE(ac.getSwing());
+ EXPECT_TRUE(ac.getSwingH());
+ ac.setSwingH(false);
+ EXPECT_FALSE(ac.getSwing());
+ EXPECT_FALSE(ac.getSwingH());
// Real examples from:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/505#issuecomment-424036602
- // TODO(Hollako): Explain why state[9] lowest bit changes between on and off.
const uint8_t expected_off[kSamsungAcStateLength] = {
0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
0x01, 0xE2, 0xFE, 0x71, 0x80, 0x11, 0xF0};
@@ -576,14 +597,24 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) {
EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan());
ac.setPowerful(false);
EXPECT_FALSE(ac.getPowerful());
- EXPECT_EQ(kSamsungAcFanAuto, ac.getFan());
- // Breeze and Powerful/Turbo are mutually exclusive.
+ // Breeze, Econo, and Powerful/Turbo are mutually exclusive.
ac.setPowerful(true);
EXPECT_TRUE(ac.getPowerful());
+ EXPECT_FALSE(ac.getBreeze());
+ EXPECT_FALSE(ac.getEcono());
ac.setBreeze(true);
EXPECT_TRUE(ac.getBreeze());
EXPECT_FALSE(ac.getPowerful());
+ EXPECT_FALSE(ac.getEcono());
+ ac.setEcono(true);
+ EXPECT_TRUE(ac.getEcono());
+ EXPECT_FALSE(ac.getBreeze());
+ EXPECT_FALSE(ac.getPowerful());
+ ac.setPowerful(true);
+ EXPECT_TRUE(ac.getPowerful());
+ EXPECT_FALSE(ac.getBreeze());
+ EXPECT_FALSE(ac.getEcono());
// Actual powerful on & off states from:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/734#issuecomment-500120270
@@ -594,9 +625,10 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) {
EXPECT_TRUE(ac.getPowerful());
EXPECT_EQ(kSamsungAcFanTurbo, ac.getFan());
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 7 (Turbo), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: On, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 7 (Turbo), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: On, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
uint8_t off[kSamsungAcStateLength] = {
@@ -606,9 +638,10 @@ TEST(TestIRSamsungAcClass, SetAndGetPowerful) {
EXPECT_FALSE(ac.getPowerful());
EXPECT_NE(kSamsungAcFanTurbo, ac.getFan());
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -633,6 +666,44 @@ TEST(TestIRSamsungAcClass, QuietAndPowerfulAreMutuallyExclusive) {
EXPECT_NE(kSamsungAcFanTurbo, ac.getFan());
}
+TEST(TestIRSamsungAcClass, SetAndGetEcono) {
+ IRSamsungAc ac(kGpioUnused);
+ ac.begin();
+ EXPECT_FALSE(ac.getEcono());
+ ac.setFan(kSamsungAcFanMed);
+ ac.setSwing(false);
+ ac.setEcono(true);
+ EXPECT_TRUE(ac.getEcono());
+ EXPECT_FALSE(ac.getBreeze());
+ EXPECT_FALSE(ac.getPowerful());
+ EXPECT_TRUE(ac.getSwing()); // Econo turns on swingv.
+ EXPECT_EQ(kSamsungAcFanAuto, ac.getFan()); // And sets the fan to Auto.
+ ac.setEcono(false);
+ EXPECT_FALSE(ac.getEcono());
+ EXPECT_FALSE(ac.getBreeze());
+ EXPECT_FALSE(ac.getPowerful());
+
+ // Breeze, Econo, and Powerful/Turbo are mutually exclusive.
+ // But that is tested in `SetAndGetPowerful`
+
+ // Actual econo on state from:
+ // https://cryptpad.fr/sheet/#/2/sheet/view/r9k8pmELYEjLyC71cD7EsThEYgKGLJygREZ5pVfNkS8/
+ // Row: 33
+ uint8_t on[kSamsungAcStateLength] = {
+ 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
+ 0x01, 0xD2, 0xAE, 0x7F, 0x80, 0x11, 0xF0};
+ ac.setRaw(on, kSamsungAcStateLength);
+ EXPECT_TRUE(ac.getEcono());
+ EXPECT_TRUE(ac.getSwing());
+ EXPECT_EQ(kSamsungAcFanAuto, ac.getFan());
+ EXPECT_EQ(
+ "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), "
+ "Swing(V): On, Swing(H): Off, Beep: -, Clean: -, "
+ "Quiet: Off, Powerful: Off, Econo: On, Breeze: Off, "
+ "Light: On, Ion: Off",
+ ac.toString());
+}
+
TEST(TestIRSamsungAcClass, ChecksumCalculation) {
IRSamsungAc ac(kGpioUnused);
@@ -668,9 +739,10 @@ TEST(TestIRSamsungAcClass, ChecksumCalculation) {
TEST(TestIRSamsungAcClass, HumanReadable) {
IRSamsungAc ac(kGpioUnused);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), Swing: On, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), "
+ "Swing(V): On, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, Econo: Off, "
+ "Breeze: Off, Light: On, Ion: Off",
ac.toString());
ac.setTemp(kSamsungAcMaxTemp);
ac.setMode(kSamsungAcHeat);
@@ -680,28 +752,32 @@ TEST(TestIRSamsungAcClass, HumanReadable) {
ac.setBeep(true);
ac.setClean(true);
EXPECT_EQ(
- "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 5 (High), Swing: Off, "
- "Beep: On, Clean: On, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 5 (High), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: Toggle, Clean: Toggle, Quiet: Off, Powerful: Off, Econo: Off, "
+ "Breeze: Off, Light: On, Ion: Off",
ac.toString());
ac.setQuiet(true);
EXPECT_EQ(
- "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), Swing: Off, "
- "Beep: On, Clean: On, Quiet: On, Powerful: Off, Breeze: Off, "
+ "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, Beep: Toggle, "
+ "Clean: Toggle, Quiet: On, Powerful: Off, Econo: Off, Breeze: Off, "
"Light: On, Ion: Off",
ac.toString());
ac.setQuiet(false);
ac.setPowerful(true);
EXPECT_EQ(
- "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), Swing: Off, "
- "Beep: On, Clean: On, Quiet: Off, Powerful: On, Breeze: Off, "
+ "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), "
+ "Swing(V): Off, Swing(H): Off, Beep: Toggle, "
+ "Clean: Toggle, Quiet: Off, Powerful: On, Econo: Off, Breeze: Off, "
"Light: On, Ion: Off",
ac.toString());
ac.setIon(true);
ac.setDisplay(false);
EXPECT_EQ(
- "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), Swing: Off, "
- "Beep: On, Clean: On, Quiet: Off, Powerful: On, Breeze: Off, "
+ "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 7 (Turbo), "
+ "Swing(V): Off, Swing(H): Off, Beep: Toggle, "
+ "Clean: Toggle, Quiet: Off, Powerful: On, Econo: Off, Breeze: Off, "
"Light: Off, Ion: On",
ac.toString());
}
@@ -802,9 +878,10 @@ TEST(TestDecodeSamsungAC, DecodeRealExample) {
IRSamsungAc ac(kGpioUnused);
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), Swing: On, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 2 (Low), "
+ "Swing(V): On, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -852,9 +929,10 @@ TEST(TestDecodeSamsungAC, DecodeRealExample2) {
IRSamsungAc ac(kGpioUnused);
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -912,9 +990,10 @@ TEST(TestDecodeSamsungAC, DecodePowerOnSample) {
IRSamsungAc ac(kGpioUnused);
ac.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -973,9 +1052,10 @@ TEST(TestDecodeSamsungAC, DecodePowerOffSample) {
IRSamsungAc ac(kGpioUnused);
ac.setRaw(irsend.capture.state, kSamsungAcExtendedStateLength);
EXPECT_EQ(
- "Power: Off, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: Off, Mode: 1 (Cool), Temp: 24C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -1021,9 +1101,10 @@ TEST(TestDecodeSamsungAC, DecodeHeatSample) {
IRSamsungAc ac(kGpioUnused);
ac.setRaw(irsend.capture.state);
EXPECT_EQ(
- "Power: On, Mode: 4 (Heat), Temp: 17C, Fan: 0 (Auto), Swing: On, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 4 (Heat), Temp: 17C, Fan: 0 (Auto), "
+ "Swing(V): On, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -1066,9 +1147,10 @@ TEST(TestDecodeSamsungAC, DecodeCoolSample) {
EXPECT_EQ(kSamsungAcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@@ -1127,9 +1209,10 @@ TEST(TestDecodeSamsungAC, Issue604DecodeExtended) {
IRSamsungAc ac(kGpioUnused);
ac.setRaw(irsend.capture.state, irsend.capture.bits / 8);
EXPECT_EQ(
- "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: Off, Mode: 4 (Heat), Temp: 30C, Fan: 0 (Auto), "
+ "Swing(V): On, Swing(H): On, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -1311,8 +1394,10 @@ TEST(TestIRSamsungAcClass, Issue604SendPowerHack) {
"m586s436m586s436m586s436m586s436m586s436m586s436m586s436m586s436"
"m586s2886";
std::string text = "Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 4 (Med), "
- "Swing: On, Beep: Off, Clean: Off, Quiet: Off, "
- "Powerful: Off, Breeze: Off, Light: On, Ion: Off";
+ "Swing(V): On, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, "
+ "Powerful: Off, Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off";
// Don't do a setPower()/on()/off() as that will trigger the special message.
// So it should only be the normal "settings" message.
ac.setTemp(23);
@@ -1445,9 +1530,10 @@ TEST(TestDecodeSamsungAC, Issue734QuietSetting) {
IRSamsungAc ac(0);
ac.setRaw(irsend.capture.state, irsend.capture.bits / 8);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: On, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: On, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
// Make sure the ac class state is in something wildly different first.
@@ -1468,9 +1554,10 @@ TEST(TestDecodeSamsungAC, Issue734QuietSetting) {
ac.setClean(false);
ac.setQuiet(true);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: On, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: On, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
// Check it matches the known good/expected state.
EXPECT_STATE_EQ(expectedState, ac.getRaw(), kSamsungAcBits);
@@ -1519,9 +1606,10 @@ TEST(TestDecodeSamsungAC, Issue734PowerfulOff) {
IRSamsungAc ac(0);
ac.setRaw(irsend.capture.state, irsend.capture.bits / 8);
EXPECT_EQ(
- "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -1553,9 +1641,10 @@ TEST(TestIRSamsungAcClass, SetAndGetBreeze) {
ac.setRaw(on);
ASSERT_TRUE(ac.getBreeze());
EXPECT_EQ(
- "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: On, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: On, Light: On, Ion: Off",
ac.toString());
// MODE FAN, 24C WINDFREE OFF, FAN = LOW
const uint8_t off[14] = {
@@ -1564,9 +1653,10 @@ TEST(TestIRSamsungAcClass, SetAndGetBreeze) {
ac.setRaw(off);
ASSERT_FALSE(ac.getBreeze());
EXPECT_EQ(
- "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 2 (Low), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: On, Ion: Off",
+ "Power: On, Mode: 3 (Fan), Temp: 24C, Fan: 2 (Low), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off",
ac.toString());
}
@@ -1614,9 +1704,10 @@ TEST(TestDecodeSamsungAC, Issue1227VeryPoorSignal) {
EXPECT_EQ(kSamsungAcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
- "Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), Swing: Off, "
- "Beep: Off, Clean: Off, Quiet: Off, Powerful: Off, Breeze: Off, "
- "Light: Off, Ion: Off",
+ "Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: Off, Ion: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@@ -1671,3 +1762,362 @@ TEST(TestIRSamsungAcClass, SectionChecksums) {
EXPECT_EQ(IRSamsungAc::getSectionChecksum(extended_off + 14),
IRSamsungAc::calcSectionChecksum(extended_off + 14));
}
+
+TEST(TestIRSamsungAcClass, Issue1648) {
+ IRSamsungAc ac(kGpioUnused);
+ IRrecv irrecv(kGpioUnused);
+ const uint8_t onState[kSamsungAcExtendedStateLength] = {
+ 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
+ 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0xC2, 0xFE, 0x71, 0x90, 0x15, 0xF0};
+ const String onText = "Power: On, Mode: 1 (Cool), Temp: 25C, Fan: 2 (Low), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, "
+ "Powerful: Off, Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off";
+ const uint8_t extended_offState[kSamsungAcExtendedStateLength] = {
+ 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
+ 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0xE2, 0xFE, 0x71, 0x90, 0x15, 0xC0};
+ const uint8_t short_offState[kSamsungAcStateLength] = {
+ 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
+ 0x01, 0xE2, 0xFE, 0x71, 0x90, 0x15, 0xC0};
+ const String offText = "Power: Off, Mode: 1 (Cool), Temp: 25C, Fan: 2 (Low), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, "
+ "Powerful: Off, Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off";
+ const uint8_t coolState[kSamsungAcStateLength] = {
+ 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
+ 0x01, 0xC2, 0xFE, 0x71, 0x90, 0x15, 0xF0};
+
+ // "setup()"" from provided code.
+ ac.begin(); // User code
+ ac.off(); // User code
+ ac.setFan(kSamsungAcFanLow); // User code
+ ac.setMode(kSamsungAcCool); // User code
+ ac.setTemp(25); // User code
+ ac.setSwing(false); // User code
+
+ // Go through "loop()" from provided code.
+ for (uint8_t i = 0; i < 2; i++) {
+ ac.on(); // User code
+ ac.send(); // User code
+
+ // Verify what was sent.
+ ac._irsend.makeDecodeResult();
+ EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
+ EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
+ EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
+ EXPECT_STATE_EQ(onState, ac._irsend.capture.state, ac._irsend.capture.bits);
+ EXPECT_EQ(onText, IRAcUtils::resultAcToString(&ac._irsend.capture));
+ EXPECT_TRUE(ac._lastsentpowerstate);
+ ac._irsend.reset();
+
+ ac.setMode(kSamsungAcCool); // User code
+ ac.send(); // User code
+
+ // Verify what was sent.
+ ac._irsend.makeDecodeResult();
+ EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
+ EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
+ EXPECT_EQ(kSamsungAcBits, ac._irsend.capture.bits);
+ EXPECT_STATE_EQ(coolState, ac._irsend.capture.state,
+ ac._irsend.capture.bits);
+ EXPECT_EQ(onText, IRAcUtils::resultAcToString(&ac._irsend.capture));
+ ac._irsend.reset();
+ EXPECT_TRUE(ac._lastsentpowerstate);
+ EXPECT_FALSE(ac._forceextended);
+
+ ac.off(); // User code
+ ac.send(); // User code
+
+ // Verify what was sent.
+ ac._irsend.makeDecodeResult();
+ EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
+ EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
+ EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
+ EXPECT_STATE_EQ(extended_offState, ac._irsend.capture.state,
+ ac._irsend.capture.bits);
+ EXPECT_EQ(offText, IRAcUtils::resultAcToString(&ac._irsend.capture));
+ EXPECT_FALSE(ac._lastsentpowerstate);
+ ac._irsend.reset();
+
+ ac.off(); // User code
+ ac.send(); // User code
+
+ // Verify what was sent.
+ ac._irsend.makeDecodeResult();
+ EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
+ EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
+ EXPECT_EQ(kSamsungAcBits, ac._irsend.capture.bits);
+ EXPECT_STATE_EQ(short_offState, ac._irsend.capture.state,
+ ac._irsend.capture.bits);
+ EXPECT_EQ(offText, IRAcUtils::resultAcToString(&ac._irsend.capture));
+ EXPECT_FALSE(ac._lastsentpowerstate);
+ ac._irsend.reset();
+ // End of "loop()" code.
+ }
+
+ // Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/1648#issuecomment-950822399
+ const uint8_t expectedState[kSamsungAcExtendedStateLength] = {
+ 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
+ 0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x12, 0xAF, 0x71, 0x80, 0x15, 0xC0};
+ const String expectedText = "Power: Off, Mode: 1 (Cool), Temp: 24C, "
+ "Fan: 2 (Low), Swing(V): On, Swing(H): Off, "
+ "Beep: -, Clean: -, "
+ "Quiet: Off, Powerful: Off, Econo: Off, "
+ "Breeze: Off, Light: On, Ion: Off";
+
+ ac.stateReset();
+ ac.setRaw(expectedState, kSamsungAcExtendedStateLength);
+ EXPECT_EQ(expectedText, ac.toString());
+
+ // Try to generate the same message.
+ ac.stateReset();
+ ac.off();
+ ac.setMode(kSamsungAcCool);
+ ac.setTemp(24);
+ ac.setFan(kSamsungAcFanLow);
+ ac.setSwing(true);
+ ac.setBeep(false);
+ ac.setClean(false);
+ ac.setQuiet(false);
+ ac.setPowerful(false);
+ ac.setBreeze(false);
+ ac.setDisplay(true);
+ ac.setIon(false);
+ EXPECT_EQ(expectedText, ac.toString());
+
+ ac.send();
+ ac._irsend.makeDecodeResult();
+ EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
+ EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
+ EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
+ EXPECT_STATE_EQ(expectedState, ac._irsend.capture.state,
+ ac._irsend.capture.bits);
+ EXPECT_EQ(expectedText, IRAcUtils::resultAcToString(&ac._irsend.capture));
+ ac._irsend.reset();
+}
+
+TEST(TestIRSamsungAcClass, Timers) {
+ IRSamsungAc ac(kGpioUnused);
+ ac.begin();
+
+ // https://github.com/crankyoldgit/IRremoteESP8266/issues/1277#issuecomment-961836703
+ const uint8_t on_timer_30m[kSamsungAcExtendedStateLength] = {
+ 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
+ 0x01, 0xA2, 0x0F, 0x30, 0x00, 0x02, 0x00,
+ 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0};
+ const uint8_t off_timer_1h_on_timer_10m[kSamsungAcExtendedStateLength] = {
+ 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
+ 0x01, 0x92, 0x8F, 0x10, 0x00, 0x06, 0x00,
+ 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0};
+ const uint8_t off_timer_1h_on_timer_0m[kSamsungAcExtendedStateLength] = {
+ 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
+ 0x01, 0xA2, 0x8F, 0x00, 0x00, 0x06, 0x00,
+ 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0};
+
+ ac.setRaw(on_timer_30m, kSamsungAcExtendedStateLength);
+ EXPECT_EQ(
+ "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, Light: On, Ion: Off, On Timer: 00:30",
+ ac.toString());
+ ac.setRaw(off_timer_1h_on_timer_10m, kSamsungAcExtendedStateLength);
+ EXPECT_EQ(
+ "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off, On Timer: 00:10, Off Timer: 01:00",
+ ac.toString());
+ ac.setRaw(off_timer_1h_on_timer_0m, kSamsungAcExtendedStateLength);
+ EXPECT_EQ(
+ "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off, On Timer: 00:00, Off Timer: 01:00",
+ ac.toString());
+
+ // https://cryptpad.fr/sheet/#/2/sheet/view/r9k8pmELYEjLyC71cD7EsThEYgKGLJygREZ5pVfNkS8/
+ // Row 155
+ const uint8_t off_timer_11h_on_timer_6h[kSamsungAcExtendedStateLength] = {
+ 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
+ 0x01, 0x62, 0x8F, 0x05, 0x03, 0x06, 0x00,
+ 0x01, 0x02, 0xFF, 0x71, 0x40, 0x11, 0xC0};
+ ac.setRaw(off_timer_11h_on_timer_6h, kSamsungAcExtendedStateLength);
+ EXPECT_EQ(
+ "Power: Off, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off, On Timer: 06:00, Off Timer: 11:00",
+ ac.toString());
+
+ ac.stateReset(false);
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOnTimer(0);
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOffTimer(0);
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ // On Timer only
+ ac.setOnTimer(30);
+ EXPECT_EQ(30, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOnTimer(90); // 1h30m
+ EXPECT_EQ(90, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOnTimer(85); // 1h25m -> 1h20m
+ EXPECT_EQ(80, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOnTimer(23 * 60 + 59); // 23:59
+ EXPECT_EQ(23 * 60 + 50, ac.getOnTimer()); // 23:50
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOnTimer(24 * 60 + 30); // 24:30
+ EXPECT_EQ(24 * 60, ac.getOnTimer()); // 24:00 (Max)
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ // Off Timer only
+ ac.setOnTimer(0);
+ ac.setOffTimer(0);
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOffTimer(30);
+ EXPECT_EQ(30, ac.getOffTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+
+ ac.setOffTimer(90); // 1h30m
+ EXPECT_EQ(90, ac.getOffTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+
+ ac.setOffTimer(85); // 1h25m -> 1h20m
+ EXPECT_EQ(80, ac.getOffTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+
+ ac.setOffTimer(23 * 60 + 59); // 23:59
+ EXPECT_EQ(23 * 60 + 50, ac.getOffTimer()); // 23:50
+ EXPECT_EQ(0, ac.getOnTimer());
+
+ ac.setOffTimer(24 * 60 + 30); // 24:30
+ EXPECT_EQ(24 * 60, ac.getOffTimer()); // 24:00 (Max)
+ EXPECT_EQ(0, ac.getOnTimer());
+
+ // Both Timers
+ ac.setOnTimer(24 * 60); // 24:00
+ EXPECT_EQ(24 * 60, ac.getOnTimer()); // 24:00 (Max)
+ EXPECT_EQ(24 * 60, ac.getOffTimer()); // 24:00 (Max)
+
+ ac.setOnTimer(1 * 60 + 30); // 1:30
+ ac.setOffTimer(11 * 60); // 11:00
+ EXPECT_EQ(1 * 60 + 30, ac.getOnTimer());
+ EXPECT_EQ(11 * 60, ac.getOffTimer());
+}
+
+TEST(TestIRSamsungAcClass, Sleep) {
+ IRSamsungAc ac(kGpioUnused);
+ ac.begin();
+
+ // https://cryptpad.fr/sheet/#/2/sheet/view/r9k8pmELYEjLyC71cD7EsThEYgKGLJygREZ5pVfNkS8/
+ const uint8_t sleep_8h[kSamsungAcExtendedStateLength] = {
+ 0x02, 0x82, 0x0F, 0x00, 0x00, 0x10, 0xF0,
+ 0x01, 0xA2, 0x0F, 0x04, 0x00, 0x0C, 0x00,
+ 0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0};
+ ac.setRaw(sleep_8h, kSamsungAcExtendedStateLength);
+ EXPECT_EQ(8 * 60, ac.getSleepTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(
+ "Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 0 (Auto), "
+ "Swing(V): Off, Swing(H): Off, "
+ "Beep: -, Clean: -, Quiet: Off, Powerful: Off, "
+ "Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off, Sleep Timer: 08:00",
+ ac.toString());
+
+ ac.stateReset(false);
+ EXPECT_EQ(0, ac.getSleepTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setOnTimer(60);
+ ac.setOffTimer(90);
+ EXPECT_EQ(0, ac.getSleepTimer());
+ EXPECT_EQ(60, ac.getOnTimer());
+ EXPECT_EQ(90, ac.getOffTimer());
+
+ ac.setSleepTimer(120);
+ EXPECT_EQ(120, ac.getSleepTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(0, ac.getOffTimer());
+
+ ac.setSleepTimer(24 * 60 + 31); // 24h31m
+ EXPECT_EQ(24 * 60, ac.getSleepTimer()); // 24h (Max)
+
+ ac.setSleepTimer(35); // 45m
+ EXPECT_EQ(30, ac.getSleepTimer()); // 30m (Only stored in 10m increments).
+
+ ac.setOnTimer(60); // Seting an On Timer should clear the sleep setting.
+ EXPECT_EQ(0, ac.getSleepTimer());
+ EXPECT_EQ(60, ac.getOnTimer());
+
+ ac.setSleepTimer(120);
+ ac.setOffTimer(90); // Setting an Off Timer will clear the sleep setting.
+ EXPECT_EQ(0, ac.getSleepTimer());
+ EXPECT_EQ(0, ac.getOnTimer());
+ EXPECT_EQ(90, ac.getOffTimer());
+}
+
+TEST(TestIRSamsungAcClass, BuildKnownSleepSate) {
+ // For https://github.com/crankyoldgit/IRremoteESP8266/issues/1277#issuecomment-965047193
+ IRSamsungAc ac(kGpioUnused);
+ IRrecv irrecv(kGpioUnused);
+ ac.begin();
+ const uint8_t expectedState[kSamsungAcExtendedStateLength] = {
+ 0x02, 0x82, 0x0F, 0x00, 0x00, 0x10, 0xF0,
+ 0x01, 0xA2, 0x0F, 0x04, 0x00, 0x0C, 0x00,
+ 0x01, 0xD2, 0xFE, 0x71, 0x50, 0x41, 0xF0};
+ const char expectedStr[] = "Power: On, Mode: 4 (Heat), Temp: 21C, "
+ "Fan: 0 (Auto), Swing(V): Off, Swing(H): Off, Beep: -, Clean: -, "
+ "Quiet: Off, Powerful: Off, Econo: Off, Breeze: Off, "
+ "Light: On, Ion: Off, Sleep Timer: 08:00";
+ ac.setPower(true);
+ ac.setMode(kSamsungAcHeat);
+ ac.setTemp(21);
+ ac.setFan(kSamsungAcFanAuto);
+ ac.setSwing(false);
+ ac.setSwingH(false);
+ ac.setBeep(false);
+ ac.setClean(false);
+ ac.setQuiet(false);
+ ac.setPowerful(false);
+ ac.setEcono(false);
+ ac.setBreeze(false);
+ ac.setDisplay(true);
+ ac.setIon(false);
+ ac.setSleepTimer(8 * 60);
+ EXPECT_EQ(expectedStr, ac.toString());
+ ac.send();
+ ac._irsend.makeDecodeResult();
+ EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
+ EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
+ EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
+ EXPECT_STATE_EQ(expectedState, ac._irsend.capture.state,
+ ac._irsend.capture.bits);
+ EXPECT_EQ(expectedStr, IRAcUtils::resultAcToString(&ac._irsend.capture));
+ ac._irsend.reset();
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Sharp_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Sharp_test.cpp
index bf08c644a..a2acafb9c 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Sharp_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Sharp_test.cpp
@@ -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());
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Tcl_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Tcl_test.cpp
index da3fefc8c..b7ab5301f 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Tcl_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Tcl_test.cpp
@@ -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());
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Teknopoint_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Teknopoint_test.cpp
index 009b2a41b..74f29d146 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Teknopoint_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Teknopoint_test.cpp
@@ -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));
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/Makefile b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/Makefile
index b30d1207c..15f192efd 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/Makefile
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/Makefile
@@ -1,7 +1,11 @@
# SYNOPSIS:
#
-# make [all] - makes everything.
-# make clean - removes all files generated by make.
+# 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
# project, except GTEST_HEADERS, which you can use in your own targets
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/auto_analyse_raw_data.py
index aa5c990a9..e061d3c42 100755
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/auto_analyse_raw_data.py
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/auto_analyse_raw_data.py
@@ -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)."""
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/generate_irtext_h.sh b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/generate_irtext_h.sh
index df2baee06..b0d9855c4 100755
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/generate_irtext_h.sh
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/generate_irtext_h.sh
@@ -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(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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/mkkeywords b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/mkkeywords
index 84f2a46fd..e050e4964 100755
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/mkkeywords
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/mkkeywords
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/raw_to_pronto_code.py b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/raw_to_pronto_code.py
index 307ae7121..8d8fc815c 100755
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/raw_to_pronto_code.py
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/tools/raw_to_pronto_code.py
@@ -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
diff --git a/lib/lib_basic/IRremoteESP8266/library.json b/lib/lib_basic/IRremoteESP8266/library.json
index f905e1c2f..6ac0ca58a 100644
--- a/lib/lib_basic/IRremoteESP8266/library.json
+++ b/lib/lib_basic/IRremoteESP8266/library.json
@@ -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" ]
|