609 lines
21 KiB
C++
609 lines
21 KiB
C++
// Copyright 2019-2020 David Conran
|
|
|
|
/// @file
|
|
/// @brief Support for Neoclima protocols.
|
|
/// Analysis by crankyoldgit, AndreyShpilevoy, & griffisc306
|
|
/// Code by crankyoldgit
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/764
|
|
/// @see https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view
|
|
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1260
|
|
|
|
#include "ir_Neoclima.h"
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include "IRrecv.h"
|
|
#include "IRsend.h"
|
|
#include "IRtext.h"
|
|
#include "IRutils.h"
|
|
|
|
// Constants
|
|
const uint16_t kNeoclimaHdrMark = 6112;
|
|
const uint16_t kNeoclimaHdrSpace = 7391;
|
|
const uint16_t kNeoclimaBitMark = 537;
|
|
const uint16_t kNeoclimaOneSpace = 1651;
|
|
const uint16_t kNeoclimaZeroSpace = 571;
|
|
const uint32_t kNeoclimaMinGap = kDefaultMessageGap;
|
|
|
|
using irutils::addBoolToString;
|
|
using irutils::addFanToString;
|
|
using irutils::addIntToString;
|
|
using irutils::addLabeledString;
|
|
using irutils::addModeToString;
|
|
using irutils::addTempToString;
|
|
|
|
#if SEND_NEOCLIMA
|
|
/// Send a Neoclima message.
|
|
/// Status: STABLE / Known to be working.
|
|
/// @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::sendNeoclima(const unsigned char data[], const uint16_t nbytes,
|
|
const uint16_t repeat) {
|
|
// Set IR carrier frequency
|
|
enableIROut(38);
|
|
|
|
for (uint16_t i = 0; i <= repeat; i++) {
|
|
sendGeneric(kNeoclimaHdrMark, kNeoclimaHdrSpace,
|
|
kNeoclimaBitMark, kNeoclimaOneSpace,
|
|
kNeoclimaBitMark, kNeoclimaZeroSpace,
|
|
kNeoclimaBitMark, kNeoclimaHdrSpace,
|
|
data, nbytes, 38000, false, 0, // Repeats are already handled.
|
|
50);
|
|
// Extra footer.
|
|
mark(kNeoclimaBitMark);
|
|
space(kNeoclimaMinGap);
|
|
}
|
|
}
|
|
#endif // SEND_NEOCLIMA
|
|
|
|
/// 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?
|
|
IRNeoclimaAc::IRNeoclimaAc(const uint16_t pin, const bool inverted,
|
|
const bool use_modulation)
|
|
: _irsend(pin, inverted, use_modulation) {
|
|
stateReset();
|
|
}
|
|
|
|
/// Reset the state of the remote to a known good state/sequence.
|
|
void IRNeoclimaAc::stateReset(void) {
|
|
static const uint8_t kReset[kNeoclimaStateLength] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x2A, 0xA5};
|
|
setRaw(kReset);
|
|
}
|
|
|
|
/// Set up hardware to be able to send a message.
|
|
void IRNeoclimaAc::begin(void) { _irsend.begin(); }
|
|
|
|
/// Calculate the checksum for a given state.
|
|
/// @param[in] state The array to calc the checksum of.
|
|
/// @param[in] length The length/size of the array.
|
|
/// @return The calculated checksum value.
|
|
uint8_t IRNeoclimaAc::calcChecksum(const uint8_t state[],
|
|
const uint16_t length) {
|
|
if (length == 0) return state[0];
|
|
return sumBytes(state, length - 1);
|
|
}
|
|
|
|
/// Verify the checksum is valid for a given state.
|
|
/// @param[in] state The array to verify the checksum of.
|
|
/// @param[in] length The length/size of the array.
|
|
/// @return true, if the state has a valid checksum. Otherwise, false.
|
|
bool IRNeoclimaAc::validChecksum(const uint8_t state[], const uint16_t length) {
|
|
if (length < 2)
|
|
return true; // No checksum to compare with. Assume okay.
|
|
return (state[length - 1] == calcChecksum(state, length));
|
|
}
|
|
|
|
/// Calculate & update the checksum for the internal state.
|
|
/// @param[in] length The length/size of the internal state.
|
|
void IRNeoclimaAc::checksum(uint16_t length) {
|
|
if (length < 2) return;
|
|
_.Sum = calcChecksum(_.raw, length);
|
|
}
|
|
|
|
#if SEND_NEOCLIMA
|
|
/// Send the current internal state as an IR message.
|
|
/// @param[in] repeat Nr. of times the message will be repeated.
|
|
void IRNeoclimaAc::send(const uint16_t repeat) {
|
|
_irsend.sendNeoclima(getRaw(), kNeoclimaStateLength, repeat);
|
|
}
|
|
#endif // SEND_NEOCLIMA
|
|
|
|
/// Get a PTR to the internal state/code for this protocol.
|
|
/// @return PTR to a code for this protocol based on the current internal state.
|
|
uint8_t *IRNeoclimaAc::getRaw(void) {
|
|
checksum();
|
|
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 The length/size of the new_code array.
|
|
void IRNeoclimaAc::setRaw(const uint8_t new_code[], const uint16_t length) {
|
|
std::memcpy(_.raw, new_code, std::min(length, kNeoclimaStateLength));
|
|
}
|
|
|
|
/// Set the Button/Command pressed setting of the A/C.
|
|
/// @param[in] button The value of the button/command that was pressed.
|
|
void IRNeoclimaAc::setButton(const uint8_t button) {
|
|
switch (button) {
|
|
case kNeoclimaButtonPower:
|
|
case kNeoclimaButtonMode:
|
|
case kNeoclimaButtonTempUp:
|
|
case kNeoclimaButtonTempDown:
|
|
case kNeoclimaButtonSwing:
|
|
case kNeoclimaButtonFanSpeed:
|
|
case kNeoclimaButtonAirFlow:
|
|
case kNeoclimaButtonHold:
|
|
case kNeoclimaButtonSleep:
|
|
case kNeoclimaButtonLight:
|
|
case kNeoclimaButtonEye:
|
|
case kNeoclimaButtonFollow:
|
|
case kNeoclimaButtonIon:
|
|
case kNeoclimaButtonFresh:
|
|
case kNeoclimaButton8CHeat:
|
|
case kNeoclimaButtonTurbo:
|
|
case kNeoclimaButtonEcono:
|
|
case kNeoclimaButtonTempUnit:
|
|
_.Button = button;
|
|
break;
|
|
default:
|
|
_.Button = kNeoclimaButtonPower;
|
|
}
|
|
}
|
|
|
|
/// Get the Button/Command setting of the A/C.
|
|
/// @return The value of the button/command that was pressed.
|
|
uint8_t IRNeoclimaAc::getButton(void) const {
|
|
return _.Button;
|
|
}
|
|
|
|
/// Set the requested power state of the A/C to on.
|
|
void IRNeoclimaAc::on(void) { setPower(true); }
|
|
|
|
/// Set the requested power state of the A/C to off.
|
|
void IRNeoclimaAc::off(void) { setPower(false); }
|
|
|
|
/// Change the power setting.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setPower(const bool on) {
|
|
_.Button = kNeoclimaButtonPower;
|
|
_.Power = on;
|
|
}
|
|
|
|
/// Get the value of the current power setting.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getPower(void) const {
|
|
return _.Power;
|
|
}
|
|
|
|
/// Set the operating mode of the A/C.
|
|
/// @param[in] mode The desired operating mode.
|
|
void IRNeoclimaAc::setMode(const uint8_t mode) {
|
|
switch (mode) {
|
|
case kNeoclimaDry:
|
|
// In this mode fan speed always LOW
|
|
setFan(kNeoclimaFanLow);
|
|
// FALL THRU
|
|
case kNeoclimaAuto:
|
|
case kNeoclimaCool:
|
|
case kNeoclimaFan:
|
|
case kNeoclimaHeat:
|
|
_.Mode = mode;
|
|
_.Button = kNeoclimaButtonMode;
|
|
break;
|
|
default:
|
|
// If we get an unexpected mode, default to AUTO.
|
|
_.Mode = kNeoclimaAuto;
|
|
_.Button = kNeoclimaButtonMode;
|
|
}
|
|
}
|
|
|
|
/// Get the operating mode setting of the A/C.
|
|
/// @return The current operating mode setting.
|
|
uint8_t IRNeoclimaAc::getMode(void) const {
|
|
return _.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 IRNeoclimaAc::convertMode(const stdAc::opmode_t mode) {
|
|
switch (mode) {
|
|
case stdAc::opmode_t::kCool: return kNeoclimaCool;
|
|
case stdAc::opmode_t::kHeat: return kNeoclimaHeat;
|
|
case stdAc::opmode_t::kDry: return kNeoclimaDry;
|
|
case stdAc::opmode_t::kFan: return kNeoclimaFan;
|
|
default: return kNeoclimaAuto;
|
|
}
|
|
}
|
|
|
|
/// 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 IRNeoclimaAc::toCommonMode(const uint8_t mode) {
|
|
switch (mode) {
|
|
case kNeoclimaCool: return stdAc::opmode_t::kCool;
|
|
case kNeoclimaHeat: return stdAc::opmode_t::kHeat;
|
|
case kNeoclimaDry: return stdAc::opmode_t::kDry;
|
|
case kNeoclimaFan: return stdAc::opmode_t::kFan;
|
|
default: return stdAc::opmode_t::kAuto;
|
|
}
|
|
}
|
|
|
|
/// Set the temperature.
|
|
/// @param[in] temp The temperature in degrees celsius.
|
|
/// @param[in] celsius Use Fahrenheit (false) or Celsius (true).
|
|
void IRNeoclimaAc::setTemp(const uint8_t temp, const bool celsius) {
|
|
uint8_t oldtemp = getTemp();
|
|
_.UseFah = !celsius;
|
|
const uint8_t min_temp = celsius ? kNeoclimaMinTempC : kNeoclimaMinTempF;
|
|
const uint8_t max_temp = celsius ? kNeoclimaMaxTempC : kNeoclimaMaxTempF;
|
|
const uint8_t newtemp = std::min(max_temp, std::max(min_temp, temp));
|
|
if (oldtemp > newtemp)
|
|
_.Button = kNeoclimaButtonTempDown;
|
|
else if (newtemp > oldtemp)
|
|
_.Button = kNeoclimaButtonTempUp;
|
|
_.Temp = newtemp - min_temp;
|
|
}
|
|
|
|
/// Get the current temperature setting.
|
|
/// @return The current setting for temp. in degrees.
|
|
/// @note The units of the temperature (F/C) is determined by `getTempUnits()`.
|
|
uint8_t IRNeoclimaAc::getTemp(void) const {
|
|
const uint8_t min_temp = getTempUnits() ? kNeoclimaMinTempC
|
|
: kNeoclimaMinTempF;
|
|
return _.Temp + min_temp;
|
|
}
|
|
|
|
/// Set the speed of the fan.
|
|
/// @param[in] speed The desired setting. 0-3, 0 is auto, 1-3 is the speed
|
|
void IRNeoclimaAc::setFan(const uint8_t speed) {
|
|
_.Button = kNeoclimaButtonFanSpeed;
|
|
if (_.Mode == kNeoclimaDry) { // Dry mode only allows low speed.
|
|
_.Fan = kNeoclimaFanLow;
|
|
return;
|
|
}
|
|
switch (speed) {
|
|
case kNeoclimaFanAuto:
|
|
case kNeoclimaFanHigh:
|
|
case kNeoclimaFanMed:
|
|
case kNeoclimaFanLow:
|
|
_.Fan = speed;
|
|
break;
|
|
default:
|
|
// If we get an unexpected speed, default to Auto.
|
|
_.Fan = kNeoclimaFanAuto;
|
|
}
|
|
}
|
|
|
|
/// Get the current fan speed setting.
|
|
/// @return The current fan speed/mode.
|
|
uint8_t IRNeoclimaAc::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 IRNeoclimaAc::convertFan(const stdAc::fanspeed_t speed) {
|
|
switch (speed) {
|
|
case stdAc::fanspeed_t::kMin:
|
|
case stdAc::fanspeed_t::kLow: return kNeoclimaFanLow;
|
|
case stdAc::fanspeed_t::kMedium: return kNeoclimaFanMed;
|
|
case stdAc::fanspeed_t::kHigh:
|
|
case stdAc::fanspeed_t::kMax: return kNeoclimaFanHigh;
|
|
default: return kNeoclimaFanAuto;
|
|
}
|
|
}
|
|
|
|
/// 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 IRNeoclimaAc::toCommonFanSpeed(const uint8_t speed) {
|
|
switch (speed) {
|
|
case kNeoclimaFanHigh: return stdAc::fanspeed_t::kMax;
|
|
case kNeoclimaFanMed: return stdAc::fanspeed_t::kMedium;
|
|
case kNeoclimaFanLow: return stdAc::fanspeed_t::kMin;
|
|
default: return stdAc::fanspeed_t::kAuto;
|
|
}
|
|
}
|
|
|
|
/// Set the Sleep setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setSleep(const bool on) {
|
|
_.Button = kNeoclimaButtonSleep;
|
|
_.Sleep = on;
|
|
}
|
|
|
|
/// Get the Sleep setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getSleep(void) const {
|
|
return _.Sleep;
|
|
}
|
|
|
|
/// Set the vertical swing setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setSwingV(const bool on) {
|
|
_.Button = kNeoclimaButtonSwing;
|
|
_.SwingV = (on ? kNeoclimaSwingVOn : kNeoclimaSwingVOff);
|
|
}
|
|
|
|
/// Get the vertical swing setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getSwingV(void) const {
|
|
return _.SwingV == kNeoclimaSwingVOn;
|
|
}
|
|
|
|
/// Set the horizontal swing setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setSwingH(const bool on) {
|
|
_.Button = kNeoclimaButtonAirFlow;
|
|
_.SwingH = !on; // Cleared when `on`
|
|
}
|
|
|
|
/// Get the horizontal swing (Air Flow) setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getSwingH(void) const {
|
|
return !_.SwingH;
|
|
}
|
|
|
|
/// Set the Turbo setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setTurbo(const bool on) {
|
|
_.Button = kNeoclimaButtonTurbo;
|
|
_.Turbo = on;
|
|
}
|
|
|
|
/// Get the Turbo setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getTurbo(void) const {
|
|
return _.Turbo;
|
|
}
|
|
|
|
/// Set the Economy (Energy Saver) setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setEcono(const bool on) {
|
|
_.Button = kNeoclimaButtonEcono;
|
|
_.Econo = on;
|
|
}
|
|
|
|
/// Get the Economy (Energy Saver) setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getEcono(void) const {
|
|
return _.Econo;
|
|
}
|
|
|
|
/// Set the Fresh (air) setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setFresh(const bool on) {
|
|
_.Button = kNeoclimaButtonFresh;
|
|
_.Fresh = on;
|
|
}
|
|
|
|
/// Get the Fresh (air) setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getFresh(void) const {
|
|
return _.Fresh;
|
|
}
|
|
|
|
/// Set the Hold setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setHold(const bool on) {
|
|
_.Button = kNeoclimaButtonHold;
|
|
_.Hold = on;
|
|
}
|
|
|
|
/// Get the Hold setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getHold(void) const {
|
|
return _.Hold;
|
|
}
|
|
|
|
/// Set the Ion (filter) setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setIon(const bool on) {
|
|
_.Button = kNeoclimaButtonIon;
|
|
_.Ion = on;
|
|
}
|
|
|
|
/// Get the Ion (filter) setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getIon(void) const {
|
|
return _.Ion;
|
|
}
|
|
|
|
/// Set the Light(LED display) setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setLight(const bool on) {
|
|
_.Button = kNeoclimaButtonLight;
|
|
_.Light = on;
|
|
}
|
|
|
|
/// Get the Light (LED display) setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getLight(void) const {
|
|
return _.Light;
|
|
}
|
|
|
|
/// Set the 8°C Heat setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
/// @note This feature maintains the room temperature steadily at 8°C and
|
|
/// prevents the room from freezing by activating the heating operation
|
|
/// automatically when nobody is at home over a longer period during severe
|
|
/// winter.
|
|
void IRNeoclimaAc::set8CHeat(const bool on) {
|
|
_.Button = kNeoclimaButton8CHeat;
|
|
_.CHeat = on;
|
|
}
|
|
|
|
/// Get the 8°C Heat setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::get8CHeat(void) const {
|
|
return _.CHeat;
|
|
}
|
|
|
|
/// Set the Eye (Sensor) setting of the A/C.
|
|
/// @param[in] on true, the setting is on. false, the setting is off.
|
|
void IRNeoclimaAc::setEye(const bool on) {
|
|
_.Button = kNeoclimaButtonEye;
|
|
_.Eye = on;
|
|
}
|
|
|
|
/// Get the Eye (Sensor) setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getEye(void) const {
|
|
return _.Eye;
|
|
}
|
|
|
|
/// Is the A/C unit using Fahrenheit or Celsius for temperature units.
|
|
/// @return false, Fahrenheit. true, Celsius.
|
|
bool IRNeoclimaAc::getTempUnits(void) const {
|
|
return !_.UseFah;
|
|
}
|
|
|
|
/* DISABLED
|
|
TODO(someone): Work out why "on" is either 0x5D or 0x5F
|
|
void IRNeoclimaAc::setFollow(const bool on) {
|
|
setButton(kNeoclimaButtonFollow);
|
|
if (on)
|
|
remote_state[8] = kNeoclimaFollowMe;
|
|
else
|
|
remote_state[8] = 0;
|
|
}
|
|
*/
|
|
|
|
/// Get the Follow Me setting of the A/C.
|
|
/// @return true, the setting is on. false, the setting is off.
|
|
bool IRNeoclimaAc::getFollow(void) const {
|
|
return (_.Follow & kNeoclimaFollowMe) == kNeoclimaFollowMe;
|
|
}
|
|
|
|
/// Convert the current internal state into its stdAc::state_t equivalent.
|
|
/// @return The stdAc equivalent of the native settings.
|
|
stdAc::state_t IRNeoclimaAc::toCommon(void) const {
|
|
stdAc::state_t result;
|
|
result.protocol = decode_type_t::NEOCLIMA;
|
|
result.model = -1; // No models used.
|
|
result.power = _.Power;
|
|
result.mode = toCommonMode(_.Mode);
|
|
result.celsius = getTempUnits();
|
|
result.degrees = getTemp();
|
|
result.fanspeed = toCommonFanSpeed(_.Fan);
|
|
result.swingv = getSwingV() ? stdAc::swingv_t::kAuto
|
|
: stdAc::swingv_t::kOff;
|
|
result.swingh = getSwingH() ? stdAc::swingh_t::kAuto
|
|
: stdAc::swingh_t::kOff;
|
|
result.turbo = _.Turbo;
|
|
result.econo = _.Econo;
|
|
result.light = _.Light;
|
|
result.filter = _.Ion;
|
|
result.sleep = _.Sleep ? 0 : -1;
|
|
// Not supported.
|
|
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 IRNeoclimaAc::toString(void) const {
|
|
String result = "";
|
|
result.reserve(110); // Reserve some heap for the string to reduce fragging.
|
|
result += addBoolToString(_.Power, kPowerStr, false);
|
|
result += addModeToString(_.Mode, kNeoclimaAuto, kNeoclimaCool,
|
|
kNeoclimaHeat, kNeoclimaDry, kNeoclimaFan);
|
|
result += addTempToString(getTemp(), getTempUnits());
|
|
result += addFanToString(_.Fan, kNeoclimaFanHigh, kNeoclimaFanLow,
|
|
kNeoclimaFanAuto, kNeoclimaFanAuto, kNeoclimaFanMed);
|
|
result += addBoolToString(getSwingV(), kSwingVStr);
|
|
result += addBoolToString(getSwingH(), kSwingHStr);
|
|
result += addBoolToString(_.Sleep, kSleepStr);
|
|
result += addBoolToString(_.Turbo, kTurboStr);
|
|
result += addBoolToString(_.Econo, kEconoStr);
|
|
result += addBoolToString(_.Hold, kHoldStr);
|
|
result += addBoolToString(_.Ion, kIonStr);
|
|
result += addBoolToString(_.Eye, kEyeStr);
|
|
result += addBoolToString(_.Light, kLightStr);
|
|
result += addBoolToString(getFollow(), kFollowStr);
|
|
result += addBoolToString(_.CHeat, k8CHeatStr);
|
|
result += addBoolToString(_.Fresh, kFreshStr);
|
|
result += addIntToString(_.Button, kButtonStr);
|
|
result += kSpaceLBraceStr;
|
|
switch (_.Button) {
|
|
case kNeoclimaButtonPower: result += kPowerStr; break;
|
|
case kNeoclimaButtonMode: result += kModeStr; break;
|
|
case kNeoclimaButtonTempUp: result += kTempUpStr; break;
|
|
case kNeoclimaButtonTempDown: result += kTempDownStr; break;
|
|
case kNeoclimaButtonSwing: result += kSwingStr; break;
|
|
case kNeoclimaButtonFanSpeed: result += kFanStr; break;
|
|
case kNeoclimaButtonAirFlow: result += kAirFlowStr; break;
|
|
case kNeoclimaButtonHold: result += kHoldStr; break;
|
|
case kNeoclimaButtonSleep: result += kSleepStr; break;
|
|
case kNeoclimaButtonLight: result += kLightStr; break;
|
|
case kNeoclimaButtonEye: result += kEyeStr; break;
|
|
case kNeoclimaButtonFollow: result += kFollowStr; break;
|
|
case kNeoclimaButtonIon: result += kIonStr; break;
|
|
case kNeoclimaButtonFresh: result += kFreshStr; break;
|
|
case kNeoclimaButton8CHeat: result += k8CHeatStr; break;
|
|
case kNeoclimaButtonTurbo: result += kTurboStr; break;
|
|
case kNeoclimaButtonEcono: result += kEconoStr; break;
|
|
case kNeoclimaButtonTempUnit: result += kCelsiusFahrenheitStr; break;
|
|
default: result += kUnknownStr;
|
|
}
|
|
result += ')';
|
|
return result;
|
|
}
|
|
|
|
#if DECODE_NEOCLIMA
|
|
/// Decode the supplied Neoclima message.
|
|
/// Status: STABLE / Known working
|
|
/// @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.
|
|
/// @return True if it can decode it, false if it can't.
|
|
bool IRrecv::decodeNeoclima(decode_results *results, uint16_t offset,
|
|
const uint16_t nbits, const bool strict) {
|
|
// Compliance
|
|
if (strict && nbits != kNeoclimaBits)
|
|
return false; // Incorrect nr. of bits per spec.
|
|
|
|
// Match Main Header + Data + Footer
|
|
uint16_t used;
|
|
used = matchGeneric(results->rawbuf + offset, results->state,
|
|
results->rawlen - offset, nbits,
|
|
kNeoclimaHdrMark, kNeoclimaHdrSpace,
|
|
kNeoclimaBitMark, kNeoclimaOneSpace,
|
|
kNeoclimaBitMark, kNeoclimaZeroSpace,
|
|
kNeoclimaBitMark, kNeoclimaHdrSpace, false,
|
|
_tolerance, 0, false);
|
|
if (!used) return false;
|
|
offset += used;
|
|
// Extra footer.
|
|
uint64_t unused;
|
|
if (!matchGeneric(results->rawbuf + offset, &unused,
|
|
results->rawlen - offset, 0, 0, 0, 0, 0, 0, 0,
|
|
kNeoclimaBitMark, kNeoclimaHdrSpace, true)) return false;
|
|
|
|
// Compliance
|
|
if (strict) {
|
|
// Check we got a valid checksum.
|
|
if (!IRNeoclimaAc::validChecksum(results->state, nbits / 8)) return false;
|
|
}
|
|
|
|
// Success
|
|
results->decode_type = decode_type_t::NEOCLIMA;
|
|
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;
|
|
}
|
|
#endif // DECODE_NEOCLIMA
|