Skip to content

Commit da30df3

Browse files
authored
Added "commandType" to IRAc (#1921)
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]>
1 parent a5ddcdd commit da30df3

File tree

10 files changed

+465
-6
lines changed

10 files changed

+465
-6
lines changed

src/IRac.cpp

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,72 @@ void IRac::argoWrem3_ACCommand(IRArgoAC_WREM3 *ac, const bool on,
529529
// No Beep setting available - always beeps in this mode :)
530530
ac->send();
531531
}
532+
533+
/// Send an Argo A/C WREM-3 iFeel (room temp) silent (no beep) report.
534+
/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use.
535+
/// @param[in] sensorTemp The room (iFeel) temperature setting
536+
/// in degrees Celsius.
537+
void IRac::argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp) {
538+
ac->begin();
539+
ac->setMessageType(argoIrMessageType_t::IFEEL_TEMP_REPORT);
540+
ac->setSensorTemp(sensorTemp);
541+
ac->send();
542+
}
543+
544+
/// Send an Argo A/C WREM-3 Config command.
545+
/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use.
546+
/// @param[in] param The parameter ID.
547+
/// @param[in] value The parameter value.
548+
/// @param[in] safe If true, will only allow setting the below parameters
549+
/// in order to avoid accidentally setting a restricted
550+
/// vendor-specific param and breaking the A/C device
551+
/// @note Known parameters (P<xx>, where xx is the @c param)
552+
/// P05 - Temperature Scale (0-Celsius, 1-Fahrenheit)
553+
/// P06 - Transmission channel (0..3)
554+
/// P12 - ECO mode power input limit (30..99, default: 75)
555+
void IRac::argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param,
556+
const uint8_t value, bool safe /*= true*/) {
557+
if (safe) {
558+
switch (param) {
559+
case 5: // temp. scale (note this is likely excess as not transmitted)
560+
if (value > 1) { return; /* invalid */ }
561+
break;
562+
case 6: // channel (note this is likely excess as not transmitted)
563+
if (value > 3) { return; /* invalid */ }
564+
break;
565+
case 12: // eco power limit
566+
if (value < 30 || value > 99) { return; /* invalid */ }
567+
break;
568+
default:
569+
return; /* invalid */
570+
}
571+
}
572+
ac->begin();
573+
ac->setMessageType(argoIrMessageType_t::CONFIG_PARAM_SET);
574+
ac->setConfigEntry(param, value);
575+
ac->send();
576+
}
577+
578+
/// Send an Argo A/C WREM-3 Delay timer command.
579+
/// @param[in, out] ac A Ptr to an IRArgoAC_WREM3 object to use.
580+
/// @param[in] on Whether the unit is currently on. The timer, upon elapse
581+
/// will toggle this state
582+
/// @param[in] currentTime currentTime in minutes, starting from 00:00
583+
/// @note For timer mode, this value is not really used much so can be zero.
584+
/// @param[in] delayMinutes Number of minutes after which the @c on state should
585+
/// be toggled
586+
/// @note Schedule timers are not exposed via this interface
587+
void IRac::argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on,
588+
const uint16_t currentTime, const uint16_t delayMinutes) {
589+
ac->begin();
590+
ac->setMessageType(argoIrMessageType_t::TIMER_COMMAND);
591+
ac->setPower(on);
592+
ac->setTimerType(argoTimerType_t::DELAY_TIMER);
593+
ac->setCurrentTimeMinutes(currentTime);
594+
// Note: Day of week is not set (no need)
595+
ac->setDelayTimerMinutes(delayMinutes);
596+
ac->send();
597+
}
532598
#endif // SEND_ARGO
533599

534600
#if SEND_BOSCH144
@@ -2917,9 +2983,28 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
29172983
{
29182984
if (send.model == argo_ac_remote_model_t::SAC_WREM3) {
29192985
IRArgoAC_WREM3 ac(_pin, _inverted, _modulation);
2920-
argoWrem3_ACCommand(&ac, send.power, send.mode, send.degrees,
2921-
send.fanspeed, send.swingv, send.quiet, send.econo, send.turbo,
2922-
send.filter, send.light);
2986+
switch (send.command) {
2987+
case stdAc::ac_command_t::kSensorTempReport:
2988+
argoWrem3_iFeelReport(&ac, send.degrees); // Uses "degrees"
2989+
// as roomTemp
2990+
break;
2991+
case stdAc::ac_command_t::kConfigCommand:
2992+
/// @warning: this is ABUSING current **common** parameters:
2993+
/// @c clock and @c sleep as config key and value
2994+
/// Hence, value pre-validation is performed (safe-mode)
2995+
/// to avoid accidental device misconfiguration
2996+
argoWrem3_ConfigSet(&ac, send.clock, send.sleep, true);
2997+
break;
2998+
case stdAc::ac_command_t::kTimerCommand:
2999+
argoWrem3_SetTimer(&ac, send.power, send.clock, send.sleep);
3000+
break;
3001+
case stdAc::ac_command_t::kControlCommand:
3002+
default:
3003+
argoWrem3_ACCommand(&ac, send.power, send.mode, send.degrees,
3004+
send.fanspeed, send.swingv, send.quiet, send.econo, send.turbo,
3005+
send.filter, send.light);
3006+
break;
3007+
}
29233008
OUTPUT_DECODE_RESULTS_FOR_UT(ac);
29243009
} else {
29253010
IRArgoAC ac(_pin, _inverted, _modulation);
@@ -3495,14 +3580,35 @@ bool IRac::cmpStates(const stdAc::state_t a, const stdAc::state_t b) {
34953580
a.fanspeed != b.fanspeed || a.swingv != b.swingv ||
34963581
a.swingh != b.swingh || a.quiet != b.quiet || a.turbo != b.turbo ||
34973582
a.econo != b.econo || a.light != b.light || a.filter != b.filter ||
3498-
a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep;
3583+
a.clean != b.clean || a.beep != b.beep || a.sleep != b.sleep ||
3584+
a.command != b.command;
34993585
}
35003586

35013587
/// Check if the internal state has changed from what was previously sent.
35023588
/// @note The comparison excludes the clock.
35033589
/// @return True if it has changed, False if not.
35043590
bool IRac::hasStateChanged(void) { return cmpStates(next, _prev); }
35053591

3592+
/// Convert the supplied str into the appropriate enum.
3593+
/// @param[in] str A Ptr to a C-style string to be converted.
3594+
/// @param[in] def The enum to return if no conversion was possible.
3595+
/// @return The equivalent enum.
3596+
stdAc::ac_command_t IRac::strToCommandType(const char *str,
3597+
const stdAc::ac_command_t def) {
3598+
if (!STRCASECMP(str, kControlCommandStr))
3599+
return stdAc::ac_command_t::kControlCommand;
3600+
else if (!STRCASECMP(str, kIFeelReportStr) ||
3601+
!STRCASECMP(str, kIFeelStr))
3602+
return stdAc::ac_command_t::kSensorTempReport;
3603+
else if (!STRCASECMP(str, kSetTimerCommandStr) ||
3604+
!STRCASECMP(str, kTimerStr))
3605+
return stdAc::ac_command_t::kTimerCommand;
3606+
else if (!STRCASECMP(str, kConfigCommandStr))
3607+
return stdAc::ac_command_t::kConfigCommand;
3608+
else
3609+
return def;
3610+
}
3611+
35063612
/// Convert the supplied str into the appropriate enum.
35073613
/// @param[in] str A Ptr to a C-style string to be converted.
35083614
/// @param[in] def The enum to return if no conversion was possible.
@@ -3780,6 +3886,19 @@ String IRac::boolToString(const bool value) {
37803886
return value ? kOnStr : kOffStr;
37813887
}
37823888

3889+
/// Convert the supplied operation mode into the appropriate String.
3890+
/// @param[in] cmdType The enum to be converted.
3891+
/// @return The equivalent String for the locale.
3892+
String IRac::commandTypeToString(const stdAc::ac_command_t cmdType) {
3893+
switch (cmdType) {
3894+
case stdAc::ac_command_t::kControlCommand: return kControlCommandStr;
3895+
case stdAc::ac_command_t::kSensorTempReport: return kIFeelReportStr;
3896+
case stdAc::ac_command_t::kTimerCommand: return kSetTimerCommandStr;
3897+
case stdAc::ac_command_t::kConfigCommand: return kConfigCommandStr;
3898+
default: return kUnknownStr;
3899+
}
3900+
}
3901+
37833902
/// Convert the supplied operation mode into the appropriate String.
37843903
/// @param[in] mode The enum to be converted.
37853904
/// @param[in] ha A flag to indicate we want GoogleHome/HomeAssistant output.

src/IRac.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class IRac {
8686
static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b);
8787
static bool strToBool(const char *str, const bool def = false);
8888
static int16_t strToModel(const char *str, const int16_t def = -1);
89+
static stdAc::ac_command_t strToCommandType(const char *str,
90+
const stdAc::ac_command_t def = stdAc::ac_command_t::kControlCommand);
8991
static stdAc::opmode_t strToOpmode(
9092
const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto);
9193
static stdAc::fanspeed_t strToFanspeed(
@@ -96,6 +98,7 @@ class IRac {
9698
static stdAc::swingh_t strToSwingH(
9799
const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff);
98100
static String boolToString(const bool value);
101+
static String commandTypeToString(const stdAc::ac_command_t cmdType);
99102
static String opmodeToString(const stdAc::opmode_t mode,
100103
const bool ha = false);
101104
static String fanspeedToString(const stdAc::fanspeed_t speed);
@@ -148,6 +151,11 @@ class IRac {
148151
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
149152
const bool night, const bool econo, const bool turbo, const bool filter,
150153
const bool light);
154+
void argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp);
155+
void argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param,
156+
const uint8_t value, bool safe = true);
157+
void argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on,
158+
const uint16_t currentTime, const uint16_t delayMinutes);
151159
#endif // SEND_ARGO
152160
#if SEND_BOSCH144
153161
void bosch144(IRBosch144AC *ac,

src/IRsend.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ enum class swingv_t {
7979
kLastSwingvEnum = kLowest,
8080
};
8181

82+
/// @brief Tyoe of A/C command (if the remote uses different codes for each)
83+
/// @note Most remotes support only a single command or aggregate multiple
84+
/// into one (e.g. control+timer). Use @c kControlCommand in such case
85+
enum class ac_command_t {
86+
kControlCommand = 0,
87+
kSensorTempReport = 1,
88+
kTimerCommand = 2,
89+
kConfigCommand = 3,
90+
// Add new entries before this one, and update it to point to the last entry
91+
kLastAcCommandEnum = kConfigCommand,
92+
};
93+
8294
/// Common A/C settings for Horizontal Swing.
8395
enum class swingh_t {
8496
kOff = -1,
@@ -113,6 +125,7 @@ struct state_t {
113125
bool beep = false;
114126
int16_t sleep = -1; // `-1` means off.
115127
int16_t clock = -1; // `-1` means not set.
128+
stdAc::ac_command_t command = stdAc::ac_command_t::kControlCommand;
116129
};
117130
}; // namespace stdAc
118131

src/IRtext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ IRTEXT_CONST_STRING(kTimerModeStr, D_STR_TIMERMODE); ///< "Timer Mode"
6969
IRTEXT_CONST_STRING(kClockStr, D_STR_CLOCK); ///< "Clock"
7070
IRTEXT_CONST_STRING(kCommandStr, D_STR_COMMAND); ///< "Command"
7171
IRTEXT_CONST_STRING(kConfigCommandStr, D_STR_CONFIG); ///< "Config"
72+
IRTEXT_CONST_STRING(kControlCommandStr, D_STR_CONTROL); ///< "Control"
7273
IRTEXT_CONST_STRING(kXFanStr, D_STR_XFAN); ///< "XFan"
7374
IRTEXT_CONST_STRING(kHealthStr, D_STR_HEALTH); ///< "Health"
7475
IRTEXT_CONST_STRING(kModelStr, D_STR_MODEL); ///< "Model"
@@ -208,6 +209,7 @@ IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode"
208209
IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///<
209210
///< "Swing(V) Toggle"
210211
IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle"
212+
IRTEXT_CONST_STRING(kSetTimerCommandStr, D_STR_SET_TIMER); ///< "Set Timer"
211213
IRTEXT_CONST_STRING(kScheduleStr, D_STR_SCHEDULE); ///< "Schedule"
212214
IRTEXT_CONST_STRING(kChStr, D_STR_CH); ///< "CH#"
213215
IRTEXT_CONST_STRING(kTimerActiveDaysStr, D_STR_TIMER_ACTIVE_DAYS);

src/IRtext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ extern IRTEXT_CONST_PTR(kComfortStr);
7070
extern IRTEXT_CONST_PTR(kCommaSpaceStr);
7171
extern IRTEXT_CONST_PTR(kCommandStr);
7272
extern IRTEXT_CONST_PTR(kConfigCommandStr);
73+
extern IRTEXT_CONST_PTR(kControlCommandStr);
7374
extern IRTEXT_CONST_PTR(kCoolStr);
7475
extern IRTEXT_CONST_PTR(kCoolingStr);
7576
extern IRTEXT_CONST_PTR(kDashStr);
@@ -224,6 +225,7 @@ extern IRTEXT_CONST_PTR(kTempUpStr);
224225
extern IRTEXT_CONST_PTR(kThreeLetterDayOfWeekStr);
225226
extern IRTEXT_CONST_PTR(kTimerActiveDaysStr);
226227
extern IRTEXT_CONST_PTR(kTimerModeStr);
228+
extern IRTEXT_CONST_PTR(kSetTimerCommandStr);
227229
extern IRTEXT_CONST_PTR(kTimerStr);
228230
extern IRTEXT_CONST_PTR(kToggleStr);
229231
extern IRTEXT_CONST_PTR(kTopStr);

src/ir_Argo.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,25 @@ uint8_t IRArgoACBase<T>::getSensorTemp(void) const {
10441044
return _.RoomTemp + kArgoTempDelta;
10451045
}
10461046

1047+
/// @brief Convert a stdAc::ac_command_t enum into its native message type.
1048+
/// @param command The enum to be converted.
1049+
/// @return The native equivalent of the enum.
1050+
template<typename T>
1051+
argoIrMessageType_t IRArgoACBase<T>::convertCommand(
1052+
const stdAc::ac_command_t command) {
1053+
switch (command) {
1054+
case stdAc::ac_command_t::kSensorTempReport:
1055+
return argoIrMessageType_t::IFEEL_TEMP_REPORT;
1056+
case stdAc::ac_command_t::kTimerCommand:
1057+
return argoIrMessageType_t::TIMER_COMMAND;
1058+
case stdAc::ac_command_t::kConfigCommand:
1059+
return argoIrMessageType_t::CONFIG_PARAM_SET;
1060+
case stdAc::ac_command_t::kControlCommand:
1061+
default:
1062+
return argoIrMessageType_t::AC_CONTROL;
1063+
}
1064+
}
1065+
10471066
/// Convert a stdAc::opmode_t enum into its native mode.
10481067
/// @param[in] mode The enum to be converted.
10491068
/// @return The native equivalent of the enum.
@@ -1169,6 +1188,26 @@ stdAc::swingv_t IRArgoACBase<ArgoProtocolWREM3>::toCommonSwingV(
11691188
}
11701189
}
11711190

1191+
/// Convert a native message type into its stdAc equivalent.
1192+
/// @param[in] command The native setting to be converted.
1193+
/// @return The stdAc equivalent of the native setting.
1194+
template<typename T>
1195+
stdAc::ac_command_t IRArgoACBase<T>::toCommonCommand(
1196+
const argoIrMessageType_t command) {
1197+
switch (command) {
1198+
case argoIrMessageType_t::AC_CONTROL:
1199+
return stdAc::ac_command_t::kControlCommand;
1200+
case argoIrMessageType_t::IFEEL_TEMP_REPORT:
1201+
return stdAc::ac_command_t::kSensorTempReport;
1202+
case argoIrMessageType_t::TIMER_COMMAND:
1203+
return stdAc::ac_command_t::kTimerCommand;
1204+
case argoIrMessageType_t::CONFIG_PARAM_SET:
1205+
return stdAc::ac_command_t::kConfigCommand;
1206+
default:
1207+
return stdAc::ac_command_t::kControlCommand;
1208+
}
1209+
}
1210+
11721211
/// Convert a native mode into its stdAc equivalent.
11731212
/// @param[in] mode The native setting to be converted.
11741213
/// @return The stdAc equivalent of the native setting.
@@ -1208,10 +1247,12 @@ stdAc::state_t IRArgoAC::toCommon(void) const {
12081247
stdAc::state_t result{};
12091248
result.protocol = decode_type_t::ARGO;
12101249
result.model = argo_ac_remote_model_t::SAC_WREM2;
1250+
result.command = toCommonCommand(_messageType);
12111251
result.power = _.Power;
12121252
result.mode = toCommonMode(getModeEx());
12131253
result.celsius = true;
1214-
result.degrees = getTemp();
1254+
result.degrees = (_messageType != argoIrMessageType_t::IFEEL_TEMP_REPORT)?
1255+
getTemp() : getSensorTemp();
12151256
result.fanspeed = toCommonFanSpeed(getFanEx());
12161257
result.turbo = _.Max;
12171258
result.sleep = _.Night ? 0 : -1;
@@ -1235,10 +1276,12 @@ stdAc::state_t IRArgoAC_WREM3::toCommon(void) const {
12351276
stdAc::state_t result{};
12361277
result.protocol = decode_type_t::ARGO;
12371278
result.model = argo_ac_remote_model_t::SAC_WREM3;
1279+
result.command = toCommonCommand(_messageType);
12381280
result.power = getPower();
12391281
result.mode = toCommonMode(getModeEx());
12401282
result.celsius = true;
1241-
result.degrees = getTemp();
1283+
result.degrees = (_messageType != argoIrMessageType_t::IFEEL_TEMP_REPORT)?
1284+
getTemp() : getSensorTemp();
12421285
result.fanspeed = toCommonFanSpeed(getFanEx());
12431286
result.turbo = _.Max;
12441287
result.swingv = toCommonSwingV(_.Flap);

src/ir_Argo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ class IRArgoACBase {
381381
static argoMode_t convertMode(const stdAc::opmode_t mode);
382382
static argoFan_t convertFan(const stdAc::fanspeed_t speed);
383383
static argoFlap_t convertSwingV(const stdAc::swingv_t position);
384+
static argoIrMessageType_t convertCommand(const stdAc::ac_command_t command);
384385

385386
protected:
386387
void _stateReset(ARGO_PROTOCOL_T *state, argoIrMessageType_t messageType
@@ -397,6 +398,7 @@ class IRArgoACBase {
397398
static stdAc::opmode_t toCommonMode(const argoMode_t mode);
398399
static stdAc::fanspeed_t toCommonFanSpeed(const argoFan_t speed);
399400
static stdAc::swingv_t toCommonSwingV(const uint8_t position);
401+
static stdAc::ac_command_t toCommonCommand(const argoIrMessageType_t command);
400402

401403
// Attributes
402404
ARGO_PROTOCOL_T _; ///< The raw protocol data

src/locale/defaults.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,12 @@ D_STR_INDIRECT " " D_STR_MODE
454454
#ifndef D_STR_CONFIG
455455
#define D_STR_CONFIG "Config"
456456
#endif // D_STR_CONFIG
457+
#ifndef D_STR_CONTROL
458+
#define D_STR_CONTROL "Control"
459+
#endif // D_STR_CONTROL
460+
#ifndef D_STR_SET_TIMER
461+
#define D_STR_SET_TIMER D_STR_SET " " D_STR_TIMER
462+
#endif // D_STR_AC_TIMER
457463
#ifndef D_STR_SCHEDULE
458464
#define D_STR_SCHEDULE "Schedule"
459465
#endif // D_STR_SCHEDULE

0 commit comments

Comments
 (0)