168 lines
4.6 KiB
C++
168 lines
4.6 KiB
C++
#include "math.h"
|
|
#include "esphome.h"
|
|
|
|
namespace {
|
|
static const char* const TAG = __FILE__;
|
|
static const uint8_t MIN_TEMPERATURE = 35;
|
|
static const uint8_t MAX_TEMPERATURE = 70;
|
|
static const uint8_t TEMPERATURE_LEVELS = 8;
|
|
}
|
|
|
|
class Zehnder : public Component, public Climate {
|
|
public:
|
|
void retransmit() {
|
|
this->transmit_temperature_(this->target_temperature);
|
|
}
|
|
|
|
void control(const ClimateCall &call) override {
|
|
ClimateMode target_mode = this->mode;
|
|
float target_temp = this->target_temperature;
|
|
|
|
if (isnan(target_temp)) {
|
|
target_temp = 0;
|
|
}
|
|
|
|
ESP_LOGD(TAG, "Received control message:");
|
|
if (call.get_mode().has_value()) {
|
|
target_mode = *call.get_mode();
|
|
if (*call.get_mode() == climate::CLIMATE_MODE_OFF) {
|
|
ESP_LOGD(TAG, " target mode off (set temperature to zero)");
|
|
target_temp = 0;
|
|
} else if (target_temp == 0) {
|
|
// If mode is switched to heat but target temperature is unset,
|
|
// adjust target temperature to the maximum -- otherwise, it
|
|
// doesn't make sense.
|
|
ESP_LOGD(TAG, " target mode heat (reset temperature to max)");
|
|
target_temp = MAX_TEMPERATURE;
|
|
}
|
|
}
|
|
|
|
if (call.get_target_temperature().has_value()) {
|
|
target_temp = *call.get_target_temperature();
|
|
// Temperature setting, if present, takes precedence over the
|
|
// mode setting.
|
|
if (target_temp == 0) {
|
|
ESP_LOGD(TAG, " target temperature zero (reset mode to off)");
|
|
target_mode = climate::CLIMATE_MODE_OFF;
|
|
} else {
|
|
ESP_LOGD(TAG, " target temperature %f (reset mode to heat)", target_temp);
|
|
target_mode = climate::CLIMATE_MODE_HEAT;
|
|
}
|
|
}
|
|
|
|
if (this->transmit_temperature_(target_temp)) {
|
|
this->mode = target_mode;
|
|
this->target_temperature = target_temp;
|
|
this->publish_state();
|
|
ESP_LOGD(TAG, "Temperature transmitted successfully, state published");
|
|
}
|
|
}
|
|
|
|
ClimateTraits traits() override {
|
|
auto traits = climate::ClimateTraits();
|
|
|
|
traits.set_visual_min_temperature(MIN_TEMPERATURE);
|
|
traits.set_visual_max_temperature(MAX_TEMPERATURE);
|
|
traits.set_visual_temperature_step(
|
|
(MAX_TEMPERATURE - MIN_TEMPERATURE) / (TEMPERATURE_LEVELS - 1)
|
|
);
|
|
|
|
if (has_temperature_sensor_) {
|
|
traits.set_supports_current_temperature(true);
|
|
}
|
|
|
|
traits.set_supported_modes({
|
|
climate::CLIMATE_MODE_HEAT,
|
|
climate::CLIMATE_MODE_OFF
|
|
});
|
|
|
|
return traits;
|
|
}
|
|
|
|
void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) {
|
|
this->transmitter_ = transmitter;
|
|
}
|
|
|
|
void add_temperature_sensor(sensor::Sensor *temperature_sensor) {
|
|
temperature_sensor->add_on_state_callback([this](float temperature_value) {
|
|
this->current_temperature = temperature_value;
|
|
this->publish_state();
|
|
});
|
|
has_temperature_sensor_ = true;
|
|
}
|
|
|
|
private:
|
|
remote_transmitter::RemoteTransmitterComponent *transmitter_ = nullptr;
|
|
bool has_temperature_sensor_ = false;
|
|
|
|
bool transmit_temperature_(float temp) {
|
|
return this->transmit_level_(temp == 0
|
|
? 0
|
|
: (
|
|
(temp - MIN_TEMPERATURE) *
|
|
(TEMPERATURE_LEVELS - 1) /
|
|
(MAX_TEMPERATURE - MIN_TEMPERATURE) +
|
|
1
|
|
)
|
|
);
|
|
}
|
|
|
|
bool transmit_level_(uint8_t level) {
|
|
if (level > 8) {
|
|
ESP_LOGE(TAG, "Unsupported heater level: %d. Ignoring command", level);
|
|
return false;
|
|
}
|
|
if (this->transmitter_ == nullptr) {
|
|
ESP_LOGE(TAG, "IR transmitter not set. Use set_transmitter.");
|
|
return false;
|
|
}
|
|
|
|
ESP_LOGD(TAG, "Transmitting heater level: %d...", level);
|
|
|
|
this->transmitter_->set_carrier_duty_percent(50);
|
|
auto call = this->transmitter_->transmit();
|
|
call.set_send_times(10);
|
|
call.set_send_wait(10); // milliseconds
|
|
auto data = call.get_data();
|
|
|
|
data->reset();
|
|
data->set_carrier_frequency(455000);
|
|
|
|
static uint8_t mode_bytes[TEMPERATURE_LEVELS + 1] = {
|
|
0x0D, // OFF
|
|
0x86,
|
|
0x95,
|
|
0xA0,
|
|
0xB3,
|
|
0xCA,
|
|
0xD9,
|
|
0xEC,
|
|
0xFF
|
|
};
|
|
|
|
add_header_(data);
|
|
add_byte_(data, 0xB8);
|
|
add_byte_(data, mode_bytes[level]);
|
|
add_post_(data);
|
|
|
|
call.perform();
|
|
return true;
|
|
}
|
|
|
|
static void add_header_(remote_base::RemoteTransmitData *data) {
|
|
data->item(30, 1000);
|
|
}
|
|
|
|
static void add_byte_(remote_base::RemoteTransmitData *data, uint8_t value) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
data->item(30, value & 0b10000000 ? 830 : 650);
|
|
value <<= 1;
|
|
}
|
|
}
|
|
|
|
static void add_post_(remote_base::RemoteTransmitData *data) {
|
|
data->item(30, 460);
|
|
data->item(30, 650);
|
|
}
|
|
};
|