"
"" D_STR_PROTOCOL " | ") +
htmlSelectClimateProtocol(KEY_PROTOCOL,
@@ -1066,6 +1165,9 @@ void handleAirCon(void) {
" |
" D_STR_MODEL " | ") +
htmlSelectModel(KEY_MODEL, climate[chan]->next.model) +
F(" |
"
+ "" D_STR_COMMAND " | ") +
+ htmlSelectCommandType(KEY_COMMAND, climate[chan]->next.command) +
+ F(" |
"
"" D_STR_POWER " | ") +
htmlSelectBool(KEY_POWER, climate[chan]->next.power) +
F(" |
"
@@ -1084,6 +1186,16 @@ void handleAirCon(void) {
(!climate[chan]->next.celsius ? " selected='selected'" : "") +
F(">F"
""
+ "" D_STR_SENSORTEMP " | "
+ "") +
+ htmlDisableCheckbox(KEY_SENSORTEMP_DISABLED, KEY_SENSORTEMP,
+ noSensorTemp) +
+ F(" |
"
"" D_STR_FAN " | ") +
htmlSelectFanspeed(KEY_FANSPEED, climate[chan]->next.fanspeed) +
F(" |
"
@@ -1096,6 +1208,9 @@ void handleAirCon(void) {
"" D_STR_QUIET " | ") +
htmlSelectBool(KEY_QUIET, climate[chan]->next.quiet) +
F(" |
"
+ "" D_STR_IFEEL " | ") +
+ htmlSelectBool(KEY_IFEEL, climate[chan]->next.iFeel) +
+ F(" |
"
"" D_STR_TURBO " | ") +
htmlSelectBool(KEY_TURBO, climate[chan]->next.turbo) +
F(" |
"
@@ -1190,7 +1305,13 @@ void handleAdmin(void) {
#if MQTT_DISCOVERY_ENABLE
html += htmlButton(
kUrlSendDiscovery, F("Send MQTT Discovery"),
- F("Send a Climate MQTT discovery message to Home Assistant.
"));
+#if SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
+ F("Send a Climate and Sensor MQTT"
+#else
+ F("Send a Climate MQTT"
+#endif // SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
+ " discovery message to Home Assistant.
"));
+
#endif // MQTT_DISCOVERY_ENABLE
#if MQTT_CLEAR_ENABLE
html += htmlButton(
@@ -1244,8 +1365,8 @@ void handleInfo(void) {
String html = htmlHeader(F("IR MQTT server info"));
html += htmlMenu();
html +=
- F("General
"
- "Hostname: ") + String(Hostname) + F("
"
+ String(F("
General
"
+ "Hostname: ")) + String(Hostname) + F("
"
"IP address: ") + WiFi.localIP().toString() + F("
"
"MAC address: ") + WiFi.macAddress() + F("
"
"Booted: ") + timeSince(1) + F("
") +
@@ -1392,6 +1513,10 @@ bool clearMqttSavedStates(const String topic_base) {
#if MQTT_DISCOVERY_ENABLE
// Clear the HA climate discovery message.
success &= mqtt_client.publish(MqttDiscovery.c_str(), "", true);
+#if SHT3X_SUPPORT && MQTT_DISCOVERY_ENABLE
+ // Clear the HA sensor discovery message.
+ success &= mqtt_client.publish(MqttDiscoverySensor.c_str(), "", true);
+#endif // SHT3X_SUPPORT && MQTT_DISCOVERY_ENABLE
#endif // MQTT_DISCOVERY_ENABLE
for (size_t channel = 0;
channel <= kNrOfIrTxGpios;
@@ -2106,13 +2231,20 @@ void init_vars(void) {
MqttClimateCmnd = MqttClimate + '/' + MQTT_CLIMATE_CMND + '/';
// Sub-topic for the climate stat topics.
#if MQTT_DISCOVERY_ENABLE
- MqttDiscovery = "homeassistant/climate/" + String(Hostname) + "/config";
+ MqttDiscovery = "homeassistant/climate/" + String(Hostname);
+#if SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
+ MqttDiscoverySensor = "homeassistant/sensor/" + String(Hostname);
+#endif // SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
MqttUniqueId = WiFi.macAddress();
MqttUniqueId.replace(":", "");
#endif // MQTT_DISCOVERY_ENABLE
MqttHAName = String(Hostname) + "_aircon";
// Create a unique MQTT client id.
MqttClientId = String(Hostname) + String(kChipId, HEX);
+#if SHT3X_SUPPORT
+ // Sub-topic for the climate stat topics.
+ MqttSensorStat = String(MqttPrefix) + '/' + MQTT_SENSOR_STAT + '/';
+#endif // SHT3X_SUPPORT
#endif // MQTT_ENABLE
}
@@ -2418,6 +2550,9 @@ void handleSendMqttDiscovery(void) {
htmlMenu() +
F("
The Home Assistant MQTT Discovery message is being sent to topic: ")
+ MqttDiscovery +
+#if SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
+ F(" and ") + MqttDiscoverySensor +
+#endif // SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
F(". It will show up in Home Assistant in a few seconds."
"
"
"Warning!
"
@@ -2425,7 +2560,15 @@ void handleSendMqttDiscovery(void) {
" is sent.") +
addJsReloadUrl(kUrlRoot, kRebootTime, true) +
htmlEnd());
- sendMQTTDiscovery(MqttDiscovery.c_str());
+ for (uint16_t i = 0; i < kNrOfIrTxGpios; i++) {
+ String channel_id = "";
+ if (i > 0) channel_id = "_" + String(i);
+ sendMQTTDiscovery(MqttDiscovery.c_str(), channel_id);
+ }
+#if SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
+ sendMQTTDiscoverySensor(MqttDiscoverySensor.c_str(), KEY_TEMP);
+ sendMQTTDiscoverySensor(MqttDiscoverySensor.c_str(), KEY_HUMIDITY);
+#endif // SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
}
#endif // MQTT_DISCOVERY_ENABLE
@@ -2598,12 +2741,13 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
}
#if MQTT_DISCOVERY_ENABLE
-void sendMQTTDiscovery(const char *topic) {
+void sendMQTTDiscovery(const char *topic, String channel_id) {
+ String pub_topic = String(topic) + channel_id + F("/config");
if (mqtt_client.publish(
- topic, String(
+ pub_topic.c_str(), String(
F("{"
- "\"~\":\"") + MqttClimate + F("\","
- "\"name\":\"") + MqttHAName + F("\","
+ "\"~\":\"") + MqttClimate + channel_id + F("\","
+ "\"name\":\"") + MqttHAName + channel_id + 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
@@ -2629,9 +2773,12 @@ 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 + F("\","
+#if SHT3X_SUPPORT
+ "\"curr_temp_t\":\"") + MqttSensorStat + F(KEY_TEMP "\","
+#endif // SHT3X_SUPPORT
+ "\"uniq_id\":\"") + MqttUniqueId + channel_id + F("\","
"\"device\":{"
- "\"identifiers\":[\"") + MqttUniqueId + F("\"],"
+ "\"identifiers\":[\"") + MqttUniqueId + channel_id + F("\"],"
"\"connections\":[[\"mac\",\"") + WiFi.macAddress() + F("\"]],"
"\"manufacturer\":\"IRremoteESP8266\","
"\"model\":\"IRMQTTServer\","
@@ -2647,6 +2794,47 @@ void sendMQTTDiscovery(const char *topic) {
mqttLog(PSTR("MQTT climate discovery FAILED to send."));
}
}
+
+#if SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
+// Send the MQTT Discovery data for the SHT3X sensor.
+// type must be a String of either KEY_TEMP or KEY_HUMIDITY.
+void sendMQTTDiscoverySensor(const char *topic, String type) {
+ String pub_topic = String(topic) + F("_") + type + F("/config");
+ String uom = "%";
+ String ha_class = type;
+ // XXX Update units of measure for temperature.
+ if (type == KEY_TEMP) {
+ uom = "°C";
+ ha_class = "temperature";
+ }
+ if (mqtt_client.publish(
+ pub_topic.c_str(), String(
+ F("{"
+ "\"name\":\"") + MqttHAName + "_" + type + F("\","
+
+ "\"stat_t\":\"") + MqttSensorStat + type + F("\","
+ "\"dev_cla\":\"") + ha_class + F("\","
+ "\"unit_of_meas\":\"") + uom + F("\","
+
+ "\"uniq_id\":\"") + MqttUniqueId + type + F("\","
+ "\"device\":{"
+ "\"identifiers\":[\"") + MqttUniqueId + type + F("\"],"
+ "\"connections\":[[\"mac\",\"") + WiFi.macAddress() + F("\"]],"
+ "\"manufacturer\":\"IRremoteESP8266\","
+ "\"model\":\"IRMQTTServer\","
+ "\"name\":\"") + Hostname + F("\","
+ "\"sw_version\":\"" _MY_VERSION_ "\""
+ "}"
+ "}")).c_str(), true)) {
+ mqttLog(PSTR("MQTT sensor discovery successful sent."));
+ hasDiscoveryBeenSent = true;
+ lastDiscovery.reset();
+ mqttSentCounter++;
+ } else {
+ mqttLog(PSTR("MQTT sensor discovery FAILED to send."));
+ }
+}
+#endif // SHT3X_SUPPORT && SHT3X_MQTT_DISCOVERY_ENABLE
#endif // MQTT_DISCOVERY_ENABLE
#endif // MQTT_ENABLE
@@ -2678,8 +2866,8 @@ void loop(void) {
boot = false;
} else {
mqttLog(String(
- F("IRMQTTServer just (re)connected to MQTT. "
- "Lost connection about ")
+ String(F("IRMQTTServer just (re)connected to MQTT. "
+ "Lost connection about "))
+ timeSince(lastConnectedTime)).c_str());
}
lastConnectedTime = now;
@@ -2715,6 +2903,29 @@ void loop(void) {
}
// Periodically send all of the climate state via MQTT.
doBroadcast(&lastBroadcast, kBroadcastPeriodMs, climate, false, false);
+#if SHT3X_SUPPORT
+ // Check if it's time to read the SHT3x sensor.
+ if (statSensorReadTime.elapsed() > SHT3X_CHECK_FREQ * 1000) {
+ byte result = TemperatureSensor.get();
+ if (result == 0) {
+ // Success
+ float temp = TemperatureSensor.cTemp;
+ // XXX Convert units
+ float humidity = TemperatureSensor.humidity;
+ // Publish the temp and humidity to MQTT.
+ String mqttTempTopic = MqttSensorStat + KEY_TEMP;
+ String mqttHumidityTopic = MqttSensorStat + KEY_HUMIDITY;
+ mqtt_client.publish(mqttTempTopic.c_str(), String(temp).c_str());
+ mqtt_client.publish(mqttHumidityTopic.c_str(),
+ String(humidity).c_str());
+ } else {
+ // Error
+ mqttLog((String(F("SHT3x sensor read error: ")) +
+ String(result)).c_str());
+ }
+ statSensorReadTime.reset();
+ }
+#endif // SHT3X_SUPPORT
}
#endif // MQTT_ENABLE
#if IR_RX
@@ -2934,6 +3145,7 @@ void sendJsonState(const stdAc::state_t state, const String topic,
DynamicJsonDocument json(kJsonAcStateMaxSize);
json[KEY_PROTOCOL] = typeToString(state.protocol);
json[KEY_MODEL] = state.model;
+ json[KEY_COMMAND] = IRac::commandToString(state.command);
json[KEY_POWER] = IRac::boolToString(state.power);
json[KEY_MODE] = IRac::opmodeToString(state.mode, ha_mode);
// Home Assistant wants mode to be off if power is also off & vice-versa.
@@ -2943,10 +3155,12 @@ void sendJsonState(const stdAc::state_t state, const String topic,
}
json[KEY_CELSIUS] = IRac::boolToString(state.celsius);
json[KEY_TEMP] = state.degrees;
+ json[KEY_SENSORTEMP] = state.sensorTemperature;
json[KEY_FANSPEED] = IRac::fanspeedToString(state.fanspeed);
json[KEY_SWINGV] = IRac::swingvToString(state.swingv);
json[KEY_SWINGH] = IRac::swinghToString(state.swingh);
json[KEY_QUIET] = IRac::boolToString(state.quiet);
+ json[KEY_IFEEL] = IRac::boolToString(state.iFeel);
json[KEY_TURBO] = IRac::boolToString(state.turbo);
json[KEY_ECONO] = IRac::boolToString(state.econo);
json[KEY_LIGHT] = IRac::boolToString(state.light);
@@ -2984,6 +3198,10 @@ stdAc::state_t jsonToState(const stdAc::state_t current, const char *str) {
result.model = IRac::strToModel(json[KEY_MODEL].as());
else if (validJsonInt(json, KEY_MODEL))
result.model = json[KEY_MODEL];
+ if (validJsonStr(json, KEY_COMMAND))
+ result.command = IRac::strToCommand(json[KEY_COMMAND].as());
+ else if (validJsonInt(json, KEY_COMMAND))
+ result.command = json[KEY_COMMAND];
if (validJsonStr(json, KEY_MODE))
result.mode = IRac::strToOpmode(json[KEY_MODE]);
if (validJsonStr(json, KEY_FANSPEED))
@@ -2994,10 +3212,14 @@ stdAc::state_t jsonToState(const stdAc::state_t current, const char *str) {
result.swingh = IRac::strToSwingH(json[KEY_SWINGH]);
if (json.containsKey(KEY_TEMP))
result.degrees = json[KEY_TEMP];
+ if (json.containsKey(KEY_SENSORTEMP))
+ result.sensorTemperature = json[KEY_SENSORTEMP];
if (validJsonInt(json, KEY_SLEEP))
result.sleep = json[KEY_SLEEP];
if (validJsonStr(json, KEY_POWER))
result.power = IRac::strToBool(json[KEY_POWER]);
+ if (validJsonStr(json, KEY_IFEEL))
+ result.iFeel = IRac::strToBool(json[KEY_IFEEL]);
if (validJsonStr(json, KEY_QUIET))
result.quiet = IRac::strToBool(json[KEY_QUIET]);
if (validJsonStr(json, KEY_TURBO))
@@ -3029,6 +3251,8 @@ void updateClimate(stdAc::state_t *state, const String str,
state->protocol = strToDecodeType(payload.c_str());
} else if (str.equals(prefix + F(KEY_MODEL))) {
state->model = IRac::strToModel(payload.c_str());
+ } else if (str.equals(prefix + F(KEY_COMMAND))) {
+ state->command = IRac::strToCommandType(payload.c_str());
} else if (str.equals(prefix + F(KEY_POWER))) {
state->power = IRac::strToBool(payload.c_str());
#if MQTT_CLIMATE_HA_MODE
@@ -3039,16 +3263,32 @@ void updateClimate(stdAc::state_t *state, const String str,
state->mode = IRac::strToOpmode(payload.c_str());
#if MQTT_CLIMATE_HA_MODE
// When in Home Assistant mode, a Mode of Off, means turn the Power off too.
- if (state->mode == stdAc::opmode_t::kOff) state->power = false;
+ if (state->mode == stdAc::opmode_t::kOff) {
+ state->power = false;
+ } else {
+ state->power = true;
+ }
#endif // MQTT_CLIMATE_HA_MODE
} else if (str.equals(prefix + F(KEY_TEMP))) {
state->degrees = payload.toFloat();
+ } else if (str.equals(prefix + F(KEY_SENSORTEMP))) {
+ state->sensorTemperature = payload.toFloat();
+ } else if (str.equals(prefix + F(KEY_SENSORTEMP_DISABLED))) {
+ // The "disabled" html form field appears after the actual sensorTemp field
+ // and the spec guarantees the form POST field order preserves body order
+ // => this will always execute after KEY_SENSORTEMP has been parsed already
+ if (IRac::strToBool(payload.c_str())) {
+ // UI control was disabled, ignore the value
+ state->sensorTemperature = kNoTempValue;
+ }
} else if (str.equals(prefix + F(KEY_FANSPEED))) {
state->fanspeed = IRac::strToFanspeed(payload.c_str());
} else if (str.equals(prefix + F(KEY_SWINGV))) {
state->swingv = IRac::strToSwingV(payload.c_str());
} else if (str.equals(prefix + F(KEY_SWINGH))) {
state->swingh = IRac::strToSwingH(payload.c_str());
+ } else if (str.equals(prefix + F(KEY_IFEEL))) {
+ state->iFeel = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + F(KEY_QUIET))) {
state->quiet = IRac::strToBool(payload.c_str());
} else if (str.equals(prefix + F(KEY_TURBO))) {
@@ -3086,14 +3326,16 @@ bool sendClimate(const String topic_prefix, const bool retain,
diff = true;
success &= sendInt(topic_prefix + KEY_MODEL, next.model, retain);
}
+ if (prev.command != next.command || forceMQTT) {
+ String command_str = IRac::commandTypeToString(next.command);
+ diff = true;
+ success &= sendString(topic_prefix + KEY_COMMAND, command_str, 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();
#if MQTT_CLIMATE_HA_MODE
// Home Assistant want's these two bound together.
if (prev.power != next.power || prev.mode != next.mode || forceMQTT) {
@@ -3107,6 +3349,10 @@ bool sendClimate(const String topic_prefix, const bool retain,
}
if (prev.mode != next.mode || forceMQTT) {
#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();
+
success &= sendString(topic_prefix + KEY_MODE, mode_str, retain);
diff = true;
}
@@ -3118,6 +3364,11 @@ bool sendClimate(const String topic_prefix, const bool retain,
diff = true;
success &= sendBool(topic_prefix + KEY_CELSIUS, next.celsius, retain);
}
+ if (prev.sensorTemperature != next.sensorTemperature || forceMQTT) {
+ diff = true;
+ success &= sendFloat(topic_prefix + KEY_SENSORTEMP,
+ next.sensorTemperature, retain);
+ }
if (prev.fanspeed != next.fanspeed || forceMQTT) {
diff = true;
success &= sendString(topic_prefix + KEY_FANSPEED,
@@ -3133,6 +3384,10 @@ bool sendClimate(const String topic_prefix, const bool retain,
success &= sendString(topic_prefix + KEY_SWINGH,
IRac::swinghToString(next.swingh), retain);
}
+ if (prev.iFeel != next.iFeel || forceMQTT) {
+ diff = true;
+ success &= sendBool(topic_prefix + KEY_IFEEL, next.iFeel, retain);
+ }
if (prev.quiet != next.quiet || forceMQTT) {
diff = true;
success &= sendBool(topic_prefix + KEY_QUIET, next.quiet, retain);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRMQTTServer/platformio.ini b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRMQTTServer/platformio.ini
index d0ac2b8c8..a20250c67 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRMQTTServer/platformio.ini
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/examples/IRMQTTServer/platformio.ini
@@ -15,6 +15,8 @@ lib_deps_builtin =
lib_deps_external =
PubSubClient@>=2.8.0
ArduinoJson@>=6.0
+# Uncomment the following to enable SHT-3x support.
+# https://github.com/wemos/WEMOS_SHT3x_Arduino_Library.git
[common_esp8266]
lib_deps_external =
@@ -36,6 +38,14 @@ lib_deps = ${common_esp8266.lib_deps_external}
board = d1_mini
lib_deps = ${common_esp8266.lib_deps_external}
+[env:d1_mini_noMDNS]
+board = d1_mini
+build_flags =
+ ${env.build_flags}
+ -DMQTT_SERVER_AUTODETECT_ENABLE=false
+ -DMDNS_ENABLE=false
+lib_deps = ${common_esp8266.lib_deps_external}
+
[env:d1_mini_no_mqtt]
board = d1_mini
build_flags =
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt
index a562d2451..ef944fdb5 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/keywords.txt
@@ -20,11 +20,14 @@
# Datatypes & Classes (KEYWORD1)
#######################################
+Config KEYWORD1
CoronaSection KEYWORD1
IRAirtonAc KEYWORD1
IRAirwellAc KEYWORD1
IRAmcorAc KEYWORD1
IRArgoAC KEYWORD1
+IRArgoACBase KEYWORD1
+IRArgoAC_WREM3 KEYWORD1
IRBosch144AC KEYWORD1
IRCarrierAc64 KEYWORD1
IRCoolixAC KEYWORD1
@@ -83,11 +86,21 @@ IRTrumaAc KEYWORD1
IRVestelAc KEYWORD1
IRVoltas KEYWORD1
IRWhirlpoolAc KEYWORD1
+IRYorkAc KEYWORD1
IRac KEYWORD1
IRrecv KEYWORD1
IRsend KEYWORD1
IRtimer KEYWORD1
+Timer KEYWORD1
TimerMs KEYWORD1
+ac_command_t KEYWORD1
+argoFan_t KEYWORD1
+argoFlap_t KEYWORD1
+argoIrMessageType_t KEYWORD1
+argoMode_t KEYWORD1
+argoTimerType_t KEYWORD1
+argoWeekday KEYWORD1
+argo_ac_remote_model_t KEYWORD1
decode_results KEYWORD1
decode_type_t KEYWORD1
fanspeed_t KEYWORD1
@@ -116,6 +129,7 @@ whirlpool_ac_remote_model_t KEYWORD1
_backupState KEYWORD2
_cancelOffTimer KEYWORD2
_cancelOnTimer KEYWORD2
+_checksum KEYWORD2
_delayMicroseconds KEYWORD2
_getEconoToggle KEYWORD2
_getOffTimer KEYWORD2
@@ -137,6 +151,7 @@ _setSleepTimer KEYWORD2
_setTemp KEYWORD2
_setTime KEYWORD2
_setTimer KEYWORD2
+_stateReset KEYWORD2
_toString KEYWORD2
_validTolerance KEYWORD2
add KEYWORD2
@@ -152,12 +167,17 @@ addSwingHToString KEYWORD2
addSwingVToString KEYWORD2
addTempFloatToString KEYWORD2
addTempToString KEYWORD2
+addTimerModeToString KEYWORD2
addToggleToString KEYWORD2
adjustRepeat KEYWORD2
airton KEYWORD2
airwell KEYWORD2
amcor KEYWORD2
argo KEYWORD2
+argoWrem3_ACCommand KEYWORD2
+argoWrem3_ConfigSet KEYWORD2
+argoWrem3_SetTimer KEYWORD2
+argoWrem3_iFeelReport KEYWORD2
bcdToUint8 KEYWORD2
begin KEYWORD2
boolToString KEYWORD2
@@ -178,6 +198,7 @@ cancelOnTimer KEYWORD2
cancelTimers KEYWORD2
carrier64 KEYWORD2
celsiusToFahrenheit KEYWORD2
+channelToString KEYWORD2
checkInvertedBytePairs KEYWORD2
checkSum KEYWORD2
checkZjsSig KEYWORD2
@@ -189,6 +210,7 @@ clearPowerSpecial KEYWORD2
clearSensorTemp KEYWORD2
clearSleepTimerFlag KEYWORD2
cmpStates KEYWORD2
+commandTypeToString KEYWORD2
compare KEYWORD2
convertFan KEYWORD2
convertMode KEYWORD2
@@ -209,12 +231,15 @@ daikin176 KEYWORD2
daikin2 KEYWORD2
daikin216 KEYWORD2
daikin64 KEYWORD2
+dayToString KEYWORD2
+daysBitmaskToString KEYWORD2
decode KEYWORD2
decodeAirton KEYWORD2
decodeAirwell KEYWORD2
decodeAiwaRCT501 KEYWORD2
decodeAmcor KEYWORD2
decodeArgo KEYWORD2
+decodeArgoWREM3 KEYWORD2
decodeArris KEYWORD2
decodeBosch144 KEYWORD2
decodeBose KEYWORD2
@@ -223,6 +248,7 @@ decodeCarrierAC KEYWORD2
decodeCarrierAC128 KEYWORD2
decodeCarrierAC40 KEYWORD2
decodeCarrierAC64 KEYWORD2
+decodeCarrierAC84 KEYWORD2
decodeClimaButler KEYWORD2
decodeCoolix48 KEYWORD2
decodeCoronaAc KEYWORD2
@@ -247,6 +273,7 @@ decodeEpson KEYWORD2
decodeFujitsuAC KEYWORD2
decodeGICable KEYWORD2
decodeGoodweather KEYWORD2
+decodeGorenje KEYWORD2
decodeGree KEYWORD2
decodeHaierAC KEYWORD2
decodeHaierAC160 KEYWORD2
@@ -318,7 +345,9 @@ decodeVestelAc KEYWORD2
decodeVoltas KEYWORD2
decodeWhirlpoolAC KEYWORD2
decodeWhynter KEYWORD2
+decodeWowwee KEYWORD2
decodeXmp KEYWORD2
+decodeYork KEYWORD2
decodeZepeal KEYWORD2
defaultBits KEYWORD2
delonghiac KEYWORD2
@@ -371,6 +400,8 @@ getBreeze KEYWORD2
getBufSize KEYWORD2
getButton KEYWORD2
getCelsius KEYWORD2
+getChannel KEYWORD2
+getChecksum KEYWORD2
getClean KEYWORD2
getCleanToggle KEYWORD2
getClock KEYWORD2
@@ -381,10 +412,13 @@ getCorrectedRawLength KEYWORD2
getCurrTime KEYWORD2
getCurrentDay KEYWORD2
getCurrentTime KEYWORD2
+getCurrentTimeMinutes KEYWORD2
+getDelayTimerMinutes KEYWORD2
getDirectIndirect KEYWORD2
getDisplay KEYWORD2
getDisplayTempSource KEYWORD2
getDryGrade KEYWORD2
+getEco KEYWORD2
getEcocool KEYWORD2
getEcono KEYWORD2
getEconoToggle KEYWORD2
@@ -438,8 +472,10 @@ getPurify KEYWORD2
getQuiet KEYWORD2
getRClevel KEYWORD2
getRaw KEYWORD2
-getRoomTemp KEYWORD2
+getRawByteLength KEYWORD2
getSave KEYWORD2
+getScheduleTimerStartMinutes KEYWORD2
+getScheduleTimerStopMinutes KEYWORD2
getSectionByte KEYWORD2
getSectionChecksum KEYWORD2
getSensor KEYWORD2
@@ -455,6 +491,7 @@ getSpeed KEYWORD2
getStartClock KEYWORD2
getState KEYWORD2
getStateLength KEYWORD2
+getStateLengthForIrMsgType KEYWORD2
getStatePrev KEYWORD2
getStopClock KEYWORD2
getSupercool KEYWORD2
@@ -476,6 +513,7 @@ getTempUnit KEYWORD2
getTempUnits KEYWORD2
getTime KEYWORD2
getTimer KEYWORD2
+getTimerActiveDaysBitmap KEYWORD2
getTimerEnabled KEYWORD2
getTimerMode KEYWORD2
getTimerTime KEYWORD2
@@ -511,6 +549,7 @@ handleToggles KEYWORD2
hasACState KEYWORD2
hasInvertedStates KEYWORD2
hasStateChanged KEYWORD2
+hasValidPreamble KEYWORD2
hitachi KEYWORD2
hitachi1 KEYWORD2
hitachi264 KEYWORD2
@@ -521,6 +560,7 @@ htmlEscape KEYWORD2
initState KEYWORD2
int64ToString KEYWORD2
invertBits KEYWORD2
+irCommandTypeToString KEYWORD2
is8CHeatToggle KEYWORD2
isCleanToggle KEYWORD2
isEconoToggle KEYWORD2
@@ -546,6 +586,7 @@ isTimeCommand KEYWORD2
isTimerActive KEYWORD2
isTurboToggle KEYWORD2
isValidLgAc KEYWORD2
+isValidWrem3Message KEYWORD2
isVaneSwingV KEYWORD2
kelon KEYWORD2
kelvinator KEYWORD2
@@ -606,6 +647,7 @@ sendAirwell KEYWORD2
sendAiwaRCT501 KEYWORD2
sendAmcor KEYWORD2
sendArgo KEYWORD2
+sendArgoWREM3 KEYWORD2
sendArris KEYWORD2
sendBosch144 KEYWORD2
sendBose KEYWORD2
@@ -614,6 +656,7 @@ sendCarrierAC KEYWORD2
sendCarrierAC128 KEYWORD2
sendCarrierAC40 KEYWORD2
sendCarrierAC64 KEYWORD2
+sendCarrierAC84 KEYWORD2
sendClimaButler KEYWORD2
sendCoolix48 KEYWORD2
sendCoronaAc KEYWORD2
@@ -642,6 +685,7 @@ sendGC KEYWORD2
sendGICable KEYWORD2
sendGeneric KEYWORD2
sendGoodweather KEYWORD2
+sendGorenje KEYWORD2
sendGree KEYWORD2
sendHaierAC KEYWORD2
sendHaierAC160 KEYWORD2
@@ -728,7 +772,9 @@ sendVestelAc KEYWORD2
sendVoltas KEYWORD2
sendWhirlpoolAC KEYWORD2
sendWhynter KEYWORD2
+sendWowwee KEYWORD2
sendXmp KEYWORD2
+sendYork KEYWORD2
sendZepeal KEYWORD2
serialPrintUint64 KEYWORD2
set10CHeat KEYWORD2
@@ -745,6 +791,7 @@ setBoost KEYWORD2
setBreeze KEYWORD2
setButton KEYWORD2
setCelsius KEYWORD2
+setChannel KEYWORD2
setCheckSumS3 KEYWORD2
setClean KEYWORD2
setCleanToggle KEYWORD2
@@ -752,13 +799,18 @@ setClock KEYWORD2
setCmd KEYWORD2
setComfort KEYWORD2
setCommand KEYWORD2
+setConfigEntry KEYWORD2
setCurrTime KEYWORD2
setCurrentDay KEYWORD2
+setCurrentDayOfWeek KEYWORD2
setCurrentTime KEYWORD2
+setCurrentTimeMinutes KEYWORD2
+setDelayTimerMinutes KEYWORD2
setDirectIndirect KEYWORD2
setDisplay KEYWORD2
setDisplayTempSource KEYWORD2
setDryGrade KEYWORD2
+setEco KEYWORD2
setEcocool KEYWORD2
setEcono KEYWORD2
setEconoToggle KEYWORD2
@@ -790,6 +842,7 @@ setLight KEYWORD2
setLightToggle KEYWORD2
setLock KEYWORD2
setMax KEYWORD2
+setMessageType KEYWORD2
setMode KEYWORD2
setModel KEYWORD2
setMold KEYWORD2
@@ -813,8 +866,10 @@ setPowerful KEYWORD2
setPurify KEYWORD2
setQuiet KEYWORD2
setRaw KEYWORD2
-setRoomTemp KEYWORD2
setSave KEYWORD2
+setScheduleTimerActiveDays KEYWORD2
+setScheduleTimerStartMinutes KEYWORD2
+setScheduleTimerStopMinutes KEYWORD2
setSensor KEYWORD2
setSensorTemp KEYWORD2
setSensorTempRaw KEYWORD2
@@ -917,6 +972,7 @@ xorBytes KEYWORD2
A705 LITERAL1
A903 LITERAL1
A907 LITERAL1
+AC_CONTROL LITERAL1
AIRTON LITERAL1
AIRWELL LITERAL1
AIWA_RC_T501 LITERAL1
@@ -956,14 +1012,18 @@ ARREB1E LITERAL1
ARREW4E LITERAL1
ARRIS LITERAL1
ARRY4 LITERAL1
+AUTO LITERAL1
BOSCH144 LITERAL1
BOSE LITERAL1
CARRIER_AC LITERAL1
CARRIER_AC128 LITERAL1
CARRIER_AC40 LITERAL1
CARRIER_AC64 LITERAL1
+CARRIER_AC84 LITERAL1
CARRIER_AC_BITS LITERAL1
CLIMABUTLER LITERAL1
+CONFIG_PARAM_SET LITERAL1
+COOL LITERAL1
COOLIX LITERAL1
COOLIX48 LITERAL1
COOLIX_BITS LITERAL1
@@ -1003,6 +1063,7 @@ DECODE_CARRIER_AC LITERAL1
DECODE_CARRIER_AC128 LITERAL1
DECODE_CARRIER_AC40 LITERAL1
DECODE_CARRIER_AC64 LITERAL1
+DECODE_CARRIER_AC84 LITERAL1
DECODE_CLIMABUTLER LITERAL1
DECODE_COOLIX LITERAL1
DECODE_COOLIX48 LITERAL1
@@ -1029,6 +1090,7 @@ DECODE_FUJITSU_AC LITERAL1
DECODE_GICABLE LITERAL1
DECODE_GLOBALCACHE LITERAL1
DECODE_GOODWEATHER LITERAL1
+DECODE_GORENJE LITERAL1
DECODE_GREE LITERAL1
DECODE_HAIER_AC LITERAL1
DECODE_HAIER_AC160 LITERAL1
@@ -1105,8 +1167,11 @@ DECODE_VESTEL_AC LITERAL1
DECODE_VOLTAS LITERAL1
DECODE_WHIRLPOOL_AC LITERAL1
DECODE_WHYNTER LITERAL1
+DECODE_WOWWEE LITERAL1
DECODE_XMP LITERAL1
+DECODE_YORK LITERAL1
DECODE_ZEPEAL LITERAL1
+DELAY_TIMER LITERAL1
DELONGHI_AC LITERAL1
DENON LITERAL1
DENON_48_BITS LITERAL1
@@ -1117,11 +1182,29 @@ DG11J191 LITERAL1
DISH LITERAL1
DISH_BITS LITERAL1
DOSHISHA LITERAL1
+DRY LITERAL1
ECOCLIM LITERAL1
ELECTRA_AC LITERAL1
ELITESCREENS LITERAL1
ENABLE_NOISE_FILTER_OPTION LITERAL1
EPSON LITERAL1
+FAN LITERAL1
+FAN_AUTO LITERAL1
+FAN_HIGH LITERAL1
+FAN_HIGHEST LITERAL1
+FAN_LOW LITERAL1
+FAN_LOWER LITERAL1
+FAN_LOWEST LITERAL1
+FAN_MEDIUM LITERAL1
+FLAP_1 LITERAL1
+FLAP_2 LITERAL1
+FLAP_3 LITERAL1
+FLAP_4 LITERAL1
+FLAP_5 LITERAL1
+FLAP_6 LITERAL1
+FLAP_AUTO LITERAL1
+FLAP_FULL LITERAL1
+FRIDAY LITERAL1
FUJITSU_AC LITERAL1
FUJITSU_AC_BITS LITERAL1
FUJITSU_AC_CMD_STAY_ON LITERAL1
@@ -1154,6 +1237,7 @@ GICABLE LITERAL1
GICABLE_BITS LITERAL1
GLOBALCACHE LITERAL1
GOODWEATHER LITERAL1
+GORENJE LITERAL1
GREE LITERAL1
GREE_AUTO LITERAL1
GREE_COOL LITERAL1
@@ -1233,6 +1317,7 @@ HAIER_AC_YRW02_SWING_MIDDLE LITERAL1
HAIER_AC_YRW02_SWING_OFF LITERAL1
HAIER_AC_YRW02_SWING_TOP LITERAL1
HAIER_AC_YRW02_TURBO_OFF LITERAL1
+HEAT LITERAL1
HIGH LITERAL1
HITACHI_AC LITERAL1
HITACHI_AC1 LITERAL1
@@ -1250,7 +1335,7 @@ HITACHI_AC344 LITERAL1
HITACHI_AC424 LITERAL1
HITACHI_AC_BITS LITERAL1
HITACHI_AC_STATE_LENGTH LITERAL1
-ICACHE_RAM_ATTR LITERAL1
+IFEEL_TEMP_REPORT LITERAL1
INAX LITERAL1
JVC LITERAL1
JVC_BITS LITERAL1
@@ -1325,6 +1410,7 @@ MITSUBISHI_AC_VANE_AUTO_MOVE LITERAL1
MITSUBISHI_BITS LITERAL1
MITSUBISHI_HEAVY_152 LITERAL1
MITSUBISHI_HEAVY_88 LITERAL1
+MONDAY LITERAL1
MULTIBRACKETS LITERAL1
MWM LITERAL1
NEC LITERAL1
@@ -1334,6 +1420,7 @@ NEOCLIMA LITERAL1
NIKAI LITERAL1
NIKAI_BITS LITERAL1
NOTHING LITERAL1
+NO_TIMER LITERAL1
ONCE LITERAL1
PANASONIC LITERAL1
PANASONIC_AC LITERAL1
@@ -1356,6 +1443,8 @@ RCMM_BITS LITERAL1
RHOSS LITERAL1
R_LT0541_HTA_A LITERAL1
R_LT0541_HTA_B LITERAL1
+SAC_WREM2 LITERAL1
+SAC_WREM3 LITERAL1
SAMSUNG LITERAL1
SAMSUNG36 LITERAL1
SAMSUNG_AC LITERAL1
@@ -1367,6 +1456,10 @@ SANYO_AC88 LITERAL1
SANYO_LC7461 LITERAL1
SANYO_LC7461_BITS LITERAL1
SANYO_SA8650B_BITS LITERAL1
+SATURDAY LITERAL1
+SCHEDULE_TIMER_1 LITERAL1
+SCHEDULE_TIMER_2 LITERAL1
+SCHEDULE_TIMER_3 LITERAL1
SEND_AIRTON LITERAL1
SEND_AIRWELL LITERAL1
SEND_AIWA_RC_T501 LITERAL1
@@ -1379,6 +1472,7 @@ SEND_CARRIER_AC LITERAL1
SEND_CARRIER_AC128 LITERAL1
SEND_CARRIER_AC40 LITERAL1
SEND_CARRIER_AC64 LITERAL1
+SEND_CARRIER_AC84 LITERAL1
SEND_CLIMABUTLER LITERAL1
SEND_COOLIX LITERAL1
SEND_COOLIX48 LITERAL1
@@ -1405,6 +1499,7 @@ SEND_FUJITSU_AC LITERAL1
SEND_GICABLE LITERAL1
SEND_GLOBALCACHE LITERAL1
SEND_GOODWEATHER LITERAL1
+SEND_GORENJE LITERAL1
SEND_GREE LITERAL1
SEND_HAIER_AC LITERAL1
SEND_HAIER_AC160 LITERAL1
@@ -1481,7 +1576,9 @@ SEND_VESTEL_AC LITERAL1
SEND_VOLTAS LITERAL1
SEND_WHIRLPOOL_AC LITERAL1
SEND_WHYNTER LITERAL1
+SEND_WOWWEE LITERAL1
SEND_XMP LITERAL1
+SEND_YORK LITERAL1
SEND_ZEPEAL LITERAL1
SHARP LITERAL1
SHARP_AC LITERAL1
@@ -1493,6 +1590,7 @@ SONY_12_BITS LITERAL1
SONY_15_BITS LITERAL1
SONY_20_BITS LITERAL1
SONY_38K LITERAL1
+SUNDAY LITERAL1
SYMPHONY LITERAL1
TAC09CHSD LITERAL1
TCL112AC LITERAL1
@@ -1500,7 +1598,9 @@ TCL96AC LITERAL1
TECHNIBEL_AC LITERAL1
TECO LITERAL1
TEKNOPOINT LITERAL1
+THURSDAY LITERAL1
TIMEOUT_MS LITERAL1
+TIMER_COMMAND LITERAL1
TOSHIBA_AC LITERAL1
TOSHIBA_AC_AUTO LITERAL1
TOSHIBA_AC_COOL LITERAL1
@@ -1528,6 +1628,7 @@ TROTEC_MAX_TEMP LITERAL1
TROTEC_MAX_TIMER LITERAL1
TROTEC_MIN_TEMP LITERAL1
TRUMA LITERAL1
+TUESDAY LITERAL1
UNKNOWN LITERAL1
UNUSED LITERAL1
USE_IRAM_ATTR LITERAL1
@@ -1535,12 +1636,15 @@ V9014557_A LITERAL1
V9014557_B LITERAL1
VESTEL_AC LITERAL1
VOLTAS LITERAL1
+WEDNESDAY LITERAL1
WHIRLPOOL_AC LITERAL1
WHYNTER LITERAL1
WHYNTER_BITS LITERAL1
+WOWWEE LITERAL1
XMP LITERAL1
YAW1F LITERAL1
YBOFB LITERAL1
+YORK LITERAL1
YX1FSF LITERAL1
ZEPEAL LITERAL1
k0Str LITERAL1
@@ -1649,6 +1753,10 @@ kAmcorVentOn LITERAL1
kAmcorZeroMark LITERAL1
kAmcorZeroSpace LITERAL1
kArdb1Str LITERAL1
+kArgo3AcControlStateLength LITERAL1
+kArgo3ConfigStateLength LITERAL1
+kArgo3TimerStateLength LITERAL1
+kArgo3iFeelReportStateLength LITERAL1
kArgoAuto LITERAL1
kArgoBitMark LITERAL1
kArgoBits LITERAL1
@@ -1667,6 +1775,7 @@ kArgoFlap5 LITERAL1
kArgoFlap6 LITERAL1
kArgoFlapAuto LITERAL1
kArgoFlapFull LITERAL1
+kArgoFrequency LITERAL1
kArgoGap LITERAL1
kArgoHdrMark LITERAL1
kArgoHdrSpace LITERAL1
@@ -1674,13 +1783,26 @@ kArgoHeat LITERAL1
kArgoHeatAuto LITERAL1
kArgoHeatBit LITERAL1
kArgoHeatBlink LITERAL1
+kArgoMaxChannel LITERAL1
kArgoMaxRoomTemp LITERAL1
kArgoMaxTemp LITERAL1
kArgoMinTemp LITERAL1
kArgoOff LITERAL1
kArgoOneSpace LITERAL1
+kArgoPost LITERAL1
+kArgoPreamble1 LITERAL1
+kArgoPreamble2 LITERAL1
+kArgoSensorCheck LITERAL1
+kArgoSensorFixed LITERAL1
+kArgoShortBits LITERAL1
+kArgoShortStateLength LITERAL1
kArgoStateLength LITERAL1
kArgoTempDelta LITERAL1
+kArgoWrem2Str LITERAL1
+kArgoWrem3Postfix_ACControl LITERAL1
+kArgoWrem3Postfix_Timer LITERAL1
+kArgoWrem3Preamble LITERAL1
+kArgoWrem3Str LITERAL1
kArgoZeroSpace LITERAL1
kArjw2Str LITERAL1
kArrah2eStr LITERAL1
@@ -1785,6 +1907,16 @@ kCarrierAc64OneSpace LITERAL1
kCarrierAc64TimerMax LITERAL1
kCarrierAc64TimerMin LITERAL1
kCarrierAc64ZeroSpace LITERAL1
+kCarrierAc84Bits LITERAL1
+kCarrierAc84ExtraBits LITERAL1
+kCarrierAc84ExtraTolerance LITERAL1
+kCarrierAc84Gap LITERAL1
+kCarrierAc84HdrMark LITERAL1
+kCarrierAc84HdrSpace LITERAL1
+kCarrierAc84MinRepeat LITERAL1
+kCarrierAc84One LITERAL1
+kCarrierAc84StateLength LITERAL1
+kCarrierAc84Zero LITERAL1
kCarrierAcBitMark LITERAL1
kCarrierAcBits LITERAL1
kCarrierAcFreq LITERAL1
@@ -1798,6 +1930,7 @@ kCeilingStr LITERAL1
kCelsiusFahrenheitStr LITERAL1
kCelsiusStr LITERAL1
kCentreStr LITERAL1
+kChStr LITERAL1
kChangeStr LITERAL1
kCirculateStr LITERAL1
kCkpStr LITERAL1
@@ -1816,6 +1949,10 @@ kColonSpaceStr LITERAL1
kComfortStr LITERAL1
kCommaSpaceStr LITERAL1
kCommandStr LITERAL1
+kConfigCommand LITERAL1
+kConfigCommandStr LITERAL1
+kControlCommand LITERAL1
+kControlCommandStr LITERAL1
kCool LITERAL1
kCoolStr LITERAL1
kCoolingStr LITERAL1
@@ -2413,6 +2550,15 @@ kGoodweatherSwingSlow LITERAL1
kGoodweatherTempMax LITERAL1
kGoodweatherTempMin LITERAL1
kGoodweatherZeroSpace LITERAL1
+kGorenjeBitMark LITERAL1
+kGorenjeBits LITERAL1
+kGorenjeFreq LITERAL1
+kGorenjeHdrMark LITERAL1
+kGorenjeHdrSpace LITERAL1
+kGorenjeMinGap LITERAL1
+kGorenjeOneSpace LITERAL1
+kGorenjeTolerance LITERAL1
+kGorenjeZeroSpace LITERAL1
kGpioUnused LITERAL1
kGreeAuto LITERAL1
kGreeBitMark LITERAL1
@@ -2735,6 +2881,7 @@ kHoldStr LITERAL1
kHourStr LITERAL1
kHoursStr LITERAL1
kHumidStr LITERAL1
+kIFeelReportStr LITERAL1
kIFeelStr LITERAL1
kISeeStr LITERAL1
kIdStr LITERAL1
@@ -2836,6 +2983,7 @@ kKelvinatorSwingVUpperMiddle LITERAL1
kKelvinatorTick LITERAL1
kKelvinatorZeroSpace LITERAL1
kKelvinatorZeroSpaceTicks LITERAL1
+kKeyStr LITERAL1
kKkg29ac1Str LITERAL1
kKkg9ac1Str LITERAL1
kLasertagBits LITERAL1
@@ -2846,6 +2994,7 @@ kLasertagMinRepeat LITERAL1
kLasertagMinSamples LITERAL1
kLasertagTick LITERAL1
kLasertagTolerance LITERAL1
+kLastAcCommandEnum LITERAL1
kLastDecodeType LITERAL1
kLastFanspeedEnum LITERAL1
kLastOpmodeEnum LITERAL1
@@ -2983,8 +3132,10 @@ kMaxRightStr LITERAL1
kMaxStr LITERAL1
kMaxTimeoutMs LITERAL1
kMaximumStr LITERAL1
+kMedHighStr LITERAL1
kMedStr LITERAL1
kMedium LITERAL1
+kMediumHigh LITERAL1
kMediumStr LITERAL1
kMetzAddressBits LITERAL1
kMetzBitMark LITERAL1
@@ -3392,6 +3543,7 @@ kNikaiZeroSpaceTicks LITERAL1
kNkeStr LITERAL1
kNoRepeat LITERAL1
kNoStr LITERAL1
+kNoTempValue LITERAL1
kNowStr LITERAL1
kOff LITERAL1
kOffStr LITERAL1
@@ -3400,6 +3552,8 @@ kOnStr LITERAL1
kOnTimerStr LITERAL1
kOutsideQuietStr LITERAL1
kOutsideStr LITERAL1
+kPanasonic40Bits LITERAL1
+kPanasonic40Manufacturer LITERAL1
kPanasonicAc32Auto LITERAL1
kPanasonicAc32BitMark LITERAL1
kPanasonicAc32Bits LITERAL1
@@ -3773,11 +3927,14 @@ kSanyoSa8650bOneMark LITERAL1
kSanyoSa8650bRptLength LITERAL1
kSanyoSa8650bZeroMark LITERAL1
kSaveStr LITERAL1
+kScheduleStr LITERAL1
kSecondStr LITERAL1
kSecondsStr LITERAL1
kSensorStr LITERAL1
+kSensorTempReport LITERAL1
kSensorTempStr LITERAL1
kSetStr LITERAL1
+kSetTimerCommandStr LITERAL1
kSharpAcAuto LITERAL1
kSharpAcBitMark LITERAL1
kSharpAcBits LITERAL1
@@ -4002,6 +4159,8 @@ kTempUpStr LITERAL1
kThreeLetterDayOfWeekStr LITERAL1
kTimeSep LITERAL1
kTimeoutMs LITERAL1
+kTimerActiveDaysStr LITERAL1
+kTimerCommand LITERAL1
kTimerModeStr LITERAL1
kTimerStr LITERAL1
kToggleStr LITERAL1
@@ -4147,10 +4306,13 @@ kTypeStr LITERAL1
kUnknownStr LITERAL1
kUnknownThreshold LITERAL1
kUpStr LITERAL1
+kUpperMiddle LITERAL1
+kUpperMiddleStr LITERAL1
kUpperStr LITERAL1
kUseDefTol LITERAL1
kV9014557AStr LITERAL1
kV9014557BStr LITERAL1
+kValueStr LITERAL1
kVaneStr LITERAL1
kVestelAcAuto LITERAL1
kVestelAcBitMark LITERAL1
@@ -4259,6 +4421,14 @@ kWhynterZeroSpaceTicks LITERAL1
kWide LITERAL1
kWideStr LITERAL1
kWifiStr LITERAL1
+kWowweeBitMark LITERAL1
+kWowweeBits LITERAL1
+kWowweeDefaultRepeat LITERAL1
+kWowweeFreq LITERAL1
+kWowweeHdrMark LITERAL1
+kWowweeHdrSpace LITERAL1
+kWowweeOneSpace LITERAL1
+kWowweeZeroSpace LITERAL1
kXFanStr LITERAL1
kXmpBaseSpace LITERAL1
kXmpBits LITERAL1
@@ -4274,6 +4444,26 @@ kXmpWordSize LITERAL1
kYaw1fStr LITERAL1
kYbofbStr LITERAL1
kYesStr LITERAL1
+kYorkAuto LITERAL1
+kYorkBitMark LITERAL1
+kYorkBits LITERAL1
+kYorkCool LITERAL1
+kYorkDry LITERAL1
+kYorkFan LITERAL1
+kYorkFanAuto LITERAL1
+kYorkFanHigh LITERAL1
+kYorkFanLow LITERAL1
+kYorkFanMedium LITERAL1
+kYorkFreq LITERAL1
+kYorkHdrMark LITERAL1
+kYorkHdrSpace LITERAL1
+kYorkHeat LITERAL1
+kYorkKnownGoodState LITERAL1
+kYorkMaxTemp LITERAL1
+kYorkMinTemp LITERAL1
+kYorkOneSpace LITERAL1
+kYorkStateLength LITERAL1
+kYorkZeroSpace LITERAL1
kYx1fsfStr LITERAL1
kZepealBits LITERAL1
kZepealCommandOffOn LITERAL1
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.json b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/library.json
index 10a428ad0..302372566 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.8.4",
+ "version": "2.8.5",
"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 801fcab6e..769cbc1fa 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.8.4
+version=2.8.5
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 af6beb30b..8826bc1ae 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.cpp
@@ -12,6 +12,7 @@
#ifndef ARDUINO
#include
#endif
+#include
#include "IRsend.h"
#include "IRremoteESP8266.h"
#include "IRtext.h"
@@ -64,6 +65,36 @@
#endif // ESP8266
#endif // STRCASECMP
+#ifndef UNIT_TEST
+#define OUTPUT_DECODE_RESULTS_FOR_UT(ac)
+#else
+/* NOTE: THIS IS NOT A DOXYGEN COMMENT (would require ENABLE_PREPROCESSING-YES)
+/// If compiling for UT *and* a test receiver @c IRrecv is provided via the
+/// @c _utReceived param, this injects an "output" gadget @c _lastDecodeResults
+/// into the @c IRAc::sendAc method, so that the UT code may parse the "sent"
+/// value and drive further assertions
+///
+/// @note The @c decode_results "returned" is a shallow copy (empty rawbuf),
+/// mostly b/c the class does not have a custom/deep copy c-tor
+/// and defining it would be an overkill for this purpose
+/// @note For future maintainers: If @c IRAc class is ever refactored to use
+/// polymorphism (static or dynamic)... this macro should be removed
+/// and replaced with proper GMock injection.
+*/
+#define OUTPUT_DECODE_RESULTS_FOR_UT(ac) \
+ { \
+ if (_utReceiver) { \
+ _lastDecodeResults = nullptr; \
+ (ac)._irsend.makeDecodeResult(); \
+ if (_utReceiver->decode(&(ac)._irsend.capture)) { \
+ _lastDecodeResults = std::unique_ptr( \
+ new decode_results((ac)._irsend.capture)); \
+ _lastDecodeResults->rawbuf = nullptr; \
+ } \
+ } \
+ }
+#endif // UNIT_TEST
+
/// 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.
@@ -331,6 +362,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#endif
#if SEND_VOLTAS
case decode_type_t::VOLTAS:
+#endif
+#if SEND_YORK
+ case decode_type_t::YORK:
#endif
case decode_type_t::WHIRLPOOL_AC:
return true;
@@ -439,19 +473,27 @@ void IRac::amcor(IRAmcorAc *ac,
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
+/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees
+/// Celsius.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] sleep Nr. of minutes for sleep mode.
/// @note -1 is Off, >= 0 is on.
void IRac::argo(IRArgoAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
- const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
+ const float sensorTemp, const stdAc::fanspeed_t fan,
+ const stdAc::swingv_t swingv, const bool iFeel,
const bool turbo, const int16_t sleep) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
- ac->setTemp(degrees);
+ ac->setTemp(static_cast(std::round(degrees)));
+ if (sensorTemp != kNoTempValue) {
+ ac->setSensorTemp(static_cast(std::round(sensorTemp)));
+ }
+ ac->setiFeel(iFeel);
ac->setFan(ac->convertFan(fan));
ac->setFlap(ac->convertSwingV(swingv));
// No Quiet setting available.
@@ -464,6 +506,121 @@ void IRac::argo(IRArgoAC *ac,
ac->setNight(sleep >= 0); // Convert to a boolean.
ac->send();
}
+
+/// Send an Argo A/C WREM-3 AC **control** message with the supplied settings.
+/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use.
+/// @param[in] on The power setting.
+/// @param[in] mode The operation mode setting.
+/// @param[in] degrees The set temperature setting in degrees Celsius.
+/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees
+/// Celsius.
+/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t)
+/// The overflow is *not* checked, though.
+/// @note The value is rounded to nearest integer, rounding halfway cases
+/// away from zero. E.g. 1.5 [C] becomes 2 [C].
+/// @param[in] fan The speed setting for the fan.
+/// @param[in] swingv The vertical swing setting.
+/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
+/// @param[in] night Enable night mode (raises temp by +1*C after 1h).
+/// @param[in] econo Enable eco mode (limits power consumed).
+/// @param[in] turbo Run the device in turbo/powerful mode.
+/// @param[in] filter Enable filter mode
+/// @param[in] light Enable device display/LEDs
+void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on,
+ const stdAc::opmode_t mode, const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv, const bool iFeel,
+ const bool night, const bool econo, const bool turbo, const bool filter,
+ const bool light) {
+ ac->begin();
+ ac->setMessageType(argoIrMessageType_t::AC_CONTROL);
+ ac->setPower(on);
+ ac->setMode(ac->convertMode(mode));
+ ac->setTemp(degrees);
+ if (sensorTemp != kNoTempValue) {
+ ac->setSensorTemp(static_cast(std::round(sensorTemp)));
+ }
+ ac->setiFeel(iFeel);
+ ac->setFan(ac->convertFan(fan));
+ ac->setFlap(ac->convertSwingV(swingv));
+ ac->setNight(night);
+ ac->setEco(econo);
+ ac->setMax(turbo);
+ ac->setFilter(filter);
+ ac->setLight(light);
+ // No Clean setting available.
+ // No Beep setting available - always beeps in this mode :)
+ ac->send();
+}
+
+/// Send an Argo A/C WREM-3 iFeel (room temp) silent (no beep) report.
+/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use.
+/// @param[in] sensorTemp The room (iFeel) temperature setting
+/// in degrees Celsius.
+/// @warning The @c sensorTemp param is assumed to be in 0..255 range (uint8_t)
+/// The overflow is *not* checked, though.
+/// @note The value is rounded to nearest integer, rounding halfway cases
+/// away from zero. E.g. 1.5 [C] becomes 2 [C].
+void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) {
+ ac->begin();
+ ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT);
+ ac->setSensorTemp(static_cast(std::round(sensorTemp)));
+ ac->send();
+}
+
+/// Send an Argo A/C WREM-3 Config command.
+/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use.
+/// @param[in] param The parameter ID.
+/// @param[in] value The parameter value.
+/// @param[in] safe If true, will only allow setting the below parameters
+/// in order to avoid accidentally setting a restricted
+/// vendor-specific param and breaking the A/C device
+/// @note Known parameters (P, where xx is the @c param)
+/// P05 - Temperature Scale (0-Celsius, 1-Fahrenheit)
+/// P06 - Transmission channel (0..3)
+/// P12 - ECO mode power input limit (30..99, default: 75)
+void IRac::argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param,
+ const uint8_t value, bool safe /*= true*/) {
+ if (safe) {
+ switch (param) {
+ case 5: // temp. scale (note this is likely excess as not transmitted)
+ if (value > 1) { return; /* invalid */ }
+ break;
+ case 6: // channel (note this is likely excess as not transmitted)
+ if (value > 3) { return; /* invalid */ }
+ break;
+ case 12: // eco power limit
+ if (value < 30 || value > 99) { return; /* invalid */ }
+ break;
+ default:
+ return; /* invalid */
+ }
+ }
+ ac->begin();
+ ac->setMessageType(argoIrMessageType_t::CONFIG_PARAM_SET);
+ ac->setConfigEntry(param, value);
+ ac->send();
+}
+
+/// Send an Argo A/C WREM-3 Delay timer command.
+/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use.
+/// @param[in] on Whether the unit is currently on. The timer, upon elapse
+/// will toggle this state
+/// @param[in] currentTime currentTime in minutes, starting from 00:00
+/// @note For timer mode, this value is not really used much so can be zero.
+/// @param[in] delayMinutes Number of minutes after which the @c on state should
+/// be toggled
+/// @note Schedule timers are not exposed via this interface
+void IRac::argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on,
+ const uint16_t currentTime, const uint16_t delayMinutes) {
+ ac->begin();
+ ac->setMessageType(argoIrMessageType_t::TIMER_COMMAND);
+ ac->setPower(on);
+ ac->setTimerType(argoTimerType_t::DELAY_TIMER);
+ ac->setCurrentTimeMinutes(currentTime);
+ // Note: Day of week is not set (no need)
+ ac->setDelayTimerMinutes(delayMinutes);
+ ac->send();
+}
#endif // SEND_ARGO
#if SEND_BOSCH144
@@ -546,9 +703,12 @@ void IRac::carrier64(IRCarrierAc64 *ac,
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
+/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees
+/// Celsius.
/// @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] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
/// @param[in] turbo Run the device in turbo/powerful 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
@@ -556,10 +716,11 @@ void IRac::carrier64(IRCarrierAc64 *ac,
/// @note -1 is Off, >= 0 is on.
void IRac::coolix(IRCoolixAC *ac,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
- const bool turbo, const bool light, const bool clean,
- const int16_t sleep) {
+ const bool iFeel, const bool turbo, const bool light,
+ const bool clean, const int16_t sleep) {
ac->begin();
ac->setPower(on);
if (!on) {
@@ -576,6 +737,12 @@ void IRac::coolix(IRCoolixAC *ac,
// No Clock setting available.
// No Econo setting available.
// No Quiet setting available.
+ if (sensorTemp != kNoTempValue) {
+ ac->setSensorTemp(static_cast(std::round(sensorTemp)));
+ } else {
+ ac->clearSensorTemp();
+ }
+ ac->setZoneFollow(iFeel);
ac->send(); // Send the state, which will also power on the unit.
// The following are all options/settings that create their own special
// messages. Often they only make sense to be sent after the unit is turned
@@ -940,13 +1107,16 @@ void IRac::delonghiac(IRDelonghiAc *ac,
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
+/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees
+/// Celsius.
/// @param[in] fan The speed setting for the fan.
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
/// @param[in] clock The time in Nr. of mins since midnight. < 0 is ignore.
void IRac::ecoclim(IREcoclimAc *ac,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
- const int16_t sleep, const int16_t clock) {
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const int16_t sleep,
+ const int16_t clock) {
ac->begin();
ac->setPower(on);
uint8_t new_mode;
@@ -956,8 +1126,13 @@ void IRac::ecoclim(IREcoclimAc *ac,
new_mode = ac->convertMode(mode); // Not Sleep, so use the supplied mode.
ac->setMode(new_mode);
ac->setTemp(degrees);
- ac->setSensorTemp(degrees); //< Set to the desired temp until we cab disable.
ac->setFan(ac->convertFan(fan));
+ if (sensorTemp != kNoTempValue) {
+ ac->setSensorTemp(static_cast(std::round(sensorTemp)));
+ } else {
+ ac->setSensorTemp(degrees); //< Set to the desired temp
+ // until we can disable.
+ }
// No SwingV setting available
// No SwingH setting available
// No Quiet setting available.
@@ -979,22 +1154,28 @@ void IRac::ecoclim(IREcoclimAc *ac,
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
+/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees
+/// Celsius.
/// @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] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] lighttoggle Should we toggle the LED/Display?
/// @param[in] clean Turn on the self-cleaning mode. e.g. Mould, dry filters etc
void IRac::electra(IRElectraAc *ac,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
- const stdAc::swingv_t swingv,
- const stdAc::swingh_t swingh, const bool turbo,
- const bool lighttoggle, const bool clean) {
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
+ const stdAc::swingh_t swingh, const bool iFeel,
+ const bool turbo, const bool lighttoggle, const bool clean) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
+ if (sensorTemp != kNoTempValue) {
+ ac->setSensorTemp(static_cast(std::round(sensorTemp)));
+ }
ac->setFan(ac->convertFan(fan));
ac->setSwingV(swingv != stdAc::swingv_t::kOff);
ac->setSwingH(swingh != stdAc::swingh_t::kOff);
@@ -1007,6 +1188,7 @@ void IRac::electra(IRElectraAc *ac,
// No Beep setting available.
// No Sleep setting available.
// No Clock setting available.
+ ac->setIFeel(iFeel);
ac->send();
}
#endif // SEND_ELECTRA_AC
@@ -1132,6 +1314,7 @@ void IRac::goodweather(IRGoodweatherAc *ac,
/// @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] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
/// @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.
@@ -1141,8 +1324,8 @@ 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 stdAc::swingh_t swingh,
- const bool turbo, const bool econo, const bool light,
- const bool clean, const int16_t sleep) {
+ const bool iFeel, const bool turbo, const bool econo,
+ const bool light, const bool clean, const int16_t sleep) {
ac->begin();
ac->setModel(model);
ac->setPower(on);
@@ -1152,6 +1335,7 @@ void IRac::gree(IRGreeAC *ac, const gree_ac_remote_model_t model,
ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag.
ac->convertSwingV(swingv));
ac->setSwingHorizontal(ac->convertSwingH(swingh));
+ ac->setIFeel(iFeel);
ac->setLight(light);
ac->setTurbo(turbo);
ac->setEcono(econo);
@@ -1661,8 +1845,11 @@ void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model,
/// @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] sensorTemp The room (iFeel) temperature sensor reading
+/// in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
/// @param[in] quiet Run the device in quiet/silent mode.
/// @param[in] quiet_prev The device's previous quiet/silent mode.
/// @param[in] turbo Toggle the device's turbo/powerful mode.
@@ -1673,9 +1860,9 @@ void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model,
/// @note On Danby A/C units, swingv controls the Ion Filter instead.
void IRac::midea(IRMideaAC *ac,
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 quiet, const bool quiet_prev,
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
+ const bool iFeel, const bool quiet, const bool quiet_prev,
const bool turbo, const bool econo, const bool light,
const bool clean, const int16_t sleep) {
ac->begin();
@@ -1683,6 +1870,10 @@ void IRac::midea(IRMideaAC *ac,
ac->setMode(ac->convertMode(mode));
ac->setUseCelsius(celsius);
ac->setTemp(degrees, celsius);
+ if (sensorTemp != kNoTempValue) {
+ ac->setSensorTemp(sensorTemp, celsius);
+ }
+ ac->setEnableSensorTemp(iFeel);
ac->setFan(ac->convertFan(fan));
ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff);
// No Horizontal swing setting available.
@@ -2080,19 +2271,29 @@ void IRac::samsung(IRSamsungAc *ac,
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
+/// @param[in] sensorTemp The room (iFeel) temperature sensor reading in degrees
+/// Celsius.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
+/// @param[in] iFeel Whether to enable iFeel (remote temp) mode on the A/C unit.
/// @param[in] beep Enable/Disable beeps when receiving IR messages.
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
void IRac::sanyo(IRSanyoAc *ac,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
- const stdAc::swingv_t swingv, const bool beep,
- const int16_t sleep) {
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
+ const bool iFeel, const bool beep, const int16_t sleep) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
+ if (sensorTemp != kNoTempValue) {
+ ac->setSensorTemp(static_cast(std::round(sensorTemp)));
+ } else {
+ ac->setSensorTemp(degrees); // Set the sensor temp to the desired
+ // (normal) temp.
+ }
+ ac->setSensor(!iFeel);
ac->setFan(ac->convertFan(fan));
ac->setSwingV(ac->convertSwingV(swingv));
// No Horizontal swing setting available.
@@ -2105,10 +2306,6 @@ void IRac::sanyo(IRSanyoAc *ac,
ac->setBeep(beep);
ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean.
// No Clock setting available.
-
- // Extra
- ac->setSensor(true); // Set the A/C to use the temp sensor in the Unit/Wall.
- ac->setSensorTemp(degrees); // Set the sensor temp to the desired temp.
ac->send();
}
#endif // SEND_SANYO_AC
@@ -2801,6 +2998,11 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
// Convert the temp from Fahrenheit to Celsius if we are not in Celsius mode.
float degC __attribute__((unused)) =
desired.celsius ? desired.degrees : fahrenheitToCelsius(desired.degrees);
+ // Convert the sensorTemp from Fahrenheit to Celsius if we are not in Celsius
+ // mode.
+ float sensorTempC __attribute__((unused)) =
+ desired.sensorTemperature ? desired.sensorTemperature
+ : fahrenheitToCelsius(desired.sensorTemperature);
// special `state_t` that is required to be sent based on that.
stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev);
// Some protocols expect a previous state for power.
@@ -2850,9 +3052,36 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
#if SEND_ARGO
case ARGO:
{
- IRArgoAC ac(_pin, _inverted, _modulation);
- argo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.turbo, send.sleep);
+ if (send.model == argo_ac_remote_model_t::SAC_WREM3) {
+ IRArgoAC_WREM3 ac(_pin, _inverted, _modulation);
+ switch (send.command) {
+ case stdAc::ac_command_t::kSensorTempReport:
+ argoWrem3_iFeelReport(&ac, sensorTempC);
+ break;
+ case stdAc::ac_command_t::kConfigCommand:
+ /// @warning: this is ABUSING current **common** parameters:
+ /// @c clock and @c sleep as config key and value
+ /// Hence, value pre-validation is performed (safe-mode)
+ /// to avoid accidental device misconfiguration
+ argoWrem3_ConfigSet(&ac, send.clock, send.sleep, true);
+ break;
+ case stdAc::ac_command_t::kTimerCommand:
+ argoWrem3_SetTimer(&ac, send.power, send.clock, send.sleep);
+ break;
+ case stdAc::ac_command_t::kControlCommand:
+ default:
+ argoWrem3_ACCommand(&ac, send.power, send.mode, degC, sensorTempC,
+ send.fanspeed, send.swingv, send.iFeel, send.quiet, send.econo,
+ send.turbo, send.filter, send.light);
+ break;
+ }
+ OUTPUT_DECODE_RESULTS_FOR_UT(ac);
+ } else {
+ IRArgoAC ac(_pin, _inverted, _modulation);
+ argo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed,
+ send.swingv, send.iFeel, send.turbo, send.sleep);
+ OUTPUT_DECODE_RESULTS_FOR_UT(ac);
+ }
break;
}
#endif // SEND_ARGO
@@ -2877,8 +3106,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case COOLIX:
{
IRCoolixAC ac(_pin, _inverted, _modulation);
- coolix(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.swingh, send.turbo, send.light, send.clean, send.sleep);
+ coolix(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed,
+ send.swingv, send.swingh, send.iFeel, send.turbo, send.light,
+ send.clean, send.sleep);
break;
}
#endif // SEND_COOLIX
@@ -2976,7 +3206,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case ECOCLIM:
{
IREcoclimAc ac(_pin, _inverted, _modulation);
- ecoclim(&ac, send.power, send.mode, degC, send.fanspeed, send.clock);
+ ecoclim(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed,
+ send.iFeel, send.clock);
break;
}
#endif // SEND_ECOCLIM
@@ -2984,8 +3215,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case ELECTRA_AC:
{
IRElectraAc ac(_pin, _inverted, _modulation);
- electra(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.swingh, send.turbo, send.light, send.clean);
+ electra(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed,
+ send.swingv, send.swingh, send.iFeel, send.turbo, send.light,
+ send.clean);
break;
}
#endif // SEND_ELECTRA_AC
@@ -3153,8 +3385,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRMideaAC ac(_pin, _inverted, _modulation);
midea(&ac, send.power, send.mode, send.celsius, send.degrees,
- send.fanspeed, send.swingv, send.quiet, prev_quiet, send.turbo,
- send.econo, send.light, send.sleep);
+ send.sensorTemperature, send.fanspeed, send.swingv, send.iFeel,
+ send.quiet, prev_quiet, send.turbo, send.econo, send.light,
+ send.clean, send.sleep);
break;
}
#endif // SEND_MIDEA
@@ -3263,8 +3496,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case SANYO_AC:
{
IRSanyoAc ac(_pin, _inverted, _modulation);
- sanyo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
- send.beep, send.sleep);
+ sanyo(&ac, send.power, send.mode, degC, sensorTempC, send.fanspeed,
+ send.swingv, send.iFeel, send.beep, send.sleep);
break;
}
#endif // SEND_SANYO_AC
@@ -3421,7 +3654,9 @@ bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) {
a.fanspeed != b.fanspeed || a.swingv != b.swingv ||
a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo ||
a.econo != b.econo || a.light != b.light || a.filter != b.filter ||
- a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep;
+ a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep ||
+ a.command != b.command || a.sensorTemperature != b.sensorTemperature ||
+ a.iFeel != b.iFeel;
}
/// Check if the internal state has changed from what was previously sent.
@@ -3429,6 +3664,26 @@ bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) {
/// @return True if it has changed, False if not.
bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); }
+/// Convert the supplied str into the appropriate enum.
+/// @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.
+stdAc::ac_command_t IRac::strToCommandType(const char *str,
+ const stdAc::ac_command_t def) {
+ if (!STRCASECMP(str, kControlCommandStr))
+ return stdAc::ac_command_t::kControlCommand;
+ else if (!STRCASECMP(str, kIFeelReportStr) ||
+ !STRCASECMP(str, kIFeelStr))
+ return stdAc::ac_command_t::kSensorTempReport;
+ else if (!STRCASECMP(str, kSetTimerCommandStr) ||
+ !STRCASECMP(str, kTimerStr))
+ return stdAc::ac_command_t::kTimerCommand;
+ else if (!STRCASECMP(str, kConfigCommandStr))
+ return stdAc::ac_command_t::kConfigCommand;
+ else
+ return def;
+}
+
/// Convert the supplied str into the appropriate enum.
/// @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.
@@ -3492,6 +3747,8 @@ stdAc::fanspeed_t IRac::strToFanspeed(const char *str,
!STRCASECMP(str, kMaximumStr) ||
!STRCASECMP(str, kHighestStr))
return stdAc::fanspeed_t::kMax;
+ else if (!STRCASECMP(str, kMedHighStr))
+ return stdAc::fanspeed_t::kMediumHigh;
else
return def;
}
@@ -3524,6 +3781,8 @@ stdAc::swingv_t IRac::strToSwingV(const char *str,
!STRCASECMP(str, kMediumStr) ||
!STRCASECMP(str, kCentreStr))
return stdAc::swingv_t::kMiddle;
+ else if (!STRCASECMP(str, kUpperMiddleStr))
+ return stdAc::swingv_t::kUpperMiddle;
else if (!STRCASECMP(str, kHighStr) ||
!STRCASECMP(str, kHiStr))
return stdAc::swingv_t::kHigh;
@@ -3666,6 +3925,11 @@ int16_t IRac::strToModel(const char *str, const int16_t def) {
return whirlpool_ac_remote_model_t::DG11J13A;
} else if (!STRCASECMP(str, kDg11j191Str)) {
return whirlpool_ac_remote_model_t::DG11J191;
+ // Argo A/C models
+ } else if (!STRCASECMP(str, kArgoWrem2Str)) {
+ return argo_ac_remote_model_t::SAC_WREM2;
+ } else if (!STRCASECMP(str, kArgoWrem3Str)) {
+ return argo_ac_remote_model_t::SAC_WREM3;
} else {
int16_t number = atoi(str);
if (number > 0)
@@ -3701,6 +3965,19 @@ String IRac::boolToString(const bool value) {
return value ? kOnStr : kOffStr;
}
+/// Convert the supplied operation mode into the appropriate String.
+/// @param[in] cmdType The enum to be converted.
+/// @return The equivalent String for the locale.
+String IRac::commandTypeToString(const stdAc::ac_command_t cmdType) {
+ switch (cmdType) {
+ case stdAc::ac_command_t::kControlCommand: return kControlCommandStr;
+ case stdAc::ac_command_t::kSensorTempReport: return kIFeelReportStr;
+ case stdAc::ac_command_t::kTimerCommand: return kSetTimerCommandStr;
+ case stdAc::ac_command_t::kConfigCommand: return kConfigCommandStr;
+ default: return kUnknownStr;
+ }
+}
+
/// 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.
@@ -3722,13 +3999,14 @@ String IRac::opmodeToString(const stdAc::opmode_t mode, const bool ha) {
/// @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::kMediumHigh: return kMedHighStr;
+ case stdAc::fanspeed_t::kLow: return kLowStr;
+ case stdAc::fanspeed_t::kMin: return kMinStr;
+ default: return kUnknownStr;
}
}
@@ -3737,14 +4015,15 @@ 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::kUpperMiddle: return kUpperMiddleStr;
+ case stdAc::swingv_t::kLow: return kLowStr;
+ case stdAc::swingv_t::kLowest: return kLowestStr;
+ default: return kUnknownStr;
}
}
@@ -3796,8 +4075,14 @@ namespace IRAcUtils {
#endif // DECODE_AMCOR
#if DECODE_ARGO
case decode_type_t::ARGO: {
+ if (IRArgoAC_WREM3::isValidWrem3Message(result->state, result->bits,
+ true)) {
+ IRArgoAC_WREM3 ac(kGpioUnused);
+ ac.setRaw(result->state, result->bits / 8);
+ return ac.toString();
+ }
IRArgoAC ac(kGpioUnused);
- ac.setRaw(result->state);
+ ac.setRaw(result->state, result->bits / 8);
return ac.toString();
}
#endif // DECODE_ARGO
@@ -4211,6 +4496,13 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_WHIRLPOOL_AC
+#if DECODE_YORK
+ case decode_type_t::YORK: {
+ IRYorkAc ac(kGpioUnused);
+ ac.setRaw(result->state);
+ return ac.toString();
+ }
+#endif // DECODE_YORK
default:
return "";
}
@@ -4258,9 +4550,24 @@ namespace IRAcUtils {
#endif // DECODE_AMCOR
#if DECODE_ARGO
case decode_type_t::ARGO: {
- IRArgoAC ac(kGpioUnused);
- ac.setRaw(decode->state);
- *result = ac.toCommon();
+ const uint16_t length = decode->bits / 8;
+ if (IRArgoAC_WREM3::isValidWrem3Message(decode->state,
+ decode->bits, true)) {
+ IRArgoAC_WREM3 ac(kGpioUnused);
+ ac.setRaw(decode->state, length);
+ *result = ac.toCommon();
+ } else {
+ IRArgoAC ac(kGpioUnused);
+ switch (length) {
+ case kArgoStateLength:
+ case kArgoShortStateLength:
+ ac.setRaw(decode->state, length);
+ *result = ac.toCommon();
+ break;
+ default:
+ return false;
+ }
+ }
break;
}
#endif // DECODE_ARGO
@@ -4732,6 +5039,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_WHIRLPOOL_AC
+#if DECODE_YORK
+ case decode_type_t::YORK: {
+ IRYorkAc ac(kGpioUnused);
+ ac.setRaw(decode->state);
+ *result = ac.toCommon(prev);
+ break;
+ }
+#endif // DECODE_YORK
default:
return false;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h
index 9193a531d..7f4a3cf01 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRac.h
@@ -5,6 +5,8 @@
#ifndef UNIT_TEST
#include
+#else
+#include
#endif
#include "IRremoteESP8266.h"
#include "ir_Airton.h"
@@ -47,6 +49,7 @@
#include "ir_Vestel.h"
#include "ir_Voltas.h"
#include "ir_Whirlpool.h"
+#include "ir_York.h"
// Constants
const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO.
@@ -84,6 +87,8 @@ class IRac {
static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b);
static bool strToBool(const char *str, const bool def = false);
static int16_t strToModel(const char *str, const int16_t def = -1);
+ static stdAc::ac_command_t strToCommandType(const char *str,
+ const stdAc::ac_command_t def = stdAc::ac_command_t::kControlCommand);
static stdAc::opmode_t strToOpmode(
const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto);
static stdAc::fanspeed_t strToFanspeed(
@@ -94,6 +99,7 @@ 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 commandTypeToString(const stdAc::ac_command_t cmdType);
static String opmodeToString(const stdAc::opmode_t mode,
const bool ha = false);
static String fanspeedToString(const stdAc::fanspeed_t speed);
@@ -103,10 +109,17 @@ class IRac {
stdAc::state_t getStatePrev(void);
bool hasStateChanged(void);
stdAc::state_t next; ///< The state we want the device to be in after we send
-#ifndef UNIT_TEST
+#ifdef UNIT_TEST
+ /// @cond IGNORE
+ /// UT-specific
+ /// See @c OUTPUT_DECODE_RESULTS_FOR_UT macro description in IRac.cpp
+ std::shared_ptr _utReceiver = nullptr;
+ std::unique_ptr _lastDecodeResults = nullptr;
+ /// @endcond
+#else
private:
-#endif
+#endif // UNIT_TEST
uint16_t _pin; ///< The GPIO to use to transmit messages from.
bool _inverted; ///< IR LED is lit when GPIO is LOW (true) or HIGH (false)?
bool _modulation; ///< Is frequency modulation to be used?
@@ -132,15 +145,26 @@ class IRac {
#if SEND_ARGO
void argo(IRArgoAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
- const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
- const bool turbo, const int16_t sleep = -1);
+ const float sensorTemp, const stdAc::fanspeed_t fan,
+ const stdAc::swingv_t swingv, const bool iFeel, const bool turbo,
+ const int16_t sleep = -1);
+ void argoWrem3_ACCommand(IRArgoAC_WREM3 *ac,
+ const bool on, const stdAc::opmode_t mode, const float degrees,
+ const float sensorTemp, const stdAc::fanspeed_t fan,
+ const stdAc::swingv_t swingv, const bool iFeel, const bool night,
+ const bool econo, const bool turbo, const bool filter, const bool light);
+ void argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp);
+ void argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param,
+ const uint8_t value, bool safe = true);
+ void argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on,
+ const uint16_t currentTime, const uint16_t delayMinutes);
#endif // SEND_ARGO
#if SEND_BOSCH144
void bosch144(IRBosch144AC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const bool quiet);
-#endif // SEND_COOLIX
+#endif // SEND_BOSCH144
#if SEND_CARRIER_AC64
void carrier64(IRCarrierAc64 *ac,
const bool on, const stdAc::opmode_t mode,
@@ -150,10 +174,10 @@ void carrier64(IRCarrierAc64 *ac,
#if SEND_COOLIX
void coolix(IRCoolixAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
- const stdAc::fanspeed_t fan,
+ const float sensorTemp, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
- const bool turbo, const bool light, const bool clean,
- const int16_t sleep = -1);
+ const bool iFeel, const bool turbo, const bool light,
+ const bool clean, const int16_t sleep = -1);
#endif // SEND_COOLIX
#if SEND_CORONA_AC
void corona(IRCoronaAc *ac,
@@ -231,15 +255,16 @@ void daikin216(IRDaikin216 *ac,
#if SEND_ECOCLIM
void ecoclim(IREcoclimAc *ac,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
- const int16_t sleep = -1, const int16_t clock = -1);
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const int16_t sleep = -1,
+ const int16_t clock = -1);
#endif // SEND_ECOCLIM
#if SEND_ELECTRA_AC
void electra(IRElectraAc *ac,
const bool on, const stdAc::opmode_t mode,
- const float degrees, const stdAc::fanspeed_t fan,
- const stdAc::swingv_t swingv,
- const stdAc::swingh_t swingh, const bool turbo,
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
+ const stdAc::swingh_t swingh, const bool iFeel, const bool turbo,
const bool lighttoggle, const bool clean);
#endif // SEND_ELECTRA_AC
#if SEND_FUJITSU_AC
@@ -265,8 +290,8 @@ void electra(IRElectraAc *ac,
const bool on, const stdAc::opmode_t mode, const bool celsius,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
- const bool turbo, const bool econo, const bool light,
- const bool clean, const int16_t sleep = -1);
+ const bool iFeel, const bool turbo, const bool econo,
+ const bool light, const bool clean, const int16_t sleep = -1);
#endif // SEND_GREE
#if SEND_HAIER_AC
void haier(IRHaierAC *ac,
@@ -363,11 +388,11 @@ void electra(IRElectraAc *ac,
#if SEND_MIDEA
void midea(IRMideaAC *ac,
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 quiet, const bool quiet_prev, const bool turbo,
- const bool econo, const bool light, const bool clean,
- const int16_t sleep = -1);
+ const float degrees, const float sensorTemp,
+ const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
+ const bool iFeel, const bool quiet, const bool quiet_prev,
+ const bool turbo, const bool econo, const bool light,
+ const bool clean, const int16_t sleep = -1);
#endif // SEND_MIDEA
#if SEND_MIRAGE
void mirage(IRMirageAc *ac, const stdAc::state_t state);
@@ -451,8 +476,9 @@ void electra(IRElectraAc *ac,
#if SEND_SANYO_AC
void sanyo(IRSanyoAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
- const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
- const bool beep, const int16_t sleep = -1);
+ const float sensorTemp, const stdAc::fanspeed_t fan,
+ const stdAc::swingv_t swingv, const bool iFeel, const bool beep,
+ const int16_t sleep = -1);
#endif // SEND_SANYO_AC
#if SEND_SANYO_AC88
void sanyo88(IRSanyoAc88 *ac,
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRmacros.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRmacros.h
index 2c2ad7d94..665286298 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRmacros.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRmacros.h
@@ -34,7 +34,7 @@
#if !VA_OPT_SUPPORTED
// #pragma message("Compiler without __VA_OPT__ support")
#define COND(cond, a, b) a
-#else
+#else // !VA_OPT_SUPPORTED
#define NOTHING
#define EXPAND(...) __VA_ARGS__
#define STUFF_P(a, ...) __VA_OPT__(a)
@@ -44,7 +44,7 @@
#define NEGATE(a) VA_TEST(a, a)
#define COND_P(cond, a, b) STUFF(a, cond)STUFF(b, NEGATE(cond))
#define COND(cond, a, b) EXPAND(COND_P(cond, a, b))
-#endif
+#endif // !VA_OPT_SUPPORTED
/// @endcond
/**
* end of COND() set of macros
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp
index 00bc1f3ab..ecb8a382b 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp
@@ -399,6 +399,7 @@ void IRrecv::disableIRIn(void) {
#endif // ESP8266
#if defined(ESP32)
timerAlarmDisable(timer);
+ timerDetachInterrupt(timer);
timerEnd(timer);
#endif // ESP32
detachInterrupt(params.recvpin);
@@ -709,9 +710,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
return true;
#endif
#if DECODE_PANASONIC
- DPRINTLN("Attempting Panasonic decode");
+ DPRINTLN("Attempting Panasonic (48-bit) decode");
if (decodePanasonic(results, offset)) return true;
-#endif
+ DPRINTLN("Attempting Panasonic (40-bit) decode");
+ if (decodePanasonic(results, offset, kPanasonic40Bits, true,
+ kPanasonic40Manufacturer)) return true;
+#endif // DECODE_PANASONIC
#if DECODE_LG
DPRINTLN("Attempting LG (28-bit) decode");
if (decodeLG(results, offset, kLgBits, true)) return true;
@@ -951,8 +955,21 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
return true;
#endif
#if DECODE_ARGO
- DPRINTLN("Attempting Argo decode");
- if (decodeArgo(results, offset)) return true;
+ DPRINTLN("Attempting Argo WREM3 decode (AC Control)");
+ if (decodeArgoWREM3(results, offset, kArgo3AcControlStateLength * 8, true))
+ return true;
+ DPRINTLN("Attempting Argo WREM3 decode (iFeel report)");
+ if (decodeArgoWREM3(results, offset, kArgo3iFeelReportStateLength * 8, true))
+ return true;
+ DPRINTLN("Attempting Argo WREM3 decode (Config)");
+ if (decodeArgoWREM3(results, offset, kArgo3ConfigStateLength * 8, true))
+ return true;
+ DPRINTLN("Attempting Argo WREM3 decode (Timer)");
+ if (decodeArgoWREM3(results, offset, kArgo3TimerStateLength * 8, true))
+ return true;
+ DPRINTLN("Attempting Argo WREM2 decode");
+ if (decodeArgo(results, offset, kArgoBits) ||
+ decodeArgo(results, offset, kArgoShortBits, false)) return true;
#endif // DECODE_ARGO
#if DECODE_SHARP_AC
DPRINTLN("Attempting SHARP_AC decode");
@@ -1160,6 +1177,22 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Daikin 312-bit decode");
if (decodeDaikin312(results, offset)) return true;
#endif // DECODE_DAIKIN312
+#if DECODE_GORENJE
+ DPRINTLN("Attempting GORENJE decode");
+ if (decodeGorenje(results, offset)) return true;
+#endif // DECODE_GORENJE
+#if DECODE_WOWWEE
+ DPRINTLN("Attempting WOWWEE decode");
+ if (decodeWowwee(results, offset)) return true;
+#endif // DECODE_WOWWEE
+#if DECODE_CARRIER_AC84
+ DPRINTLN("Attempting Carrier A/C 84-bit decode");
+ if (decodeCarrierAC84(results, offset)) return true;
+#endif // DECODE_CARRIER_AC84
+#if DECODE_YORK
+ DPRINTLN("Attempting York decode");
+ if (decodeYork(results, offset, kYorkBits)) return true;
+#endif // DECODE_YORK
// Typically new protocols are added above this line.
}
#if DECODE_HASH
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h
index ad09f6d35..7adf5eb1b 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h
@@ -294,6 +294,9 @@ class IRrecv {
#if DECODE_ARGO
bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kArgoBits, const bool strict = true);
+ bool decodeArgoWREM3(decode_results *results, uint16_t offset = kStartOffset,
+ const uint16_t nbits = kArgo3AcControlStateLength * 8,
+ const bool strict = true);
#endif // DECODE_ARGO
#if DECODE_ARRIS
bool decodeArris(decode_results *results, uint16_t offset = kStartOffset,
@@ -580,6 +583,12 @@ class IRrecv {
const uint16_t nbits = kCarrierAc40Bits,
const bool strict = true);
#endif // DECODE_CARRIER_AC40
+#if DECODE_CARRIER_AC84
+ bool decodeCarrierAC84(decode_results *results,
+ uint16_t offset = kStartOffset,
+ const uint16_t nbits = kCarrierAc84Bits,
+ const bool strict = true);
+#endif // DECODE_CARRIER_AC84
#if DECODE_CARRIER_AC64
bool decodeCarrierAC64(decode_results *results,
uint16_t offset = kStartOffset,
@@ -598,6 +607,11 @@ class IRrecv {
const uint16_t nbits = kGoodweatherBits,
const bool strict = true);
#endif // DECODE_GOODWEATHER
+#if DECODE_GORENJE
+ bool decodeGorenje(decode_results *results, uint16_t offset = kStartOffset,
+ const uint16_t nbits = kGorenjeBits,
+ const bool strict = true);
+#endif // DECODE_GORENJE
#if DECODE_GREE
bool decodeGree(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kGreeBits,
@@ -857,6 +871,18 @@ class IRrecv {
const uint16_t nbits = kBosch144Bits,
const bool strict = true);
#endif // DECODE_BOSCH144
+#if DECODE_WOWWEE
+ bool decodeWowwee(decode_results *results,
+ uint16_t offset = kStartOffset,
+ const uint16_t nbits = kWowweeBits,
+ const bool strict = true);
+#endif // DECODE_WOWWEE
+#if DECODE_YORK
+ bool decodeYork(decode_results *results,
+ uint16_t kStartOffset,
+ const uint16_t kYorkBits,
+ const bool strict = true);
+#endif // DECODE_YORK
};
#endif // IRRECV_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h
index 09e5d045b..133d507b8 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRremoteESP8266.h
@@ -58,7 +58,7 @@
// Minor version number (x.X.x)
#define _IRREMOTEESP8266_VERSION_MINOR 8
// Patch version number (x.x.X)
-#define _IRREMOTEESP8266_VERSION_PATCH 4
+#define _IRREMOTEESP8266_VERSION_PATCH 5
// Macro to convert version info into an integer
#define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \
(((major) << 16) | ((minor) << 8) | (patch))
@@ -924,6 +924,34 @@
#define SEND_DAIKIN312 _IR_ENABLE_DEFAULT_
#endif // SEND_DAIKIN312
+#ifndef DECODE_GORENJE
+#define DECODE_GORENJE _IR_ENABLE_DEFAULT_
+#endif // DECODE_GORENJE
+#ifndef SEND_GORENJE
+#define SEND_GORENJE _IR_ENABLE_DEFAULT_
+#endif // SEND_GORENJE
+
+#ifndef DECODE_WOWWEE
+#define DECODE_WOWWEE _IR_ENABLE_DEFAULT_
+#endif // DECODE_WOWWEE
+#ifndef SEND_WOWWEE
+#define SEND_WOWWEE _IR_ENABLE_DEFAULT_
+#endif // SEND_WOWWEE
+
+#ifndef DECODE_CARRIER_AC84
+#define DECODE_CARRIER_AC84 _IR_ENABLE_DEFAULT_
+#endif // DECODE_CARRIER_AC84
+#ifndef SEND_CARRIER_AC84
+#define SEND_CARRIER_AC84 _IR_ENABLE_DEFAULT_
+#endif // SEND_CARRIER_AC84
+
+#ifndef DECODE_YORK
+#define DECODE_YORK _IR_ENABLE_DEFAULT_
+#endif // DECODE_YORK
+#ifndef SEND_YORK
+#define SEND_YORK _IR_ENABLE_DEFAULT_
+#endif // SEND_YORK
+
#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 || \
@@ -942,6 +970,7 @@
DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \
DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \
DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \
+ DECODE_CARRIER_AC84 || DECODE_YORK || \
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
@@ -1104,8 +1133,12 @@ enum decode_type_t {
BOSCH144, // 120
SANYO_AC152,
DAIKIN312,
+ GORENJE,
+ WOWWEE,
+ CARRIER_AC84, // 125
+ YORK,
// Add new entries before this one, and update it to point to the last entry.
- kLastDecodeType = DAIKIN312,
+ kLastDecodeType = YORK,
};
// Message lengths & required repeat values
@@ -1123,7 +1156,13 @@ const uint16_t kAmcorStateLength = 8;
const uint16_t kAmcorBits = kAmcorStateLength * 8;
const uint16_t kAmcorDefaultRepeat = kSingleRepeat;
const uint16_t kArgoStateLength = 12;
+const uint16_t kArgoShortStateLength = 4;
const uint16_t kArgoBits = kArgoStateLength * 8;
+const uint16_t kArgoShortBits = kArgoShortStateLength * 8;
+const uint16_t kArgo3AcControlStateLength = 6; // Bytes
+const uint16_t kArgo3iFeelReportStateLength = 2; // Bytes
+const uint16_t kArgo3TimerStateLength = 9; // Bytes
+const uint16_t kArgo3ConfigStateLength = 4; // Bytes
const uint16_t kArgoDefaultRepeat = kNoRepeat;
const uint16_t kArrisBits = 32;
const uint16_t kBosch144StateLength = 18;
@@ -1137,6 +1176,9 @@ const uint16_t kCarrierAc40Bits = 40;
const uint16_t kCarrierAc40MinRepeat = 2;
const uint16_t kCarrierAc64Bits = 64;
const uint16_t kCarrierAc64MinRepeat = kNoRepeat;
+const uint16_t kCarrierAc84StateLength = 11;
+const uint16_t kCarrierAc84Bits = kCarrierAc84StateLength * 8 - 4;
+const uint16_t kCarrierAc84MinRepeat = kNoRepeat;
const uint16_t kCarrierAc128StateLength = 16;
const uint16_t kCarrierAc128Bits = kCarrierAc128StateLength * 8;
const uint16_t kCarrierAc128MinRepeat = kNoRepeat;
@@ -1203,6 +1245,7 @@ const uint16_t kGicableBits = 16;
const uint16_t kGicableMinRepeat = kSingleRepeat;
const uint16_t kGoodweatherBits = 48;
const uint16_t kGoodweatherMinRepeat = kNoRepeat;
+const uint16_t kGorenjeBits = 8;
const uint16_t kGreeStateLength = 8;
const uint16_t kGreeBits = kGreeStateLength * 8;
const uint16_t kGreeDefaultRepeat = kNoRepeat;
@@ -1292,6 +1335,8 @@ const uint16_t kNeoclimaBits = kNeoclimaStateLength * 8;
const uint16_t kNeoclimaMinRepeat = kNoRepeat;
const uint16_t kPanasonicBits = 48;
const uint32_t kPanasonicManufacturer = 0x4004;
+const uint32_t kPanasonic40Manufacturer = 0x34;
+const uint16_t kPanasonic40Bits = 40;
const uint16_t kPanasonicAcStateLength = 27;
const uint16_t kPanasonicAcStateShortLength = 16;
const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8;
@@ -1372,6 +1417,8 @@ const uint16_t kWhirlpoolAcStateLength = 21;
const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8;
const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat;
const uint16_t kWhynterBits = 32;
+const uint16_t kWowweeBits = 11;
+const uint16_t kWowweeDefaultRepeat = kNoRepeat;
const uint8_t kVestelAcBits = 56;
const uint16_t kXmpBits = 64;
const uint16_t kZepealBits = 16;
@@ -1386,6 +1433,8 @@ const uint16_t kRhossStateLength = 12;
const uint16_t kRhossBits = kRhossStateLength * 8;
const uint16_t kRhossDefaultRepeat = 0;
const uint16_t kClimaButlerBits = 52;
+const uint16_t kYorkBits = 136;
+const uint16_t kYorkStateLength = 17;
// Legacy defines. (Deprecated)
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp
index 1fb1339c5..10e440b32 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.cpp
@@ -603,7 +603,10 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) {
uint16_t IRsend::defaultBits(const decode_type_t protocol) {
switch (protocol) {
case MULTIBRACKETS:
+ case GORENJE:
return 8;
+ case WOWWEE:
+ return 11;
case RC5:
case SYMPHONY:
return 12;
@@ -690,6 +693,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kBosch144Bits;
case CORONA_AC:
return kCoronaAcBits;
+ case CARRIER_AC84:
+ return kCarrierAc84Bits;
case CARRIER_AC128:
return kCarrierAc128Bits;
case DAIKIN:
@@ -791,6 +796,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kWhirlpoolAcBits;
case XMP:
return kXmpBits;
+ case YORK:
+ return kYorkBits;
// No default amount of bits.
case FUJITSU_AC:
case MWM:
@@ -916,6 +923,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
sendGoodweather(data, nbits, min_repeat);
break;
#endif
+#if SEND_GORENJE
+ case GORENJE:
+ sendGorenje(data, nbits, min_repeat);
+ break;
+#endif
#if SEND_GREE
case GREE:
sendGree(data, nbits, min_repeat);
@@ -1114,6 +1126,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
sendWhynter(data, nbits, min_repeat);
break;
#endif
+#if SEND_WOWWEE
+ case WOWWEE:
+ sendWowwee(data, nbits, min_repeat);
+ break;
+#endif // SEND_WOWWEE
#if SEND_XMP
case XMP:
sendXmp(data, nbits, min_repeat);
@@ -1159,6 +1176,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendBosch144(state, nbytes);
break;
#endif // SEND_BOSCH144
+#if SEND_CARRIER_AC84
+ case CARRIER_AC84:
+ sendCarrierAC84(state, nbytes);
+ break;
+#endif // SEND_CARRIER_AC84
#if SEND_CARRIER_AC128
case CARRIER_AC128:
sendCarrierAC128(state, nbytes);
@@ -1407,6 +1429,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendWhirlpoolAC(state, nbytes);
break;
#endif // SEND_WHIRLPOOL_AC
+#if SEND_YORK
+ case YORK:
+ sendYork(state, nbytes);
+ break;
+#endif // SEND_YORK
default:
return false;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h
index 02f2c939e..f8a447197 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRsend.h
@@ -39,6 +39,9 @@ const uint8_t kDutyMax = 100; // Percentage
const uint16_t kMaxAccurateUsecDelay = 16383;
// Usecs to wait between messages we don't know the proper gap time.
const uint32_t kDefaultMessageGap = 100000;
+/// Placeholder for missing sensor temp value
+/// @note Not using "-1" as it may be a valid external temp
+const float kNoTempValue = -100.0;
/// Enumerators and Structures for the Common A/C API.
namespace stdAc {
@@ -56,14 +59,15 @@ enum class opmode_t {
/// Common A/C settings for Fan Speeds.
enum class fanspeed_t {
- kAuto = 0,
- kMin = 1,
- kLow = 2,
- kMedium = 3,
- kHigh = 4,
- kMax = 5,
+ kAuto = 0,
+ kMin = 1,
+ kLow = 2,
+ kMedium = 3,
+ kHigh = 4,
+ kMax = 5,
+ kMediumHigh = 6,
// Add new entries before this one, and update it to point to the last entry
- kLastFanspeedEnum = kMax,
+ kLastFanspeedEnum = kMediumHigh,
};
/// Common A/C settings for Vertical Swing.
@@ -75,8 +79,21 @@ enum class swingv_t {
kMiddle = 3,
kLow = 4,
kLowest = 5,
+ kUpperMiddle = 6,
// Add new entries before this one, and update it to point to the last entry
- kLastSwingvEnum = kLowest,
+ kLastSwingvEnum = kUpperMiddle,
+};
+
+/// @brief Tyoe of A/C command (if the remote uses different codes for each)
+/// @note Most remotes support only a single command or aggregate multiple
+/// into one (e.g. control+timer). Use @c kControlCommand in such case
+enum class ac_command_t {
+ kControlCommand = 0,
+ kSensorTempReport = 1,
+ kTimerCommand = 2,
+ kConfigCommand = 3,
+ // Add new entries before this one, and update it to point to the last entry
+ kLastAcCommandEnum = kConfigCommand,
};
/// Common A/C settings for Horizontal Swing.
@@ -113,6 +130,9 @@ struct state_t {
bool beep = false;
int16_t sleep = -1; // `-1` means off.
int16_t clock = -1; // `-1` means not set.
+ stdAc::ac_command_t command = stdAc::ac_command_t::kControlCommand;
+ bool iFeel = false;
+ float sensorTemperature = kNoTempValue; // `kNoTempValue` means not set.
};
}; // namespace stdAc
@@ -202,6 +222,11 @@ enum lg_ac_remote_model_t {
LG6711A20083V, // (5) Same as GE6711AR2853M, but only SwingV toggle.
};
+/// Argo A/C model numbers
+enum argo_ac_remote_model_t {
+ SAC_WREM2 = 1, // (1) ARGO WREM2 remote (default)
+ SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd
+};
// Classes
@@ -519,14 +544,22 @@ class IRsend {
const uint16_t nbits = kGoodweatherBits,
const uint16_t repeat = kGoodweatherMinRepeat);
#endif // SEND_GOODWEATHER
+#if SEND_GORENJE
+ void sendGorenje(const uint64_t data, const uint16_t nbits = kGorenjeBits,
+ const uint16_t repeat = kNoRepeat);
+#endif // SEND_GORENJE
#if SEND_PRONTO
void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat);
#endif
#if SEND_ARGO
void sendArgo(const unsigned char data[],
+ const uint16_t nbytes = kArgoStateLength,
+ const uint16_t repeat = kArgoDefaultRepeat,
+ bool sendFooter = false);
+ void sendArgoWREM3(const unsigned char data[],
const uint16_t nbytes = kArgoStateLength,
const uint16_t repeat = kArgoDefaultRepeat);
-#endif
+#endif // SEND_ARGO
#if SEND_TROTEC
void sendTrotec(const unsigned char data[],
const uint16_t nbytes = kTrotecStateLength,
@@ -575,6 +608,11 @@ class IRsend {
void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits,
uint16_t repeat = kCarrierAc64MinRepeat);
#endif
+#if SEND_CARRIER_AC84
+ void sendCarrierAC84(const uint8_t data[],
+ const uint16_t nbytes = kCarrierAc84StateLength,
+ const uint16_t repeat = kNoRepeat);
+#endif // SEND_CARRIER_AC84
#if SEND_CARRIER_AC128
void sendCarrierAC128(const uint8_t data[],
uint16_t nbytes = kCarrierAc128StateLength,
@@ -837,6 +875,15 @@ class IRsend {
const uint16_t nbytes = kBosch144StateLength,
const uint16_t repeat = kNoRepeat);
#endif // SEND_BOSCH144
+#if SEND_WOWWEE
+ void sendWowwee(const uint64_t data, const uint16_t nbits = kWowweeBits,
+ const uint16_t repeat = kWowweeDefaultRepeat);
+#endif // SEND_WOWWEE
+#if SEND_YORK
+ void sendYork(const unsigned char data[],
+ const uint16_t nbytes = kYorkStateLength,
+ const uint16_t repeat = kNoRepeat);
+#endif // SEND_YORK
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 5bd25ed95..9cb39b772 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.cpp
@@ -68,10 +68,13 @@ 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(kConfigCommandStr, D_STR_CONFIG); ///< "Config"
+IRTEXT_CONST_STRING(kControlCommandStr, D_STR_CONTROL); ///< "Control"
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(kIFeelReportStr, D_STR_IFEELREPORT); ///< "IFeel Report"
IRTEXT_CONST_STRING(kIFeelStr, D_STR_IFEEL); ///< "IFeel"
IRTEXT_CONST_STRING(kHumidStr, D_STR_HUMID); ///< "Humid"
IRTEXT_CONST_STRING(kSaveStr, D_STR_SAVE); ///< "Save"
@@ -123,6 +126,7 @@ 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(kUpperMiddleStr, D_STR_UPPER_MIDDLE); ///< "Upper-Middle"
IRTEXT_CONST_STRING(kBreezeStr, D_STR_BREEZE); ///< "Breeze"
IRTEXT_CONST_STRING(kCirculateStr, D_STR_CIRCULATE); ///< "Circulate"
IRTEXT_CONST_STRING(kCeilingStr, D_STR_CEILING); ///< "Ceiling"
@@ -160,6 +164,7 @@ 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(kMedHighStr, D_STR_MED_HIGH); ///< "Med-high"
IRTEXT_CONST_STRING(kMedStr, D_STR_MED); ///< "Med"
IRTEXT_CONST_STRING(kMediumStr, D_STR_MEDIUM); ///< "Medium"
@@ -205,6 +210,13 @@ IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode"
IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///<
///< "Swing(V) Toggle"
IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle"
+IRTEXT_CONST_STRING(kSetTimerCommandStr, D_STR_SET_TIMER); ///< "Set Timer"
+IRTEXT_CONST_STRING(kScheduleStr, D_STR_SCHEDULE); ///< "Schedule"
+IRTEXT_CONST_STRING(kChStr, D_STR_CH); ///< "CH#"
+IRTEXT_CONST_STRING(kTimerActiveDaysStr, D_STR_TIMER_ACTIVE_DAYS);
+///< "TimerActiveDays"
+IRTEXT_CONST_STRING(kKeyStr, D_STR_KEY); ///< "Key"
+IRTEXT_CONST_STRING(kValueStr, D_STR_VALUE); ///< "Value"
// Separators & Punctuation
const char kTimeSep = D_CHR_TIME_SEP; ///< ':'
@@ -281,6 +293,8 @@ 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"
+IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3"
+IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3"
#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as
// a question mark, check for length > 1
@@ -533,6 +547,14 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
D_STR_SANYO_AC152, D_STR_UNSUPPORTED) "\x0"
COND(DECODE_DAIKIN312 || SEND_DAIKIN312,
D_STR_DAIKIN312, D_STR_UNSUPPORTED) "\x0"
+ COND(DECODE_GORENJE || SEND_GORENJE,
+ D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0"
+ COND(DECODE_WOWWEE || SEND_WOWWEE,
+ D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0"
+ COND(DECODE_CARRIER_AC84 || SEND_CARRIER_AC84,
+ D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0"
+ COND(DECODE_YORK || SEND_YORK,
+ D_STR_YORK, D_STR_UNSUPPORTED) "\x0"
///< New protocol (macro) strings should be added just above this line.
"\x0" ///< This string requires double null termination.
};
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h
index 7bd4fbed3..15d2690b7 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRtext.h
@@ -39,6 +39,8 @@ extern IRTEXT_CONST_PTR(kAkb73757604Str);
extern IRTEXT_CONST_PTR(kAkb74955603Str);
extern IRTEXT_CONST_PTR(kAkb75215403Str);
extern IRTEXT_CONST_PTR(kArdb1Str);
+extern IRTEXT_CONST_PTR(kArgoWrem2Str);
+extern IRTEXT_CONST_PTR(kArgoWrem3Str);
extern IRTEXT_CONST_PTR(kArjw2Str);
extern IRTEXT_CONST_PTR(kArrah2eStr);
extern IRTEXT_CONST_PTR(kArreb1eStr);
@@ -57,6 +59,7 @@ extern IRTEXT_CONST_PTR(kCelsiusFahrenheitStr);
extern IRTEXT_CONST_PTR(kCelsiusStr);
extern IRTEXT_CONST_PTR(kCentreStr);
extern IRTEXT_CONST_PTR(kChangeStr);
+extern IRTEXT_CONST_PTR(kChStr);
extern IRTEXT_CONST_PTR(kCirculateStr);
extern IRTEXT_CONST_PTR(kCkpStr);
extern IRTEXT_CONST_PTR(kCleanStr);
@@ -66,6 +69,8 @@ extern IRTEXT_CONST_PTR(kColonSpaceStr);
extern IRTEXT_CONST_PTR(kComfortStr);
extern IRTEXT_CONST_PTR(kCommaSpaceStr);
extern IRTEXT_CONST_PTR(kCommandStr);
+extern IRTEXT_CONST_PTR(kConfigCommandStr);
+extern IRTEXT_CONST_PTR(kControlCommandStr);
extern IRTEXT_CONST_PTR(kCoolStr);
extern IRTEXT_CONST_PTR(kCoolingStr);
extern IRTEXT_CONST_PTR(kDashStr);
@@ -109,6 +114,7 @@ extern IRTEXT_CONST_PTR(kHoldStr);
extern IRTEXT_CONST_PTR(kHourStr);
extern IRTEXT_CONST_PTR(kHoursStr);
extern IRTEXT_CONST_PTR(kHumidStr);
+extern IRTEXT_CONST_PTR(kIFeelReportStr);
extern IRTEXT_CONST_PTR(kIFeelStr);
extern IRTEXT_CONST_PTR(kISeeStr);
extern IRTEXT_CONST_PTR(kIdStr);
@@ -116,6 +122,7 @@ extern IRTEXT_CONST_PTR(kIndirectStr);
extern IRTEXT_CONST_PTR(kInsideStr);
extern IRTEXT_CONST_PTR(kIonStr);
extern IRTEXT_CONST_PTR(kJkeStr);
+extern IRTEXT_CONST_PTR(kKeyStr);
extern IRTEXT_CONST_PTR(kKkg29ac1Str);
extern IRTEXT_CONST_PTR(kKkg9ac1Str);
extern IRTEXT_CONST_PTR(kLastStr);
@@ -139,6 +146,7 @@ extern IRTEXT_CONST_PTR(kMaxRightNoSpaceStr);
extern IRTEXT_CONST_PTR(kMaxRightStr);
extern IRTEXT_CONST_PTR(kMaxStr);
extern IRTEXT_CONST_PTR(kMaximumStr);
+extern IRTEXT_CONST_PTR(kMedHighStr);
extern IRTEXT_CONST_PTR(kMedStr);
extern IRTEXT_CONST_PTR(kMediumStr);
extern IRTEXT_CONST_PTR(kMidStr);
@@ -188,8 +196,10 @@ extern IRTEXT_CONST_PTR(kRlt0541htaaStr);
extern IRTEXT_CONST_PTR(kRlt0541htabStr);
extern IRTEXT_CONST_PTR(kRoomStr);
extern IRTEXT_CONST_PTR(kSaveStr);
+extern IRTEXT_CONST_PTR(kScheduleStr);
extern IRTEXT_CONST_PTR(kSecondStr);
extern IRTEXT_CONST_PTR(kSecondsStr);
+extern IRTEXT_CONST_PTR(kSensorReportStr);
extern IRTEXT_CONST_PTR(kSensorStr);
extern IRTEXT_CONST_PTR(kSensorTempStr);
extern IRTEXT_CONST_PTR(kSetStr);
@@ -213,7 +223,9 @@ extern IRTEXT_CONST_PTR(kTempDownStr);
extern IRTEXT_CONST_PTR(kTempStr);
extern IRTEXT_CONST_PTR(kTempUpStr);
extern IRTEXT_CONST_PTR(kThreeLetterDayOfWeekStr);
+extern IRTEXT_CONST_PTR(kTimerActiveDaysStr);
extern IRTEXT_CONST_PTR(kTimerModeStr);
+extern IRTEXT_CONST_PTR(kSetTimerCommandStr);
extern IRTEXT_CONST_PTR(kTimerStr);
extern IRTEXT_CONST_PTR(kToggleStr);
extern IRTEXT_CONST_PTR(kTopStr);
@@ -224,6 +236,8 @@ extern IRTEXT_CONST_PTR(kTypeStr);
extern IRTEXT_CONST_PTR(kUnknownStr);
extern IRTEXT_CONST_PTR(kUpStr);
extern IRTEXT_CONST_PTR(kUpperStr);
+extern IRTEXT_CONST_PTR(kUpperMiddleStr);
+extern IRTEXT_CONST_PTR(kValueStr);
extern IRTEXT_CONST_PTR(kV9014557AStr);
extern IRTEXT_CONST_PTR(kV9014557BStr);
extern IRTEXT_CONST_PTR(kVaneStr);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp
index 7c59228c0..4c713c87b 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.cpp
@@ -6,6 +6,7 @@
#endif
#define __STDC_LIMIT_MACROS
+#include
#include
#include
#include
@@ -173,6 +174,7 @@ bool hasACState(const decode_type_t protocol) {
case AMCOR:
case ARGO:
case BOSCH144:
+ case CARRIER_AC84:
case CARRIER_AC128:
case CORONA_AC:
case DAIKIN:
@@ -224,6 +226,7 @@ bool hasACState(const decode_type_t protocol) {
case TROTEC_3550:
case VOLTAS:
case WHIRLPOOL_AC:
+ case YORK:
return true;
default:
return false;
@@ -304,7 +307,7 @@ String resultToSourceCode(const decode_results * const results) {
if (results->decode_type != UNKNOWN) {
if (hasState) {
#if DECODE_AC
- uint16_t nbytes = results->bits / 8;
+ uint16_t nbytes = ceil(static_cast(results->bits) / 8.0);
output += F("uint8_t state[");
output += uint64ToString(nbytes);
output += F("] = {");
@@ -695,6 +698,13 @@ namespace irutils {
default: return kUnknownStr;
}
break;
+ case decode_type_t::ARGO:
+ switch (model) {
+ case argo_ac_remote_model_t::SAC_WREM2: return kArgoWrem2Str;
+ case argo_ac_remote_model_t::SAC_WREM3: return kArgoWrem3Str;
+ default: return kUnknownStr;
+ }
+ break;
default: return kUnknownStr;
}
}
@@ -722,10 +732,12 @@ namespace irutils {
/// @param[in] celsius Is the temp Celsius or Fahrenheit.
/// true is C, false is F
/// @param[in] precomma Should the output string start with ", " or not?
+ /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target?
/// @return The resulting String.
String addTempToString(const uint16_t degrees, const bool celsius,
- const bool precomma) {
- String result = addIntToString(degrees, kTempStr, precomma);
+ const bool precomma, const bool isSensorTemp) {
+ String result = addIntToString(degrees, (isSensorTemp)?
+ kSensorTempStr : kTempStr, precomma);
result += celsius ? 'C' : 'F';
return result;
}
@@ -736,12 +748,14 @@ namespace irutils {
/// @param[in] celsius Is the temp Celsius or Fahrenheit.
/// true is C, false is F
/// @param[in] precomma Should the output string start with ", " or not?
+ /// @param[in] isSensorTemp Is the value a room (ambient) temp. or target?
/// @return The resulting String.
String addTempFloatToString(const float degrees, const bool celsius,
- const bool precomma) {
+ const bool precomma, const bool isSensorTemp) {
String result = "";
- result.reserve(14); // Assuming ", Temp: XXX.5F" is the largest.
- result += addIntToString(degrees, kTempStr, precomma);
+ result.reserve(21); // Assuming ", Sensor Temp: XXX.5F" is the largest.
+ result += addIntToString(degrees, (isSensorTemp)?
+ kSensorTempStr : kTempStr, precomma);
// Is it a half degree?
if (((uint16_t)(2 * degrees)) & 1) result += F(".5");
result += celsius ? 'C' : 'F';
@@ -788,43 +802,57 @@ namespace irutils {
result.reserve(19); // ", Day: N (UNKNOWN)"
result += addIntToString(day_of_week, kDayStr, precomma);
result += kSpaceLBraceStr;
+ result += dayToString(day_of_week, offset);
+ return result + ')';
+ }
+
+ /// Create a String of the 3-letter day of the week from a numerical day of
+ /// the week. e.g. "Mon"
+ /// @param[in] day_of_week A numerical version of the sequential day of the
+ /// week. e.g. Sunday = 1, Monday = 2, ..., Saturday = 7
+ /// @param[in] offset Days to offset by.
+ /// e.g. For different day starting the week.
+ /// @return The resulting String.
+ String dayToString(const uint8_t day_of_week, const int8_t offset) {
if ((uint8_t)(day_of_week + offset) < 7)
#if UNIT_TEST
- result += String(kThreeLetterDayOfWeekStr).substr(
- (day_of_week + offset) * 3, 3);
+ return String(kThreeLetterDayOfWeekStr).substr(
+ (day_of_week + offset) * 3, 3);
#else // UNIT_TEST
- result += String(kThreeLetterDayOfWeekStr).substring(
- (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3);
+ return String(kThreeLetterDayOfWeekStr).substring(
+ (day_of_week + offset) * 3, (day_of_week + offset) * 3 + 3);
#endif // UNIT_TEST
else
- result += kUnknownStr;
- return result + ')';
+ return kUnknownStr;
}
/// Create a String of human output for the given fan speed.
/// e.g. "Fan: 0 (Auto)"
/// @param[in] speed The numeric speed of the fan to display.
- /// @param[in] high The numeric value for High speed.
+ /// @param[in] high The numeric value for High speed. (second highest)
/// @param[in] low The numeric value for Low speed.
/// @param[in] automatic The numeric value for Auto speed.
/// @param[in] quiet The numeric value for Quiet speed.
/// @param[in] medium The numeric value for Medium speed.
/// @param[in] maximum The numeric value for Highest speed. (if > high)
+ /// @param[in] medium_high The numeric value for third-highest speed.
+ /// (if > medium)
/// @return The resulting String.
String addFanToString(const uint8_t speed, const uint8_t high,
const uint8_t low, const uint8_t automatic,
const uint8_t quiet, const uint8_t medium,
- const uint8_t maximum) {
+ const uint8_t maximum, const uint8_t medium_high) {
String result = "";
result.reserve(21); // ", Fan: NNN (UNKNOWN)"
result += addIntToString(speed, kFanStr);
result += kSpaceLBraceStr;
- if (speed == high) result += kHighStr;
- else if (speed == low) result += kLowStr;
- else if (speed == automatic) result += kAutoStr;
- else if (speed == quiet) result += kQuietStr;
- else if (speed == medium) result += kMediumStr;
- else if (speed == maximum) result += kMaximumStr;
+ if (speed == high) result += kHighStr;
+ else if (speed == low) result += kLowStr;
+ else if (speed == automatic) result += kAutoStr;
+ else if (speed == quiet) result += kQuietStr;
+ else if (speed == medium) result += kMediumStr;
+ else if (speed == maximum) result += kMaximumStr;
+ else if (speed == medium_high) result += kMedHighStr;
else
result += kUnknownStr;
return result + ')';
@@ -950,6 +978,106 @@ namespace irutils {
return result + ')';
}
+ /// @brief Create a String of human output for the given timer setting.
+ /// e.g. "Timer Mode: 2 (Schedule 1)"
+ /// @param[in] timerMode The numeric value of the timer mode to display.
+ /// @param[in] noTimer The numeric value for no timer (off)
+ /// @param[in] delayTimer The numeric value for delay (sleep) timer
+ /// @param[in] schedule1 The numeric value for schedule timer #1
+ /// @param[in] schedule2 The numeric value for schedule timer #2
+ /// @param[in] schedule3 The numeric value for schedule timer #3
+ /// @param[in] precomma Should the output string start with ", " or not?
+ /// @return String representation
+ String addTimerModeToString(const uint8_t timerMode, const uint8_t noTimer,
+ const uint8_t delayTimer, const uint8_t schedule1,
+ const uint8_t schedule2, const uint8_t schedule3,
+ const bool precomma) {
+ String result = "";
+ result.reserve(28); // ", Timer Mode: 2 (Schedule 1)"
+ result += addIntToString(timerMode, kTimerModeStr, precomma);
+ result += kSpaceLBraceStr;
+ if (timerMode == noTimer) {
+ result += kOffStr;
+ } else if (timerMode == delayTimer) {
+ result += kSleepTimerStr;
+ } else if (timerMode == schedule1) {
+ result += kScheduleStr;
+ result += '1';
+ } else if (timerMode == schedule2) {
+ result += kScheduleStr;
+ result += '2';
+ } else if (timerMode == schedule3) {
+ result += kScheduleStr;
+ result += '3';
+ } else {
+ result += kUnknownStr;
+ }
+ return result + ')';
+ }
+
+ /// @brief Create a String of human output for the given channel
+ /// e.g. "[CH#0]"
+ /// @param channel The numeric value of the channel to display.
+ /// @return String representation
+ String channelToString(const uint8_t channel) {
+ String result = "";
+ result.reserve(6); // "[CH#4]"
+ result += "[";
+ result += kChStr;
+ result += uint64ToString(channel);
+ result += "]";
+ return result;
+ }
+
+ /// @brief Create a String of human output for the given command type
+ /// e.g. "IFeel Report"
+ /// @param irCommandType The numeric value of the command type to display.
+ /// @param acControlCmd The numeric value of the "control" (default) command
+ /// @param iFeelReportCmd The numeric value of the sensor temperature command
+ /// @param timerCmd The numeric value of the timer config IR command
+ /// @param configCmd The numeric value of the config param set IR command
+ /// @return String representation
+ String irCommandTypeToString(uint8_t irCommandType, uint8_t acControlCmd,
+ uint8_t iFeelReportCmd, uint8_t timerCmd,
+ uint8_t configCmd) {
+ String result = "";
+ result.reserve(12); // "IFeel Report"
+ if (irCommandType == acControlCmd) {
+ result += kCommandStr;
+ } else if (irCommandType == iFeelReportCmd) {
+ result += kIFeelReportStr;
+ } else if (irCommandType == timerCmd) {
+ result += kTimerStr;
+ } else if (irCommandType == configCmd) {
+ result += kConfigCommandStr;
+ } else {
+ result += kUnknownStr;
+ }
+ return result;
+ }
+
+ /// @brief Create a String of the 3-letter day of the week bitmap
+ // e.g. 0b0000101 is "Sun | Tue"
+ /// @param[in] daysBitmap The bitmap representing days of week to represent
+ /// e.g bit[0]=Sunday, bit[1]=Monday, ...
+ /// @param[in] offset Days to offset by.
+ /// e.g. For different day starting the week.
+ /// @return String representation.
+ String daysBitmaskToString(uint8_t daysBitmap, uint8_t offset) {
+ String result = "";
+ result.reserve(27); // Sun|Mon|Tue|Wed|Thu|Fri|Sat
+
+ for (uint8_t i = 0; i < 7; ++i) {
+ if (((daysBitmap >> i) & 0b1) == 0b1) {
+ if (result.length() > 0) {
+ result += "|";
+ }
+ result += irutils::dayToString(i, offset);
+ }
+ }
+ return result;
+ }
+
/// Escape any special HTML (unsafe) characters in a string. e.g. anti-XSS.
/// @param[in] unescaped A String containing text to make HTML safe.
/// @return A string that is HTML safe.
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h
index a5dcde043..8c94df228 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRutils.h
@@ -60,16 +60,19 @@ namespace irutils {
String addLabeledString(const String value, const String label,
const bool precomma = true);
String addTempToString(const uint16_t degrees, const bool celsius = true,
- const bool precomma = true);
+ const bool precomma = true,
+ const bool isSensorTemp = false);
String addTempFloatToString(const float degrees, const bool celsius = true,
- const bool precomma = true);
+ const bool precomma = true,
+ const bool isSensorTemp = false);
String addModeToString(const uint8_t mode, const uint8_t automatic,
const uint8_t cool, const uint8_t heat,
const uint8_t dry, const uint8_t fan);
String addFanToString(const uint8_t speed, const uint8_t high,
const uint8_t low, const uint8_t automatic,
const uint8_t quiet, const uint8_t medium,
- const uint8_t maximum = 0xFF);
+ const uint8_t maximum = 0xFF,
+ const uint8_t medium_high = 0xFF);
String addSwingHToString(const uint8_t position, const uint8_t automatic,
const uint8_t maxleft, const uint8_t left,
const uint8_t middle,
@@ -87,6 +90,19 @@ namespace irutils {
const uint8_t breeze, const uint8_t circulate);
String addDayToString(const uint8_t day_of_week, const int8_t offset = 0,
const bool precomma = true);
+ String addTimerModeToString(const uint8_t timerType, const uint8_t noTimer,
+ const uint8_t delayTimer,
+ const uint8_t schedule1 = 0xFF,
+ const uint8_t schedule2 = 0xFF,
+ const uint8_t schedule3 = 0xFF,
+ const bool precomma = true);
+ String irCommandTypeToString(uint8_t commandType, uint8_t acControlCmd,
+ uint8_t iFeelReportCmd = 0xFF,
+ uint8_t timerCmd = 0xFF,
+ uint8_t configCmd = 0xFF);
+ String dayToString(const uint8_t day_of_week, const int8_t offset = 0);
+ String daysBitmaskToString(uint8_t daysBitmap, uint8_t offset = 0);
+ String channelToString(const uint8_t channel);
String htmlEscape(const String unescaped);
String msToString(uint32_t const msecs);
String minsToString(const uint16_t mins);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.cpp
index 680178ceb..2f5ce4ea6 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.cpp
@@ -1,11 +1,16 @@
// Copyright 2017 Schmolders
// Copyright 2019 crankyoldgit
+// Copyright 2022 Mateusz Bronk (mbronk)
/// @file
/// @brief Argo A/C protocol.
-/// Controls an Argo Ulisse 13 DCI A/C
+
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1859
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1912
+
#include "ir_Argo.h"
#include
+#include
#include
#ifndef UNIT_TEST
#include
@@ -22,211 +27,890 @@ const uint16_t kArgoBitMark = 400;
const uint16_t kArgoOneSpace = 2200;
const uint16_t kArgoZeroSpace = 900;
const uint32_t kArgoGap = kDefaultMessageGap; // Made up value. Complete guess.
+const uint8_t kArgoSensorCheck = 52; // Part of the sensor message check calc.
+const uint8_t kArgoSensorFixed = 0b011;
+const uint8_t kArgoWrem3Preamble = 0b1011;
+const uint8_t kArgoWrem3Postfix_Timer = 0b1;
+const uint8_t kArgoWrem3Postfix_ACControl = 0b110000;
using irutils::addBoolToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
+using irutils::addFanToString;
+using irutils::addSwingVToString;
+using irutils::minsToString;
+using irutils::addDayToString;
+using irutils::addModelToString;
+using irutils::daysBitmaskToString;
+using irutils::addTimerModeToString;
#if SEND_ARGO
/// Send a Argo A/C formatted message.
-/// Status: BETA / Probably works.
+/// Status: [WREM-2] BETA / Probably works.
+/// [WREM-3] Confirmed working w/ Argo 13 ECO (WREM-3)
+/// @note The "no footer" part needs re-checking for validity but retained for
+/// backwards compatibility.
+/// Consider using @c sendFooter=true code for WREM-2 as well
/// @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.
+/// @param[in] sendFooter Whether to send footer and add a final gap.
+/// *REQUIRED* for WREM-3, UNKNOWN for WREM-2 (used to be
+/// disabled in previous impl., hence retained)
+/// @note Consider removing this param (default to true) if WREM-2 works w/ it
void IRsend::sendArgo(const unsigned char data[], const uint16_t nbytes,
- const uint16_t repeat) {
- // Check if we have enough bytes to send a proper message.
- if (nbytes < kArgoStateLength) return;
- // TODO(kaschmo): validate
+ const uint16_t repeat, bool sendFooter /*= false*/) {
+ if (nbytes < std::min({kArgo3AcControlStateLength,
+ kArgo3ConfigStateLength,
+ kArgo3iFeelReportStateLength,
+ kArgo3TimerStateLength,
+ kArgoStateLength,
+ kArgoShortStateLength})) {
+ return; // Not enough bytes to send a proper message.
+ }
+
+ const uint16_t _footermark = (sendFooter)? kArgoBitMark : 0;
+ const uint32_t _gap = (sendFooter)? kArgoGap : 0;
+
sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace,
- kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer.
- data, nbytes, 38, false, repeat, kDutyDefault);
+ kArgoBitMark, kArgoZeroSpace,
+ _footermark, _gap,
+ data, nbytes, kArgoFrequency, false, repeat, kDutyDefault);
+}
+
+
+/// Send a Argo A/C formatted message.
+/// Status: Confirmed working w/ Argo 13 ECO (WREM-3)
+/// @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::sendArgoWREM3(const unsigned char data[], const uint16_t nbytes,
+ const uint16_t repeat) {
+ sendArgo(data, nbytes, repeat, true);
}
#endif // SEND_ARGO
+
+/// 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?
+template
+IRArgoACBase::IRArgoACBase(const uint16_t pin, const bool inverted,
+ const bool use_modulation)
+ : _irsend(pin, inverted, use_modulation) { stateReset(); }
+
+
/// 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?
IRArgoAC::IRArgoAC(const uint16_t pin, const bool inverted,
const bool use_modulation)
- : _irsend(pin, inverted, use_modulation) { stateReset(); }
+ : IRArgoACBase(pin, inverted, use_modulation) { }
+
+
+/// 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?
+IRArgoAC_WREM3::IRArgoAC_WREM3(const uint16_t pin, const bool inverted,
+ const bool use_modulation)
+ : IRArgoACBase(pin, inverted, use_modulation) {}
/// Set up hardware to be able to send a message.
-void IRArgoAC::begin(void) { _irsend.begin(); }
+template
+void IRArgoACBase::begin(void) { _irsend.begin(); }
-#if SEND_ARGO
-/// Send the current internal state as an IR message.
-/// @param[in] repeat Nr. of times the message will be repeated.
-void IRArgoAC::send(const uint16_t repeat) {
- _irsend.sendArgo(getRaw(), kArgoStateLength, repeat);
+
+/// @cond
+/// @brief Get byte length of raw WREM-2 message based on IR cmd type
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param type The type of IR command
+/// @note Not all types are supported. AC_CONTROL and TIMER are the same cmd
+/// @return Byte length of state command
+/// @relates IRArgoACBase\
+template<>
+uint16_t IRArgoACBase::getStateLengthForIrMsgType(
+ argoIrMessageType_t type) {
+ switch (type) {
+ case argoIrMessageType_t::AC_CONTROL:
+ case argoIrMessageType_t::TIMER_COMMAND:
+ return kArgoStateLength;
+ case argoIrMessageType_t::IFEEL_TEMP_REPORT:
+ return kArgoShortStateLength;
+ case argoIrMessageType_t::CONFIG_PARAM_SET:
+ default:
+ return 0; // Not supported by WREM-2
+ }
+}
+/// @endcond
+
+/// @brief Get byte length of raw WREM-3 message based on IR cmd type
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param type The type of IR command
+/// @return Byte length of state command
+/// @relates IRArgoACBase\
+template<>
+uint16_t IRArgoACBase::getStateLengthForIrMsgType(
+ argoIrMessageType_t type) {
+ switch (type) {
+ case argoIrMessageType_t::AC_CONTROL:
+ return kArgo3AcControlStateLength;
+ case argoIrMessageType_t::IFEEL_TEMP_REPORT:
+ return kArgo3iFeelReportStateLength;
+ case argoIrMessageType_t::TIMER_COMMAND:
+ return kArgo3TimerStateLength;
+ case argoIrMessageType_t::CONFIG_PARAM_SET:
+ return kArgo3ConfigStateLength;
+ default:
+ return 0;
+ }
}
-/// Send current room temperature for the iFeel feature as a silent IR
-/// message (no acknowledgement from the device).
-/// @param[in] temp The temperature in degrees celsius.
-/// @param[in] repeat Nr. of times the message will be repeated.
-void IRArgoAC::sendSensorTemp(const uint8_t temp, const uint16_t repeat) {
- uint8_t tempc = temp - kArgoTempDelta;
- uint8_t check = 52 + tempc;
- uint8_t end = 0b011;
-
- ArgoProtocol data;
- data.raw[0] = 0b10101100;
- data.raw[1] = 0b11110101;
- data.raw[2] = (tempc << 3) | (check >> 5);
- data.raw[3] = (check << 3) | end;
- for (uint8_t i = 4; i < kArgoStateLength; i++) data.raw[i] = 0x0;
- uint8_t sum = IRArgoAC::calcChecksum(data.raw, kArgoStateLength);
- data.raw[10] = 0b00000010;
- data.Sum = sum;
-
- _irsend.sendArgo(data.raw, kArgoStateLength, repeat);
+/// @cond
+/// @brief Get message type from raw WREM-2 data
+/// 1st param ignored: WREM-2 does not carry type in payload, allegedly
+/// @param length Message length: used for *heuristic* detection of message type
+/// @return IR message type
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @relates IRArgoACBase\
+template<>
+argoIrMessageType_t IRArgoACBase::getMessageType(
+ const uint8_t[], const uint16_t length) {
+ if (length == kArgoShortStateLength) {
+ return argoIrMessageType_t::IFEEL_TEMP_REPORT;
+ }
+ return argoIrMessageType_t::AC_CONTROL;
}
-#endif // SEND_ARGO
+/// @endcond
-/// Verify the checksum is valid for a given state.
-/// @param[in] state The array to verify the checksum of.
+
+/// @brief Get message type from raw WREM-3 data
+/// @param state The raw IR data
+/// @param length Length of @c state (in byte)
+/// @return IR message type
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @relates IRArgoACBase\
+template<>
+argoIrMessageType_t IRArgoACBase::getMessageType(
+ const uint8_t state[], const uint16_t length) {
+ if (length < 1) {
+ return static_cast(-1);
+ }
+ return static_cast(state[0] >> 6);
+}
+
+/// @brief Get message type from raw WREM-3 data
+/// @param raw Raw data
+/// @return IR message type
+argoIrMessageType_t IRArgoAC_WREM3::getMessageType(
+ const ArgoProtocolWREM3& raw) {
+ return static_cast(raw.IrCommandType);
+}
+
+
+/// @brief Get actual raw state byte length for the current state
+/// _param 1st param ignored: WREM-2 does not caryy type in payload, allegedly
+/// @param messageType Type of message the state is carrying
+/// @return Actual length of state (in bytes)
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @relates IRArgoACBase\
+template<>
+uint16_t IRArgoACBase::getRawByteLength(const ArgoProtocol&,
+ argoIrMessageType_t messageType) {
+ if (messageType == argoIrMessageType_t::IFEEL_TEMP_REPORT) {
+ return kArgoShortStateLength;
+ }
+ return kArgoStateLength;
+}
+
+
+/// @brief Get actual raw state byte length for the current state
+/// @param raw The raw state
+/// _param 2nd param ignored (1st byte of @c raw is sufficient to get len)
+/// @return Actual length of state (in bytes)
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @relates IRArgoACBase\
+template<>
+uint16_t IRArgoACBase::getRawByteLength(
+ const ArgoProtocolWREM3& raw, argoIrMessageType_t) {
+ return IRArgoAC_WREM3::getStateLengthForIrMsgType(
+ IRArgoAC_WREM3::getMessageType(raw));
+}
+
+
+/// @brief Get actual raw state byte length for the current state
+/// @return Actual length of state (in bytes)
+template
+uint16_t IRArgoACBase::getRawByteLength() const {
+ return getRawByteLength(_, _messageType);
+}
+
+/// @cond
+/// Calculate the checksum for a given state (WREM-2).
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @warning This does NOT calculate 'short' (iFeel) message checksums
+/// @param[in] state The array to calculate the checksum for.
/// @param[in] length The size of the state.
-/// @return A boolean indicating if it's checksum is valid.
-uint8_t IRArgoAC::calcChecksum(const uint8_t state[], const uint16_t length) {
+/// @return The 8-bit calculated result.
+/// @relates IRArgoACBase\
+template<>
+uint8_t IRArgoACBase::calcChecksum(const uint8_t state[],
+ const uint16_t length) {
// Corresponds to byte 11 being constant 0b01
// Only add up bytes to 9. byte 10 is 0b01 constant anyway.
// Assume that argo array is MSB first (left)
return sumBytes(state, length - 2, 2);
}
+/// @endcond
+
+
+/// Calculate the checksum for a given state (WREM-3).
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] state The array to calculate the checksum for.
+/// @param[in] length The size of the state.
+/// @return The 8-bit calculated result.
+/// @relates IRArgoACBase\
+template<>
+uint8_t IRArgoACBase::calcChecksum(const uint8_t state[],
+ const uint16_t length) {
+ if (length < 1) {
+ return -1; // Nothing to compute on
+ }
+
+ uint16_t payloadSizeBits = (length - 1) * 8; // Last byte carries checksum
+
+ argoIrMessageType_t msgType = getMessageType(state, length);
+ if (msgType == argoIrMessageType_t::IFEEL_TEMP_REPORT) {
+ payloadSizeBits += 5; // For WREM3::iFeel the checksum is 3-bit
+ } else if (msgType == argoIrMessageType_t::TIMER_COMMAND) {
+ payloadSizeBits += 3; // For WREM3::Timer the checksum is 5-bit
+ } // Otherwise: full 8-bit checksum
+
+ uint8_t checksum = sumBytes(state, payloadSizeBits / 8, 0);
+
+ // Add stray bits from last byte to the checksum (if any)
+ const uint8_t maskPayload = 0xFF >> (8 - (payloadSizeBits % 8));
+ checksum += (state[length-1] & maskPayload);
+
+ const uint8_t maskChecksum = 0xFF >> (payloadSizeBits % 8);
+ return checksum & maskChecksum;
+}
+
+
+/// Update the checksum for a given state (WREM2).
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @warning This impl does not support short message format (iFeel)
+/// @param[in,out] state Pointer to a binary representation of the A/C state.
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::_checksum(ArgoProtocol *state) {
+ uint8_t sum = calcChecksum(state->raw, kArgoStateLength);
+ // Append sum to end of array
+ // Set const part of checksum bit 10
+ state->Post = kArgoPost;
+ state->Sum = sum;
+}
+
+
+/// @brief Update the checksum for a given state (WREM3).
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in,out] state Pointer to a binary representation of the A/C state.
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::_checksum(ArgoProtocolWREM3 *state) {
+ argoIrMessageType_t msgType = IRArgoAC_WREM3::getMessageType(*state);
+
+ uint8_t sum = calcChecksum(state->raw, getRawByteLength(*state));
+ switch (msgType) {
+ case argoIrMessageType_t::IFEEL_TEMP_REPORT:
+ state->CheckHi = sum;
+ break;
+ case argoIrMessageType_t::TIMER_COMMAND:
+ state->timer.Checksum = sum;
+ break;
+ case argoIrMessageType_t::CONFIG_PARAM_SET:
+ state->config.Checksum = sum;
+ break;
+ case argoIrMessageType_t::AC_CONTROL:
+ default:
+ state->Sum = sum;
+ break;
+ }
+}
+
+
+/// Update the checksum for the internal state.
+template
+void IRArgoACBase::checksum(void) { _checksum(&_); }
+
+
+/// Reset the given state to a known good state.
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in,out] state Pointer to a binary representation of the A/C state.
+/// _param 2nd param unused (always resets to AC_CONTROL state)
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::_stateReset(ArgoProtocol *state,
+ argoIrMessageType_t) {
+ for (uint8_t i = 2; i < kArgoStateLength; i++) state->raw[i] = 0x0;
+ state->Pre1 = kArgoPreamble1; // LSB first (as sent) 0b00110101;
+ state->Pre2 = kArgoPreamble2; // LSB first: 0b10101111;
+ state->Post = kArgoPost;
+}
+
+
+/// Reset the given state to a known good state
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in,out] state Pointer to a binary representation of the A/C state.
+/// @param messageType Type of message to reset the state for
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::_stateReset(ArgoProtocolWREM3 *state,
+ argoIrMessageType_t messageType) {
+ for (uint8_t i = 1; i < sizeof(state->raw) / sizeof(state->raw[0]); i++) {
+ state->raw[i] = 0x0;
+ }
+ state->Pre1 = kArgoWrem3Preamble; // LSB first (as sent) 0b00110101;
+ state->IrChannel = 0;
+ state->IrCommandType = static_cast(messageType);
+
+ if (messageType == argoIrMessageType_t::TIMER_COMMAND) {
+ state->timer.Post1 = kArgoWrem3Postfix_Timer; // 0b1
+ } else if (messageType == argoIrMessageType_t::AC_CONTROL) {
+ state->Post1 = kArgoWrem3Postfix_ACControl; // 0b110000
+ }
+}
+
+
+/// @brief Reset the internals of the object to a known good state.
+/// @param messageType Type of message to reset the state for
+template
+void IRArgoACBase::stateReset(argoIrMessageType_t messageType) {
+ _stateReset(&_, messageType);
+ if (messageType == argoIrMessageType_t::AC_CONTROL) {
+ off();
+ setTemp(20);
+ setSensorTemp(25);
+ setMode(argoMode_t::AUTO);
+ setFan(argoFan_t::FAN_AUTO);
+ }
+ _messageType = messageType;
+ _length = getStateLengthForIrMsgType(_messageType);
+}
+
+
+/// @brief Retrieve the checksum value from transmitted state
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] state Raw state
+/// @param length Length of @c state in bytes
+/// @return Checksum value (8-bit)
+/// @relates IRArgoACBase\
+template<>
+uint8_t IRArgoACBase::getChecksum(const uint8_t state[],
+ const uint16_t length) {
+ if (length < 1) {
+ return -1;
+ }
+ return (state[length - 2] >> 2) + (state[length - 1] << 6);
+}
+
+/// @cond
+/// @brief Retrieve the checksum value from transmitted state
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] state Raw state
+/// @param length Length of @c state in bytes
+/// @return Checksum value (up to 8-bit)
+template<>
+uint8_t IRArgoACBase::getChecksum(const uint8_t state[],
+ const uint16_t length) {
+ if (length < 1) {
+ return -1;
+ }
+ argoIrMessageType_t msgType = getMessageType(state, length);
+ if (msgType == argoIrMessageType_t::IFEEL_TEMP_REPORT) {
+ return (state[length - 1] & 0b11100000) >> 5;
+ }
+ if (msgType == argoIrMessageType_t::TIMER_COMMAND) {
+ return state[length - 1] >> 3;
+ }
+ return (state[length - 1]);
+}
+/// @endcond
+
/// 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 IRArgoAC::validChecksum(const uint8_t state[], const uint16_t length) {
- return ((state[length - 2] >> 2) + (state[length - 1] << 6)) ==
- IRArgoAC::calcChecksum(state, length);
+template
+bool IRArgoACBase::validChecksum(const uint8_t state[],
+ const uint16_t length) {
+ return (getChecksum(state, length) == calcChecksum(state, length));
}
-/// Update the checksum for the internal state.
-void IRArgoAC::checksum(void) {
- uint8_t sum = IRArgoAC::calcChecksum(_.raw, kArgoStateLength);
- // Append sum to end of array
- // Set const part of checksum bit 10
- _.raw[10] = 0b00000010;
- _.Sum = sum;
+
+#if SEND_ARGO
+/// Send the current internal state as an IR message.
+/// @param[in] repeat Nr. of times the message will be repeated.
+template
+void IRArgoACBase::send(const uint16_t repeat) {
+ _irsend.sendArgo(getRaw(), getRawByteLength(), repeat);
}
-/// Reset the internals of the object to a known good state.
-void IRArgoAC::stateReset(void) {
- for (uint8_t i = 0; i < kArgoStateLength; i++) _.raw[i] = 0x0;
-
- // Argo Message. Store MSB left.
- // Default message:
- _.raw[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble
- _.raw[1] = 0b11110101; // LSB first: 0b10101111; //const preamble
- // Keep payload 2-9 at zero
- _.raw[10] = 0b00000010; // Const 01
- _.Sum = 0;
-
- off();
- setTemp(20);
- setRoomTemp(25);
- setMode(kArgoAuto);
- setFan(kArgoFanAuto);
+/// @cond
+/// Send the current internal state as an IR message.
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] repeat Nr. of times the message will be repeated.
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::send(const uint16_t repeat) {
+ _irsend.sendArgoWREM3(getRaw(), getRawByteLength(), repeat);
}
+/// @endcond
+
+
+/// Send current room temperature for the iFeel feature as a silent IR
+/// message (no acknowledgement from the device) (WREM2)
+/// @param[in] degrees The temperature in degrees celsius.
+/// @param[in] repeat Nr. of times the message will be repeated.
+void IRArgoAC::sendSensorTemp(const uint8_t degrees, const uint16_t repeat) {
+ const uint8_t temp = std::max(std::min(degrees, kArgoMaxRoomTemp),
+ kArgoTempDelta) - kArgoTempDelta;
+ const uint8_t check = kArgoSensorCheck + temp;
+
+ ArgoProtocol data;
+ _stateReset(&data, argoIrMessageType_t::IFEEL_TEMP_REPORT);
+ data.SensorT = temp;
+ data.CheckHi = check >> 5;
+ data.CheckLo = check;
+ data.Fixed = kArgoSensorFixed;
+ _checksum(&data);
+ uint16_t msgLen = getRawByteLength(data,
+ argoIrMessageType_t::IFEEL_TEMP_REPORT);
+
+ _irsend.sendArgo(data.raw, msgLen, repeat);
+}
+
+/// Send current room temperature for the iFeel feature as a silent IR
+/// message (no acknowledgement from the device) (WREM3)
+/// @param[in] degrees The temperature in degrees celsius.
+/// @param[in] repeat Nr. of times the message will be repeated.
+void IRArgoAC_WREM3::sendSensorTemp(const uint8_t degrees,
+ const uint16_t repeat) {
+ const uint8_t temp = std::max(std::min(degrees, kArgoMaxRoomTemp),
+ kArgoTempDelta) - kArgoTempDelta;
+ ArgoProtocolWREM3 data = {};
+ _stateReset(&data, argoIrMessageType_t::IFEEL_TEMP_REPORT);
+ data.SensorT = temp;
+ _checksum(&data);
+ uint16_t msgLen = getRawByteLength(data,
+ argoIrMessageType_t::IFEEL_TEMP_REPORT);
+ _irsend.sendArgoWREM3(data.raw, msgLen, repeat);
+}
+#endif
+
/// 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* IRArgoAC::getRaw(void) {
+template
+uint8_t* IRArgoACBase::getRaw(void) {
checksum(); // Ensure correct bit array before returning
return _.raw;
}
+
/// Set the raw state of the object.
/// @param[in] state The raw state from the native IR message.
-void IRArgoAC::setRaw(const uint8_t state[]) {
- std::memcpy(_.raw, state, kArgoStateLength);
+/// @param[in] length The length of raw state in bytes.
+template
+void IRArgoACBase::setRaw(const uint8_t state[], const uint16_t length) {
+ std::memcpy(_.raw, state, length);
+ _messageType = getMessageType(state, length);
+ _length = length;
}
/// Set the internal state to have the power on.
-void IRArgoAC::on(void) { setPower(true); }
+template
+void IRArgoACBase::on(void) { setPower(true); }
/// Set the internal state to have the power off.
-void IRArgoAC::off(void) { setPower(false); }
+template
+void IRArgoACBase::off(void) { setPower(false); }
+/// @cond
/// Set the internal state to have the desired power.
/// @param[in] on The desired power state.
-void IRArgoAC::setPower(const bool on) {
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::setPower(const bool on) {
_.Power = on;
}
+/// @endcond
+
+/// @brief Set the internal state to have the desired power.
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] on The desired power state.
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::setPower(const bool on) {
+ if (_messageType == argoIrMessageType_t::TIMER_COMMAND) {
+ _.timer.IsOn = on;
+ } else {
+ _.Power = on;
+ }
+}
/// Get the power setting from the internal state.
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
/// @return A boolean indicating the power setting.
-bool IRArgoAC::getPower(void) const { return _.Power; }
+/// @relates IRArgoACBase\
+template<>
+bool IRArgoACBase::getPower(void) const { return _.Power; }
+
+/// Get the power setting from the internal state.
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @return A boolean indicating the power setting.
+/// @relates IRArgoACBase\
+template<>
+bool IRArgoACBase::getPower(void) const {
+ if (_messageType == argoIrMessageType_t::TIMER_COMMAND) {
+ return _.timer.IsOn;
+ }
+ return _.Power;
+}
/// Control the current Max setting. (i.e. Turbo)
/// @param[in] on The desired setting.
-void IRArgoAC::setMax(const bool on) {
+template
+void IRArgoACBase::setMax(const bool on) {
_.Max = on;
}
/// Is the Max (i.e. Turbo) setting on?
/// @return The current value.
-bool IRArgoAC::getMax(void) const { return _.Max; }
+template
+bool IRArgoACBase::getMax(void) const { return _.Max; }
/// Set the temperature.
/// @param[in] degrees The temperature in degrees celsius.
/// @note Sending 0 equals +4
-void IRArgoAC::setTemp(const uint8_t degrees) {
+template
+void IRArgoACBase::setTemp(const uint8_t degrees) {
uint8_t temp = std::max(kArgoMinTemp, degrees);
// delta 4 degrees. "If I want 12 degrees, I need to send 8"
temp = std::min(kArgoMaxTemp, temp) - kArgoTempDelta;
// mask out bits
- // argo[13] & 0x00000100; // mask out ON/OFF Bit
_.Temp = temp;
}
/// Get the current temperature setting.
/// @return The current setting for temp. in degrees celsius.
-uint8_t IRArgoAC::getTemp(void) const {
+template
+uint8_t IRArgoACBase::getTemp(void) const {
return _.Temp + kArgoTempDelta;
}
+
+/// @brief Get the current fan mode setting as a strongly typed value (WREM2).
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @return The current fan mode.
+/// @relates IRArgoACBase\
+template<>
+argoFan_t IRArgoACBase::getFanEx(void) const {
+ switch (_.Fan) {
+ case kArgoFan3:
+ return argoFan_t::FAN_HIGHEST;
+ case kArgoFan2:
+ return argoFan_t::FAN_MEDIUM;
+ case kArgoFan1:
+ return argoFan_t::FAN_LOWEST;
+ case kArgoFanAuto:
+ return argoFan_t::FAN_AUTO;
+ default:
+ return static_cast(_.Fan);
+ }
+}
+
+/// @brief Get the current fan mode setting as a strongly typed value (WREM3).
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @return The current fan mode.
+/// @relates IRArgoACBase\
+template<>
+argoFan_t IRArgoACBase::getFanEx(void) const {
+ return static_cast(_.Fan);
+}
+
+/// @cond
+/// Set the desired fan mode (WREM2).
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] fan The desired fan speed.
+/// @note Only a subset of fan speeds are supported (1|2|3|Auto)
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::setFan(argoFan_t fan) {
+ switch (fan) {
+ case argoFan_t::FAN_AUTO:
+ _.Fan = kArgoFanAuto;
+ break;
+ case argoFan_t::FAN_HIGHEST:
+ case argoFan_t::FAN_HIGH:
+ _.Fan = kArgoFan3;
+ break;
+ case argoFan_t::FAN_MEDIUM:
+ case argoFan_t::FAN_LOW:
+ _.Fan = kArgoFan2;
+ break;
+ case argoFan_t::FAN_LOWER:
+ case argoFan_t::FAN_LOWEST:
+ _.Fan = kArgoFan1;
+ break;
+ default:
+ uint8_t raw_value = static_cast(fan); // 2-bit value, per def.
+ if ((raw_value & 0b11) == raw_value) {
+ // Outside of known value range, but matches field length
+ // Let's assume the caller knows what they're doing and pass it through
+ _.Fan = raw_value;
+ } else {
+ _.Fan = kArgoFanAuto;
+ }
+ break;
+ }
+}
+/// @endcond
+
+/// Set the desired fan mode (WREM3).
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] fan The desired fan speed.
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::setFan(argoFan_t fan) {
+ switch (fan) {
+ case argoFan_t::FAN_AUTO:
+ case argoFan_t::FAN_HIGHEST:
+ case argoFan_t::FAN_HIGH:
+ case argoFan_t::FAN_MEDIUM:
+ case argoFan_t::FAN_LOW:
+ case argoFan_t::FAN_LOWER:
+ case argoFan_t::FAN_LOWEST:
+ _.Fan = static_cast(fan);
+ break;
+ default:
+ _.Fan = static_cast(argoFan_t::FAN_AUTO);
+ break;
+ }
+}
+
/// Set the speed of the fan.
+/// @deprecated
/// @param[in] fan The desired setting.
void IRArgoAC::setFan(const uint8_t fan) {
_.Fan = std::min(fan, kArgoFan3);
}
/// Get the current fan speed setting.
+/// @deprecated
/// @return The current fan speed.
uint8_t IRArgoAC::getFan(void) const {
return _.Fan;
}
-/// Set the flap position. i.e. Swing.
+/// @brief Get Flap (VSwing) value as a strongly-typed value
+/// @note This @c getFlapEx() method has been introduced to be able to retain
+/// old implementation of @c getFlap() for @c IRArgoAc which used uint8_t
+/// @return Flap setting
+template
+argoFlap_t IRArgoACBase::getFlapEx(void) const {
+ return static_cast(_.Flap);
+}
+
+/// Set the desired flap mode
+/// @param[in] flap The desired flap mode.
+template
+void IRArgoACBase::setFlap(argoFlap_t flap) {
+ uint8_t raw_value = static_cast(flap);
+ if ((raw_value & 0b111) == raw_value) {
+ // Outside of known value range, but matches field length
+ // Let's assume the caller knows what they're doing and pass it through
+ _.Flap = raw_value;
+ } else {
+ _.Flap = static_cast(argoFlap_t::FLAP_AUTO);
+ }
+}
+
+/// Set the flap position. i.e. Swing. (WREM2)
/// @warning Not yet working!
+/// @deprecated
/// @param[in] flap The desired setting.
void IRArgoAC::setFlap(const uint8_t flap) {
- flap_mode = flap;
+ setFlap(static_cast(flap));
// TODO(kaschmo): set correct bits for flap mode
}
-/// Get the flap position. i.e. Swing.
+/// Get the flap position. i.e. Swing. (WREM2)
/// @warning Not yet working!
+/// @deprecated
/// @return The current flap setting.
-uint8_t IRArgoAC::getFlap(void) const { return flap_mode; }
-
-/// Get the current operation mode setting.
-/// @return The current operation mode.
-uint8_t IRArgoAC::getMode(void) const {
- return _.Mode;
+uint8_t IRArgoAC::getFlap(void) const {
+ return _.Flap;
}
+/// Get the current operation mode setting.
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @return The current operation mode.
+/// @note This @c getModeEx() method has been introduced to be able to retain
+/// old implementation of @c getMode() for @c IRArgoAc which used uint8_t
+/// @relates IRArgoACBase\
+template<>
+argoMode_t IRArgoACBase::getModeEx(void) const {
+ switch (_.Mode) {
+ case kArgoCool:
+ return argoMode_t::COOL;
+ case kArgoDry:
+ return argoMode_t::DRY;
+ case kArgoAuto:
+ return argoMode_t::AUTO;
+ case kArgoHeat:
+ return argoMode_t::HEAT;
+ case kArgoOff: // Modelling "FAN" as "OFF", for the lack of better constant
+ return argoMode_t::FAN;
+ case kArgoHeatAuto:
+ default:
+ return static_cast(_.Mode);
+ }
+}
+
+/// Get the current operation mode setting.
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function.
+/// @return The current operation mode.
+/// @note This @c getModeEx() method has been introduced to be able to retain
+/// old implementation of @c getMode() for @c IRArgoAc which used uint8_t
+/// @relates IRArgoACBase\
+template<>
+argoMode_t IRArgoACBase::getModeEx(void) const {
+ return static_cast(_.Mode);
+}
+
+/// @cond
/// Set the desired operation mode.
/// @param[in] mode The desired operation mode.
-void IRArgoAC::setMode(const uint8_t mode) {
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::setMode(argoMode_t mode) {
+ switch (mode) {
+ case argoMode_t::COOL:
+ _.Mode = static_cast(kArgoCool);
+ break;
+ case argoMode_t::DRY:
+ _.Mode = static_cast(kArgoDry);
+ break;
+ case argoMode_t::HEAT:
+ _.Mode = static_cast(kArgoHeat);
+ break;
+ case argoMode_t::FAN:
+ _.Mode = static_cast(kArgoOff);
+ break;
+ case argoMode_t::AUTO:
+ _.Mode = static_cast(kArgoAuto);
+ break;
+ default:
+ uint8_t raw_value = static_cast(mode);
+ if ((raw_value & 0b111) == raw_value) {
+ // Outside of known value range, but matches field length
+ // Let's assume the caller knows what they're doing and pass it through
+ _.Mode = raw_value;
+ } else {
+ _.Mode = static_cast(kArgoAuto);
+ }
+ break;
+ }
+}
+/// @endcond
+
+/// @brief Set the desired operation mode.
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] mode The desired operation mode.
+/// @relates IRArgoACBase\
+template<>
+void IRArgoACBase::setMode(argoMode_t mode) {
+ switch (mode) {
+ case argoMode_t::COOL:
+ case argoMode_t::DRY:
+ case argoMode_t::HEAT:
+ case argoMode_t::FAN:
+ case argoMode_t::AUTO:
+ _.Mode = static_cast(mode);
+ break;
+ default:
+ _.Mode = static_cast(argoMode_t::AUTO);
+ break;
+ }
+}
+
+/// @brief Set the desired operation mode.
+/// @deprecated
+/// @param mode The desired operation mode.
+void IRArgoAC::setMode(uint8_t mode) {
switch (mode) {
case kArgoCool:
case kArgoDry:
@@ -235,118 +919,309 @@ void IRArgoAC::setMode(const uint8_t mode) {
case kArgoHeat:
case kArgoHeatAuto:
_.Mode = mode;
- return;
+ break;
default:
_.Mode = kArgoAuto;
+ break;
}
}
+/// @brief Get the current operation mode
+/// @deprecated
+/// @return The current operation mode
+uint8_t IRArgoAC::getMode() const { return _.Mode;}
+
+argoFan_t IRArgoAC_WREM3::getFan(void) const { return getFanEx(); }
+argoFlap_t IRArgoAC_WREM3::getFlap(void) const { return getFlapEx(); }
+argoMode_t IRArgoAC_WREM3::getMode(void) const { return getModeEx(); }
+
/// Turn on/off the Night mode. i.e. Sleep.
/// @param[in] on The desired setting.
-void IRArgoAC::setNight(const bool on) {
- _.Night = on;
-}
+template
+void IRArgoACBase::setNight(const bool on) { _.Night = on; }
/// Get the status of Night mode. i.e. Sleep.
/// @return true if on, false if off.
-bool IRArgoAC::getNight(void) const { return _.Night; }
+template
+bool IRArgoACBase::getNight(void) const { return _.Night; }
+
+/// @brief Turn on/off the Economy mode (lowered power mode)
+/// @param[in] on The desired setting.
+void IRArgoAC_WREM3::setEco(const bool on) { _.Eco = on; }
+
+/// @brief Get the status of Economy function
+/// @return true if on, false if off.
+bool IRArgoAC_WREM3::getEco(void) const { return _.Eco; }
+
+/// @brief Turn on/off the Filter mode (not supported by Argo Ulisse)
+/// @param[in] on The desired setting.
+void IRArgoAC_WREM3::setFilter(const bool on) { _.Filter = on; }
+
+/// @brief Get status of the filter function
+/// @return true if on, false if off.
+bool IRArgoAC_WREM3::getFilter(void) const { return _.Filter; }
+
+/// @brief Turn on/off the device Lights (LED)
+/// @param[in] on The desired setting.
+void IRArgoAC_WREM3::setLight(const bool on) { _.Light = on; }
+
+/// @brief Get status of device lights
+/// @return true if on, false if off.
+bool IRArgoAC_WREM3::getLight(void) const { return _.Light; }
+
+/// @brief Set the IR channel on which to communicate
+/// @param[in] channel The desired IR channel.
+void IRArgoAC_WREM3::setChannel(const uint8_t channel) {
+ _.IrChannel = std::min(channel, kArgoMaxChannel);
+}
+
+/// @brief Get the currently set transmission channel
+/// @return Channel number
+uint8_t IRArgoAC_WREM3::getChannel(void) const { return _.IrChannel;}
+
+/// @brief Set the config data to send
+/// Valid only for @c argoIrMessageType_t::CONFIG_PARAM_SET message
+/// @param paramId The param ID
+/// @param value The value of the parameter
+void IRArgoAC_WREM3::setConfigEntry(const uint8_t paramId,
+ const uint8_t value) {
+ _.config.Key = paramId;
+ _.config.Value = value;
+}
+
+/// @brief Get the config entry previously set
+/// @return Key->value pair (paramID: value)
+std::pair IRArgoAC_WREM3::getConfigEntry(void) const {
+ return std::make_pair(_.config.Key, _.config.Value);
+}
/// Turn on/off the iFeel mode.
/// @param[in] on The desired setting.
-void IRArgoAC::setiFeel(const bool on) {
- _.iFeel = on;
-}
+template
+void IRArgoACBase::setiFeel(const bool on) { _.iFeel = on; }
/// Get the status of iFeel mode.
/// @return true if on, false if off.
-bool IRArgoAC::getiFeel(void) const { return _.iFeel; }
+template
+bool IRArgoACBase::getiFeel(void) const { return _.iFeel; }
-/// Set the time for the A/C
-/// @warning Not yet working!
-void IRArgoAC::setTime(void) {
- // TODO(kaschmo): use function call from checksum to set time first
+/// @brief Set the message type of the next command (setting this resets state)
+/// @param msgType The message type to set
+template
+void IRArgoACBase::setMessageType(const argoIrMessageType_t msgType) {
+ stateReset(msgType);
+}
+
+/// @brief Get the message type
+/// @return Message type currently set
+template
+argoIrMessageType_t IRArgoACBase::getMessageType(void) const {
+ return _messageType;
}
/// Set the value for the current room temperature.
+/// @note Depending on message type - this will set `sensor` or `roomTemp` value
/// @param[in] degrees The temperature in degrees celsius.
-void IRArgoAC::setRoomTemp(const uint8_t degrees) {
+template
+void IRArgoACBase::setSensorTemp(const uint8_t degrees) {
uint8_t temp = std::min(degrees, kArgoMaxRoomTemp);
temp = std::max(temp, kArgoTempDelta) - kArgoTempDelta;
- _.RoomTemp = temp;
+ if (getMessageType() == argoIrMessageType_t::IFEEL_TEMP_REPORT) {
+ _.SensorT = temp;
+ } else {
+ _.RoomTemp = temp;
+ }
}
/// Get the currently stored value for the room temperature setting.
+/// @note Depending on message type - this will get `sensor` or `roomTemp` value
/// @return The current setting for the room temp. in degrees celsius.
-uint8_t IRArgoAC::getRoomTemp(void) const {
+template
+uint8_t IRArgoACBase::getSensorTemp(void) const {
+ if (getMessageType() == argoIrMessageType_t::IFEEL_TEMP_REPORT) {
+ return _.SensorT + kArgoTempDelta;
+ }
return _.RoomTemp + kArgoTempDelta;
}
+/// @brief Convert a stdAc::ac_command_t enum into its native message type.
+/// @param command The enum to be converted.
+/// @return The native equivalent of the enum.
+template
+argoIrMessageType_t IRArgoACBase::convertCommand(
+ const stdAc::ac_command_t command) {
+ switch (command) {
+ case stdAc::ac_command_t::kSensorTempReport:
+ return argoIrMessageType_t::IFEEL_TEMP_REPORT;
+ case stdAc::ac_command_t::kTimerCommand:
+ return argoIrMessageType_t::TIMER_COMMAND;
+ case stdAc::ac_command_t::kConfigCommand:
+ return argoIrMessageType_t::CONFIG_PARAM_SET;
+ case stdAc::ac_command_t::kControlCommand:
+ default:
+ return argoIrMessageType_t::AC_CONTROL;
+ }
+}
+
/// 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 IRArgoAC::convertMode(const stdAc::opmode_t mode) {
+template
+argoMode_t IRArgoACBase::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool:
- return kArgoCool;
+ return argoMode_t::COOL;
case stdAc::opmode_t::kHeat:
- return kArgoHeat;
+ return argoMode_t::HEAT;
case stdAc::opmode_t::kDry:
- return kArgoDry;
- case stdAc::opmode_t::kOff:
- return kArgoOff;
- // No fan mode.
- default:
- return kArgoAuto;
+ return argoMode_t::DRY;
+ case stdAc::opmode_t::kFan:
+ return argoMode_t::FAN;
+ case stdAc::opmode_t::kAuto:
+ default: // No off mode.
+ return argoMode_t::AUTO;
}
}
/// 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 IRArgoAC::convertFan(const stdAc::fanspeed_t speed) {
+template
+argoFan_t IRArgoACBase::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
+ return argoFan_t::FAN_LOWEST;
case stdAc::fanspeed_t::kLow:
- return kArgoFan1;
+ return argoFan_t::FAN_LOWER;
case stdAc::fanspeed_t::kMedium:
- return kArgoFan2;
+ return argoFan_t::FAN_LOW;
+ case stdAc::fanspeed_t::kMediumHigh:
+ return argoFan_t::FAN_MEDIUM;
case stdAc::fanspeed_t::kHigh:
+ return argoFan_t::FAN_HIGH;
case stdAc::fanspeed_t::kMax:
- return kArgoFan3;
+ return argoFan_t::FAN_HIGHEST;
default:
- return kArgoFanAuto;
+ return argoFan_t::FAN_AUTO;
}
}
/// 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 IRArgoAC::convertSwingV(const stdAc::swingv_t position) {
+template
+argoFlap_t IRArgoACBase::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest:
- return kArgoFlapFull;
+ return argoFlap_t::FLAP_1;
case stdAc::swingv_t::kHigh:
- return kArgoFlap5;
+ return argoFlap_t::FLAP_2;
+ case stdAc::swingv_t::kUpperMiddle:
+ return argoFlap_t::FLAP_3;
case stdAc::swingv_t::kMiddle:
- return kArgoFlap4;
+ return argoFlap_t::FLAP_4;
case stdAc::swingv_t::kLow:
- return kArgoFlap3;
+ return argoFlap_t::FLAP_5;
case stdAc::swingv_t::kLowest:
- return kArgoFlap1;
+ return argoFlap_t::FLAP_6;
+ case stdAc::swingv_t::kOff: // This is abusing the semantics quite a bit
+ return argoFlap_t::FLAP_FULL;
+ case stdAc::swingv_t::kAuto:
default:
- return kArgoFlapAuto;
+ return argoFlap_t::FLAP_AUTO;
+ }
+}
+
+/// @cond
+/// Convert a native flap mode into its stdAc equivalent (WREM2).
+/// @note This is a full specialization for @c ArgoProtocol type and while
+/// it semantically belongs to @c IrArgoAC class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] position The native setting to be converted.
+/// @return The stdAc equivalent of the native setting.
+/// @relates IRArgoACBase\
+template<>
+stdAc::swingv_t IRArgoACBase::toCommonSwingV(
+ const uint8_t position) {
+ switch (position) {
+ case kArgoFlapFull:
+ return stdAc::swingv_t::kHighest;
+ case kArgoFlap5:
+ return stdAc::swingv_t::kHigh;
+ case kArgoFlap4:
+ return stdAc::swingv_t::kMiddle;
+ case kArgoFlap3:
+ return stdAc::swingv_t::kLow;
+ case kArgoFlap1:
+ return stdAc::swingv_t::kLowest;
+ default:
+ return stdAc::swingv_t::kAuto;
+ }
+}
+/// @endcond
+
+/// Convert a native flap mode into its stdAc equivalent (WREM3).
+/// @note This is a full specialization for @c ArgoProtocolWREM3 type and while
+/// it semantically belongs to @c IrArgoAC_WREM3 class impl., it has *not*
+/// been pushed there, to avoid having to use a virtual function
+/// @param[in] position The native setting to be converted.
+/// @return The stdAc equivalent of the native setting.
+/// @relates IRArgoACBase\
+template<>
+stdAc::swingv_t IRArgoACBase::toCommonSwingV(
+ const uint8_t position) {
+ switch (static_cast(position)) {
+ case argoFlap_t::FLAP_FULL:
+ return stdAc::swingv_t::kOff;
+ case argoFlap_t::FLAP_6:
+ return stdAc::swingv_t::kHighest;
+ case argoFlap_t::FLAP_5:
+ return stdAc::swingv_t::kHigh;
+ case argoFlap_t::FLAP_4:
+ return stdAc::swingv_t::kUpperMiddle;
+ case argoFlap_t::FLAP_3:
+ return stdAc::swingv_t::kMiddle;
+ case argoFlap_t::FLAP_2:
+ return stdAc::swingv_t::kLow;
+ case argoFlap_t::FLAP_1:
+ return stdAc::swingv_t::kLowest;
+ case argoFlap_t::FLAP_AUTO:
+ default:
+ return stdAc::swingv_t::kAuto;
+ }
+}
+
+/// Convert a native message type into its stdAc equivalent.
+/// @param[in] command The native setting to be converted.
+/// @return The stdAc equivalent of the native setting.
+template
+stdAc::ac_command_t IRArgoACBase::toCommonCommand(
+ const argoIrMessageType_t command) {
+ switch (command) {
+ case argoIrMessageType_t::AC_CONTROL:
+ return stdAc::ac_command_t::kControlCommand;
+ case argoIrMessageType_t::IFEEL_TEMP_REPORT:
+ return stdAc::ac_command_t::kSensorTempReport;
+ case argoIrMessageType_t::TIMER_COMMAND:
+ return stdAc::ac_command_t::kTimerCommand;
+ case argoIrMessageType_t::CONFIG_PARAM_SET:
+ return stdAc::ac_command_t::kConfigCommand;
+ default:
+ return stdAc::ac_command_t::kControlCommand;
}
}
/// 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 IRArgoAC::toCommonMode(const uint8_t mode) {
+template
+stdAc::opmode_t IRArgoACBase::toCommonMode(const argoMode_t mode) {
switch (mode) {
- case kArgoCool: return stdAc::opmode_t::kCool;
- case kArgoHeat: return stdAc::opmode_t::kHeat;
- case kArgoDry: return stdAc::opmode_t::kDry;
- // No fan mode.
+ case argoMode_t::COOL: return stdAc::opmode_t::kCool;
+ case argoMode_t::DRY : return stdAc::opmode_t::kDry;
+ case argoMode_t::FAN : return stdAc::opmode_t::kFan;
+ case argoMode_t::HEAT : return stdAc::opmode_t::kHeat;
+ case argoMode_t::AUTO : return stdAc::opmode_t::kAuto;
default: return stdAc::opmode_t::kAuto;
}
}
@@ -354,11 +1229,16 @@ stdAc::opmode_t IRArgoAC::toCommonMode(const uint8_t mode) {
/// 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 IRArgoAC::toCommonFanSpeed(const uint8_t speed) {
+template
+stdAc::fanspeed_t IRArgoACBase::toCommonFanSpeed(const argoFan_t speed) {
switch (speed) {
- case kArgoFan3: return stdAc::fanspeed_t::kMax;
- case kArgoFan2: return stdAc::fanspeed_t::kMedium;
- case kArgoFan1: return stdAc::fanspeed_t::kMin;
+ case argoFan_t::FAN_AUTO: return stdAc::fanspeed_t::kAuto;
+ case argoFan_t::FAN_HIGHEST: return stdAc::fanspeed_t::kMax;
+ case argoFan_t::FAN_HIGH: return stdAc::fanspeed_t::kHigh;
+ case argoFan_t::FAN_MEDIUM: return stdAc::fanspeed_t::kMediumHigh;
+ case argoFan_t::FAN_LOW: return stdAc::fanspeed_t::kMedium;
+ case argoFan_t::FAN_LOWER: return stdAc::fanspeed_t::kLow;
+ case argoFan_t::FAN_LOWEST: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
@@ -368,15 +1248,18 @@ stdAc::fanspeed_t IRArgoAC::toCommonFanSpeed(const uint8_t speed) {
stdAc::state_t IRArgoAC::toCommon(void) const {
stdAc::state_t result{};
result.protocol = decode_type_t::ARGO;
+ result.model = argo_ac_remote_model_t::SAC_WREM2;
+ result.command = toCommonCommand(_messageType);
result.power = _.Power;
- result.mode = toCommonMode(_.Mode);
+ result.mode = toCommonMode(getModeEx());
result.celsius = true;
result.degrees = getTemp();
- result.fanspeed = toCommonFanSpeed(_.Fan);
+ result.sensorTemperature = getSensorTemp();
+ result.iFeel = getiFeel();
+ result.fanspeed = toCommonFanSpeed(getFanEx());
result.turbo = _.Max;
result.sleep = _.Night ? 0 : -1;
// Not supported.
- result.model = -1; // Not supported.
result.swingv = stdAc::swingv_t::kOff;
result.swingh = stdAc::swingh_t::kOff;
result.light = false;
@@ -389,71 +1272,413 @@ stdAc::state_t IRArgoAC::toCommon(void) const {
return result;
}
-/// Convert the current internal state into a human readable string.
-/// @return A human readable string.
-String IRArgoAC::toString(void) const {
- String result = "";
- result.reserve(100); // Reserve some heap for the string to reduce fragging.
- result += addBoolToString(_.Power, kPowerStr, false);
- result += addIntToString(_.Mode, kModeStr);
- result += kSpaceLBraceStr;
- switch (_.Mode) {
- case kArgoAuto:
- result += kAutoStr;
- break;
- case kArgoCool:
- result += kCoolStr;
- break;
- case kArgoHeat:
- result += kHeatStr;
- break;
- case kArgoDry:
- result += kDryStr;
- break;
- case kArgoHeatAuto:
- result += kHeatStr;
- result += ' ';
- result += kAutoStr;
- break;
- case kArgoOff:
- result += kOffStr;
- break;
- default:
- result += kUnknownStr;
+/// Convert the current internal state into its stdAc::state_t equivalent.
+/// @return The stdAc equivalent of the native settings.
+stdAc::state_t IRArgoAC_WREM3::toCommon(void) const {
+ stdAc::state_t result{};
+ result.protocol = decode_type_t::ARGO;
+ result.model = argo_ac_remote_model_t::SAC_WREM3;
+ result.command = toCommonCommand(_messageType);
+ result.power = getPower();
+ result.mode = toCommonMode(getModeEx());
+ result.celsius = true;
+ result.degrees = getTemp();
+ result.sensorTemperature = getSensorTemp();
+ result.iFeel = getiFeel();
+ result.fanspeed = toCommonFanSpeed(getFanEx());
+ result.turbo = _.Max;
+ result.swingv = toCommonSwingV(_.Flap);
+ result.light = getLight();
+ result.filter = getFilter();
+ result.econo = getEco();
+ result.quiet = getNight();
+ result.beep = (_messageType != argoIrMessageType_t::IFEEL_TEMP_REPORT);
+
+ result.clock = -1;
+ result.sleep = _.Night ? 0 : -1;
+ if (_messageType == argoIrMessageType_t::TIMER_COMMAND) {
+ result.clock = getCurrentTimeMinutes();
+ result.sleep = getDelayTimerMinutes();
}
- result += ')';
- result += addIntToString(_.Fan, kFanStr);
- result += kSpaceLBraceStr;
- switch (_.Fan) {
- case kArgoFanAuto:
- result += kAutoStr;
- break;
- case kArgoFan3:
- result += kMaxStr;
- break;
- case kArgoFan1:
- result += kMinStr;
- break;
- case kArgoFan2:
- result += kMedStr;
- break;
- default:
- result += kUnknownStr;
- }
- result += ')';
- result += addTempToString(getTemp());
- result += kCommaSpaceStr;
- result += kRoomStr;
- result += ' ';
- result += addTempToString(getRoomTemp(), true, false);
- result += addBoolToString(_.Max, kMaxStr);
- result += addBoolToString(_.iFeel, kIFeelStr);
- result += addBoolToString(_.Night, kNightStr);
+
+ // Not supported.
+ result.swingh = stdAc::swingh_t::kOff;
+ result.clean = false;
+
+
return result;
}
+
+namespace {
+ /// @brief Short-hand for casting enum to its underlying storage type
+ /// @tparam E The type of enum
+ /// @param e Enum value
+ /// @return Type of underlying value
+ template
+ constexpr typename std::underlying_type::type to_underlying(E e) noexcept {
+ return static_cast::type>(e);
+ }
+}
+
+/// Convert the current internal state into a human readable string (WREM2).
+/// @return A human readable string.
+String IRArgoAC::toString(void) const {
+ String result = "";
+ result.reserve(118); // Reserve some heap for the string to reduce fragging.
+ // E.g.: Model: 1 (WREM2), Power: On, Mode: 0 (Cool), Fan: 0 (Auto),
+ // Temp: 20C, Room Temp: 21C, Max: On, IFeel: On, Night: On
+ result += addModelToString(decode_type_t::ARGO,
+ argo_ac_remote_model_t::SAC_WREM2, false);
+ if (_messageType == argoIrMessageType_t::IFEEL_TEMP_REPORT) {
+ result += addIntToString(getSensorTemp(), kSensorTempStr);
+ result += 'C';
+ } else {
+ result += addBoolToString(_.Power, kPowerStr);
+ result += addIntToString(_.Mode, kModeStr);
+ result += kSpaceLBraceStr;
+ switch (_.Mode) {
+ case kArgoAuto:
+ result += kAutoStr;
+ break;
+ case kArgoCool:
+ result += kCoolStr;
+ break;
+ case kArgoHeat:
+ result += kHeatStr;
+ break;
+ case kArgoDry:
+ result += kDryStr;
+ break;
+ case kArgoHeatAuto:
+ result += kHeatStr;
+ result += ' ';
+ result += kAutoStr;
+ break;
+ case kArgoOff:
+ result += kOffStr;
+ break;
+ default:
+ result += kUnknownStr;
+ }
+ result += ')';
+ result += addIntToString(_.Fan, kFanStr);
+ result += kSpaceLBraceStr;
+ switch (_.Fan) {
+ case kArgoFanAuto:
+ result += kAutoStr;
+ break;
+ case kArgoFan3:
+ result += kMaxStr;
+ break;
+ case kArgoFan1:
+ result += kMinStr;
+ break;
+ case kArgoFan2:
+ result += kMedStr;
+ break;
+ default:
+ result += kUnknownStr;
+ }
+ result += ')';
+ result += addTempToString(getTemp());
+ result += addTempToString(getSensorTemp(), true, true, true);
+ result += addBoolToString(_.Max, kMaxStr);
+ result += addBoolToString(_.iFeel, kIFeelStr);
+ result += addBoolToString(_.Night, kNightStr);
+ }
+ return result;
+}
+
+/// @brief Set current clock (as minutes, counted from 0:00)
+/// E.g. 13:38 becomes 818 (13*60+38)
+/// @param currentTimeMinutes Current time (in minutes)
+void IRArgoAC_WREM3::setCurrentTimeMinutes(uint16_t currentTimeMinutes) {
+ uint16_t time = std::min(currentTimeMinutes, static_cast(23*60+59));
+ _.timer.CurrentTimeHi = (time >> 4);
+ _.timer.CurrentTimeLo = (time & 0b1111);
+}
+
+/// @brief Retrieve current time
+/// @return Current time as minutes from 0:00
+uint16_t IRArgoAC_WREM3::getCurrentTimeMinutes(void) const {
+ return (_.timer.CurrentTimeHi << 4) + _.timer.CurrentTimeLo;
+}
+
+/// @brief Set current day of week
+/// @param dayOfWeek Current day of week
+void IRArgoAC_WREM3::setCurrentDayOfWeek(argoWeekday dayOfWeek) {
+ uint8_t day = std::min(to_underlying(dayOfWeek),
+ to_underlying(argoWeekday::SATURDAY));
+ _.timer.CurrentWeekdayHi = (day >> 1);
+ _.timer.CurrentWeekdayLo = (day & 0b1);
+}
+
+/// @brief Get current day of week
+/// @return Current day of week
+argoWeekday IRArgoAC_WREM3::getCurrentDayOfWeek(void) const {
+ return static_cast((_.timer.CurrentWeekdayHi << 1) +
+ _.timer.CurrentWeekdayLo);
+}
+
+/// @brief Set timer type
+/// @param timerType Timer type to use OFF | DELAY | SCHEDULE<1|2|3>
+/// @note 2 timer types supported: delay | schedule timer
+/// - @c DELAY_TIMER requires setting @c setDelayTimerMinutes
+/// and @c setCurrentTimeMinutes and (optionally) @c setCurrentDayOfWeek
+/// - @c SCHEDULE_TIMER requires setting:
+/// @c setScheduleTimerStartMinutes
+/// @c setScheduleTimerStopMinutes
+/// @c setScheduleTimerActiveDays
+/// as well as current time *and* day
+/// @c setCurrentTimeMinutes and @c setCurrentDayOfWeek
+void IRArgoAC_WREM3::setTimerType(const argoTimerType_t timerType) {
+ if (timerType > argoTimerType_t::SCHEDULE_TIMER_3) {
+ _.timer.TimerType = to_underlying(argoTimerType_t::NO_TIMER);
+ } else {
+ _.timer.TimerType = to_underlying(timerType);
+ }
+}
+
+/// @brief Get currently set timer type
+/// @return Timer type
+argoTimerType_t IRArgoAC_WREM3::getTimerType(void) const {
+ return static_cast(_.timer.TimerType);
+}
+
+/// @brief Set delay timer delay in minutes (10-minute increments only)
+/// Max is 1190 (19h50m)
+/// @note The delay timer also accepts current device state: set by @c setPower
+/// @param delayMinutes Delay minutes
+void IRArgoAC_WREM3::setDelayTimerMinutes(const uint16_t delayMinutes) {
+ const uint16_t DELAY_TIMER_MAX = 19*60+50;
+ uint16_t time = std::min(delayMinutes, DELAY_TIMER_MAX);
+
+ // only full 10 minute increments are allowed
+ time = static_cast((time / 10.0) + 0.5) * 10;
+
+ _.timer.DelayTimeHi = (time >> 6);
+ _.timer.DelayTimeLo = (time & 0b111111);
+}
+
+/// @brief Get current delay timer value
+/// @return Delay timer value (in minutes)
+uint16_t IRArgoAC_WREM3::getDelayTimerMinutes(void) const {
+ return (_.timer.DelayTimeHi << 6) + _.timer.DelayTimeLo;
+}
+
+/// @brief Set schedule timer on time (time when the device should turn on)
+/// (10-minute increments only)
+/// @param startTimeMinutes Time when the device should turn itself on
+/// expressed as # of minutes counted from 0:00
+/// The value is in 10-minute increments (rounded)
+/// E.g. 13:38 becomes 820 (13:40 in minutes)
+void IRArgoAC_WREM3::setScheduleTimerStartMinutes(
+ const uint16_t startTimeMinutes) {
+ const uint16_t SCHEDULE_TIMER_MAX = 23*60+50;
+ uint16_t time = std::min(startTimeMinutes, SCHEDULE_TIMER_MAX);
+
+ // only full 10 minute increments are allowed
+ time = static_cast((time / 10.0) + 0.5) * 10;
+
+ _.timer.TimerStartHi = (time >> 3);
+ _.timer.TimerStartLo = (time & 0b111);
+}
+
+/// @brief Get schedule timer ON time
+/// @return Schedule on time (as # of minutes from 0:00)
+uint16_t IRArgoAC_WREM3::getScheduleTimerStartMinutes(void) const {
+ return (_.timer.TimerStartHi << 3) + _.timer.TimerStartLo;
+}
+
+/// @brief Set schedule timer off time (time when the device should turn off)
+/// (10-minute increments only)
+/// @param stopTimeMinutes Time when the device should turn itself off
+/// expressed as # of minutes counted from 0:00
+/// The value is in 10-minute increments (rounded)
+/// E.g. 13:38 becomes 820 (13:40 in minutes)
+void IRArgoAC_WREM3::setScheduleTimerStopMinutes(
+ const uint16_t stopTimeMinutes) {
+ const uint16_t SCHEDULE_TIMER_MAX = 23*60+50;
+ uint16_t time = std::min(stopTimeMinutes, SCHEDULE_TIMER_MAX);
+
+ // only full 10 minute increments are allowed
+ time = static_cast((time / 10.0) + 0.5) * 10;
+
+ _.timer.TimerEndHi = (time >> 8);
+ _.timer.TimerEndLo = (time & 0b11111111);
+}
+
+/// @brief Get schedule timer OFF time
+/// @return Schedule off time (as # of minutes from 0:00)
+uint16_t IRArgoAC_WREM3::getScheduleTimerStopMinutes(void) const {
+ return (_.timer.TimerEndHi << 8) + _.timer.TimerEndLo;
+}
+
+/// @brief Get the days when shedule timer shall be active (as bitmap)
+/// @return Days when schedule timer is active, as raw bitmap type
+/// where bit[0] is Sunday, bit[1] -> Monday, ...
+uint8_t IRArgoAC_WREM3::getTimerActiveDaysBitmap(void) const {
+ return (_.timer.TimerActiveDaysHi << 5) + _.timer.TimerActiveDaysLo;
+}
+
+/// @brief Set the days when the schedule timer shall be active
+/// @param days A set of days when the timer shall run
+void IRArgoAC_WREM3::setScheduleTimerActiveDays(
+ const std::set& days) {
+ uint8_t daysBitmap = 0;
+ for (const argoWeekday& day : days) {
+ daysBitmap |= (0b1 << to_underlying(day));
+ }
+ _.timer.TimerActiveDaysHi = (daysBitmap >> 5);
+ _.timer.TimerActiveDaysLo = (daysBitmap & 0b11111);
+}
+
+/// @brief Get the days when shedule timer shall be active (as set)
+/// @return Days when the schedule timer runs
+std::set IRArgoAC_WREM3::getScheduleTimerActiveDays(void) const {
+ std::set result = {};
+ uint8_t daysBitmap = getTimerActiveDaysBitmap();
+ for (uint8_t i = to_underlying(argoWeekday::SUNDAY);
+ i <= to_underlying(argoWeekday::SATURDAY);
+ ++i) {
+ if (((daysBitmap >> i) & 0b1) == 0b1) {
+ result.insert(static_cast(i));
+ }
+ }
+ return result;
+}
+
+/// @brief Get device model
+/// @return Device model
+argo_ac_remote_model_t IRArgoAC_WREM3::getModel() const {
+ return argo_ac_remote_model_t::SAC_WREM3;
+}
+
+namespace {
+ String commandTypeToString(argoIrMessageType_t type, uint8_t channel) {
+ String result = irutils::irCommandTypeToString(to_underlying(type),
+ to_underlying(argoIrMessageType_t::AC_CONTROL),
+ to_underlying(argoIrMessageType_t::IFEEL_TEMP_REPORT),
+ to_underlying(argoIrMessageType_t::TIMER_COMMAND),
+ to_underlying(argoIrMessageType_t::CONFIG_PARAM_SET));
+ result += irutils::channelToString(channel);
+ result += kColonSpaceStr;
+ return result;
+ }
+} // namespace
+
+/// Convert the current internal state into a human readable string (WREM3).
+/// @return A human readable string.
+String IRArgoAC_WREM3::toString(void) const {
+ String result = "";
+ result.reserve(190); // Reserve some heap for the string to reduce fragging.
+ // E.g.: Command[CH#0]: Model: 2 (WREM3), Power: On, Mode: 1 (Cool),
+ // Temp: 22C, Room: 26C, Fan: 0 (Auto), Swing(V): 7 (Breeze),
+ // IFeel: Off, Night: Off, Econo: Off, Max: Off, Filter: Off, Light: On
+ // Temp: 20C, Room Temp: 21C, Max: On, IFeel: On, Night: On
+
+ argoIrMessageType_t commandType = this->getMessageType();
+ argo_ac_remote_model_t model = getModel();
+
+ result += commandTypeToString(commandType, getChannel());
+ result += addModelToString(decode_type_t::ARGO, model, false);
+
+ switch (commandType) {
+ case argoIrMessageType_t::IFEEL_TEMP_REPORT:
+ result += addTempToString(getSensorTemp(), true, true, true);
+ break;
+
+ case argoIrMessageType_t::AC_CONTROL:
+ result += addBoolToString(getPower(), kPowerStr);
+ result += addModeToString(to_underlying(getModeEx()),
+ to_underlying(argoMode_t::AUTO),
+ to_underlying(argoMode_t::COOL),
+ to_underlying(argoMode_t::HEAT),
+ to_underlying(argoMode_t::DRY),
+ to_underlying(argoMode_t::FAN));
+ result += addTempToString(getTemp());
+ result += addTempToString(getSensorTemp(), true, true, true);
+ result += addFanToString(to_underlying(getFanEx()),
+ to_underlying(argoFan_t::FAN_HIGH),
+ to_underlying(argoFan_t::FAN_LOWER),
+ to_underlying(argoFan_t::FAN_AUTO),
+ to_underlying(argoFan_t::FAN_LOWEST),
+ to_underlying(argoFan_t::FAN_LOW),
+ to_underlying(argoFan_t::FAN_HIGHEST),
+ to_underlying(argoFan_t::FAN_MEDIUM));
+ result += addSwingVToString(to_underlying(getFlapEx()),
+ to_underlying(argoFlap_t::FLAP_AUTO),
+ to_underlying(argoFlap_t::FLAP_1),
+ to_underlying(argoFlap_t::FLAP_2),
+ to_underlying(argoFlap_t::FLAP_3),
+ to_underlying(argoFlap_t::FLAP_4), -1,
+ to_underlying(argoFlap_t::FLAP_5),
+ to_underlying(argoFlap_t::FLAP_6), -1, -1,
+ to_underlying(argoFlap_t::FLAP_FULL), -1);
+ result += addBoolToString(getiFeel(), kIFeelStr);
+ result += addBoolToString(getNight(), kNightStr);
+ result += addBoolToString(getEco(), kEconoStr);
+ result += addBoolToString(getMax(), kMaxStr); // Turbo
+ result += addBoolToString(getFilter(), kFilterStr);
+ result += addBoolToString(getLight(), kLightStr);
+ break;
+
+ case argoIrMessageType_t::TIMER_COMMAND:
+ result += addBoolToString(_.timer.IsOn, kPowerStr);
+ result += addTimerModeToString(to_underlying(getTimerType()),
+ to_underlying(argoTimerType_t::NO_TIMER),
+ to_underlying(argoTimerType_t::DELAY_TIMER),
+ to_underlying(argoTimerType_t::SCHEDULE_TIMER_1),
+ to_underlying(argoTimerType_t::SCHEDULE_TIMER_2),
+ to_underlying(argoTimerType_t::SCHEDULE_TIMER_3));
+ result += addLabeledString(minsToString(getCurrentTimeMinutes()),
+ kClockStr);
+ result += addDayToString(to_underlying(getCurrentDayOfWeek()));
+ switch (getTimerType()) {
+ case argoTimerType_t::NO_TIMER:
+ result += addLabeledString(kOffStr, kTimerStr);
+ break;
+ case argoTimerType_t::DELAY_TIMER:
+ result += addLabeledString(minsToString(getDelayTimerMinutes()),
+ kTimerStr);
+ break;
+ default:
+ result += addLabeledString(minsToString(getScheduleTimerStartMinutes()),
+ kOnTimerStr);
+ result += addLabeledString(minsToString(getScheduleTimerStopMinutes()),
+ kOffTimerStr);
+
+ result += addLabeledString(daysBitmaskToString(
+ getTimerActiveDaysBitmap()), kTimerActiveDaysStr);
+ break;
+ }
+ break;
+
+ case argoIrMessageType_t::CONFIG_PARAM_SET:
+ result += addIntToString(_.config.Key, kKeyStr);
+ result += addIntToString(_.config.Value, kValueStr);
+ break;
+ }
+
+ return result;
+}
+
+/// @brief Check if raw ARGO state starts with valid WREM3 preamble
+/// @param state The state bytes
+/// @param length Length of state in bytes
+/// @return True if state starts wiht valid WREM3 preamble, False otherwise
+bool IRArgoAC_WREM3::hasValidPreamble(const uint8_t state[],
+ const uint16_t length) {
+ if (length < 1) {
+ return false;
+ }
+ uint8_t preamble = state[0] & 0x0F;
+ return preamble == kArgoWrem3Preamble;
+}
+
#if DECODE_ARGO
-/// Decode the supplied Argo message.
+/// Decode the supplied Argo message (WREM2).
/// Status: BETA / Probably works.
/// @param[in,out] results Ptr to the data to decode & where to store the decode
/// result.
@@ -480,7 +1705,9 @@ bool IRrecv::decodeArgo(decode_results *results, uint16_t offset,
// Compliance
// Verify we got a valid checksum.
- if (strict && !IRArgoAC::validChecksum(results->state)) return false;
+ if (strict && !IRArgoAC::validChecksum(results->state, kArgoStateLength)) {
+ return false;
+ }
// Success
results->decode_type = decode_type_t::ARGO;
results->bits = nbits;
@@ -489,4 +1716,107 @@ bool IRrecv::decodeArgo(decode_results *results, uint16_t offset,
// is a union data type.
return true;
}
+
+/// Decode the supplied Argo message (WREM3).
+/// Status: Confirmed working w/ Argo 13 ECO (WREM-3)
+/// @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.
+/// @note This decoder is separate from @c decodeArgo to maintain backwards
+/// compatibility. Contrary to WREM2, this expects a footer and gap!
+bool IRrecv::decodeArgoWREM3(decode_results *results, uint16_t offset,
+ const uint16_t nbits,
+ const bool strict) {
+ if (strict
+ && nbits != kArgo3AcControlStateLength * 8
+ && nbits != kArgo3ConfigStateLength * 8
+ && nbits != kArgo3iFeelReportStateLength * 8
+ && nbits != kArgo3TimerStateLength * 8) {
+ return false;
+ }
+
+ uint16_t bytesRead = matchGeneric(results->rawbuf + offset, results->state,
+ results->rawlen - offset, nbits,
+ kArgoHdrMark, kArgoHdrSpace,
+ kArgoBitMark, kArgoOneSpace,
+ kArgoBitMark, kArgoZeroSpace,
+ kArgoBitMark, kArgoGap, // difference vs decodeArgo
+ true, _tolerance, 0,
+ false);
+ if (!bytesRead) {
+ return false;
+ }
+
+ // If 'strict', assert it is a valid WREM-3 'model' protocolar message
+ // vs. just 'any ARGO'
+ if (strict &&
+ !IRArgoAC_WREM3::isValidWrem3Message(results->state, nbits, true)) {
+ return false;
+ }
+
+ // Success: Matched ARGO protocol and WREM3-model
+ // Note that unfortunately decode_type does not allow to persist model...
+ // so we will be re-detecting it later :)
+ results->decode_type = decode_type_t::ARGO;
+ results->bits = nbits;
+ // No need to record the state as we stored it as we decoded it.
+ // As we use result->state, we don't record value, address, or command as it
+ // is a union data type.
+ return true;
+}
+
+/// @brief Detects if an ARGO protocol message is a WREM-3 sub-type (model)
+/// @param state The raw IR decore state
+/// @param nbits The length of @c state **IN BITS**
+/// @param verifyChecksum Whether to perform checksum verification
+/// @return True if the message is a WREM-3 one
+bool IRArgoAC_WREM3::isValidWrem3Message(const uint8_t state[],
+ const uint16_t nbits,
+ bool verifyChecksum) {
+ if ((nbits % 8) != 0) {
+ return false; // WREM-3 protocol always has a full byte length commands
+ }
+ uint16_t stateLengthBytes = std::min(static_cast(nbits / 8),
+ kStateSizeMax);
+ if (!IRArgoAC_WREM3::hasValidPreamble(state, stateLengthBytes)) {
+ return false;
+ }
+
+ argoIrMessageType_t messageType = IRArgoACBase
+ ::getMessageType(state, stateLengthBytes);
+
+ switch (messageType) {
+ case argoIrMessageType_t::AC_CONTROL :
+ if (stateLengthBytes != kArgo3AcControlStateLength) { return false; }
+ break;
+ case argoIrMessageType_t::CONFIG_PARAM_SET:
+ if (stateLengthBytes != kArgo3ConfigStateLength) { return false; }
+ break;
+ case argoIrMessageType_t::TIMER_COMMAND:
+ if (stateLengthBytes != kArgo3TimerStateLength) { return false; }
+ break;
+ case argoIrMessageType_t::IFEEL_TEMP_REPORT:
+ if (stateLengthBytes != kArgo3iFeelReportStateLength) { return false; }
+ break;
+ default:
+ return false;
+ }
+
+ // Compliance: Verify we got a valid checksum.
+ if (verifyChecksum &&
+ !IRArgoAC_WREM3::validChecksum(state, stateLengthBytes)) {
+ return false;
+ }
+ return true;
+}
+
#endif // DECODE_ARGO
+
+
+// force template instantiation
+template class IRArgoACBase;
+template class IRArgoACBase;
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.h
index 8ef7bb386..ba804f93c 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Argo.h
@@ -1,13 +1,18 @@
// Copyright 2017 Schmolders
+// Copyright 2022 crankyoldgit
+// Copyright 2022 Mateusz Bronk (mbronk)
/// @file
/// @brief Support for Argo Ulisse 13 DCI Mobile Split ACs.
// Supports:
-// Brand: Argo, Model: Ulisse 13 DCI Mobile Split A/C
+// Brand: Argo, Model: Ulisse 13 DCI Mobile Split A/C [WREM2 remote]
+// Brand: Argo, Model: Ulisse Eco Mobile Split A/C (Wifi) [WREM3 remote]
#ifndef IR_ARGO_H_
#define IR_ARGO_H_
+#include
+#include
#ifndef UNIT_TEST
#include
#endif
@@ -20,14 +25,14 @@
// ARGO Ulisse DCI
-/// Native representation of a Argo A/C message.
+/// Native representation of a Argo A/C message for WREM-2 remote.
union ArgoProtocol {
uint8_t raw[kArgoStateLength]; ///< The state in native IR code form
struct {
// Byte 0
- uint64_t :8; // Typically 0b00110101
+ uint64_t Pre1 :8; // Typically 0b00110101
// Byte 1
- uint64_t :8; // Typically 0b10101111
+ uint64_t Pre2 :8; // Typically 0b10101111
// Byte 2~4
uint64_t :3;
uint64_t Mode :3;
@@ -57,17 +62,143 @@ union ArgoProtocol {
uint32_t :1; // const 0
uint32_t iFeel :1;
// Byte 10~11
- uint32_t :2; // const 01
+ uint32_t Post :2;
uint32_t Sum :8; // straddle byte 10 and 11
uint32_t :6;
};
+ struct {
+ // Byte 0-1
+ uint8_t :8;
+ uint8_t :8;
+ // Byte 2-3
+ uint8_t CheckHi :3;
+ uint8_t SensorT :5;
+ uint8_t Fixed :3; // Typically 0b011
+ uint8_t CheckLo :5;
+ };
};
-// Constants. Store MSB left.
+/// Native representation of A/C IR message for WREM-3 remote
+/// @note The remote sends 4 different IR command types, varying in length
+/// and methods of checksum calculation
+/// - [0b00] Regular A/C command (change operation mode) - 6-byte
+/// - [0b01] iFeel Temperature report - 2-byte
+/// - [0b10] Timer command - 9-byte
+/// - [0b11] Config command - 4-byte
+/// @note The 1st 2 structures are unnamed for compat. with @c ArgoProtocol
+/// 1st byte definition is a header common across all commands though
+union ArgoProtocolWREM3 {
+ uint8_t raw[kArgoStateLength]; ///< The state in native IR code form
+ struct {
+ // Byte 0 (same definition across the union)
+ uint8_t Pre1 :4; /// Preamble: 0b1011 @ref kArgoWrem3Preamble
+ uint8_t IrChannel :2; /// 0..3 range
+ uint8_t IrCommandType :2; /// @ref argoIrMessageType_t
+ // Byte 1
+ uint8_t RoomTemp :5; // in Celsius, range: 4..35 (offset by -4[*C])
+ uint8_t Mode :3; /// @ref argoMode_t
+ // Byte 2
+ uint8_t Temp :5; // in Celsius, range: 10..32 (offset by -4[*C])
+ uint8_t Fan :3; /// @ref argoFan_t
+ // Byte3
+ uint8_t Flap :3; /// SwingV @ref argoFlap_t
+ uint8_t Power :1;
+ uint8_t iFeel :1;
+ uint8_t Night :1;
+ uint8_t Eco :1;
+ uint8_t Max :1; ///< a.k.a. Turbo
+ // Byte4
+ uint8_t Filter :1;
+ uint8_t Light :1;
+ uint8_t Post1 :6; /// Unknown, always 0b110000 (TempScale?)
+ // Byte5
+ uint8_t Sum :8; /// Checksum
+ };
+ struct {
+ // Byte 0 (same definition across the union)
+ uint8_t :8; // {Pre1 | IrChannel | IrCommandType}
+ // Byte 1
+ uint8_t SensorT :5; // in Celsius, range: 4..35 (offset by -4[*C])
+ uint8_t CheckHi :3; // Checksum (short)
+ };
+ struct Timer {
+ // Byte 0 (same definition across the union)
+ uint8_t : 8; // {Pre1 | IrChannel | IrCommandType}
+ // Byte 1
+ uint8_t IsOn : 1;
+ uint8_t TimerType : 3;
+ uint8_t CurrentTimeLo : 4;
+ // Byte 2
+ uint8_t CurrentTimeHi : 7;
+ uint8_t CurrentWeekdayLo : 1;
+ // Byte 3
+ uint8_t CurrentWeekdayHi : 2;
+ uint8_t DelayTimeLo : 6;
+ // Byte 4
+ uint8_t DelayTimeHi : 5;
+ uint8_t TimerStartLo : 3;
+ // Byte 5
+ uint8_t TimerStartHi : 8;
+ // Byte 6
+ uint8_t TimerEndLo : 8;
+ // Byte 7
+ uint8_t TimerEndHi : 3;
+ uint8_t TimerActiveDaysLo : 5; // Bitmap (LSBit is Sunday)
+ // Byte 8
+ uint8_t TimerActiveDaysHi : 2; // Bitmap (LSBit is Sunday)
+ uint8_t Post1 : 1; // Unknown, always 1
+ uint8_t Checksum : 5;
+ } timer;
+ struct Config {
+ uint8_t :8; // Byte 0 {Pre1 | IrChannel | IrCommandType}
+ uint8_t Key :8; // Byte 1
+ uint8_t Value :8; // Byte 2
+ uint8_t Checksum :8; // Byte 3
+ } config;
+};
-const uint8_t kArgoHeatBit = 0b00100000;
+// Constants (WREM-2). Store MSB left.
+const uint8_t kArgoHeatBit = 0b00100000;
+const uint8_t kArgoPreamble1 = 0b10101100;
+const uint8_t kArgoPreamble2 = 0b11110101;
+const uint8_t kArgoPost = 0b00000010;
-// Mode 0b00111000
+// Constants (generic)
+const uint16_t kArgoFrequency = 38000; // Hz
+// Temp
+const uint8_t kArgoTempDelta = 4;
+const uint8_t kArgoMaxRoomTemp = 35; // Celsius
+const uint8_t kArgoMinTemp = 10; // Celsius delta +4
+const uint8_t kArgoMaxTemp = 32; // Celsius
+const uint8_t kArgoMaxChannel = 3;
+
+
+/// @brief IR message type (determines the payload part of IR command)
+/// @note Raw values match WREM-3 protocol, but the enum is used in generic
+/// context
+/// @note WREM-3 remote supports all commands separately, whereas
+/// WREM-2 (allegedly) only has the @c AC_CONTROL and @c IFEEL_TEMP_REPORT
+/// (timers are part of @c AC_CONTROL command), and there's no config.
+enum class argoIrMessageType_t : uint8_t {
+ AC_CONTROL = 0b00,
+ IFEEL_TEMP_REPORT = 0b01,
+ TIMER_COMMAND = 0b10, // WREM-3 only (WREM-2 has it under AC_CONTROL)
+ CONFIG_PARAM_SET = 0b11 // WREM-3 only
+};
+
+/// @brief A/C operation mode
+/// @note Raw values match WREM-3 protocol, but the enum is used in generic
+/// context
+enum class argoMode_t : uint8_t {
+ COOL = 0b001,
+ DRY = 0b010,
+ HEAT = 0b011,
+ FAN = 0b100,
+ AUTO = 0b101
+};
+
+// Raw mode definitions for WREM-2 remote
+// (not wraped into a ns nor enum for backwards-compat.)
const uint8_t kArgoCool = 0b000;
const uint8_t kArgoDry = 0b001;
const uint8_t kArgoAuto = 0b010;
@@ -77,19 +208,42 @@ const uint8_t kArgoHeatAuto = 0b101;
// ?no idea what mode that is
const uint8_t kArgoHeatBlink = 0b110;
-// Fan 0b00011000
+/// @brief Fan speed
+/// @note Raw values match WREM-3 protocol, but the enum is used in generic
+/// context
+enum class argoFan_t : uint8_t {
+ FAN_AUTO = 0b000,
+ FAN_LOWEST = 0b001,
+ FAN_LOWER = 0b010,
+ FAN_LOW = 0b011,
+ FAN_MEDIUM = 0b100,
+ FAN_HIGH = 0b101,
+ FAN_HIGHEST = 0b110
+};
+
+// Raw fan speed definitions for WREM-2 remote
+// (not wraped into a ns nor enum for backwards-compat.)
const uint8_t kArgoFanAuto = 0; // 0b00
const uint8_t kArgoFan1 = 1; // 0b01
const uint8_t kArgoFan2 = 2; // 0b10
const uint8_t kArgoFan3 = 3; // 0b11
-// Temp
-const uint8_t kArgoTempDelta = 4;
-const uint8_t kArgoMaxRoomTemp = 35; // Celsius
-const uint8_t kArgoMinTemp = 10; // Celsius delta +4
-const uint8_t kArgoMaxTemp = 32; // Celsius
+/// @brief Flap position (swing-V)
+/// @note Raw values match WREM-3 protocol, but the enum is used in generic
+/// context
+enum class argoFlap_t : uint8_t {
+ FLAP_AUTO = 0,
+ FLAP_1 = 1, // Highest
+ FLAP_2 = 2,
+ FLAP_3 = 3,
+ FLAP_4 = 4,
+ FLAP_5 = 5,
+ FLAP_6 = 6, // Lowest
+ FLAP_FULL = 7
+};
-// Flap/SwingV
+// Raw Flap/SwingV definitions for WREM-2 remote
+// (not wraped into a ns nor enum for backwards-compat.)
const uint8_t kArgoFlapAuto = 0;
const uint8_t kArgoFlap1 = 1;
const uint8_t kArgoFlap2 = 2;
@@ -123,22 +277,61 @@ const uint8_t kArgoFlapFull = 7;
#define ARGO_FLAP_FULL kArgoFlapFull
-/// Class for handling detailed Argo A/C messages.
-class IRArgoAC {
+/// @brief Timer type to set (for @c argoIrMessageType_t::TIMER_COMMAND)
+/// @note Raw values match WREM-3 protocol
+enum class argoTimerType_t : uint8_t {
+ NO_TIMER = 0b000,
+ DELAY_TIMER = 0b001,
+ SCHEDULE_TIMER_1 = 0b010,
+ SCHEDULE_TIMER_2 = 0b011,
+ SCHEDULE_TIMER_3 = 0b100
+};
+
+/// @brief Day type to set (for @c argoIrMessageType_t::TIMER_COMMAND)
+/// @note Raw values match WREM-3 protocol
+enum class argoWeekday : uint8_t {
+ SUNDAY = 0b000,
+ MONDAY = 0b001,
+ TUESDAY = 0b010,
+ WEDNESDAY = 0b011,
+ THURSDAY = 0b100,
+ FRIDAY = 0b101,
+ SATURDAY = 0b110
+};
+
+
+
+/// @brief Base class for handling *common* support for Argo remote protocols
+/// (functionality is shared across WREM-2 and WREM-3 IR protocols)
+/// @note This class uses static polymorphism and full template specializations
+/// when required, to avoid a performance penalty of doing v-table lookup.
+/// 2 instantiations are forced in impl. file: for @c ArgoProtocol and
+/// @c ArgoProtocolWREM3
+/// @note This class is abstract (though does not declare a pure-virtual fn.
+/// for abovementioned reasons), and instead declares protected c-tor
+/// @tparam ARGO_PROTOCOL_T The Raw device protocol/message used
+template
+class IRArgoACBase {
+#ifndef UNIT_TEST // A less cloggy way of expressing FRIEND_TEST(...)
+
+ protected:
+#else
+
public:
- explicit IRArgoAC(const uint16_t pin, const bool inverted = false,
+#endif
+ explicit IRArgoACBase(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
+ public:
#if SEND_ARGO
void send(const uint16_t repeat = kArgoDefaultRepeat);
- void sendSensorTemp(const uint8_t temp,
- const uint16_t repeat = kArgoDefaultRepeat);
/// 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_ARGO
+
void begin(void);
void on(void);
void off(void);
@@ -149,14 +342,20 @@ class IRArgoAC {
void setTemp(const uint8_t degrees);
uint8_t getTemp(void) const;
- void setFan(const uint8_t fan);
- uint8_t getFan(void) const;
+ void setSensorTemp(const uint8_t degrees);
+ uint8_t getSensorTemp(void) const;
- void setFlap(const uint8_t flap);
- uint8_t getFlap(void) const;
+ void setFan(const argoFan_t fan);
+ void setFanEx(const argoFan_t fan) { setFan(fan); }
+ argoFan_t getFanEx(void) const; ///< `-Ex` for backw. compat w/ @c IRArgoAC
- void setMode(const uint8_t mode);
- uint8_t getMode(void) const;
+ void setFlap(const argoFlap_t flap);
+ void setFlapEx(const argoFlap_t flap) { setFlap(flap); }
+ argoFlap_t getFlapEx(void) const; ///< `-Ex` for backw. compat w/ @c IRArgoAC
+
+ void setMode(const argoMode_t mode);
+ void setModeEx(const argoMode_t mode) { setMode(mode); }
+ argoMode_t getModeEx(void) const; ///< `-Ex` for backw. compat w/ @c IRArgoAC
void setMax(const bool on);
bool getMax(void) const;
@@ -167,41 +366,156 @@ class IRArgoAC {
void setiFeel(const bool on);
bool getiFeel(void) const;
- void setTime(void);
- void setRoomTemp(const uint8_t degrees);
- uint8_t getRoomTemp(void) const;
+ void setMessageType(const argoIrMessageType_t msgType);
+ argoIrMessageType_t getMessageType(void) const;
+ static argoIrMessageType_t getMessageType(const uint8_t state[],
+ const uint16_t length);
uint8_t* getRaw(void);
- void setRaw(const uint8_t state[]);
- static uint8_t calcChecksum(const uint8_t state[],
- const uint16_t length = kArgoStateLength);
- static bool validChecksum(const uint8_t state[],
- const uint16_t length = kArgoStateLength);
- static uint8_t convertMode(const stdAc::opmode_t mode);
- static uint8_t convertFan(const stdAc::fanspeed_t speed);
- static uint8_t convertSwingV(const stdAc::swingv_t position);
- static stdAc::opmode_t toCommonMode(const uint8_t mode);
- static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
- stdAc::state_t toCommon(void) const;
- String toString(void) const;
+ uint16_t getRawByteLength() const;
+ static uint16_t getStateLengthForIrMsgType(argoIrMessageType_t type);
+ void setRaw(const uint8_t state[], const uint16_t length);
+
+ static bool validChecksum(const uint8_t state[], const uint16_t length);
+
+ static argoMode_t convertMode(const stdAc::opmode_t mode);
+ static argoFan_t convertFan(const stdAc::fanspeed_t speed);
+ static argoFlap_t convertSwingV(const stdAc::swingv_t position);
+ static argoIrMessageType_t convertCommand(const stdAc::ac_command_t command);
+
+ protected:
+ void _stateReset(ARGO_PROTOCOL_T *state, argoIrMessageType_t messageType
+ = argoIrMessageType_t::AC_CONTROL);
+ void stateReset(argoIrMessageType_t messageType
+ = argoIrMessageType_t::AC_CONTROL);
+ void _checksum(ARGO_PROTOCOL_T *state);
+ void checksum(void);
+ static uint16_t getRawByteLength(const ARGO_PROTOCOL_T& raw,
+ argoIrMessageType_t messageTypeHint = argoIrMessageType_t::AC_CONTROL);
+ static uint8_t calcChecksum(const uint8_t state[], const uint16_t length);
+ static uint8_t getChecksum(const uint8_t state[], const uint16_t length);
+
+ static stdAc::opmode_t toCommonMode(const argoMode_t mode);
+ static stdAc::fanspeed_t toCommonFanSpeed(const argoFan_t speed);
+ static stdAc::swingv_t toCommonSwingV(const uint8_t position);
+ static stdAc::ac_command_t toCommonCommand(const argoIrMessageType_t command);
+
+ // Attributes
+ ARGO_PROTOCOL_T _; ///< The raw protocol data
+ uint16_t _length = kArgoStateLength;
+ argoIrMessageType_t _messageType = argoIrMessageType_t::AC_CONTROL;
+
#ifndef UNIT_TEST
- private:
+ protected:
IRsend _irsend; ///< instance of the IR send class
#else
+
+ public:
/// @cond IGNORE
IRsendTest _irsend; ///< instance of the testing IR send class
/// @endcond
#endif
- // # of bytes per command
- ArgoProtocol _;
- void stateReset(void);
- void checksum(void);
+};
- // Attributes
- uint8_t flap_mode;
- uint8_t heat_mode;
- uint8_t cool_mode;
+/// @brief Supports Argo A/C SAC-WREM2 IR remote protocol
+class IRArgoAC : public IRArgoACBase {
+ public:
+ explicit IRArgoAC(const uint16_t pin, const bool inverted = false,
+ const bool use_modulation = true);
+
+ #if SEND_ARGO
+ void sendSensorTemp(const uint8_t degrees,
+ const uint16_t repeat = kArgoDefaultRepeat);
+ #endif // SEND_ARGO
+
+ String toString(void) const;
+ stdAc::state_t toCommon(void) const;
+
+ using IRArgoACBase::setMode;
+ void setMode(const uint8_t mode); /// @deprecated, for backwards-compat.
+ uint8_t getMode(void) const; /// @deprecated, for backwards-compat.
+
+ using IRArgoACBase::setFan;
+ void setFan(const uint8_t fan); /// @deprecated, for backwards-compat.
+ uint8_t getFan(void) const; /// @deprecated, for backwards-compat.
+
+ using IRArgoACBase::setFlap;
+ void setFlap(const uint8_t flap); /// @deprecated, for backwards-compat.
+ uint8_t getFlap(void) const; /// @deprecated, for backwards-compat.
+};
+
+/// @brief Supports Argo A/C SAC-WREM3 IR remote protocol
+class IRArgoAC_WREM3 : public IRArgoACBase {
+ public:
+ explicit IRArgoAC_WREM3(const uint16_t pin, const bool inverted = false,
+ const bool use_modulation = true);
+
+ #if SEND_ARGO
+ void sendSensorTemp(const uint8_t degrees,
+ const uint16_t repeat = kArgoDefaultRepeat);
+ #endif // SEND_ARGO
+
+ argo_ac_remote_model_t getModel(void) const;
+
+
+ argoFan_t getFan(void) const;
+ argoFlap_t getFlap(void) const;
+ argoMode_t getMode(void) const;
+
+ void setEco(const bool on);
+ bool getEco(void) const;
+
+ void setFilter(const bool on);
+ bool getFilter(void) const;
+
+ void setLight(const bool on);
+ bool getLight(void) const;
+
+ void setChannel(const uint8_t channel);
+ uint8_t getChannel(void) const;
+
+ void setConfigEntry(const uint8_t paramId, const uint8_t value);
+ std::pair getConfigEntry(void) const;
+
+ void setCurrentTimeMinutes(uint16_t currentTimeMinutes);
+ uint16_t getCurrentTimeMinutes(void) const;
+
+ void setCurrentDayOfWeek(argoWeekday dayOfWeek);
+ argoWeekday getCurrentDayOfWeek(void) const;
+
+ void setTimerType(const argoTimerType_t timerType);
+ argoTimerType_t getTimerType(void) const;
+
+ void setDelayTimerMinutes(const uint16_t delayMinutes);
+ uint16_t getDelayTimerMinutes(void) const;
+
+ void setScheduleTimerStartMinutes(const uint16_t startTimeMinutes);
+ uint16_t getScheduleTimerStartMinutes(void) const;
+ // uint16_t getTimerXStartMinutes(void) const
+
+ void setScheduleTimerStopMinutes(const uint16_t stopTimeMinutes);
+ uint16_t getScheduleTimerStopMinutes(void) const;
+ // uint16_t getTimerXStopMinutes(void) const;
+
+
+ void setScheduleTimerActiveDays(const std::set& days);
+ std::set getScheduleTimerActiveDays(void) const;
+ uint8_t getTimerActiveDaysBitmap(void) const;
+
+ using IRArgoACBase::getMessageType;
+ static argoIrMessageType_t getMessageType(const ArgoProtocolWREM3& raw);
+
+ String toString(void) const;
+ stdAc::state_t toCommon(void) const;
+
+ static bool hasValidPreamble(const uint8_t state[], const uint16_t length);
+
+ public:
+#if DECODE_ARGO
+ static bool isValidWrem3Message(const uint8_t state[], const uint16_t nbits,
+ bool verifyChecksum = true);
+#endif
};
#endif // IR_ARGO_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.cpp
index 42b45f9ab..ef51cc439 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.cpp
@@ -46,6 +46,15 @@ const uint16_t kCarrierAc64OneSpace = 1736;
const uint16_t kCarrierAc64ZeroSpace = 615;
const uint32_t kCarrierAc64Gap = kDefaultMessageGap; // A guess.
+//< @see: https://github.com/crankyoldgit/IRremoteESP8266/issues/1943#issue-1519570772
+const uint16_t kCarrierAc84HdrMark = 5850;
+const uint16_t kCarrierAc84Zero = 1175;
+const uint16_t kCarrierAc84One = 430;
+const uint16_t kCarrierAc84HdrSpace = kCarrierAc84Zero;
+const uint32_t kCarrierAc84Gap = kDefaultMessageGap; // A guess.
+const uint8_t kCarrierAc84ExtraBits = 4;
+const uint8_t kCarrierAc84ExtraTolerance = 5;
+
const uint16_t kCarrierAc128HdrMark = 4600;
const uint16_t kCarrierAc128HdrSpace = 2600;
const uint16_t kCarrierAc128Hdr2Mark = 9300;
@@ -645,3 +654,94 @@ bool IRrecv::decodeCarrierAC128(decode_results *results, uint16_t offset,
return true;
}
#endif // DECODE_CARRIER_AC128
+
+#if SEND_CARRIER_AC84
+/// Send a Carroer A/C 84 Bit formatted message.
+/// Status: BETA / Untested but probably works.
+/// @param[in] data The message to be sent.
+/// @param[in] nbytes The byte size of the message being sent.
+/// @param[in] repeat The number of times the command is to be repeated.
+void IRsend::sendCarrierAC84(const uint8_t data[], const uint16_t nbytes,
+ const uint16_t repeat) {
+ // Protocol uses a constant bit time encoding.
+ for (uint16_t r = 0; r <= repeat; r++) {
+ if (nbytes) {
+ // The least significant `kCarrierAc84ExtraBits` bits of the first byte
+ sendGeneric(kCarrierAc84HdrMark, kCarrierAc84HdrSpace, // Header
+ kCarrierAc84Zero, kCarrierAc84One, // Data
+ kCarrierAc84One, kCarrierAc84Zero,
+ 0, 0, // No footer
+ GETBITS64(data[0], 0, kCarrierAc84ExtraBits),
+ kCarrierAc84ExtraBits,
+ 38000, false, 0, 33);
+ // The rest of the data.
+ sendGeneric(0, 0, // No Header
+ kCarrierAc84Zero, kCarrierAc84One, // Data
+ kCarrierAc84One, kCarrierAc84Zero,
+ kCarrierAc84Zero, kDefaultMessageGap, // Footer
+ data + 1, nbytes - 1, 38000, false, 0, 33);
+ }
+ }
+}
+#endif // SEND_CARRIER_AC84
+
+#if DECODE_CARRIER_AC84
+/// Decode the supplied Carroer A/C 84 Bit formatted 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.
+bool IRrecv::decodeCarrierAC84(decode_results *results, uint16_t offset,
+ const uint16_t nbits, const bool strict) {
+ // Check if we have enough data to even possibly match.
+ if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
+ return false; // Can't possibly be a valid Carrier message.
+ // Compliance check.
+ if (strict && nbits != kCarrierAc84Bits) return false;
+
+ // This decoder expects to decode an unusual number of bits. Check before we
+ // start.
+ if (nbits % 8 != kCarrierAc84ExtraBits) return false;
+
+ uint64_t data = 0;
+ uint16_t used = 0;
+
+ // Header + Data (kCarrierAc84ExtraBits only)
+ used = matchGenericConstBitTime(results->rawbuf + offset, &data,
+ results->rawlen - offset,
+ kCarrierAc84ExtraBits,
+ // Header (None)
+ kCarrierAc84HdrMark, kCarrierAc84HdrSpace,
+ // Data
+ kCarrierAc84Zero, kCarrierAc84One,
+ // No Footer
+ 0, 0,
+ false,
+ _tolerance + kCarrierAc84ExtraTolerance,
+ kMarkExcess, false);
+ if (!used) return false;
+ // Stuff the captured data so far into the first byte of the state.
+ *results->state = data;
+ offset += used;
+ // Capture the rest of the data as normal as we should be on a byte boundary.
+ // Data + Footer
+ if (!matchGeneric(results->rawbuf + offset, results->state + 1,
+ results->rawlen - offset, nbits - kCarrierAc84ExtraBits,
+ 0, 0, // No Header expected.
+ kCarrierAc84Zero, kCarrierAc84One, // Data
+ kCarrierAc84One, kCarrierAc84Zero,
+ kCarrierAc84Zero, kDefaultMessageGap, true,
+ _tolerance + kCarrierAc84ExtraTolerance,
+ kMarkExcess, false)) return false;
+
+ // Success
+ results->decode_type = decode_type_t::CARRIER_AC84;
+ results->bits = nbits;
+ results->repeat = false;
+ return true;
+}
+#endif // DECODE_CARRIER_AC84
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.h
index c642c51e5..7554a45ba 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Carrier.h
@@ -4,6 +4,7 @@
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1127
/// @see https://docs.google.com/spreadsheets/d/1EZy78L0cn1KDIX1aKq2biptejFqCjD5HO3tLiRvXf48/edit#gid=0
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1797
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1943
// Supports:
// Brand: Carrier/Surrey, Model: 42QG5A55970 remote
@@ -13,6 +14,8 @@
// Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C
// Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter
// Brand: Carrier, Model: 40GKX0E2006 remote (CARRIER_AC128)
+// Brand: Carrier, Model: 3021203 RR03-S-Remote (CARRIER_AC84)
+// Brand: Carrier, Model: 342WM100CT A/C (CARRIER_AC84)
#ifndef IR_CARRIER_H_
#define IR_CARRIER_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp
index 755e89190..3fb4e8d0a 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.cpp
@@ -548,6 +548,8 @@ stdAc::state_t IRCoolixAC::toCommon(const stdAc::state_t *prev) const {
// Back to "normal" stateful messages.
result.mode = toCommonMode(getMode());
result.degrees = getTemp();
+ result.sensorTemperature = getSensorTemp();
+ result.iFeel = getZoneFollow();
result.fanspeed = toCommonFanSpeed(getFan());
return result;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.h
index 2155b54b0..d711367b2 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Coolix.h
@@ -165,6 +165,7 @@ class IRCoolixAC {
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
String toString(void) const;
+ void setZoneFollow(const bool on);
#ifndef UNIT_TEST
private:
@@ -189,7 +190,6 @@ class IRCoolixAC {
void setTempRaw(const uint8_t code);
uint8_t getTempRaw(void) const;
void setSensorTempRaw(const uint8_t code);
- void setZoneFollow(const bool on);
bool isSpecialState(void) const;
bool handleSpecialState(const uint32_t data);
void updateAndSaveState(const uint32_t raw_state);
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Ecoclim.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Ecoclim.cpp
index 983913d38..e24559b5f 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Ecoclim.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Ecoclim.cpp
@@ -365,6 +365,7 @@ stdAc::state_t IREcoclimAc::toCommon(void) const {
result.mode = toCommonMode(getMode());
result.celsius = true;
result.degrees = getTemp();
+ result.sensorTemperature = getSensorTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.sleep = (getMode() == kEcoclimSleep) ? 0 : -1;
result.clock = getClock();
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp
index f0882bc6d..f8edf9729 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.cpp
@@ -365,6 +365,7 @@ stdAc::state_t IRElectraAc::toCommon(void) const {
result.mode = toCommonMode(_.Mode);
result.celsius = true;
result.degrees = getTemp();
+ result.sensorTemperature = getSensorTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.swingv = getSwingV() ? stdAc::swingv_t::kAuto
: stdAc::swingv_t::kOff;
@@ -373,6 +374,7 @@ stdAc::state_t IRElectraAc::toCommon(void) const {
result.light = getLightToggle();
result.turbo = _.Turbo;
result.clean = _.Clean;
+ result.iFeel = getIFeel();
// Not supported.
result.model = -1; // No models used.
result.quiet = false;
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h
index 142b52562..602406d8d 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Electra.h
@@ -14,6 +14,7 @@
// Brand: Centek, Model: SCT-65Q09 A/C
// Brand: Centek, Model: YKR-P/002E remote
// Brand: AEG, Model: Chillflex Pro AXP26U338CW A/C
+// Brand: Electrolux, Model: YKR-H/531E A/C
#ifndef IR_ELECTRA_H_
#define IR_ELECTRA_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gorenje.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gorenje.cpp
new file mode 100644
index 000000000..f93e4e231
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gorenje.cpp
@@ -0,0 +1,71 @@
+// Copyright 2022 Mateusz Bronk (mbronk)
+/// @file
+/// @brief Support for the Gorenje cooker hood IR protocols.
+/// @see https://techfresh.pl/wp-content/uploads/2017/08/Gorenje-DKF-2600-MWT.pdf
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1887
+
+// Supports:
+// Brand: Gorenje, Model: DKF 2600 MWT Cooker Hood
+
+#include "IRrecv.h"
+#include "IRsend.h"
+#include "IRutils.h"
+
+const uint32_t kGorenjeMinGap = 100000U; // 0.1s
+const uint16_t kGorenjeHdrMark = 0;
+const uint32_t kGorenjeHdrSpace = 0;
+const uint16_t kGorenjeBitMark = 1300;
+const uint32_t kGorenjeOneSpace = 5700;
+const uint32_t kGorenjeZeroSpace = 1700;
+const uint16_t kGorenjeFreq = 38000; // Hz
+const uint16_t kGorenjeTolerance = 7; // %
+
+#if SEND_GORENJE
+/// Send a Gorenje Cooker Hood formatted message.
+/// Status: STABLE / Known working.
+/// @param[in] data containing the IR command to be sent.
+/// @param[in] nbits Nr. of bits of the message to send. usually kGorenjeBits
+/// @param[in] repeat Nr. of times the message is to be repeated.
+void IRsend::sendGorenje(const uint64_t data, const uint16_t nbits,
+ const uint16_t repeat) {
+ sendGeneric(kGorenjeHdrMark, kGorenjeHdrSpace,
+ kGorenjeBitMark, kGorenjeOneSpace,
+ kGorenjeBitMark, kGorenjeZeroSpace,
+ kGorenjeBitMark, kGorenjeMinGap,
+ data, nbits, kGorenjeFreq, true, repeat, kDutyDefault);
+}
+#endif // SEND_GORENJE
+
+#if DECODE_GORENJE
+/// Decode the supplied Gorenje Cooker Hood message.
+/// Status: STABLE / Known working.
+/// @param[in,out] results Ptr to the data to decode & where to store the
+/// decoded 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.
+bool IRrecv::decodeGorenje(decode_results *results, uint16_t offset,
+ const uint16_t nbits, const bool strict) {
+ if (strict && nbits != kGorenjeBits)
+ return false; // We expect Gorenje to be a certain sized message.
+
+ uint64_t data = 0;
+ if (!matchGeneric(results->rawbuf + offset, &data,
+ results->rawlen - offset, nbits,
+ kGorenjeHdrMark, kGorenjeHdrSpace,
+ kGorenjeBitMark, kGorenjeOneSpace,
+ kGorenjeBitMark, kGorenjeZeroSpace,
+ kGorenjeBitMark, kGorenjeMinGap,
+ true, kGorenjeTolerance)) return false;
+
+ // Matched!
+ results->bits = nbits;
+ results->value = data;
+ results->decode_type = decode_type_t::GORENJE;
+ results->command = 0;
+ results->address = 0;
+ return true;
+}
+#endif // DECODE_GORENJE
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp
index ead7178e3..2c44cfe52 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Gree.cpp
@@ -591,6 +591,8 @@ stdAc::state_t IRGreeAC::toCommon(void) {
result.mode = toCommonMode(_.Mode);
result.celsius = !_.UseFahrenheit;
result.degrees = getTemp();
+ // no support for Sensor temp.
+ result.iFeel = getIFeel();
result.fanspeed = toCommonFanSpeed(_.Fan);
if (_.SwingAuto)
result.swingv = stdAc::swingv_t::kAuto;
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Midea.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Midea.cpp
index 4fbc07973..80acfcda3 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Midea.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Midea.cpp
@@ -679,6 +679,7 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) {
result.mode = toCommonMode(_.Mode);
result.celsius = !_.useFahrenheit;
result.degrees = getTemp(result.celsius);
+ result.sensorTemperature = getSensorTemp(result.celsius);
result.fanspeed = toCommonFanSpeed(_.Fan);
result.sleep = _.Sleep ? 0 : -1;
result.econo = getEconoToggle();
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp
index e3e7c60d3..b7d6ce42d 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mirage.cpp
@@ -740,6 +740,7 @@ stdAc::state_t IRMirageAc::toCommon(void) const {
result.mode = toCommonMode(_.Mode);
result.celsius = true;
result.degrees = getTemp();
+ result.sensorTemperature = getSensorTemp();
result.fanspeed = toCommonFanSpeed(getFan(), _model);
result.swingv = toCommonSwingV(getSwingV());
result.swingh = getSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
@@ -750,6 +751,7 @@ stdAc::state_t IRMirageAc::toCommon(void) const {
result.sleep = getSleep() ? 0 : -1;
result.quiet = getQuiet();
result.clock = getClock() / 60;
+ result.iFeel = getIFeel();
// Not supported.
result.econo = false;
result.beep = false;
@@ -775,10 +777,14 @@ void IRMirageAc::fromCommon(const stdAc::state_t state) {
setFilter(state.filter);
// setClock() expects seconds, not minutes.
setClock((state.clock > 0) ? state.clock * 60 : 0);
+ setIFeel(state.iFeel);
+ if (state.sensorTemperature != kNoTempValue) {
+ setSensorTemp(state.celsius ? state.sensorTemperature
+ : fahrenheitToCelsius(state.sensorTemperature));
+ }
// Non-common settings.
setOnTimer(0);
setOffTimer(0);
- setIFeel(false);
}
/// Convert the internal state into a human readable string.
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.h
index 85e61eefd..4f9954f8b 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Mitsubishi.h
@@ -36,6 +36,7 @@
// Brand: Mitsubishi Electric, Model: MSZ-ZW4017S A/C (MITSUBISHI_AC)
// Brand: Mitsubishi Electric, Model: MSZ-FHnnVE A/C (MITSUBISHI_AC)
// Brand: Mitsubishi Electric, Model: RH151 remote (MITSUBISHI_AC)
+// Brand: Mitsubishi Electric, Model: PAR-FA32MA remote (MITSUBISHI136)
#ifndef IR_MITSUBISHI_H_
#define IR_MITSUBISHI_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Panasonic.cpp
index a0a0b6b23..82acaac71 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Panasonic.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Panasonic.cpp
@@ -128,8 +128,15 @@ uint64_t IRsend::encodePanasonic(const uint16_t manufacturer,
bool IRrecv::decodePanasonic(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict,
const uint32_t manufacturer) {
- if (strict && nbits != kPanasonicBits)
- return false; // Request is out of spec.
+ if (strict) { // Compliance checks
+ switch (nbits) {
+ case kPanasonic40Bits:
+ case kPanasonicBits:
+ break;
+ default:
+ return false; // Request is out of spec.
+ }
+ }
uint64_t data = 0;
@@ -147,8 +154,10 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t offset,
if (address != manufacturer) // Verify the Manufacturer code.
return false;
// Verify the checksum.
- uint8_t checksumOrig = data;
- uint8_t checksumCalc = (data >> 24) ^ (data >> 16) ^ (data >> 8);
+ const uint8_t checksumOrig = data;
+ uint8_t checksumCalc = (data >> 16) ^ (data >> 8);
+ if (nbits != kPanasonic40Bits)
+ checksumCalc ^= (data >> 24);
if (checksumOrig != checksumCalc) return false;
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sanyo.cpp
index 4a38e8e09..4b99d0492 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sanyo.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Sanyo.cpp
@@ -622,10 +622,12 @@ stdAc::state_t IRSanyoAc::toCommon(void) const {
result.mode = toCommonMode(_.Mode);
result.celsius = true;
result.degrees = getTemp();
+ result.sensorTemperature = getSensorTemp();
result.fanspeed = toCommonFanSpeed(_.Fan);
result.sleep = _.Sleep ? 0 : -1;
result.swingv = toCommonSwingV(_.SwingV);
result.beep = _.Beep;
+ result.iFeel = !getSensor();
// Not supported.
result.swingh = stdAc::swingh_t::kOff;
result.turbo = false;
@@ -762,13 +764,13 @@ void IRSanyoAc88::stateReset(void) {
/// Set up hardware to be able to send a message.
void IRSanyoAc88::begin(void) { _irsend.begin(); }
-#if SEND_SANYO_AC
+#if SEND_SANYO_AC88
/// Send the current internal state as IR messages.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRSanyoAc88::send(const uint16_t repeat) {
_irsend.sendSanyoAc88(getRaw(), kSanyoAc88StateLength, repeat);
}
-#endif // SEND_SANYO_AC
+#endif // SEND_SANYO_AC88
/// Get a PTR to the internal state/code for this protocol with all integrity
/// checks passing.
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Wowwee.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Wowwee.cpp
new file mode 100644
index 000000000..d999f0c4b
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Wowwee.cpp
@@ -0,0 +1,91 @@
+// Copyright 2022 David Conran
+
+/// @file
+/// @brief Support for WowWee RoboRapter protocol
+/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues1938
+
+// Supports:
+// Brand: WowWee, Model: RoboRapter-X
+
+// WowWee RoboRapter-X messages
+// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1938#issuecomment-1367968228
+//
+// Button Code
+// ====== =====
+// Left 0x180
+// Forward 0x186
+// Backward 0x187
+// Right 0x188
+// Stop 0x18E
+// Head Counterclockwise 0x191
+// Tail Left 0x192
+// Tail Right 0x193
+// Head Clockwise 0x194
+// Guard Mode 0x1B0
+// Roam 0x1B1
+// Cautious Mood 0x1B2
+// Playful Mood 0x1B3
+// Hunting Mood 0x1B4
+// Demo 0x1D0
+// Bite 0x1D1
+
+#include
+#include "IRrecv.h"
+#include "IRsend.h"
+#include "IRutils.h"
+
+// Constants
+const uint16_t kWowweeHdrMark = 6684;
+const uint16_t kWowweeHdrSpace = 723;
+const uint16_t kWowweeBitMark = 912;
+const uint16_t kWowweeOneSpace = 3259;
+const uint16_t kWowweeZeroSpace = kWowweeHdrSpace;
+const uint16_t kWowweeFreq = 38000; // Hz. (Just a guess)
+
+
+#if SEND_WOWWEE
+/// Send a WowWee formatted message.
+/// Status: STABLE / Confirmed working with real device.
+/// @param[in] data The message to be sent.
+/// @param[in] nbits The number of bits of message to be sent.
+/// @param[in] repeat The number of times the command is to be repeated.
+void IRsend::sendWowwee(uint64_t data, uint16_t nbits, uint16_t repeat) {
+ sendGeneric(kWowweeHdrMark, kWowweeHdrSpace,
+ kWowweeBitMark, kWowweeOneSpace,
+ kWowweeBitMark, kWowweeZeroSpace,
+ kWowweeBitMark, kDefaultMessageGap, data,
+ nbits, kWowweeFreq, true, repeat, 33);
+}
+#endif // SEND_WOWWEE
+
+#if DECODE_WOWWEE
+/// Decode the supplied WowWee message.
+/// Status: STABLE / Confirmed working with real device.
+/// @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::decodeWowwee(decode_results *results, uint16_t offset,
+ const uint16_t nbits, const bool strict) {
+ if (strict && nbits != kWowweeBits)
+ return false; // We expect Wowwee to be a certain sized message.
+
+ uint64_t data = 0;
+
+ // Match Header + Data + Footer
+ if (!matchGeneric(results->rawbuf + offset, &data,
+ results->rawlen - offset, nbits,
+ kWowweeHdrMark, kWowweeHdrSpace,
+ kWowweeBitMark, kWowweeOneSpace,
+ kWowweeBitMark, kWowweeZeroSpace,
+ kWowweeBitMark, kDefaultMessageGap, true)) return false;
+ // Success
+ results->bits = nbits;
+ results->value = data;
+ results->decode_type = WOWWEE;
+ results->command = 0;
+ results->address = 0;
+ return true;
+}
+#endif // DECODE_WOWWEE
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Xmp.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Xmp.cpp
index 29d7f6c1b..5d9da4c52 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Xmp.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_Xmp.cpp
@@ -115,7 +115,7 @@ using IRXmpUtils::adjustRepeat;
#if SEND_XMP
/// Send a XMP packet.
-/// Status: Beta / Untested against a real device.
+/// Status: STABLE / Confirmed working against a real device.
/// @param[in] data The message to be sent.
/// @param[in] nbits The number of bits of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
@@ -150,7 +150,7 @@ void IRsend::sendXmp(const uint64_t data, const uint16_t nbits,
#if DECODE_XMP
/// Decode the supplied XMP packet/message.
-/// Status: BETA / Probably works.
+/// Status: STABLE / Confirmed working against a real device.
/// @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.
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_York.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_York.cpp
new file mode 100644
index 000000000..f01729328
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_York.cpp
@@ -0,0 +1,357 @@
+// Copyright 2022 Daniele Gobbetti
+
+/// @file
+/// @brief Support for the York AC protocol (remote GRYLH2A)
+
+// Note: Most of the code is autogenerated by the provided tools or assembled
+// from other support classes
+
+#include "ir_York.h"
+#include
+#include
+#ifndef ARDUINO
+#include
+#endif
+#include "IRrecv.h"
+#include "IRremoteESP8266.h"
+#include "IRsend.h"
+#ifdef UNIT_TEST
+#include "IRsend_test.h"
+#endif
+#include "IRtext.h"
+#include "IRutils.h"
+
+
+using irutils::addBoolToString;
+using irutils::addModeToString;
+using irutils::addFanToString;
+using irutils::addTempToString;
+using irutils::addLabeledString;
+using irutils::minsToString;
+
+
+// Constants
+const uint16_t kYorkHdrMark = 4887;
+const uint16_t kYorkBitMark = 612;
+const uint16_t kYorkHdrSpace = 2267;
+const uint16_t kYorkOneSpace = 1778;
+const uint16_t kYorkZeroSpace = 579;
+const uint16_t kYorkFreq = 38000; // Hz. (Guessing the most common frequency.)
+
+#if SEND_YORK
+/// Send a 17 Byte / 136 bit York A/C message.
+/// Status: ALPHA / Untested.
+/// @param[in] data An array of bytes containing the IR command.
+/// @param[in] nbytes Nr. of bytes of data in the array. (>=kStateLength)
+/// @param[in] repeat Nr. of times the message is to be repeated.
+void IRsend::sendYork(const uint8_t data[], const uint16_t nbytes,
+ const uint16_t repeat) {
+ if (nbytes < kYorkStateLength)
+ return;
+ sendGeneric(kYorkHdrMark, kYorkHdrSpace,
+ kYorkBitMark, kYorkOneSpace,
+ kYorkBitMark, kYorkZeroSpace,
+ kYorkBitMark, kDefaultMessageGap,
+ data, nbytes, kYorkFreq,
+ false, repeat, kDutyDefault); // false == LSB
+}
+#endif // SEND_YORK
+
+#if DECODE_YORK
+/// Decode the supplied message.
+/// Status: ALPHA / Tested, some values still are not mapped to the internal
+/// state of AC
+/// @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::decodeYork(decode_results *results, uint16_t offset,
+ const uint16_t nbits, const bool strict) {
+ if (strict && nbits != kYorkBits)
+ return false;
+
+ uint16_t used = 0;
+
+ used = matchGeneric(results->rawbuf + offset, results->state,
+ results->rawlen - offset, nbits,
+ kYorkHdrMark, kYorkHdrSpace,
+ kYorkBitMark, kYorkOneSpace,
+ kYorkBitMark, kYorkZeroSpace,
+ kYorkBitMark, kDefaultMessageGap,
+ false, _tolerance, kMarkExcess,
+ false); // LSB
+ if (used == 0) return false; // We failed to find any data.
+
+ // Succes
+ results->decode_type = decode_type_t::YORK;
+ results->bits = nbits;
+
+ return true;
+}
+#endif // DECODE_YORK
+
+//
+//
+/// 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?
+IRYorkAc::IRYorkAc(const uint16_t pin, const bool inverted,
+ const bool use_modulation)
+ : _irsend(pin, inverted, use_modulation) {
+ stateReset();
+ }
+
+// Reset the internal state to a fixed known good state.
+void IRYorkAc::stateReset() {
+ // This resets to a known-good state.
+ setRaw(kYorkKnownGoodState);
+}
+
+/// Set up hardware to be able to send a message.
+void IRYorkAc::begin(void) { _irsend.begin(); }
+
+/// Get the raw state of the object, suitable to be sent with the appropriate
+/// IRsend object method.
+/// @return A copy of the internal state.
+uint8_t *IRYorkAc::getRaw(void) {
+ calcChecksum();
+ return _.raw;
+}
+
+/// Set the internal state from a valid code for this protocol.
+/// @param[in] new_code A valid code for this protocol.
+/// @param[in] length Length of the code in bytes.
+void IRYorkAc::setRaw(const uint8_t new_code[], const uint16_t length) {
+ std::memcpy(_.raw, new_code, length);
+}
+
+#if SEND_YORK
+/// Send the current internal state as an IR message.
+/// @param[in] repeat Nr. of times the message will be repeated.
+void IRYorkAc::send(const uint16_t repeat) {
+ _irsend.sendYork(getRaw(), kYorkStateLength, repeat);
+}
+#endif // SEND_YORK
+
+/// Get the current operation mode setting.
+/// @return The current operation mode.
+uint8_t IRYorkAc::getMode(void) const {
+ return _.Mode;
+}
+
+/// Set the desired operation mode.
+/// @param[in] mode The desired operation mode.
+void IRYorkAc::setMode(const uint8_t mode) {
+ switch (mode) {
+ case kYorkFan:
+ case kYorkCool:
+ case kYorkHeat:
+ case kYorkDry:
+ _.Mode = mode;
+ break;
+ default:
+ _.Mode = kYorkAuto;
+ }
+ setFan(getFan()); // Ensure the fan is at the correct speed for the new mode.
+}
+
+/// 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 IRYorkAc::convertMode(const stdAc::opmode_t mode) {
+ switch (mode) {
+ case stdAc::opmode_t::kCool: return kYorkCool;
+ case stdAc::opmode_t::kHeat: return kYorkHeat;
+ case stdAc::opmode_t::kDry: return kYorkDry;
+ case stdAc::opmode_t::kFan: return kYorkFan;
+ default: return kYorkAuto;
+ }
+}
+
+/// 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 IRYorkAc::toCommonMode(const uint8_t mode) {
+ switch (mode) {
+ case kYorkCool: return stdAc::opmode_t::kCool;
+ case kYorkHeat: return stdAc::opmode_t::kHeat;
+ case kYorkDry: return stdAc::opmode_t::kDry;
+ case kYorkFan: return stdAc::opmode_t::kFan;
+ default: return stdAc::opmode_t::kAuto;
+ }
+}
+
+/// Set the speed of the fan.
+/// @param[in] speed The desired setting.
+/// @note The fan speed is locked to Low when in Dry mode, to auto when in auto
+/// mode. "Fan" mode has no support for "auto" speed.
+void IRYorkAc::setFan(const uint8_t speed) {
+ switch (getMode()) {
+ case kYorkDry:
+ _.Fan = kYorkFanLow;
+ break;
+ case kYorkFan:
+ _.Fan = std::min(speed, kYorkFanHigh);
+ break;
+ case kYorkAuto:
+ _.Fan = kYorkFanAuto;
+ break;
+ default:
+ _.Fan = std::min(speed, kYorkFanAuto);
+ }
+}
+
+/// Get the current fan speed setting.
+/// @return The current fan speed.
+uint8_t IRYorkAc::getFan(void) const {
+ return _.Fan;
+}
+
+/// Convert a stdAc::fanspeed_t enum into it's native speed.
+/// @param[in] speed The enum to be converted.
+/// @return The native equivalent of the enum.
+uint8_t IRYorkAc::convertFan(const stdAc::fanspeed_t speed) {
+ switch (speed) {
+ case stdAc::fanspeed_t::kMin:
+ case stdAc::fanspeed_t::kLow:
+ return kYorkFanLow;
+ case stdAc::fanspeed_t::kMedium:
+ return kYorkFanMedium;
+ case stdAc::fanspeed_t::kHigh:
+ case stdAc::fanspeed_t::kMax:
+ return kYorkFanHigh;
+ default:
+ return kYorkFanAuto;
+ }
+}
+
+/// 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 IRYorkAc::toCommonFanSpeed(const uint8_t speed) {
+ switch (speed) {
+ case kYorkFanHigh: return stdAc::fanspeed_t::kMax;
+ case kYorkFanMedium: return stdAc::fanspeed_t::kMedium;
+ case kYorkFanLow: return stdAc::fanspeed_t::kMin;
+ default: return stdAc::fanspeed_t::kAuto;
+ }
+}
+
+/// Set the temperature.
+/// @param[in] degrees The temperature in degrees celsius.
+void IRYorkAc::setTemp(const uint8_t degrees) {
+ _.Temp = std::min(kYorkMaxTemp, std::max(kYorkMinTemp, degrees));
+}
+
+/// Get the current temperature setting.
+/// @return Get current setting for temp. in degrees celsius.
+uint8_t IRYorkAc::getTemp(void) const {
+ return _.Temp;
+}
+
+/// 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 IRYorkAc::setOnTimer(const uint16_t nr_of_mins) {
+ _.OnTimer = nr_of_mins / 10;
+}
+
+/// 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 IRYorkAc::setOffTimer(const uint16_t nr_of_mins) {
+ _.OffTimer = nr_of_mins / 10;
+}
+
+
+/// Get the On Timer setting of the A/C.
+/// @return The Nr. of minutes the On Timer is set for.
+uint16_t IRYorkAc::getOnTimer(void) const {
+ return _.OnTimer * 10;
+}
+
+/// 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 IRYorkAc::getOffTimer(void) const {
+ return _.OffTimer * 10;
+}
+
+/// CRC16-16 (a.k.a. CRC-16-IBM)
+void IRYorkAc::calcChecksum() {
+ uint8_t length = 14;
+ uint16_t reg_crc = 0x0000;
+ uint8_t* data = _.raw;
+ while (length--) {
+ reg_crc ^= *data++;
+ for (uint16_t index = 0; index < 8; index++) {
+ if (reg_crc & 0x01) {
+ reg_crc = (reg_crc>>1) ^ 0xA001;
+ } else {
+ reg_crc = reg_crc >>1;
+ }
+ }
+ }
+ _.Chk1 = (reg_crc & 0xff);
+ _.Chk2 = ((reg_crc >> 8) & 0x00ff);
+}
+
+/// 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 IRYorkAc::toCommon(const stdAc::state_t *prev) const {
+ stdAc::state_t result{};
+ // Start with the previous state if given it.
+ if (prev != NULL) {
+ result = *prev;
+ } else {
+ // Set defaults for non-zero values that are not implicitly set for when
+ // there is no previous state.
+ // e.g. Any setting that toggles should probably go here.
+ result.power = false;
+ }
+ result.protocol = decode_type_t::YORK;
+ result.mode = toCommonMode(_.Mode);
+ result.celsius = true;
+ result.degrees = getTemp();
+ result.fanspeed = toCommonFanSpeed(_.Fan);
+ result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
+ result.sleep = getOffTimer();
+ // 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.clock = -1;
+ return result;
+}
+
+/// Convert the current internal state into a human readable string.
+/// @return A human readable string.
+String IRYorkAc::toString(void) const {
+ String result = "";
+ result.reserve(70); // Reserve some heap for the string to reduce fragging.
+ result += addBoolToString(_.Power, kPowerStr, false);
+
+ result += addModeToString(_.Mode, kYorkAuto, kYorkCool,
+ kYorkHeat, kYorkDry, kYorkFan);
+ result += addFanToString(_.Fan, kYorkFanHigh, kYorkFanLow,
+ kYorkFanAuto, kYorkFanAuto,
+ kYorkFanMedium);
+ result += addTempToString(getTemp(), true);
+ result += addBoolToString(_.SwingV, kSwingVStr);
+ result += addLabeledString(minsToString(getOnTimer()), kOnTimerStr);
+ result += addLabeledString(minsToString(getOffTimer()), kOffTimerStr);
+
+ return result;
+}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_York.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_York.h
new file mode 100644
index 000000000..3de56bb89
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/ir_York.h
@@ -0,0 +1,137 @@
+// Copyright 2022 Daniele Gobbetti
+
+/// @file
+/// @brief Support for the York AC protocol (remote GRYLH2A)
+
+// Supports:
+// Brand: York, Model: MHH07P17 A/C
+// Brand: York, Model: GRYLH2A remote
+
+#ifndef IR_YORK_H_
+#define IR_YORK_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 York A/C message.
+union YorkProtocol{
+ uint8_t raw[kYorkStateLength]; ///< The state of the IR remote.
+ struct {
+ // byte 0-5
+ uint8_t preamble[6]; // unknown, fixed 0x08, 0x10, 0x07, 0x02, 0x40, 0x08
+ // byte 6
+ uint8_t Key1 :4; // key pressed on the remote: 1 power, 2 temp up, 3
+ // temp down...
+ uint8_t Key2 :4; // only set when setting ontime/offtime:
+ // Key1 value is 0x6 (enter key) and Key2 is 0x3 for
+ // "start" and 0x2 for "stop"
+ // byte 7
+ uint8_t Fan :4; // Fan speed: 1 low, 2 mid, 3 max, 8 auto
+ uint8_t Power :1; // main unit power: 1 on, 0 off
+ uint8_t :3;
+ // byte 8
+ uint8_t Mode :4; // 1 heat, 2 cool, 3 dry, 4 fan, 8 auto
+ uint8_t :4;
+ // byte 9
+ uint8_t :2;
+ uint8_t Temp :6; // Degrees Celsius
+ // byte 10
+ uint8_t OffTimer :8; // Power off time: 10s of minutes from now
+ // byte 11
+ uint8_t OnTimer :8; // Power on time: 10s of minutes from now
+ // byte 12
+ uint8_t :8; // unknown, normally 0x00, could be 0x08 when ontime
+ // set, 0x88 if both on and offtime set, 0x60 if
+ // sleep mode set
+ // byte 13
+ uint8_t SwingV :1; // 0 off, 1 on
+ uint8_t :7;
+ // byte 14
+ uint8_t :8; // checksum preamble, fixed 0xEC
+ // byte 15-16
+ uint8_t Chk1 :8; // checksum, algorithm CRC-16/ARC, first byte
+ uint8_t Chk2 :8; // checksum, algorithm CRC-16/ARC, second byte
+ };
+};
+
+// Constants
+const uint8_t kYorkKnownGoodState[kYorkStateLength] = {
+ 0x08, 0x10, 0x07, 0x02, 0x40, 0x08,
+ 0x03, 0x18, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0xEC,
+ 0xF5, 0xF2}; // Mode "Heat", Fan Speed "auto", Temp: 24, Power: on
+
+// Temperature
+const uint8_t kYorkMinTemp = 18; // Celsius
+const uint8_t kYorkMaxTemp = 32; // Celsius
+// Fan
+const uint8_t kYorkFanLow = 1;
+const uint8_t kYorkFanMedium = 2;
+const uint8_t kYorkFanHigh = 3;
+const uint8_t kYorkFanAuto = 8;
+// Modes
+const uint8_t kYorkHeat = 1;
+const uint8_t kYorkCool = 2;
+const uint8_t kYorkDry = 3;
+const uint8_t kYorkFan = 4;
+const uint8_t kYorkAuto = 8;
+
+// Classes
+/// Class for handling detailed York A/C messages.
+class IRYorkAc {
+ public:
+ explicit IRYorkAc(const uint16_t pin, const bool inverted = false,
+ const bool use_modulation = true);
+ void stateReset();
+#if SEND_YORK
+ void send(const uint16_t repeat = kNoRepeat);
+ /// 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_YORK
+ void begin();
+ void setPowerToggle(const bool on);
+ bool getPowerToggle() const;
+ void setTemp(const uint8_t temp);
+ uint8_t getTemp() const;
+ void setFan(const uint8_t speed);
+ uint8_t getFan() const;
+ void setMode(const uint8_t mode);
+ uint8_t getMode() const;
+ uint16_t getOnTimer(void) const;
+ uint16_t getOffTimer(void) const;
+ void setOnTimer(const uint16_t mins);
+ void setOffTimer(const uint16_t mins);
+ uint8_t* getRaw();
+ void setRaw(const uint8_t new_code[],
+ const uint16_t length = kYorkStateLength);
+ static uint8_t convertMode(const stdAc::opmode_t mode);
+ static uint8_t convertFan(const stdAc::fanspeed_t speed);
+ void calcChecksum();
+ static stdAc::opmode_t toCommonMode(const uint8_t mode);
+ static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
+ stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
+ String toString() const;
+#ifndef UNIT_TEST
+
+ private:
+ IRsend _irsend; ///< Instance of the IR send class
+#else
+ /// @cond IGNORE
+ IRsendTest _irsend; ///< Instance of the testing IR send class
+ /// @endcond
+#endif
+ YorkProtocol _;
+};
+#endif // IR_YORK_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h
index 40b85c27c..438cc5da3 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/defaults.h
@@ -310,6 +310,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_LOCK
#define D_STR_LOCK "Lock"
#endif // D_STR_LOCK
+#ifndef D_STR_REPORT
+#define D_STR_REPORT "Report"
+#endif // D_STR_REPORT
#ifndef D_STR_AUTO
#define D_STR_AUTO "Auto"
@@ -378,6 +381,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_MEDIUM
#define D_STR_MEDIUM "Medium"
#endif // D_STR_MEDIUM
+#ifndef D_STR_MED_HIGH
+#define D_STR_MED_HIGH D_STR_MED "-" D_STR_HIGH
+#endif // D_STR_MED_HIGH
#ifndef D_STR_HIGHEST
#define D_STR_HIGHEST "Highest"
@@ -445,6 +451,33 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_BOTTOM
#define D_STR_BOTTOM "Bottom"
#endif // D_STR_BOTTOM
+#ifndef D_STR_UPPER_MIDDLE
+#define D_STR_UPPER_MIDDLE D_STR_UPPER "-" D_STR_MIDDLE
+#endif // D_STR_UPPER_MIDDLE
+#ifndef D_STR_CONFIG
+#define D_STR_CONFIG "Config"
+#endif // D_STR_CONFIG
+#ifndef D_STR_CONTROL
+#define D_STR_CONTROL "Control"
+#endif // D_STR_CONTROL
+#ifndef D_STR_SET_TIMER
+#define D_STR_SET_TIMER D_STR_SET " " D_STR_TIMER
+#endif // D_STR_AC_TIMER
+#ifndef D_STR_SCHEDULE
+#define D_STR_SCHEDULE "Schedule"
+#endif // D_STR_SCHEDULE
+#ifndef D_STR_CH
+#define D_STR_CH "CH#"
+#endif // D_STR_CH
+#ifndef D_STR_TIMER_ACTIVE_DAYS
+#define D_STR_TIMER_ACTIVE_DAYS "TimerActiveDays"
+#endif // D_STR_TIMER_ACTIVE_DAYS
+#ifndef D_STR_KEY
+#define D_STR_KEY "Key"
+#endif // D_STR_KEY
+#ifndef D_STR_VALUE
+#define D_STR_VALUE "Value"
+#endif // D_STR_VALUE
// Compound words/phrases/descriptions from pre-defined words.
// Note: Obviously these need to be defined *after* their component words.
@@ -472,6 +505,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_DISPLAYTEMP
#define D_STR_DISPLAYTEMP D_STR_DISPLAY " " D_STR_TEMP
#endif // D_STR_DISPLAYTEMP
+#ifndef D_STR_IFEELREPORT
+#define D_STR_IFEELREPORT D_STR_IFEEL " " D_STR_REPORT
+#endif // D_STR_IFEELREPORT
#ifndef D_STR_SENSORTEMP
#define D_STR_SENSORTEMP D_STR_SENSOR " " D_STR_TEMP
#endif // D_STR_SENSORTEMP
@@ -689,6 +725,12 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_DG11J191
#define D_STR_DG11J191 "DG11J191"
#endif // D_STR_DG11J191
+#ifndef D_STR_ARGO_WREM2
+#define D_STR_ARGO_WREM2 "WREM2"
+#endif // D_STR_ARGO_WREM2
+#ifndef D_STR_ARGO_WREM3
+#define D_STR_ARGO_WREM3 "WREM3"
+#endif // D_STR_ARGO_WREM3
// Protocols Names
#ifndef D_STR_AIRTON
@@ -727,6 +769,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_CARRIER_AC64
#define D_STR_CARRIER_AC64 D_STR_CARRIER_AC "64"
#endif // D_STR_CARRIER_AC64
+#ifndef D_STR_CARRIER_AC84
+#define D_STR_CARRIER_AC84 D_STR_CARRIER_AC "84"
+#endif // D_STR_CARRIER_AC84
#ifndef D_STR_CARRIER_AC128
#define D_STR_CARRIER_AC128 D_STR_CARRIER_AC "128"
#endif // D_STR_CARRIER_AC128
@@ -808,6 +853,9 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_GOODWEATHER
#define D_STR_GOODWEATHER "GOODWEATHER"
#endif // D_STR_GOODWEATHER
+#ifndef D_STR_GORENJE
+#define D_STR_GORENJE "GORENJE"
+#endif // D_STR_GORENJE
#ifndef D_STR_GREE
#define D_STR_GREE "GREE"
#endif // D_STR_GREE
@@ -1060,9 +1108,15 @@ D_STR_INDIRECT " " D_STR_MODE
#ifndef D_STR_WHYNTER
#define D_STR_WHYNTER "WHYNTER"
#endif // D_STR_WHYNTER
+#ifndef D_STR_WOWWEE
+#define D_STR_WOWWEE "WOWWEE"
+#endif // D_STR_WOWWEE
#ifndef D_STR_XMP
#define D_STR_XMP "XMP"
#endif // D_STR_XMP
+#ifndef D_STR_YORK
+#define D_STR_YORK "YORK"
+#endif // D_STR_YORK
#ifndef D_STR_ZEPEAL
#define D_STR_ZEPEAL "ZEPEAL"
#endif // D_STR_ZEPEAL
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/nl-NL.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/nl-NL.h
new file mode 100644
index 000000000..3cac00b5e
--- /dev/null
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/locale/nl-NL.h
@@ -0,0 +1,136 @@
+// Copyright 2022 - Stijn (@stijnb1234)
+// Locale/language file for Dutch / The Netherlands.
+// This file will override the default values located in `defaults.h`.
+#ifndef LOCALE_NL_NL_H_
+#define LOCALE_NL_NL_H_
+
+#define D_STR_UNKNOWN "ONBEKEND"
+#define D_STR_POWER "Stroom"
+#define D_STR_PREVIOUS "Vorige"
+#define D_STR_ON "Aan"
+#define D_STR_OFF "Uit"
+#define D_STR_MODE "Modus"
+#define D_STR_TOGGLE "Omschakelen"
+#define D_STR_SLEEP "Slaap"
+#define D_STR_LIGHT "Licht"
+#define D_STR_POWERFUL "Sterk"
+#define D_STR_QUIET "Rustig"
+#define D_STR_ECONO "Eco"
+#define D_STR_SWING "Zwaai"
+#define D_STR_BEEP "Piep"
+#define D_STR_MOULD "Schimmel"
+#define D_STR_CLEAN "Reinigen"
+#define D_STR_PURIFY "Zuiver"
+#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_CLOCK "Klok"
+#define D_STR_COMMAND "Commando"
+#define D_STR_XFAN "XVentilator"
+#define D_STR_HEALTH "Gezondheid"
+#define D_STR_IFEEL "IkVoel"
+#define D_STR_ISEE "IkZie"
+#define D_STR_HUMID "Vochtigheid"
+#define D_STR_SAVE "Opslaan"
+#define D_STR_EYE "Ogen"
+#define D_STR_FOLLOW "Volgen"
+#define D_STR_FRESH "Fris"
+#define D_STR_HOLD "Houd"
+#define D_STR_BUTTON "Knop"
+#define D_STR_NIGHT "Nacht"
+#define D_STR_SILENT "Stil"
+#define D_STR_UP "Omhoog"
+#define D_STR_TEMPUP D_STR_TEMP " " D_STR_UP
+#define D_STR_DOWN "Omlaag"
+#define D_STR_TEMPDOWN D_STR_TEMP " " D_STR_DOWN
+#define D_STR_CHANGE "Wisselen"
+#define D_STR_MOVE "Verplaatsen"
+#define D_STR_SET "Instellen"
+#define D_STR_CANCEL "Annuleren"
+#define D_STR_COMFORT "Comfortabel"
+#define D_STR_WEEKLY "Weekelijks"
+#define D_STR_WEEKLYTIMER D_STR_TIMER " " D_STR_WEEKLY
+#define D_STR_FAST "Snel"
+#define D_STR_SLOW "Langzaam"
+#define D_STR_AIRFLOW "Luchtstroom"
+#define D_STR_STEP "Stap"
+#define D_STR_NA "N/A"
+#define D_STR_OUTSIDE "Buiten"
+#define D_STR_LOUD "Luid"
+#define D_STR_UPPER "Boven"
+#define D_STR_LOWER "Beneden"
+#define D_STR_BREEZE "Wind"
+#define D_STR_CIRCULATE "Circulatie"
+#define D_STR_CEILING "Plafond"
+#define D_STR_WALL "Muur"
+#define D_STR_ROOM "Kamer"
+#define D_STR_6THSENSE "6e Zintuig"
+#define D_STR_FIXED "Vast"
+
+#define D_STR_AUTOMATIC "Automatisch"
+#define D_STR_MANUAL "Handmatig"
+#define D_STR_COOL "Koelen"
+#define D_STR_HEAT "Verwarmen"
+#define D_STR_FAN "Venilator"
+#define D_STR_FANONLY "alleen_fan"
+#define D_STR_DRY "Drogen"
+
+#define D_STR_MED "Mid"
+#define D_STR_MEDIUM "Medium"
+
+#define D_STR_HIGHEST "Hoogste"
+#define D_STR_HIGH "Hoog"
+#define D_STR_HI "H"
+#define D_STR_MID "M"
+#define D_STR_MIDDLE "Medium"
+#define D_STR_LOW "Laag"
+#define D_STR_LO "L"
+#define D_STR_LOWEST "Laagste"
+#define D_STR_RIGHT "Rechts"
+#define D_STR_MAXRIGHT D_STR_MAX " " D_STR_RIGHT
+#define D_STR_RIGHTMAX_NOSPACE D_STR_RIGHT D_STR_MAX
+#define D_STR_LEFT "Links"
+#define D_STR_MAXLEFT D_STR_MAX " " D_STR_LEFT
+#define D_STR_LEFTMAX_NOSPACE D_STR_LEFT D_STR_MAX
+#define D_STR_WIDE "Breed"
+#define D_STR_CENTRE "Midden"
+#define D_STR_TOP "Boven"
+#define D_STR_BOTTOM "Onder"
+
+#define D_STR_DAY "Dag"
+#define D_STR_DAYS D_STR_DAY "en"
+#define D_STR_HOUR "Uur"
+#define D_STR_HOURS D_STR_HOUR
+#define D_STR_MINUTE "Minuut"
+#define D_STR_MINUTES "Minuten"
+#define D_STR_SECOND "Seconde"
+#define D_STR_SECONDS D_STR_SECOND "n"
+#define D_STR_NOW "Nu"
+#define D_STR_THREELETTERDAYS "ZonMaaDinWoeDonVriZat"
+
+#define D_STR_YES "Ja"
+#define D_STR_NO "Nee"
+#define D_STR_TRUE "Waar"
+#define D_STR_FALSE "Niet Waar"
+
+#define D_STR_REPEAT "Herhalen"
+#define D_STR_PREVIOUS "Vorige"
+#define D_STR_DISPLAY "Display"
+#define D_STR_INSIDE "Binnen"
+#define D_STR_POWERBUTTON "Hoofdschakelaar"
+#define D_STR_PREVIOUSPOWER "Vorige inschakelstatus"
+#define D_STR_DISPLAYTEMP "Temperatuurweergave"
+
+// IRrecvDumpV2+
+#define D_STR_TIMESTAMP "Tijdsaanduiding"
+#define D_STR_LIBRARY "Bibliotheek"
+#define D_STR_TOLERANCE "Tolerantie"
+#define D_STR_MESGDESC "Beschrijving"
+#define D_STR_IRRECVDUMP_STARTUP \
+ "IRrecvDump draait en wacht op IR-signaal op pin %d"
+#define D_WARN_BUFFERFULL \
+ "WAARSCHUWING: IR-code is te groot voor buffer (>= %d). " \
+ "Het resultaat kan niet worden vertrouwd totdat het is verholpen. " \
+ "Wijzig & vergroot `kCaptureBufferSize`."
+
+#endif // LOCALE_NL_NL_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp
index 15351ba61..32a745a31 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRac_test.cpp
@@ -73,6 +73,7 @@ TEST(TestIRac, Airton) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Airwell) {
@@ -96,6 +97,7 @@ TEST(TestIRac, Airwell) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Amcor) {
@@ -119,6 +121,7 @@ TEST(TestIRac, Amcor) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Argo) {
@@ -130,16 +133,19 @@ TEST(TestIRac, Argo) {
true, // Power
stdAc::opmode_t::kHeat, // Mode
21, // Celsius
+ 22, // Sensor Temp.
stdAc::fanspeed_t::kHigh, // Fan speed
stdAc::swingv_t::kOff, // Vertical swing
+ false, // iFeel
false, // Turbo
-1); // Sleep
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(kArgoHeat, ac.getMode());
EXPECT_EQ(21, ac.getTemp());
- EXPECT_EQ(kArgoFlapAuto, ac.getFlap());
+ EXPECT_EQ(kArgoFlapFull, ac.getFlap());
EXPECT_FALSE(ac.getMax()); // Turbo
EXPECT_FALSE(ac.getNight()); // Sleep
+ EXPECT_EQ(argoIrMessageType_t::AC_CONTROL, ac.getMessageType());
}
TEST(TestIRac, Carrier64) {
@@ -174,6 +180,7 @@ TEST(TestIRac, Carrier64) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Coolix) {
@@ -189,9 +196,11 @@ TEST(TestIRac, Coolix) {
true, // Power
stdAc::opmode_t::kHeat, // Mode
21, // Celsius
+ kNoTempValue, // Sensor Temp
stdAc::fanspeed_t::kHigh, // Fan speed
stdAc::swingv_t::kOff, // Vertical swing
stdAc::swingh_t::kOff, // Horizontal swing
+ false, // iFeel
false, // Turbo
false, // Light
false, // Clean
@@ -235,6 +244,7 @@ TEST(TestIRac, Coolix) {
// End of message #2 (i.e. Repeat '1')
// Note: the two messages (#1 & #2) are identical.
ac._irsend.outputStr());
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Corona) {
@@ -277,6 +287,7 @@ TEST(TestIRac, Corona) {
ASSERT_EQ(expectedCapture, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin) {
@@ -310,6 +321,7 @@ TEST(TestIRac, Daikin) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin128) {
@@ -343,6 +355,7 @@ TEST(TestIRac, Daikin128) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin152) {
@@ -371,6 +384,7 @@ TEST(TestIRac, Daikin152) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin160) {
@@ -396,6 +410,7 @@ TEST(TestIRac, Daikin160) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin176) {
@@ -421,6 +436,7 @@ TEST(TestIRac, Daikin176) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin2) {
@@ -460,6 +476,7 @@ TEST(TestIRac, Daikin2) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin216) {
@@ -488,6 +505,7 @@ TEST(TestIRac, Daikin216) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Daikin64) {
@@ -548,7 +566,7 @@ TEST(TestIRac, Ecoclim) {
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
- "Power: On, Mode: 1 (Cool), Temp: 26C, SensorTemp: 26C, Fan: 2 (High), "
+ "Power: On, Mode: 1 (Cool), Temp: 26C, SensorTemp: 27C, Fan: 2 (High), "
"Clock: 12:34, On Timer: Off, Off Timer: Off, Type: 0";
ac.begin();
@@ -556,6 +574,7 @@ TEST(TestIRac, Ecoclim) {
true, // Power
stdAc::opmode_t::kCool, // Mode
26, // Celsius
+ 27, // Sensor Temp.
stdAc::fanspeed_t::kHigh, // Fan speed
-1, // Sleep
12 * 60 + 34); // Clock
@@ -567,7 +586,7 @@ TEST(TestIRac, Ecoclim) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
char expected_sleep[] =
- "Power: On, Mode: 7 (Sleep), Temp: 21C, SensorTemp: 21C, Fan: 0 (Low), "
+ "Power: On, Mode: 7 (Sleep), Temp: 21C, SensorTemp: 22C, Fan: 0 (Low), "
"Clock: 17:17, On Timer: Off, Off Timer: Off, Type: 0";
ac._irsend.reset();
@@ -575,6 +594,7 @@ TEST(TestIRac, Ecoclim) {
true, // Power
stdAc::opmode_t::kCool, // Mode
21, // Celsius
+ 22, // Sensor Temp.
stdAc::fanspeed_t::kLow, // Fan speed
8 * 60, // Sleep
17 * 60 + 17); // Clock
@@ -600,9 +620,11 @@ TEST(TestIRac, Electra) {
true, // Power
stdAc::opmode_t::kFan, // Mode
26, // Celsius
+ 27, // Sensor Temp.
stdAc::fanspeed_t::kHigh, // Fan speed
stdAc::swingv_t::kAuto, // Vertical swing
stdAc::swingh_t::kLeft, // Horizontal swing
+ false, // iFeel
true, // Turbo
true, // Light (toggle)
true); // Clean
@@ -656,6 +678,7 @@ TEST(TestIRac, Fujitsu) {
ASSERT_EQ(ardb1_expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
// Try to set the device to 10C Heat mode.
@@ -681,6 +704,7 @@ TEST(TestIRac, Fujitsu) {
ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits);
ASSERT_EQ(arrah2e_expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
irac.fujitsu(&ac,
fujitsu_ac_remote_model_t::ARRY4, // Model
@@ -703,6 +727,7 @@ TEST(TestIRac, Fujitsu) {
ASSERT_EQ(kFujitsuAcBits, ac._irsend.capture.bits);
ASSERT_EQ(arry4_expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
irac.fujitsu(&ac,
@@ -753,6 +778,7 @@ TEST(TestIRac, Goodweather) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Gree) {
@@ -761,7 +787,7 @@ TEST(TestIRac, Gree) {
IRrecv capture(kGpioUnused);
char expected[] =
"Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 71F, "
- "Fan: 2 (Medium), Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, "
+ "Fan: 2 (Medium), Turbo: Off, Econo: Off, IFeel: On, 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)";
@@ -776,6 +802,7 @@ TEST(TestIRac, Gree) {
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kHigh, // Vertical swing
stdAc::swingh_t::kRight, // Horizontal swing
+ true, // iFeel
false, // Turbo
false, // Econo
true, // Light
@@ -789,6 +816,7 @@ TEST(TestIRac, Gree) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Haier) {
@@ -818,6 +846,7 @@ TEST(TestIRac, Haier) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Haier160) {
@@ -853,6 +882,7 @@ TEST(TestIRac, Haier160) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Haier176) {
@@ -886,6 +916,7 @@ TEST(TestIRac, Haier176) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, HaierYrwo2) {
@@ -919,6 +950,7 @@ TEST(TestIRac, HaierYrwo2) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Hitachi) {
@@ -946,6 +978,7 @@ TEST(TestIRac, Hitachi) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Hitachi1) {
@@ -978,6 +1011,7 @@ TEST(TestIRac, Hitachi1) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Hitachi264) {
@@ -1003,6 +1037,7 @@ TEST(TestIRac, Hitachi264) {
ASSERT_EQ(expected_swingon, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
EXPECT_EQ(decode_type_t::HITACHI_AC264, r.protocol);
EXPECT_TRUE(r.power);
EXPECT_EQ(stdAc::opmode_t::kHeat, r.mode);
@@ -1031,6 +1066,7 @@ TEST(TestIRac, Hitachi296) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
EXPECT_EQ(decode_type_t::HITACHI_AC296, r.protocol);
EXPECT_TRUE(r.power);
EXPECT_EQ(stdAc::opmode_t::kHeat, r.mode);
@@ -1062,6 +1098,7 @@ TEST(TestIRac, Hitachi344) {
ASSERT_EQ(expected_swingon, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
EXPECT_EQ(decode_type_t::HITACHI_AC344, r.protocol);
EXPECT_TRUE(r.power);
EXPECT_EQ(stdAc::opmode_t::kHeat, r.mode);
@@ -1082,6 +1119,7 @@ TEST(TestIRac, Hitachi344) {
ASSERT_EQ(expected_swingoff, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ASSERT_EQ(HITACHI_AC344, ac._irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc344Bits, ac._irsend.capture.bits);
ASSERT_EQ(expected_swingoff,
@@ -1115,6 +1153,7 @@ TEST(TestIRac, Hitachi424) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
irac.hitachi424(&ac,
@@ -1131,6 +1170,7 @@ TEST(TestIRac, Hitachi424) {
ASSERT_EQ(kHitachiAc424Bits, ac._irsend.capture.bits);
ASSERT_EQ(expected_swingv, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Kelvinator) {
@@ -1164,6 +1204,7 @@ TEST(TestIRac, Kelvinator) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, LG) {
@@ -1196,6 +1237,7 @@ TEST(TestIRac, LG) {
ASSERT_EQ(61, ac._irsend.capture.rawlen);
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, LG2) {
@@ -1228,6 +1270,7 @@ TEST(TestIRac, LG2) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
// Message #2 - SwingV Low
EXPECT_TRUE(capture.decodeLG(&ac._irsend.capture, 61));
ASSERT_EQ(LG2, ac._irsend.capture.decode_type);
@@ -1443,6 +1486,7 @@ TEST(TestIRac, LG2_AKB73757604) {
ASSERT_EQ(LG2, ac._irsend.capture.decode_type);
ASSERT_EQ(kLgBits, ac._irsend.capture.bits);
ASSERT_EQ(kLgAcSwingHAuto, ac._irsend.capture.value);
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Midea) {
@@ -1461,8 +1505,10 @@ TEST(TestIRac, Midea) {
stdAc::opmode_t::kDry, // Mode
true, // Celsius
27, // Degrees
+ 28, // Sensor Temp.
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kOff, // Swing(V)
+ false, // iFeel
false, // Silent/Quiet
false, // Previous Silent/Quiet setting
false, // Turbo
@@ -1479,6 +1525,7 @@ TEST(TestIRac, Midea) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Mirage) {
@@ -1518,6 +1565,7 @@ TEST(TestIRac, Mirage) {
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));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
const char expected_KKG29AC1[] =
"Model: 2 (KKG29AC1), Power: On, Mode: 3 (Dry), Temp: 27C, "
@@ -1535,6 +1583,7 @@ TEST(TestIRac, Mirage) {
ASSERT_EQ(expected_KKG29AC1,
IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Mitsubishi) {
@@ -1567,6 +1616,7 @@ TEST(TestIRac, Mitsubishi) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Mitsubishi136) {
@@ -1593,6 +1643,7 @@ TEST(TestIRac, Mitsubishi136) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, MitsubishiHeavy88) {
@@ -1623,6 +1674,7 @@ TEST(TestIRac, MitsubishiHeavy88) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, MitsubishiHeavy152) {
@@ -1656,6 +1708,7 @@ TEST(TestIRac, MitsubishiHeavy152) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Neoclima) {
@@ -1690,6 +1743,7 @@ TEST(TestIRac, Neoclima) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Panasonic) {
@@ -1722,6 +1776,7 @@ TEST(TestIRac, Panasonic) {
ASSERT_EQ(expected_nke, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
char expected_dke[] =
"Model: 3 (DKE), Power: On, Mode: 3 (Cool), Temp: 18C, Fan: 4 (Maximum), "
@@ -1748,6 +1803,7 @@ TEST(TestIRac, Panasonic) {
ASSERT_EQ(kPanasonicAcBits, ac._irsend.capture.bits);
ASSERT_EQ(expected_dke, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Panasonic32) {
@@ -1774,6 +1830,7 @@ TEST(TestIRac, Panasonic32) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Samsung) {
@@ -1813,6 +1870,7 @@ TEST(TestIRac, Samsung) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
irac.samsung(&ac,
@@ -1841,6 +1899,7 @@ TEST(TestIRac, Samsung) {
ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
const char sleep[] =
@@ -1874,6 +1933,7 @@ TEST(TestIRac, Samsung) {
ASSERT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
ASSERT_EQ(sleep, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Sanyo) {
@@ -1890,8 +1950,10 @@ TEST(TestIRac, Sanyo) {
true, // Power
stdAc::opmode_t::kCool, // Mode
28, // Celsius
+ kNoTempValue, // SensorTemp
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kHighest, // Vertical Swing
+ false, // iFeel
true, // Beep
17); // Sleep
ASSERT_EQ(expected, ac.toString());
@@ -1902,6 +1964,7 @@ TEST(TestIRac, Sanyo) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Sanyo88) {
@@ -1931,6 +1994,7 @@ TEST(TestIRac, Sanyo88) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Sharp) {
@@ -1963,6 +2027,7 @@ TEST(TestIRac, Sharp) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Tcl112) {
@@ -1997,6 +2062,7 @@ TEST(TestIRac, Tcl112) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
// Test the quiet mode, which should generate two messages.
ac._irsend.reset();
irac.tcl112(&ac,
@@ -2052,6 +2118,7 @@ TEST(TestIRac, Technibel) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Teco) {
@@ -2079,6 +2146,7 @@ TEST(TestIRac, Teco) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Toshiba) {
@@ -2108,6 +2176,7 @@ TEST(TestIRac, Toshiba) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
EXPECT_EQ(
"f38000d50"
"m4400s4300"
@@ -2183,6 +2252,7 @@ TEST(TestIRac, Transcold) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Trotec) {
@@ -2212,6 +2282,7 @@ TEST(TestIRac, Trotec) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Trotec3550) {
@@ -2242,6 +2313,7 @@ TEST(TestIRac, Trotec3550) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Truma) {
@@ -2271,6 +2343,7 @@ TEST(TestIRac, Truma) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, Vestel) {
@@ -2300,6 +2373,7 @@ TEST(TestIRac, Vestel) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
char expected_clocks[] =
@@ -2325,6 +2399,7 @@ TEST(TestIRac, Vestel) {
ASSERT_EQ(kVestelAcBits, ac._irsend.capture.bits);
ASSERT_EQ(expected_clocks, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
// Now check it sends both messages during normal operation when the
// clock is set.
@@ -2404,6 +2479,7 @@ TEST(TestIRac, Voltas) {
ASSERT_EQ(expected_unknown, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
ac._irsend.reset();
// Test the UNKNOWN model type
@@ -2470,6 +2546,7 @@ TEST(TestIRac, Whirlpool) {
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
TEST(TestIRac, cmpStates) {
@@ -2507,6 +2584,21 @@ TEST(TestIRac, cmpStates) {
// Now make them different.
b.power = false;
ASSERT_TRUE(IRac::cmpStates(a, b));
+
+ b = a;
+ ASSERT_FALSE(IRac::cmpStates(a, b));
+ b.command = stdAc::ac_command_t::kTimerCommand;
+ ASSERT_TRUE(IRac::cmpStates(a, b));
+
+ b = a;
+ ASSERT_FALSE(IRac::cmpStates(a, b));
+ b.iFeel = true;
+ ASSERT_TRUE(IRac::cmpStates(a, b));
+
+ b = a;
+ ASSERT_FALSE(IRac::cmpStates(a, b));
+ b.sensorTemperature = 12.5;
+ ASSERT_TRUE(IRac::cmpStates(a, b));
}
TEST(TestIRac, handleToggles) {
@@ -2610,6 +2702,7 @@ TEST(TestIRac, strToFanspeed) {
EXPECT_EQ(stdAc::fanspeed_t::kMin, IRac::strToFanspeed("MIN"));
EXPECT_EQ(stdAc::fanspeed_t::kLow, IRac::strToFanspeed("LOW"));
EXPECT_EQ(stdAc::fanspeed_t::kMedium, IRac::strToFanspeed("MEDIUM"));
+ EXPECT_EQ(stdAc::fanspeed_t::kMediumHigh, IRac::strToFanspeed("MED-HIGH"));
EXPECT_EQ(stdAc::fanspeed_t::kHigh, IRac::strToFanspeed("HIGH"));
EXPECT_EQ(stdAc::fanspeed_t::kMax, IRac::strToFanspeed("MAX"));
EXPECT_EQ(stdAc::fanspeed_t::kAuto, IRac::strToFanspeed("FOOBAR"));
@@ -2622,6 +2715,7 @@ TEST(TestIRac, strToSwingV) {
EXPECT_EQ(stdAc::swingv_t::kLowest, IRac::strToSwingV("LOWEST"));
EXPECT_EQ(stdAc::swingv_t::kLow, IRac::strToSwingV("LOW"));
EXPECT_EQ(stdAc::swingv_t::kMiddle, IRac::strToSwingV("MIDDLE"));
+ EXPECT_EQ(stdAc::swingv_t::kUpperMiddle, IRac::strToSwingV("UPPER-MIDDLE"));
EXPECT_EQ(stdAc::swingv_t::kHigh, IRac::strToSwingV("HIGH"));
EXPECT_EQ(stdAc::swingv_t::kHighest, IRac::strToSwingV("HIGHEST"));
EXPECT_EQ(stdAc::swingv_t::kOff, IRac::strToSwingV("OFF"));
@@ -2652,6 +2746,10 @@ TEST(TestIRac, strToModel) {
IRac::strToModel("ARRAH2E"));
EXPECT_EQ(whirlpool_ac_remote_model_t::DG11J13A,
IRac::strToModel("DG11J13A"));
+ EXPECT_EQ(argo_ac_remote_model_t::SAC_WREM2,
+ IRac::strToModel("WREM2"));
+ EXPECT_EQ(argo_ac_remote_model_t::SAC_WREM3,
+ IRac::strToModel("WREM3"));
EXPECT_EQ(1, IRac::strToModel("1"));
EXPECT_EQ(10, IRac::strToModel("10"));
EXPECT_EQ(-1, IRac::strToModel("0"));
@@ -2659,6 +2757,26 @@ TEST(TestIRac, strToModel) {
EXPECT_EQ(0, IRac::strToModel("FOOBAR", 0));
}
+TEST(TestIRac, strToCommandType) {
+ EXPECT_EQ(stdAc::ac_command_t::kControlCommand,
+ IRac::strToCommandType("Control"));
+ EXPECT_EQ(stdAc::ac_command_t::kSensorTempReport,
+ IRac::strToCommandType("IFeel Report"));
+ EXPECT_EQ(stdAc::ac_command_t::kSensorTempReport,
+ IRac::strToCommandType("IFeel"));
+ EXPECT_EQ(stdAc::ac_command_t::kTimerCommand,
+ IRac::strToCommandType("Set Timer"));
+ EXPECT_EQ(stdAc::ac_command_t::kTimerCommand,
+ IRac::strToCommandType("Timer"));
+ EXPECT_EQ(stdAc::ac_command_t::kConfigCommand,
+ IRac::strToCommandType("Config"));
+ EXPECT_EQ(stdAc::ac_command_t::kControlCommand,
+ IRac::strToCommandType("FOOBAR"));
+ EXPECT_EQ(stdAc::ac_command_t::kTimerCommand,
+ IRac::strToCommandType("FOOBAR",
+ stdAc::ac_command_t::kTimerCommand));
+}
+
TEST(TestIRac, boolToString) {
EXPECT_EQ("On", IRac::boolToString(true));
EXPECT_EQ("Off", IRac::boolToString(false));
@@ -2678,6 +2796,7 @@ TEST(TestIRac, opmodeToString) {
TEST(TestIRac, fanspeedToString) {
EXPECT_EQ("Low", IRac::fanspeedToString(stdAc::fanspeed_t::kLow));
EXPECT_EQ("Auto", IRac::fanspeedToString(stdAc::fanspeed_t::kAuto));
+ EXPECT_EQ("Med-High", IRac::fanspeedToString(stdAc::fanspeed_t::kMediumHigh));
EXPECT_EQ("UNKNOWN", IRac::fanspeedToString((stdAc::fanspeed_t)500));
}
@@ -2685,6 +2804,8 @@ TEST(TestIRac, swingvToString) {
EXPECT_EQ("Off", IRac::swingvToString(stdAc::swingv_t::kOff));
EXPECT_EQ("Low", IRac::swingvToString(stdAc::swingv_t::kLow));
EXPECT_EQ("Auto", IRac::swingvToString(stdAc::swingv_t::kAuto));
+ EXPECT_EQ("Upper-Middle", IRac::swingvToString(
+ stdAc::swingv_t::kUpperMiddle));
EXPECT_EQ("UNKNOWN", IRac::swingvToString((stdAc::swingv_t)500));
}
@@ -2696,6 +2817,17 @@ TEST(TestIRac, swinghToString) {
EXPECT_EQ("UNKNOWN", IRac::swinghToString((stdAc::swingh_t)500));
}
+TEST(TestIRac, commandTypeToString) {
+ EXPECT_EQ("Control",
+ IRac::commandTypeToString(stdAc::ac_command_t::kControlCommand));
+ EXPECT_EQ("IFeel Report",
+ IRac::commandTypeToString(stdAc::ac_command_t::kSensorTempReport));
+ EXPECT_EQ("Set Timer",
+ IRac::commandTypeToString(stdAc::ac_command_t::kTimerCommand));
+ EXPECT_EQ("Config",
+ IRac::commandTypeToString(stdAc::ac_command_t::kConfigCommand));
+}
+
// Check that we keep the previous state info if the message is a special
// state-less command.
TEST(TestIRac, CoolixDecodeToState) {
@@ -2714,6 +2846,7 @@ TEST(TestIRac, CoolixDecodeToState) {
ASSERT_TRUE(irrecv.decode(&irsend.capture));
stdAc::state_t result;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, result.command);
ASSERT_EQ(decode_type_t::COOLIX, result.protocol);
ASSERT_FALSE(result.power);
ASSERT_EQ(stdAc::opmode_t::kHeat, result.mode);
@@ -2762,9 +2895,11 @@ TEST(TestIRac, Issue821) {
result.power, // Power
result.mode, // Mode
result.degrees, // Celsius
+ kNoTempValue, // Sensor Temp
result.fanspeed, // Fan speed
result.swingv, // Vertical swing
result.swingh, // Horizontal swing
+ result.iFeel, // iFeel
result.turbo, // Turbo
result.light, // Light
result.clean, // Clean
@@ -2877,6 +3012,7 @@ TEST(TestIRac, Issue1001) {
IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
// Now check if the mode is set to "Off" instead of just change to power off.
// i.e. How Home Assistant expects things to work.
@@ -2907,6 +3043,7 @@ TEST(TestIRac, Issue1001) {
"Command: 1 (Power)",
IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, r.command);
}
// Check power switching in Daikin2 common a/c handling when from an IR message.
@@ -2956,6 +3093,7 @@ TEST(TestIRac, Issue1035) {
ASSERT_FALSE(prev.power);
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result, &prev));
ASSERT_TRUE(result.power);
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, result.command);
prev = result;
@@ -2967,6 +3105,7 @@ TEST(TestIRac, Issue1035) {
ASSERT_EQ(DAIKIN2, ac._irsend.capture.decode_type);
ASSERT_TRUE(prev.power);
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result, &prev));
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, result.command);
ASSERT_FALSE(result.power);
}
@@ -3138,4 +3277,26 @@ TEST(TestIRac, initState) {
EXPECT_EQ(-1, builtin_init.model);
EXPECT_EQ(stdAc::swingv_t::kOff, builtin_init.swingv);
EXPECT_EQ(decode_type_t::UNKNOWN, no_init.protocol);
+ EXPECT_EQ(stdAc::ac_command_t::kControlCommand, no_init.command);
+ EXPECT_FALSE(no_init.iFeel);
+ EXPECT_EQ(kNoTempValue, no_init.sensorTemperature);
+}
+
+TEST(TestIRac, cleanState) {
+ IRac irac(kGpioUnused);
+ stdAc::state_t s = {};
+ s.mode = stdAc::opmode_t::kFan;
+ s.power = true;
+ s.sensorTemperature = 20.5;
+ s.degrees = 22.3;
+
+ auto clean = irac.cleanState(s);
+ EXPECT_TRUE(clean.power);
+ EXPECT_EQ(s.mode, clean.mode);
+ EXPECT_EQ(s.sensorTemperature, clean.sensorTemperature);
+ EXPECT_EQ(s.degrees, clean.degrees);
+
+ s.mode = stdAc::opmode_t::kOff;
+ clean = irac.cleanState(s);
+ EXPECT_FALSE(clean.power);
}
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRrecv_test.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRrecv_test.h
index bb366c1ee..42c3f0a17 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRrecv_test.h
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/IRrecv_test.h
@@ -3,15 +3,16 @@
#ifndef TEST_IRRECV_TEST_H_
#define TEST_IRRECV_TEST_H_
+#include
#include
#include
#include
#include "IRutils.h"
-#define EXPECT_STATE_EQ(a, b, c) \
- for (uint8_t i = 0; i < c / 8; ++i) { \
- EXPECT_EQ(a[i], b[i]) << "Expected state " \
- "differs at i = " \
- << uint64ToString(i); \
+#define EXPECT_STATE_EQ(a, b, c) \
+ for (uint8_t i = 0; i < ceil((c) / 8.0); ++i) { \
+ EXPECT_EQ((a)[i], (b)[i]) << "Expected state " \
+ "differs at i = " \
+ << uint64ToString(i); \
}
#endif // TEST_IRRECV_TEST_H_
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile
index 7a193e813..8c0a33da4 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/Makefile
@@ -16,6 +16,7 @@
# Points to the root of Google Test, relative to where this file is.
# Remember to tweak this if you move this file.
GTEST_DIR = ../lib/googletest/googletest
+GMOCK_DIR = ../lib/googletest/googlemock
# Where to find user code.
USER_DIR = ../src
@@ -24,11 +25,14 @@ INCLUDES = -I$(USER_DIR) -I.
# Flags passed to the preprocessor.
# Set Google Test's header directory as a system directory, such that
# the compiler doesn't generate warnings in Google Test headers.
-CPPFLAGS += -isystem $(GTEST_DIR)/include -DUNIT_TEST -D_IR_LOCALE_=en-AU
+CPPFLAGS += -isystem $(GTEST_DIR)/include -isystem $(GMOCK_DIR)/include -DUNIT_TEST -D_IR_LOCALE_=en-AU
# Flags passed to the C++ compiler.
CXXFLAGS += -g -Wall -Wextra -Werror -pthread -std=gnu++11
+# Google Test libraries
+GTEST_LIBS = gtest.a gtest_main.a gmock.a gmock_main.a
+
# All tests produced by this Makefile. generated from all *_test.cpp files
TESTS = $(patsubst %.cpp,%,$(wildcard *_test.cpp))
@@ -37,12 +41,19 @@ TESTS = $(patsubst %.cpp,%,$(wildcard *_test.cpp))
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \
$(GTEST_DIR)/include/gtest/internal/*.h
+# All Google Mock headers. Note that all Google Test headers are
+# included here too, as they are #included by Google Mock headers.
+# Usually you shouldn't change this definition.
+GMOCK_HEADERS = $(GMOCK_DIR)/include/gmock/*.h \
+ $(GMOCK_DIR)/include/gmock/internal/*.h \
+ $(GTEST_HEADERS)
+
# House-keeping build targets.
-all : $(TESTS)
+all : $(GTEST_LIBS) $(TESTS)
clean :
- rm -f $(TESTS) gtest.a gtest_main.a *.o
+ rm -f $(GTEST_LIBS) $(TESTS) *.o
# Build and run all the tests.
run : all
@@ -65,13 +76,14 @@ run-% : %_test
install-googletest :
rm -rf ../lib/googletest
- git clone -b v1.8.x https://github.com/google/googletest.git ../lib/googletest
+ git clone -b v1.12.x https://github.com/google/googletest.git ../lib/googletest
-# Builds gtest.a and gtest_main.a.
+# Builds gtest.a, gtest_main.a, gmock.a, gmock_main.a.
# Usually you shouldn't tweak such internal variables, indicated by a
# trailing _.
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)
+GMOCK_SRCS_ = $(GMOCK_DIR)/src/*.cc $(GMOCK_HEADERS)
# All the IR protocol object files.
PROTOCOL_OBJS = $(patsubst %.cpp,%.o,$(wildcard $(USER_DIR)/ir_*.cpp))
@@ -82,7 +94,7 @@ PROTOCOLS_H = $(wildcard $(USER_DIR)/ir_*.h)
# Common object files
COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o IRac.o ir_GlobalCache.o \
- IRtext.o $(PROTOCOLS) gtest_main.a
+ IRtext.o $(PROTOCOLS) gtest_main.a gmock_main.a
# Common dependencies
COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \
$(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h \
@@ -90,26 +102,41 @@ COMMON_DEPS = $(USER_DIR)/IRrecv.h $(USER_DIR)/IRsend.h $(USER_DIR)/IRtimer.h \
$(PROTOCOLS_H)
# Common test dependencies
-COMMON_TEST_DEPS = $(COMMON_DEPS) IRrecv_test.h IRsend_test.h
+COMMON_TEST_DEPS = $(COMMON_DEPS) IRrecv_test.h IRsend_test.h ut_utils.h
-# For simplicity and to avoid depending on Google Test's
-# implementation details, the dependencies specified below are
-# conservative and not optimized. This is fine as Google Test
-# compiles fast and for ordinary users its source rarely changes.
+# For simplicity and to avoid depending on implementation details of
+# Google Mock and Google Test, the dependencies specified below are
+# conservative and not optimized. This is fine as Google Mock and
+# Google Test compile fast and for ordinary users their source rarely
+# changes.
gtest-all.o : $(GTEST_SRCS_)
- $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
+ $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) -I$(GMOCK_DIR) $(CXXFLAGS) -c \
$(GTEST_DIR)/src/gtest-all.cc
gtest_main.o : $(GTEST_SRCS_)
- $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
+ $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) -I$(GMOCK_DIR) $(CXXFLAGS) -c \
$(GTEST_DIR)/src/gtest_main.cc
+gmock-all.o : $(GMOCK_SRCS_)
+ $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) -I$(GMOCK_DIR) $(CXXFLAGS) \
+ -c $(GMOCK_DIR)/src/gmock-all.cc
+
+gmock_main.o : $(GMOCK_SRCS_)
+ $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) -I$(GMOCK_DIR) $(CXXFLAGS) \
+ -c $(GMOCK_DIR)/src/gmock_main.cc
+
gtest.a : gtest-all.o
$(AR) $(ARFLAGS) $@ $^
gtest_main.a : gtest-all.o gtest_main.o
$(AR) $(ARFLAGS) $@ $^
+gmock.a : gmock-all.o
+ $(AR) $(ARFLAGS) $@ $^
+
+gmock_main.a : gmock_main.o
+ $(AR) $(ARFLAGS) $@ $^
+
# Keep all intermediate files.
.SECONDARY:
@@ -123,10 +150,10 @@ IRtext.o : $(USER_DIR)/IRtext.cpp $(USER_DIR)/IRtext.h $(USER_DIR)/IRremoteESP82
IRutils.o : $(USER_DIR)/IRutils.cpp $(USER_DIR)/IRutils.h $(USER_DIR)/IRremoteESP8266.h $(USER_DIR)/i18n.h $(USER_DIR)/IRtext.cpp $(USER_DIR)/IRtext.h $(USER_DIR)/locale/*.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/IRutils.cpp
-IRutils_test.o : IRutils_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
+IRutils_test.o : IRutils_test.cpp $(COMMON_TEST_DEPS) $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRutils_test.cpp
-IRutils_test : IRutils_test.o ir_NEC.o ir_Nikai.o ir_Toshiba.o IRtext.o $(COMMON_OBJ) gtest_main.a
+IRutils_test : IRutils_test.o ir_NEC.o ir_Nikai.o ir_Toshiba.o IRtext.o $(COMMON_OBJ) $(GTEST_LIBS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
IRtimer.o : $(USER_DIR)/IRtimer.cpp $(USER_DIR)/IRtimer.h
@@ -135,19 +162,19 @@ IRtimer.o : $(USER_DIR)/IRtimer.cpp $(USER_DIR)/IRtimer.h
IRsend.o : $(USER_DIR)/IRsend.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRremoteESP8266.h
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRsend.cpp
-IRsend_test.o : IRsend_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS)
+IRsend_test.o : IRsend_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRsend_test.cpp
-IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP8266.h $(GTEST_HEADERS)
+IRrecv.o : $(USER_DIR)/IRrecv.cpp $(USER_DIR)/IRrecv.h $(USER_DIR)/IRremoteESP8266.h $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/IRrecv.cpp
-IRrecv_test.o : IRrecv_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GTEST_HEADERS)
+IRrecv_test.o : IRrecv_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend_test.h $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRrecv_test.cpp
-IRac.o : $(USER_DIR)/IRac.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS)
+IRac.o : $(USER_DIR)/IRac.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/IRac.cpp
-IRac_test.o : IRac_test.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GTEST_HEADERS)
+IRac_test.o : IRac_test.cpp $(USER_DIR)/IRac.h $(COMMON_DEPS) $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c IRac_test.cpp
# new specific targets goes above this line
@@ -158,11 +185,11 @@ ir_%.o : $(USER_DIR)/ir_%.h $(USER_DIR)/ir_%.cpp $(COMMON_DEPS)
ir_%.o : $(USER_DIR)/ir_%.cpp $(COMMON_DEPS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c $(USER_DIR)/ir_$*.cpp
-ir_%_test.o : ir_%_test.cpp $(USER_DIR)/ir_$%.h $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
+ir_%_test.o : ir_%_test.cpp $(USER_DIR)/ir_$%.h $(COMMON_TEST_DEPS) $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_$*_test.cpp
-ir_%_test.o : ir_%_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
+ir_%_test.o : ir_%_test.cpp $(COMMON_TEST_DEPS) $(GMOCK_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) -c ir_$*_test.cpp
-%_test : $(COMMON_OBJ) %_test.o
+%_test : $(COMMON_OBJ) %_test.o $(GTEST_LIBS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Argo_test.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Argo_test.cpp
index 3ab890adc..817272a17 100644
--- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Argo_test.cpp
+++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/test/ir_Argo_test.cpp
@@ -1,15 +1,22 @@
// Copyright 2019 David Conran
+// Copyright 2022 Mateusz Bronk (mbronk)
+#include
+#include
+#include
#include "ir_Argo.h"
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
#include "IRsend.h"
#include "IRsend_test.h"
-#include "gtest/gtest.h"
+#include "./ut_utils.h"
+/******************************************************************************/
+/* Tests for toCommon() */
+/******************************************************************************/
TEST(TestArgoACClass, toCommon) {
- IRArgoAC ac(0);
+ IRArgoAC ac(kGpioUnused);
ac.setPower(true);
ac.setMode(kArgoCool);
ac.setTemp(20);
@@ -18,6 +25,7 @@ TEST(TestArgoACClass, toCommon) {
ac.setNight(true);
// Now test it.
ASSERT_EQ(decode_type_t::ARGO, ac.toCommon().protocol);
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, ac.toCommon().command);
ASSERT_TRUE(ac.toCommon().power);
ASSERT_TRUE(ac.toCommon().celsius);
ASSERT_EQ(20, ac.toCommon().degrees);
@@ -25,8 +33,8 @@ TEST(TestArgoACClass, toCommon) {
ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
ASSERT_EQ(0, ac.toCommon().sleep);
ASSERT_TRUE(ac.toCommon().turbo);
+ ASSERT_EQ(argo_ac_remote_model_t::SAC_WREM2, ac.toCommon().model);
// Unsupported.
- ASSERT_EQ(-1, ac.toCommon().model);
ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
ASSERT_FALSE(ac.toCommon().econo);
@@ -36,30 +44,229 @@ TEST(TestArgoACClass, toCommon) {
ASSERT_FALSE(ac.toCommon().beep);
ASSERT_FALSE(ac.toCommon().quiet);
ASSERT_EQ(-1, ac.toCommon().clock);
+ ASSERT_FALSE(ac.toCommon().iFeel);
+ ASSERT_EQ(25, ac.toCommon().sensorTemperature);
}
+TEST(TestArgoAC_WREM3Class, toCommon) {
+ IRArgoAC_WREM3 ac(kGpioUnused);
+ ac.setPower(true);
+ ac.setMode(argoMode_t::COOL);
+ ac.setTemp(21);
+ ac.setFan(argoFan_t::FAN_HIGHEST);
+ ac.setMax(true);
+ ac.setNight(true);
+ ac.setFlap(argoFlap_t::FLAP_4);
+ ac.setEco(true);
+ ac.setFilter(true);
+ ac.setLight(true);
+ ac.setiFeel(true);
+ // Now test it.
+ ASSERT_EQ(decode_type_t::ARGO, ac.toCommon().protocol);
+ ASSERT_EQ(argo_ac_remote_model_t::SAC_WREM3, ac.toCommon().model);
+ ASSERT_EQ(stdAc::ac_command_t::kControlCommand, ac.toCommon().command);
+ ASSERT_TRUE(ac.toCommon().celsius);
+ ASSERT_TRUE(ac.toCommon().beep); // Always on (except for iFeel)
+ ASSERT_FALSE(ac.toCommon().clean);
+ ASSERT_EQ(-1, ac.toCommon().clock);
+ ASSERT_EQ(0, ac.toCommon().sleep);
+ ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
+ ASSERT_TRUE(ac.toCommon().power);
+ ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
+ ASSERT_EQ(21, ac.toCommon().degrees);
+ ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
+ ASSERT_TRUE(ac.toCommon().turbo);
+ ASSERT_TRUE(ac.toCommon().quiet); // Night
+ ASSERT_EQ(stdAc::swingv_t::kUpperMiddle, ac.toCommon().swingv);
+ ASSERT_TRUE(ac.toCommon().econo);
+ ASSERT_TRUE(ac.toCommon().light);
+ ASSERT_TRUE(ac.toCommon().filter);
+ ASSERT_TRUE(ac.toCommon().iFeel);
+ ASSERT_EQ(25, ac.toCommon().sensorTemperature);
+}
+
+/******************************************************************************/
+/* Tests of message construction */
+/******************************************************************************/
+
TEST(TestArgoACClass, MessageConstructon) {
- IRArgoAC ac(0);
+ IRArgoAC ac(kGpioUnused);
ac.setPower(true);
ac.setTemp(20);
ac.setMode(kArgoCool);
ac.setFan(kArgoFanAuto);
- ac.setRoomTemp(21);
+ ac.setSensorTemp(21);
ac.setiFeel(true);
ac.setMax(true);
ac.setNight(true);
// Don't implicitly trust this. It's just a guess.
- uint8_t expected[kArgoStateLength] = {
- 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01};
- EXPECT_STATE_EQ(expected, ac.getRaw(), kArgoBits);
+ auto expected = std::vector({
+ 0xAC, 0xF5, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xD6, 0x01});
+ auto actual = ac.getRaw();
+ EXPECT_THAT(std::vector(actual, actual + kArgoBits / 8),
+ ::testing::ElementsAreArray(expected));
EXPECT_EQ(
- "Power: On, Mode: 0 (Cool), Fan: 0 (Auto), Temp: 20C, Room Temp: 21C, "
- "Max: On, IFeel: On, Night: On",
+ "Model: 1 (WREM2), Power: On, Mode: 0 (Cool), Fan: 0 (Auto), Temp: 20C, "
+ "Sensor Temp: 21C, Max: On, IFeel: On, Night: On",
ac.toString());
}
-// Tests for sendArgo().
+TEST(TestArgoAC_WREM3Class, MessageConstructon_ACControl) {
+ IRArgoAC_WREM3 ac(kGpioUnused);
+ ac.setChannel(0);
+ ac.setPower(true);
+ ac.setMode(argoMode_t::COOL);
+ ac.setTemp(22);
+ ac.setSensorTemp(26);
+ ac.setFan(argoFan_t::FAN_AUTO);
+ ac.setFlap(argoFlap_t::FLAP_FULL);
+ ac.setiFeel(false);
+ ac.setNight(false);
+ ac.setEco(false);
+ ac.setMax(false);
+ ac.setFilter(false);
+ ac.setLight(true);
+ auto expected = std::vector({
+ 0x0B, 0x36, 0x12, 0x0F, 0xC2, 0x24});
+ auto actual = ac.getRaw();
+ ASSERT_EQ(ac.getRawByteLength(), kArgo3AcControlStateLength);
+ EXPECT_THAT(std::vector(actual, actual + ac.getRawByteLength()),
+ ::testing::ElementsAreArray(expected));
+ EXPECT_EQ(
+ "Command[CH#0]: Model: 2 (WREM3), Power: On, Mode: 1 (Cool), Temp: 22C, "
+ "Sensor Temp: 26C, Fan: 0 (Auto), Swing(V): 7 (Breeze), IFeel: Off, "
+ "Night: Off, Econo: Off, Max: Off, Filter: Off, Light: On",
+ ac.toString());
+}
+
+TEST(TestArgoAC_WREM3Class, MessageConstructon_ACControl_2) {
+ IRArgoAC_WREM3 ac(kGpioUnused);
+ ac.setChannel(2);
+ ac.setPower(true);
+ ac.setMode(argoMode_t::AUTO);
+ ac.setTemp(23);
+ ac.setSensorTemp(28);
+ ac.setFan(argoFan_t::FAN_MEDIUM);
+ ac.setMax(true);
+ ac.setNight(true);
+ ac.setFlap(argoFlap_t::FLAP_4);
+ ac.setEco(true);
+ ac.setFilter(true);
+ ac.setLight(true);
+ ac.setiFeel(true);
+ auto expected = std::vector({
+ 0x2B, 0xB8, 0x93, 0xFC, 0xC3, 0x35});
+ auto actual = ac.getRaw();
+ ASSERT_EQ(ac.getRawByteLength(), kArgo3AcControlStateLength);
+ EXPECT_THAT(std::vector(actual, actual + ac.getRawByteLength()),
+ ::testing::ElementsAreArray(expected));
+ EXPECT_EQ(
+ "Command[CH#2]: Model: 2 (WREM3), Power: On, Mode: 5 (Auto), Temp: 23C, "
+ "Sensor Temp: 28C, Fan: 4 (Med-High), Swing(V): 4 (Middle), IFeel: On, "
+ "Night: On, Econo: On, Max: On, Filter: On, Light: On",
+ ac.toString());
+}
+
+TEST(TestArgoAC_WREM3Class, MessageConstructon_iFeelReport) {
+ IRArgoAC_WREM3 ac(kGpioUnused);
+ ac.setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT);
+ ac.setSensorTemp(31);
+
+ auto expected = std::vector({0x4B, 0xDB});
+ auto actual = ac.getRaw();
+ ASSERT_EQ(ac.getRawByteLength(), kArgo3iFeelReportStateLength);
+ EXPECT_THAT(std::vector(actual, actual + ac.getRawByteLength()),
+ ::testing::ElementsAreArray(expected));
+ EXPECT_EQ(
+ "IFeel Report[CH#0]: Model: 2 (WREM3), Sensor Temp: 31C",
+ ac.toString());
+}
+
+TEST(TestArgoAC_WREM3Class, MessageConstructon_Config) {
+ IRArgoAC_WREM3 ac(kGpioUnused);
+ ac.setMessageType(argoIrMessageType_t::CONFIG_PARAM_SET);
+ ac.setConfigEntry(6, 30);
+
+ auto expected = std::vector