Skip to content

Commit

Permalink
Added "commandType" to IRAc (#1921)
Browse files Browse the repository at this point in the history
Allows supporting A/C IR remote protocols which use different commands
for representing slices of functionality.
By default, all IRac commands are of type ac_command_t::kControlCommand

Added Argo WREM3 implementation which uses disjoint commands.

Signed-off-by: Mateusz Bronk <[email protected]>
Co-authored-by: Mateusz Bronk <[email protected]>
  • Loading branch information
mbronk and mbronk authored Dec 1, 2022
1 parent a5ddcdd commit da30df3
Show file tree
Hide file tree
Showing 10 changed files with 465 additions and 6 deletions.
127 changes: 123 additions & 4 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,72 @@ void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on,
// 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.
void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) {
ac->begin();
ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT);
ac->setSensorTemp(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<xx>, 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
Expand Down Expand Up @@ -2917,9 +2983,28 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
if (send.model == argo_ac_remote_model_t::SAC_WREM3) {
IRArgoAC_WREM3 ac(_pin, _inverted, _modulation);
argoWrem3_ACCommand(&ac, send.power, send.mode, send.degrees,
send.fanspeed, send.swingv, send.quiet, send.econo, send.turbo,
send.filter, send.light);
switch (send.command) {
case stdAc::ac_command_t::kSensorTempReport:
argoWrem3_iFeelReport(&ac, send.degrees); // Uses "degrees"
// as roomTemp
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, send.degrees,
send.fanspeed, send.swingv, send.quiet, send.econo, send.turbo,
send.filter, send.light);
break;
}
OUTPUT_DECODE_RESULTS_FOR_UT(ac);
} else {
IRArgoAC ac(_pin, _inverted, _modulation);
Expand Down Expand Up @@ -3495,14 +3580,35 @@ 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;
}

/// Check if the internal state has changed from what was previously sent.
/// @note The comparison excludes the clock.
/// @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.
Expand Down Expand Up @@ -3780,6 +3886,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.
Expand Down
8 changes: 8 additions & 0 deletions src/IRac.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,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(
Expand All @@ -96,6 +98,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);
Expand Down Expand Up @@ -148,6 +151,11 @@ class IRac {
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
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,
Expand Down
13 changes: 13 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ enum class swingv_t {
kLastSwingvEnum = kLowest,
};

/// @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.
enum class swingh_t {
kOff = -1,
Expand Down Expand Up @@ -113,6 +125,7 @@ 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;
};
}; // namespace stdAc

Expand Down
2 changes: 2 additions & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ 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"
Expand Down Expand Up @@ -208,6 +209,7 @@ 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);
Expand Down
2 changes: 2 additions & 0 deletions src/IRtext.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ 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);
Expand Down Expand Up @@ -224,6 +225,7 @@ 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);
Expand Down
47 changes: 45 additions & 2 deletions src/ir_Argo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,25 @@ uint8_t IRArgoACBase<T>::getSensorTemp(void) const {
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<typename T>
argoIrMessageType_t IRArgoACBase<T>::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.
Expand Down Expand Up @@ -1169,6 +1188,26 @@ stdAc::swingv_t IRArgoACBase<ArgoProtocolWREM3>::toCommonSwingV(
}
}

/// 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<typename T>
stdAc::ac_command_t IRArgoACBase<T>::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.
Expand Down Expand Up @@ -1208,10 +1247,12 @@ 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(getModeEx());
result.celsius = true;
result.degrees = getTemp();
result.degrees = (_messageType != argoIrMessageType_t::IFEEL_TEMP_REPORT)?
getTemp() : getSensorTemp();
result.fanspeed = toCommonFanSpeed(getFanEx());
result.turbo = _.Max;
result.sleep = _.Night ? 0 : -1;
Expand All @@ -1235,10 +1276,12 @@ 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.degrees = (_messageType != argoIrMessageType_t::IFEEL_TEMP_REPORT)?
getTemp() : getSensorTemp();
result.fanspeed = toCommonFanSpeed(getFanEx());
result.turbo = _.Max;
result.swingv = toCommonSwingV(_.Flap);
Expand Down
2 changes: 2 additions & 0 deletions src/ir_Argo.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ class IRArgoACBase {
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
Expand All @@ -397,6 +398,7 @@ class IRArgoACBase {
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
Expand Down
6 changes: 6 additions & 0 deletions src/locale/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ D_STR_INDIRECT " " D_STR_MODE
#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
Expand Down
Loading

0 comments on commit da30df3

Please sign in to comment.