mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 09:18:00 +01:00
feat: Add Argo Ulisse A/C IR remote
tested on Argo Ulisse 13 DCI ECO (WREM-3 remote)
This commit is contained in:
parent
61c6581123
commit
743e487dd4
4 changed files with 461 additions and 0 deletions
1
esphome/components/argo_ulisse/__init__.py
Normal file
1
esphome/components/argo_ulisse/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@yoziru"]
|
239
esphome/components/argo_ulisse/argo_ulisse.cpp
Normal file
239
esphome/components/argo_ulisse/argo_ulisse.cpp
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
#include "argo_ulisse.h"
|
||||||
|
|
||||||
|
#include <cmath> // std::isnan
|
||||||
|
|
||||||
|
#include "esphome/components/remote_base/remote_base.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace argo_ulisse {
|
||||||
|
|
||||||
|
static const char *const TAG = "argo.climate";
|
||||||
|
|
||||||
|
void ArgoUlisseClimate::setup() {
|
||||||
|
climate_ir::ClimateIR::setup();
|
||||||
|
|
||||||
|
// Never send nan to HA
|
||||||
|
if (std::isnan(this->current_temperature))
|
||||||
|
this->current_temperature = 0;
|
||||||
|
|
||||||
|
// add a callback to handle the iFeel feature
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up iFeel callback..");
|
||||||
|
this->add_on_state_callback([this](climate::Climate &climate) {
|
||||||
|
ESP_LOGV(TAG, "Received state callback for iFeel report..");
|
||||||
|
if (this->ifeel_) {
|
||||||
|
this->transmit_ifeel_();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ArgoUlisseClimate::calc_checksum_(const ArgoProtocolWREM3 *data, size_t length) {
|
||||||
|
ESP_LOGV(TAG, "Calculating checksum..");
|
||||||
|
if (length < 1) {
|
||||||
|
ESP_LOGW(TAG, "Nothing to calculate checksum on");
|
||||||
|
return 0; // Changed from -1 to 0 as the return type is uint8_t
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t payloadSizeBits = (length - 1) * 8; // Last byte carries checksum
|
||||||
|
|
||||||
|
uint8_t checksum = 0; // Initialize checksum
|
||||||
|
const uint8_t *ptr = data->raw; // Point to the start of the raw data
|
||||||
|
|
||||||
|
// Calculate checksum for all bytes except the last one
|
||||||
|
for (size_t i = 0; i < length - 1; i++) {
|
||||||
|
checksum += ptr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add stray bits from last byte to the checksum (if any)
|
||||||
|
const uint8_t maskPayload = 0xFF >> (8 - (payloadSizeBits % 8));
|
||||||
|
checksum += (ptr[length - 1] & maskPayload);
|
||||||
|
|
||||||
|
const uint8_t maskChecksum = 0xFF >> (payloadSizeBits % 8);
|
||||||
|
return checksum & maskChecksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgoUlisseClimate::transmit_state() {
|
||||||
|
ESP_LOGD(TAG, "Transmitting state..");
|
||||||
|
this->last_transmit_time_ = millis();
|
||||||
|
ArgoProtocolWREM3 ac_packet;
|
||||||
|
memset(&ac_packet, 0, sizeof(ac_packet));
|
||||||
|
|
||||||
|
// Set up the header
|
||||||
|
ac_packet.Pre1 = ARGO_PREAMBLE;
|
||||||
|
ac_packet.IrChannel = 0; // Assume channel 0, adjust if needed
|
||||||
|
ac_packet.IrCommandType = ArgoIRMessageType_AC_CONTROL;
|
||||||
|
|
||||||
|
// Set default HA clima features
|
||||||
|
ESP_LOGV(TAG, " Setting default HA climate features..");
|
||||||
|
ac_packet.Mode = this->operation_mode_(); // Set mode
|
||||||
|
ac_packet.RoomTemp = this->sensor_temperature_(); // for iFeel
|
||||||
|
ac_packet.Temp = this->temperature_(); // target temperature
|
||||||
|
ac_packet.Fan = this->fan_speed_();
|
||||||
|
ac_packet.Flap = this->swing_mode_();
|
||||||
|
|
||||||
|
// Set other features (adjust as needed)
|
||||||
|
ESP_LOGV(TAG, " Setting up other features..");
|
||||||
|
ac_packet.Power = this->mode != climate::CLIMATE_MODE_OFF;
|
||||||
|
ac_packet.iFeel = this->ifeel_;
|
||||||
|
ac_packet.Night = this->preset == climate::CLIMATE_PRESET_SLEEP;
|
||||||
|
ac_packet.Eco = this->preset == climate::CLIMATE_PRESET_ECO;
|
||||||
|
ac_packet.Boost = this->preset == climate::CLIMATE_PRESET_BOOST;
|
||||||
|
ac_packet.Filter = this->filter_;
|
||||||
|
ac_packet.Light =
|
||||||
|
this->light_ && this->preset != climate::CLIMATE_PRESET_SLEEP && this->mode != climate::CLIMATE_MODE_OFF;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, " iFeel: %s", ac_packet.iFeel ? "true" : "false");
|
||||||
|
ESP_LOGD(TAG, " Light: %s", ac_packet.Light ? "true" : "false");
|
||||||
|
ESP_LOGD(TAG, " Filter: %s", ac_packet.Filter ? "true" : "false");
|
||||||
|
|
||||||
|
ac_packet.Post1 = ARGO_POST1;
|
||||||
|
|
||||||
|
// Calculate checksum
|
||||||
|
ESP_LOGV(TAG, " Calculating AC checksum..");
|
||||||
|
ac_packet.Sum = this->calc_checksum_(&ac_packet, ArgoIRMessageLength_AC_CONTROL);
|
||||||
|
|
||||||
|
// Transmit the IR signal
|
||||||
|
auto transmit = this->transmitter_->transmit();
|
||||||
|
auto *data = transmit.get_data();
|
||||||
|
data->set_carrier_frequency(ARGO_IR_FREQUENCY); // Adjust if the frequency is different
|
||||||
|
|
||||||
|
// Send the header
|
||||||
|
data->mark(ARGO_HEADER_MARK);
|
||||||
|
data->space(ARGO_HEADER_SPACE);
|
||||||
|
|
||||||
|
// Transmit the data
|
||||||
|
for (uint8_t i = 0; i < ArgoIRMessageLength_AC_CONTROL; i++) {
|
||||||
|
for (uint8_t mask = 1; mask > 0; mask <<= 1) {
|
||||||
|
data->mark(ARGO_BIT_MARK);
|
||||||
|
bool bit = ac_packet.raw[i] & mask;
|
||||||
|
data->space(bit ? ARGO_ONE_SPACE : ARGO_ZERO_SPACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data->mark(ARGO_BIT_MARK);
|
||||||
|
data->space(0);
|
||||||
|
|
||||||
|
transmit.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgoUlisseClimate::transmit_ifeel_() {
|
||||||
|
// Send current room temperature for the iFeel feature as a silent IR
|
||||||
|
// message (no acknowledgement from the device) (WREM3)
|
||||||
|
ESP_LOGD(TAG, "Transmitting iFeel report..");
|
||||||
|
this->last_transmit_time_ = millis();
|
||||||
|
|
||||||
|
ArgoProtocolWREM3 ifeel_packet;
|
||||||
|
|
||||||
|
// Set up the header
|
||||||
|
ifeel_packet.Pre1 = ARGO_PREAMBLE;
|
||||||
|
ifeel_packet.IrChannel = 0; // Assume channel 0, adjust if needed
|
||||||
|
ifeel_packet.IrCommandType = ArgoIRMessageType_IFEEL_TEMP_REPORT;
|
||||||
|
|
||||||
|
ifeel_packet.ifeel.SensorT = this->sensor_temperature_();
|
||||||
|
|
||||||
|
// Calculate checksum
|
||||||
|
ESP_LOGV(TAG, " Calculating iFeel checksum..");
|
||||||
|
ifeel_packet.ifeel.CheckHi = this->calc_checksum_(&ifeel_packet, ArgoIRMessageLength_IFEEL_TEMP_REPORT);
|
||||||
|
|
||||||
|
// Transmit the IR signal
|
||||||
|
auto transmit = this->transmitter_->transmit();
|
||||||
|
auto *data = transmit.get_data();
|
||||||
|
data->set_carrier_frequency(ARGO_IR_FREQUENCY); // Adjust if the frequency is different
|
||||||
|
|
||||||
|
// Send the header
|
||||||
|
data->mark(ARGO_HEADER_MARK);
|
||||||
|
data->space(ARGO_HEADER_SPACE);
|
||||||
|
|
||||||
|
// Transmit the data
|
||||||
|
for (uint8_t i = 0; i < ArgoIRMessageLength_IFEEL_TEMP_REPORT; i++) {
|
||||||
|
for (uint8_t mask = 1; mask > 0; mask <<= 1) {
|
||||||
|
data->mark(ARGO_BIT_MARK);
|
||||||
|
bool bit = ifeel_packet.raw[i] & mask;
|
||||||
|
data->space(bit ? ARGO_ONE_SPACE : ARGO_ZERO_SPACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data->mark(ARGO_BIT_MARK);
|
||||||
|
data->space(0);
|
||||||
|
|
||||||
|
transmit.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ArgoUlisseClimate::operation_mode_() {
|
||||||
|
switch (this->mode) {
|
||||||
|
case climate::CLIMATE_MODE_COOL:
|
||||||
|
return ARGO_MODE_COOL;
|
||||||
|
case climate::CLIMATE_MODE_DRY:
|
||||||
|
return ARGO_MODE_DRY;
|
||||||
|
case climate::CLIMATE_MODE_HEAT:
|
||||||
|
return ARGO_MODE_HEAT;
|
||||||
|
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||||
|
return ARGO_MODE_AUTO;
|
||||||
|
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||||
|
return ARGO_MODE_FAN;
|
||||||
|
case climate::CLIMATE_MODE_OFF:
|
||||||
|
default:
|
||||||
|
return ARGO_MODE_OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ArgoUlisseClimate::fan_speed_() {
|
||||||
|
switch (this->fan_mode.value()) {
|
||||||
|
case climate::CLIMATE_FAN_LOW:
|
||||||
|
return ARGO_FAN_SILENT;
|
||||||
|
case climate::CLIMATE_FAN_MEDIUM:
|
||||||
|
return ARGO_FAN_MEDIUM;
|
||||||
|
case climate::CLIMATE_FAN_HIGH:
|
||||||
|
return ARGO_FAN_HIGHEST;
|
||||||
|
case climate::CLIMATE_FAN_AUTO:
|
||||||
|
default:
|
||||||
|
return ARGO_FAN_AUTO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ArgoUlisseClimate::swing_mode_() {
|
||||||
|
switch (this->swing_mode) {
|
||||||
|
case climate::CLIMATE_SWING_VERTICAL:
|
||||||
|
return ARGO_FLAP_FULL_AUTO;
|
||||||
|
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||||
|
return ARGO_FLAP_HALF_AUTO; // hacky way to set half auto vertical swing
|
||||||
|
default:
|
||||||
|
return ARGO_FLAP_MIDDLE_HIGH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ArgoUlisseClimate::temperature_() {
|
||||||
|
// Clamp the temperature to the valid range
|
||||||
|
float temp_valid =
|
||||||
|
clamp<float>(this->target_temperature, ARGO_TEMP_MIN - ARGO_TEMP_OFFSET, ARGO_TEMP_MAX - ARGO_TEMP_OFFSET);
|
||||||
|
return process_temperature_(temp_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ArgoUlisseClimate::sensor_temperature_() {
|
||||||
|
float temp_valid = clamp<float>(this->current_temperature, ARGO_SENSOR_TEMP_MIN - ARGO_TEMP_OFFSET,
|
||||||
|
ARGO_SENSOR_TEMP_MAX - ARGO_TEMP_OFFSET);
|
||||||
|
return process_temperature_(temp_valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ArgoUlisseClimate::process_temperature_(float temperature) {
|
||||||
|
ESP_LOGV(TAG, "Processing temperature..");
|
||||||
|
ESP_LOGV(TAG, " Input Temperature: %.2f°C", temperature);
|
||||||
|
|
||||||
|
// Sending 0 equals +4, e.g. "If I want 12 degrees, I need to send 8"
|
||||||
|
temperature -= ARGO_TEMP_OFFSET;
|
||||||
|
ESP_LOGV(TAG, " Delta Temperature: %.2f°C", temperature);
|
||||||
|
|
||||||
|
// floor the temperature
|
||||||
|
uint8_t temp = (uint8_t) round(temperature);
|
||||||
|
ESP_LOGV(TAG, " Processed Temperature: %d°C", temp);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
climate::ClimateTraits ArgoUlisseClimate::traits() {
|
||||||
|
climate::ClimateTraits traits = climate_ir::ClimateIR::traits();
|
||||||
|
traits.set_supports_current_temperature(true);
|
||||||
|
return traits;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgoUlisseClimate::control(const climate::ClimateCall &call) { climate_ir::ClimateIR::control(call); }
|
||||||
|
|
||||||
|
} // namespace argo_ulisse
|
||||||
|
} // namespace esphome
|
203
esphome/components/argo_ulisse/argo_ulisse.h
Normal file
203
esphome/components/argo_ulisse/argo_ulisse.h
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/climate_ir/climate_ir.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace argo_ulisse {
|
||||||
|
|
||||||
|
// Values for Argo Ulisse 13 DCI Eco (WREM-3 remote) IR Controllers
|
||||||
|
// Originally reverse-engineered by @mbronk
|
||||||
|
// Temperature
|
||||||
|
const uint8_t ARGO_TEMP_MIN = 10; // Celsius
|
||||||
|
const uint8_t ARGO_TEMP_MAX = 32; // Celsius
|
||||||
|
const uint8_t ARGO_SENSOR_TEMP_MIN = 0; // Celsius
|
||||||
|
const uint8_t ARGO_SENSOR_TEMP_MAX = 35; // Celsius
|
||||||
|
const uint8_t ARGO_TEMP_OFFSET = 4; // Celsius
|
||||||
|
|
||||||
|
// Modes
|
||||||
|
const uint8_t ARGO_MODE_OFF = 0x00;
|
||||||
|
const uint8_t ARGO_MODE_ON = 0x01;
|
||||||
|
const uint8_t ARGO_MODE_COOL = 0x01;
|
||||||
|
const uint8_t ARGO_MODE_DRY = 0x02;
|
||||||
|
const uint8_t ARGO_MODE_HEAT = 0x03;
|
||||||
|
const uint8_t ARGO_MODE_FAN = 0x04;
|
||||||
|
const uint8_t ARGO_MODE_AUTO = 0x05;
|
||||||
|
|
||||||
|
// Fan Speed
|
||||||
|
const uint8_t ARGO_FAN_AUTO = 0x00;
|
||||||
|
const uint8_t ARGO_FAN_SILENT = 0x01; // lowest
|
||||||
|
const uint8_t ARGO_FAN_LOWEST = 0x02;
|
||||||
|
const uint8_t ARGO_FAN_LOW = 0x03;
|
||||||
|
const uint8_t ARGO_FAN_MEDIUM = 0x04;
|
||||||
|
const uint8_t ARGO_FAN_HIGH = 0x05;
|
||||||
|
const uint8_t ARGO_FAN_HIGHEST = 0x06; // highest
|
||||||
|
|
||||||
|
// Flap position (swing modes)
|
||||||
|
const uint8_t ARGO_FLAP_HALF_AUTO = 0x00;
|
||||||
|
const uint8_t ARGO_FLAP_HIGHEST = 0x01;
|
||||||
|
const uint8_t ARGO_FLAP_HIGH = 0x02;
|
||||||
|
const uint8_t ARGO_FLAP_MIDDLE_HIGH = 0x03;
|
||||||
|
const uint8_t ARGO_FLAP_MIDDLE_LOW = 0x04;
|
||||||
|
const uint8_t ARGO_FLAP_LOW = 0x05;
|
||||||
|
const uint8_t ARGO_FLAP_LOWEST = 0x06;
|
||||||
|
const uint8_t ARGO_FLAP_FULL_AUTO = 0x07;
|
||||||
|
|
||||||
|
// IR Transmission
|
||||||
|
const uint32_t ARGO_IR_FREQUENCY = 38000;
|
||||||
|
|
||||||
|
const uint32_t ARGO_HEADER_MARK = 6400; // 00F7 = 247 * 26.3 = 6496.1 µs
|
||||||
|
const uint32_t ARGO_HEADER_SPACE = 3200; // 007C = 124 * 26.3 = 3261.2 µs
|
||||||
|
const uint32_t ARGO_BIT_MARK = 400; // 0010 = 16 * 26.3 = 420.8 µs
|
||||||
|
const uint32_t ARGO_ONE_SPACE = 2200; // 0054 = 84 * 26.3 = 2209.2 µs
|
||||||
|
const uint32_t ARGO_ZERO_SPACE = 900; // 0022 = 34 * 26.3 = 894.2 µs
|
||||||
|
|
||||||
|
const uint8_t ARGO_PREAMBLE = 0xB; // 0b1011
|
||||||
|
const uint8_t ARGO_POST1 = 0x30; // unknown, always 0b110000 (TempScale?)
|
||||||
|
const size_t ARGO_STATE_LENGTH = 12;
|
||||||
|
|
||||||
|
// Native representation of A/C IR message for WREM-3 remote
|
||||||
|
#pragma pack(push, 1) // Add a packing directive to ensure the structure is tightly packed:
|
||||||
|
union ArgoProtocolWREM3 {
|
||||||
|
uint8_t raw[ARGO_STATE_LENGTH]; ///< The state in native IR code form
|
||||||
|
struct {
|
||||||
|
// Byte 0 (same definition across the union)
|
||||||
|
uint8_t Pre1 : 4; /// Preamble: 0b1011 @ref ARGO_PREAMBLE
|
||||||
|
uint8_t IrChannel : 2; /// 0..3 range
|
||||||
|
uint8_t IrCommandType : 2; /// @ref argoIrMessageType_t
|
||||||
|
// Byte 1
|
||||||
|
uint8_t RoomTemp : 5; // in Celsius, range: 4..35 (offset by -4[*C])
|
||||||
|
uint8_t Mode : 3; /// @ref argoMode_t
|
||||||
|
// Byte 2
|
||||||
|
uint8_t Temp : 5; // in Celsius, range: 10..32 (offset by -4[*C])
|
||||||
|
uint8_t Fan : 3; /// @ref argoFan_t
|
||||||
|
// Byte3
|
||||||
|
uint8_t Flap : 3; /// SwingV @ref argoFlap_t
|
||||||
|
uint8_t Power : 1; // boost mode
|
||||||
|
uint8_t iFeel : 1;
|
||||||
|
uint8_t Night : 1;
|
||||||
|
uint8_t Eco : 1;
|
||||||
|
uint8_t Boost : 1; ///< a.k.a. Turbo
|
||||||
|
// Byte4
|
||||||
|
uint8_t Filter : 1;
|
||||||
|
uint8_t Light : 1;
|
||||||
|
uint8_t Post1 : 6; /// Unknown, always 0b110000 (TempScale?)
|
||||||
|
// Byte5
|
||||||
|
uint8_t Sum : 8; /// Checksum
|
||||||
|
};
|
||||||
|
struct iFeel {
|
||||||
|
// Byte 0 (same definition across the union)
|
||||||
|
uint8_t : 8; // {Pre1 | IrChannel | IrCommandType}
|
||||||
|
// Byte 1
|
||||||
|
uint8_t SensorT : 5; // in Celsius, range: 4..35 (offset by -4[*C])
|
||||||
|
uint8_t CheckHi : 3; // Checksum (short)
|
||||||
|
} ifeel;
|
||||||
|
struct Timer {
|
||||||
|
// Byte 0 (same definition across the union)
|
||||||
|
uint8_t : 8; // {Pre1 | IrChannel | IrCommandType}
|
||||||
|
// Byte 1
|
||||||
|
uint8_t IsOn : 1;
|
||||||
|
uint8_t TimerType : 3;
|
||||||
|
uint8_t CurrentTimeLo : 4;
|
||||||
|
// Byte 2
|
||||||
|
uint8_t CurrentTimeHi : 7;
|
||||||
|
uint8_t CurrentWeekdayLo : 1;
|
||||||
|
// Byte 3
|
||||||
|
uint8_t CurrentWeekdayHi : 2;
|
||||||
|
uint8_t DelayTimeLo : 6;
|
||||||
|
// Byte 4
|
||||||
|
uint8_t DelayTimeHi : 5;
|
||||||
|
uint8_t TimerStartLo : 3;
|
||||||
|
// Byte 5
|
||||||
|
uint8_t TimerStartHi : 8;
|
||||||
|
// Byte 6
|
||||||
|
uint8_t TimerEndLo : 8;
|
||||||
|
// Byte 7
|
||||||
|
uint8_t TimerEndHi : 3;
|
||||||
|
uint8_t TimerActiveDaysLo : 5; // Bitmap (LSBit is Sunday)
|
||||||
|
// Byte 8
|
||||||
|
uint8_t TimerActiveDaysHi : 2; // Bitmap (LSBit is Sunday)
|
||||||
|
uint8_t Post1 : 1; // Unknown, always 1
|
||||||
|
uint8_t Checksum : 5;
|
||||||
|
} timer;
|
||||||
|
struct Config {
|
||||||
|
uint8_t : 8; // Byte 0 {Pre1 | IrChannel | IrCommandType}
|
||||||
|
uint8_t Key : 8; // Byte 1
|
||||||
|
uint8_t Value : 8; // Byte 2
|
||||||
|
uint8_t Checksum : 8; // Byte 3
|
||||||
|
} config;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef enum _ArgoIRMessageType {
|
||||||
|
ArgoIRMessageType_AC_CONTROL = 0,
|
||||||
|
ArgoIRMessageType_IFEEL_TEMP_REPORT = 1,
|
||||||
|
ArgoIRMessageType_TIMER_COMMAND = 2,
|
||||||
|
ArgoIRMessageType_CONFIG_PARAM_SET = 3,
|
||||||
|
} ArgoIRMessageType;
|
||||||
|
|
||||||
|
// raw byte length depends on message type
|
||||||
|
typedef enum _ArgoIRMessageLength {
|
||||||
|
ArgoIRMessageLength_AC_CONTROL = 6,
|
||||||
|
ArgoIRMessageLength_IFEEL_TEMP_REPORT = 2,
|
||||||
|
ArgoIRMessageLength_TIMER_COMMAND = 9,
|
||||||
|
ArgoIRMessageLength_CONFIG_PARAM_SET = 4,
|
||||||
|
} ArgoIRMessageLength;
|
||||||
|
|
||||||
|
class ArgoUlisseClimate : public climate_ir::ClimateIR {
|
||||||
|
public:
|
||||||
|
ArgoUlisseClimate()
|
||||||
|
: climate_ir::ClimateIR(
|
||||||
|
ARGO_TEMP_MIN, ARGO_TEMP_MAX, 1.0f, true, true,
|
||||||
|
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||||
|
climate::CLIMATE_FAN_HIGH},
|
||||||
|
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_VERTICAL},
|
||||||
|
{
|
||||||
|
climate::CLIMATE_PRESET_NONE,
|
||||||
|
climate::CLIMATE_PRESET_ECO,
|
||||||
|
climate::CLIMATE_PRESET_BOOST,
|
||||||
|
climate::CLIMATE_PRESET_SLEEP,
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
// Setters for the additional features (e.g. for template switches)
|
||||||
|
void set_ifeel(bool ifeel) {
|
||||||
|
this->ifeel_ = ifeel;
|
||||||
|
transmit_state();
|
||||||
|
}
|
||||||
|
void set_light(bool light) {
|
||||||
|
this->light_ = light;
|
||||||
|
transmit_state();
|
||||||
|
}
|
||||||
|
void set_filter(bool filter) {
|
||||||
|
this->filter_ = filter;
|
||||||
|
transmit_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_ifeel() { return this->ifeel_; }
|
||||||
|
bool get_light() { return this->light_; }
|
||||||
|
bool get_filter() { return this->filter_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void control(const climate::ClimateCall &call) override;
|
||||||
|
// Transmit via IR the state of this climate controller.
|
||||||
|
int32_t last_transmit_time_{};
|
||||||
|
void transmit_state() override;
|
||||||
|
void transmit_ifeel_();
|
||||||
|
uint8_t calc_checksum_(const ArgoProtocolWREM3 *data, size_t size);
|
||||||
|
climate::ClimateTraits traits() override;
|
||||||
|
uint8_t operation_mode_();
|
||||||
|
uint8_t fan_speed_();
|
||||||
|
uint8_t swing_mode_();
|
||||||
|
uint8_t temperature_();
|
||||||
|
uint8_t sensor_temperature_();
|
||||||
|
uint8_t process_temperature_(float temperature);
|
||||||
|
|
||||||
|
// booleans to handle the additional features
|
||||||
|
bool ifeel_{true};
|
||||||
|
bool light_{true};
|
||||||
|
bool filter_{true};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace argo_ulisse
|
||||||
|
} // namespace esphome
|
18
esphome/components/argo_ulisse/climate.py
Normal file
18
esphome/components/argo_ulisse/climate.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import climate_ir
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
|
||||||
|
argo_ulisse_ns = cg.esphome_ns.namespace("argo_ulisse")
|
||||||
|
ArgoUlisseClimate = argo_ulisse_ns.class_("ArgoUlisseClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||||
|
{cv.GenerateID(): cv.declare_id(ArgoUlisseClimate)}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await climate_ir.register_climate_ir(var, config)
|
Loading…
Reference in a new issue