Skip to content

Commit f8e5662

Browse files
authored
Make KELON (48 bit) protocol decoding stricter. (#1746)
* Ensure checking of inter-message gap is being done. - Fixes a false positive detection. * Code style cleanup. * Ensure result address & commands are cleared. * Add unit tests based on Discussion #1744 * Improve older unit tests by confirming capture code is correct. Ref #1744
1 parent ccb8542 commit f8e5662

File tree

2 files changed

+123
-112
lines changed

2 files changed

+123
-112
lines changed

src/ir_Kelon.cpp

Lines changed: 52 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ const uint32_t kKelonGap = 2 * kDefaultMessageGap;
4040
const uint16_t kKelonFreq = 38000;
4141

4242
#if SEND_KELON
43-
4443
/// Send a Kelon message.
4544
/// Status: STABLE / Working.
4645
/// @param[in] data The data to be transmitted.
@@ -55,7 +54,6 @@ void IRsend::sendKelon(const uint64_t data, const uint16_t nbits,
5554
data, nbits, kKelonFreq, false, // LSB First.
5655
repeat, 50);
5756
}
58-
5957
#endif // SEND_KELON
6058

6159
#if DECODE_KELON
@@ -67,27 +65,24 @@ void IRsend::sendKelon(const uint64_t data, const uint16_t nbits,
6765
/// @param[in] nbits The number of data bits to expect.
6866
/// @param[in] strict Flag indicating if we should perform strict matching.
6967
/// @return True if it can decode it, false if it can't.
70-
7168
bool IRrecv::decodeKelon(decode_results *results, uint16_t offset,
7269
const uint16_t nbits, const bool strict) {
73-
if (strict && nbits != kKelonBits) {
74-
return false;
75-
}
76-
if (!matchGeneric(results->rawbuf + offset, results->state,
70+
if (strict && nbits != kKelonBits) return false;
71+
72+
if (!matchGeneric(results->rawbuf + offset, &(results->value),
7773
results->rawlen - offset, nbits,
7874
kKelonHdrMark, kKelonHdrSpace,
7975
kKelonBitMark, kKelonOneSpace,
8076
kKelonBitMark, kKelonZeroSpace,
81-
kKelonBitMark, 0, false,
82-
_tolerance, 0, false)) {
83-
return false;
84-
}
77+
kKelonBitMark, kKelonGap, true,
78+
_tolerance, 0, false)) return false;
8579

8680
results->decode_type = decode_type_t::KELON;
81+
results->address = 0;
82+
results->command = 0;
8783
results->bits = nbits;
8884
return true;
8985
}
90-
9186
#endif // DECODE_KELON
9287

9388
/// Class constructor
@@ -151,21 +146,15 @@ void IRKelonAc::ensurePower(bool on) {
151146
#endif // SEND_KELON
152147

153148
/// Set up hardware to be able to send a message.
154-
void IRKelonAc::begin() {
155-
_irsend.begin();
156-
}
149+
void IRKelonAc::begin() { _irsend.begin(); }
157150

158151
/// Request toggling power - will be reset to false after sending
159152
/// @param[in] toggle Whether to toggle the power state
160-
void IRKelonAc::setTogglePower(const bool toggle) {
161-
_.PowerToggle = toggle;
162-
}
153+
void IRKelonAc::setTogglePower(const bool toggle) { _.PowerToggle = toggle; }
163154

164155
/// Get whether toggling power will be requested
165156
/// @return The power toggle state
166-
bool IRKelonAc::getTogglePower() const {
167-
return _.PowerToggle;
168-
}
157+
bool IRKelonAc::getTogglePower() const { return _.PowerToggle; }
169158

170159
/// Set the temperature setting.
171160
/// @param[in] degrees The temperature in degrees celsius.
@@ -178,9 +167,7 @@ void IRKelonAc::setTemp(const uint8_t degrees) {
178167

179168
/// Get the current temperature setting.
180169
/// @return Get current setting for temp. in degrees celsius.
181-
uint8_t IRKelonAc::getTemp() const {
182-
return _.Temperature + kKelonMinTemp;
183-
}
170+
uint8_t IRKelonAc::getTemp() const { return _.Temperature + kKelonMinTemp; }
184171

185172
/// Set the speed of the fan.
186173
/// @param[in] speed 0 is auto, 1-5 is the speed
@@ -207,11 +194,10 @@ void IRKelonAc::setDryGrade(const int8_t grade) {
207194

208195
// Two's complement is clearly too bleeding edge for this manufacturer
209196
uint8_t outval;
210-
if (drygrade < 0) {
197+
if (drygrade < 0)
211198
outval = 0b100 | (-drygrade & 0b011);
212-
} else {
199+
else
213200
outval = drygrade & 0b011;
214-
}
215201
_.DehumidifierGrade = outval;
216202
}
217203

@@ -260,9 +246,7 @@ void IRKelonAc::setMode(const uint8_t mode) {
260246

261247
/// Get the current operation mode setting.
262248
/// @return The current operation mode.
263-
uint8_t IRKelonAc::getMode() const {
264-
return _.Mode;
265-
}
249+
uint8_t IRKelonAc::getMode() const { return _.Mode; }
266250

267251
/// Request toggling the vertical swing - will be reset to false after sending
268252
/// @param[in] toggle If true, the swing mode will be toggled when sent.
@@ -272,21 +256,15 @@ void IRKelonAc::setToggleSwingVertical(const bool toggle) {
272256

273257
/// Get whether the swing mode is set to be toggled
274258
/// @return Whether the toggle bit is set
275-
bool IRKelonAc::getToggleSwingVertical() const {
276-
return _.SwingVToggle;
277-
}
259+
bool IRKelonAc::getToggleSwingVertical() const { return _.SwingVToggle; }
278260

279261
/// Control the current sleep (quiet) setting.
280262
/// @param[in] on The desired setting.
281-
void IRKelonAc::setSleep(const bool on) {
282-
_.SleepEnabled = on;
283-
}
263+
void IRKelonAc::setSleep(const bool on) { _.SleepEnabled = on; }
284264

285265
/// Is the sleep setting on?
286266
/// @return The current value.
287-
bool IRKelonAc::getSleep() const {
288-
return _.SleepEnabled;
289-
}
267+
bool IRKelonAc::getSleep() const { return _.SleepEnabled; }
290268

291269
/// Control the current super cool mode setting.
292270
/// @param[in] on The desired setting.
@@ -305,9 +283,7 @@ void IRKelonAc::setSupercool(const bool on) {
305283

306284
/// Is the super cool mode setting on?
307285
/// @return The current value.
308-
bool IRKelonAc::getSupercool() const {
309-
return _.SuperCoolEnabled1;
310-
}
286+
bool IRKelonAc::getSupercool() const { return _.SuperCoolEnabled1; }
311287

312288
/// Set the timer time and enable it. Timer is an off timer if the unit is on,
313289
/// it is an on timer if the unit is off.
@@ -334,54 +310,39 @@ void IRKelonAc::setTimer(uint16_t mins) {
334310
/// later disabled.
335311
/// @return The timer set minutes
336312
uint16_t IRKelonAc::getTimer() const {
337-
if (_.TimerHours >= 10) {
313+
if (_.TimerHours >= 10)
338314
return ((uint16_t) ((_.TimerHours << 1) | _.TimerHalfHour) - 10) * 60;
339-
}
340315
return (((uint16_t) _.TimerHours) * 60) + (_.TimerHalfHour ? 30 : 0);
341316
}
342317

343318
/// Enable or disable the timer. Note that in order to enable the timer the
344319
/// minutes must be set with setTimer().
345320
/// @param[in] on Whether to enable or disable the timer
346-
void IRKelonAc::setTimerEnabled(bool on) {
347-
_.TimerEnabled = on;
348-
}
321+
void IRKelonAc::setTimerEnabled(bool on) { _.TimerEnabled = on; }
349322

350323
/// Get the current timer status
351324
/// @return Whether the timer is enabled.
352-
bool IRKelonAc::getTimerEnabled() const {
353-
return _.TimerEnabled;
354-
}
355-
325+
bool IRKelonAc::getTimerEnabled() const { return _.TimerEnabled; }
356326

357327
/// Get the raw state of the object, suitable to be sent with the appropriate
358328
/// IRsend object method.
359329
/// @return A PTR to the internal state.
360-
uint64_t IRKelonAc::getRaw() const {
361-
return _.raw;
362-
}
330+
uint64_t IRKelonAc::getRaw() const { return _.raw; }
363331

364332
/// Set the raw state of the object.
365333
/// @param[in] new_code The raw state from the native IR message.
366-
void IRKelonAc::setRaw(const uint64_t new_code) {
367-
_.raw = new_code;
368-
}
334+
void IRKelonAc::setRaw(const uint64_t new_code) { _.raw = new_code; }
369335

370336
/// Convert a standard A/C mode (stdAc::opmode_t) into it a native mode.
371337
/// @param[in] mode A stdAc::opmode_t operation mode.
372338
/// @return The native mode equivalent.
373339
uint8_t IRKelonAc::convertMode(const stdAc::opmode_t mode) {
374340
switch (mode) {
375-
case stdAc::opmode_t::kCool:
376-
return kKelonModeCool;
377-
case stdAc::opmode_t::kHeat:
378-
return kKelonModeHeat;
379-
case stdAc::opmode_t::kDry:
380-
return kKelonModeDry;
381-
case stdAc::opmode_t::kFan:
382-
return kKelonModeFan;
383-
default:
384-
return kKelonModeSmart;
341+
case stdAc::opmode_t::kCool: return kKelonModeCool;
342+
case stdAc::opmode_t::kHeat: return kKelonModeHeat;
343+
case stdAc::opmode_t::kDry: return kKelonModeDry;
344+
case stdAc::opmode_t::kFan: return kKelonModeFan;
345+
default: return kKelonModeSmart; // aka Auto.
385346
}
386347
}
387348

@@ -391,15 +352,11 @@ uint8_t IRKelonAc::convertMode(const stdAc::opmode_t mode) {
391352
uint8_t IRKelonAc::convertFan(stdAc::fanspeed_t fan) {
392353
switch (fan) {
393354
case stdAc::fanspeed_t::kMin:
394-
case stdAc::fanspeed_t::kLow:
395-
return kKelonFanMin;
396-
case stdAc::fanspeed_t::kMedium:
397-
return kKelonFanMedium;
355+
case stdAc::fanspeed_t::kLow: return kKelonFanMin;
356+
case stdAc::fanspeed_t::kMedium: return kKelonFanMedium;
398357
case stdAc::fanspeed_t::kHigh:
399-
case stdAc::fanspeed_t::kMax:
400-
return kKelonFanMax;
401-
default:
402-
return kKelonFanAuto;
358+
case stdAc::fanspeed_t::kMax: return kKelonFanMax;
359+
default: return kKelonFanAuto;
403360
}
404361
}
405362

@@ -408,16 +365,11 @@ uint8_t IRKelonAc::convertFan(stdAc::fanspeed_t fan) {
408365
/// @return The stdAc::opmode_t equivalent.
409366
stdAc::opmode_t IRKelonAc::toCommonMode(const uint8_t mode) {
410367
switch (mode) {
411-
case kKelonModeCool:
412-
return stdAc::opmode_t::kCool;
413-
case kKelonModeHeat:
414-
return stdAc::opmode_t::kHeat;
415-
case kKelonModeDry:
416-
return stdAc::opmode_t::kDry;
417-
case kKelonModeFan:
418-
return stdAc::opmode_t::kFan;
419-
default:
420-
return stdAc::opmode_t::kAuto;
368+
case kKelonModeCool: return stdAc::opmode_t::kCool;
369+
case kKelonModeHeat: return stdAc::opmode_t::kHeat;
370+
case kKelonModeDry: return stdAc::opmode_t::kDry;
371+
case kKelonModeFan: return stdAc::opmode_t::kFan;
372+
default: return stdAc::opmode_t::kAuto;
421373
}
422374
}
423375

@@ -426,14 +378,10 @@ stdAc::opmode_t IRKelonAc::toCommonMode(const uint8_t mode) {
426378
/// @return The stdAc::fanspeed_t equivalent.
427379
stdAc::fanspeed_t IRKelonAc::toCommonFanSpeed(const uint8_t speed) {
428380
switch (speed) {
429-
case kKelonFanMin:
430-
return stdAc::fanspeed_t::kLow;
431-
case kKelonFanMedium:
432-
return stdAc::fanspeed_t::kMedium;
433-
case kKelonFanMax:
434-
return stdAc::fanspeed_t::kHigh;
435-
default:
436-
return stdAc::fanspeed_t::kAuto;
381+
case kKelonFanMin: return stdAc::fanspeed_t::kLow;
382+
case kKelonFanMedium: return stdAc::fanspeed_t::kMedium;
383+
case kKelonFanMax: return stdAc::fanspeed_t::kHigh;
384+
default: return stdAc::fanspeed_t::kAuto;
437385
}
438386
}
439387

@@ -443,21 +391,20 @@ stdAc::state_t IRKelonAc::toCommon(const stdAc::state_t *prev) const {
443391
stdAc::state_t result{};
444392
result.protocol = decode_type_t::KELON;
445393
result.model = -1; // Unused.
394+
// AC only supports toggling it
395+
result.power = (prev == nullptr || prev->power) ^ _.PowerToggle;
446396
result.mode = toCommonMode(getMode());
447397
result.celsius = true;
448398
result.degrees = getTemp();
449399
result.fanspeed = toCommonFanSpeed(getFan());
450-
result.turbo = getSupercool();
451-
result.sleep = getSleep() ? 0 : -1;
452-
// Not supported.
453-
// N/A, AC only supports toggling it
454-
result.power = (prev == nullptr || prev->power) ^ _.PowerToggle;
455-
// N/A, AC only supports toggling it
400+
// AC only supports toggling it
456401
result.swingv = stdAc::swingv_t::kAuto;
457402
if (prev != nullptr &&
458-
(prev->swingv != stdAc::swingv_t::kAuto) ^ _.SwingVToggle) {
403+
(prev->swingv != stdAc::swingv_t::kAuto) ^ _.SwingVToggle)
459404
result.swingv = stdAc::swingv_t::kOff;
460-
}
405+
result.turbo = getSupercool();
406+
result.sleep = getSleep() ? 0 : -1;
407+
// Not supported.
461408
result.swingh = stdAc::swingh_t::kOff;
462409
result.light = true;
463410
result.beep = true;
@@ -483,20 +430,13 @@ String IRKelonAc::toString() const {
483430
result += addBoolToString(_.SleepEnabled, kSleepStr);
484431
result += addSignedIntToString(getDryGrade(), kDryStr);
485432
result += addLabeledString(
486-
getTimerEnabled()
487-
? (
488-
getTimer() > 0
489-
? minsToString(getTimer())
490-
: kOnStr
491-
)
492-
: kOffStr,
433+
getTimerEnabled() ? (getTimer() > 0 ? minsToString(getTimer()) : kOnStr)
434+
: kOffStr,
493435
kTimerStr);
494436
result += addBoolToString(getSupercool(), kTurboStr);
495-
if (getTogglePower()) {
437+
if (getTogglePower())
496438
result += addBoolToString(true, kPowerToggleStr);
497-
}
498-
if (getToggleSwingVertical()) {
439+
if (getToggleSwingVertical())
499440
result += addBoolToString(true, kSwingVToggleStr);
500-
}
501441
return result;
502442
}

0 commit comments

Comments
 (0)