Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added "commandType" to IRac, enabling support of multi-command IR protocols #1921

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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