mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 17:27:59 +01:00
Added support for Hitachi AC224
This commit is contained in:
parent
e02319dcff
commit
58e782877f
5 changed files with 327 additions and 0 deletions
|
@ -164,6 +164,7 @@ esphome/components/hbridge/fan/* @WeekendWarrior
|
|||
esphome/components/hbridge/light/* @DotNetDann
|
||||
esphome/components/he60r/* @clydebarrow
|
||||
esphome/components/heatpumpir/* @rob-deutsch
|
||||
esphome/components/hitachi_ac224/* @christianhubmann
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/hm3301/* @freekode
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
|
|
1
esphome/components/hitachi_ac224/__init__.py
Normal file
1
esphome/components/hitachi_ac224/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
CODEOWNERS = ["@christianhubmann"]
|
20
esphome/components/hitachi_ac224/climate.py
Normal file
20
esphome/components/hitachi_ac224/climate.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
import esphome.codegen as cg
|
||||
from esphome.components import climate_ir
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
AUTO_LOAD = ["climate_ir"]
|
||||
|
||||
hitachi_ac224_ns = cg.esphome_ns.namespace("hitachi_ac224")
|
||||
HitachiClimate = hitachi_ac224_ns.class_("HitachiClimate", climate_ir.ClimateIR)
|
||||
|
||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HitachiClimate),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await climate_ir.register_climate_ir(var, config)
|
221
esphome/components/hitachi_ac224/hitachi_ac224.cpp
Normal file
221
esphome/components/hitachi_ac224/hitachi_ac224.cpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
#include "hitachi_ac224.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace hitachi_ac224 {
|
||||
|
||||
static const char *const TAG = "climate.hitachi_ac224";
|
||||
|
||||
uint8_t reverse_bits(uint8_t n) {
|
||||
uint8_t reversed = 0;
|
||||
for (uint8_t i = 0; i < 8; ++i) {
|
||||
reversed <<= 1;
|
||||
reversed |= (n & 1);
|
||||
n >>= 1;
|
||||
}
|
||||
return reversed;
|
||||
}
|
||||
|
||||
void set_bit(uint8_t *const data, const uint8_t position, const bool on) {
|
||||
uint8_t mask = 1 << position;
|
||||
if (on) {
|
||||
*data |= mask;
|
||||
} else {
|
||||
*data &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
boolean get_bit(const uint8_t data, const uint8_t position) { return (data & (1 << position)) != 0; }
|
||||
|
||||
void HitachiClimate::set_checksum_() {
|
||||
uint8_t sum = 62;
|
||||
for (uint16_t i = 0; i < HITACHI_AC224_STATE_LENGTH - 1; i++) {
|
||||
sum -= reverse_bits(remote_state_[i]);
|
||||
}
|
||||
remote_state_[HITACHI_AC224_CHECKSUM_BYTE] = reverse_bits(sum);
|
||||
}
|
||||
|
||||
bool HitachiClimate::get_power_() {
|
||||
return get_bit(remote_state_[HITACHI_AC224_POWER_BYTE], HITACHI_AC224_POWER_OFFSET);
|
||||
}
|
||||
|
||||
void HitachiClimate::set_power_(bool on) {
|
||||
set_bit(&remote_state_[HITACHI_AC224_POWER_BYTE], HITACHI_AC224_POWER_OFFSET, on);
|
||||
}
|
||||
|
||||
uint8_t HitachiClimate::get_mode_() { return reverse_bits(remote_state_[HITACHI_AC224_MODE_BYTE]); }
|
||||
|
||||
void HitachiClimate::set_mode_(uint8_t mode) {
|
||||
uint8_t new_mode = mode;
|
||||
switch (mode) {
|
||||
// Fan mode sets a special temp.
|
||||
case HITACHI_AC224_MODE_FAN:
|
||||
set_temp_(HITACHI_AC224_TEMP_FAN);
|
||||
break;
|
||||
case HITACHI_AC224_MODE_HEAT:
|
||||
case HITACHI_AC224_MODE_COOL:
|
||||
case HITACHI_AC224_MODE_DRY:
|
||||
break;
|
||||
default:
|
||||
new_mode = HITACHI_AC224_MODE_AUTO;
|
||||
}
|
||||
remote_state_[HITACHI_AC224_MODE_BYTE] = reverse_bits(new_mode);
|
||||
if (new_mode != HITACHI_AC224_MODE_FAN)
|
||||
set_temp_(previous_temp_);
|
||||
set_fan_(get_fan_()); // Reset the fan speed after the mode change.
|
||||
set_power_(true);
|
||||
}
|
||||
|
||||
void HitachiClimate::set_temp_(uint8_t celsius) {
|
||||
uint8_t temp;
|
||||
if (celsius == HITACHI_AC224_TEMP_FAN) {
|
||||
temp = HITACHI_AC224_TEMP_FAN;
|
||||
} else {
|
||||
temp = std::min(celsius, HITACHI_AC224_TEMP_MAX);
|
||||
temp = std::max(temp, HITACHI_AC224_TEMP_MIN);
|
||||
previous_temp_ = temp;
|
||||
}
|
||||
remote_state_[HITACHI_AC224_TEMP_BYTE] = reverse_bits(temp << 1);
|
||||
if (temp == HITACHI_AC224_TEMP_MIN) {
|
||||
remote_state_[HITACHI_AC224_TEMP_BOOST_BYTE] = HITACHI_AC224_TEMP_BOOST_ON;
|
||||
} else {
|
||||
remote_state_[HITACHI_AC224_TEMP_BOOST_BYTE] = HITACHI_AC224_TEMP_BOOST_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t HitachiClimate::get_fan_() { return reverse_bits(remote_state_[HITACHI_AC224_FAN_BYTE]); }
|
||||
|
||||
void HitachiClimate::set_fan_(uint8_t speed) {
|
||||
uint8_t fan_min = HITACHI_AC224_FAN_AUTO;
|
||||
uint8_t fan_max = HITACHI_AC224_FAN_HIGH;
|
||||
switch (get_mode_()) {
|
||||
case HITACHI_AC224_MODE_DRY: // Only 2 x low speeds in Dry mode.
|
||||
fan_min = HITACHI_AC224_FAN_QUIET;
|
||||
fan_max = HITACHI_AC224_FAN_LOW;
|
||||
break;
|
||||
case HITACHI_AC224_MODE_FAN:
|
||||
fan_min = HITACHI_AC224_FAN_QUIET; // No Auto in Fan mode.
|
||||
break;
|
||||
}
|
||||
uint8_t new_speed = std::max(speed, fan_min);
|
||||
new_speed = std::min(new_speed, fan_max);
|
||||
remote_state_[HITACHI_AC224_FAN_BYTE] = reverse_bits(new_speed);
|
||||
}
|
||||
|
||||
void HitachiClimate::set_swing_v_(bool on) {
|
||||
set_bit(&remote_state_[HITACHI_AC224_SWINGV_BYTE], HITACHI_AC224_SWINGV_OFFSET, on);
|
||||
}
|
||||
|
||||
bool HitachiClimate::get_swing_v_() {
|
||||
return get_bit(remote_state_[HITACHI_AC224_SWINGV_BYTE], HITACHI_AC224_SWINGV_OFFSET);
|
||||
}
|
||||
|
||||
void HitachiClimate::set_swing_h_(bool on) {
|
||||
set_bit(&remote_state_[HITACHI_AC224_SWINGH_BYTE], HITACHI_AC224_SWINGH_OFFSET, on);
|
||||
}
|
||||
|
||||
bool HitachiClimate::get_swing_h_() {
|
||||
return get_bit(remote_state_[HITACHI_AC224_SWINGH_BYTE], HITACHI_AC224_SWINGH_OFFSET);
|
||||
}
|
||||
|
||||
void HitachiClimate::transmit_state() {
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
set_mode_(HITACHI_AC224_MODE_COOL);
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
set_mode_(HITACHI_AC224_MODE_DRY);
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
set_mode_(HITACHI_AC224_MODE_HEAT);
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||
set_mode_(HITACHI_AC224_MODE_AUTO);
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
set_mode_(HITACHI_AC224_MODE_FAN);
|
||||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
set_power_(false);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unsupported mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode)));
|
||||
}
|
||||
|
||||
set_temp_(static_cast<uint8_t>(this->target_temperature));
|
||||
|
||||
switch (this->fan_mode.value()) {
|
||||
case climate::CLIMATE_FAN_QUIET:
|
||||
set_fan_(HITACHI_AC224_FAN_QUIET);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
set_fan_(HITACHI_AC224_FAN_LOW);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
set_fan_(HITACHI_AC224_FAN_MEDIUM);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
set_fan_(HITACHI_AC224_FAN_HIGH);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_ON:
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
default:
|
||||
set_fan_(HITACHI_AC224_FAN_AUTO);
|
||||
}
|
||||
|
||||
switch (this->swing_mode) {
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
set_swing_v_(true);
|
||||
set_swing_h_(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
set_swing_v_(true);
|
||||
set_swing_h_(false);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
set_swing_v_(false);
|
||||
set_swing_h_(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
set_swing_v_(false);
|
||||
set_swing_h_(false);
|
||||
break;
|
||||
}
|
||||
|
||||
set_checksum_();
|
||||
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
auto *data = transmit.get_data();
|
||||
data->set_carrier_frequency(HITACHI_AC224_FREQ);
|
||||
|
||||
uint8_t repeat = 0;
|
||||
for (uint8_t r = 0; r <= repeat; r++) {
|
||||
// Header
|
||||
data->item(HITACHI_AC224_HDR_MARK, HITACHI_AC224_HDR_SPACE);
|
||||
// Data
|
||||
for (uint8_t i : remote_state_) {
|
||||
for (int8_t j = 7; j >= 0; j--) {
|
||||
data->mark(HITACHI_AC224_BIT_MARK);
|
||||
bool bit = i & (1 << j);
|
||||
data->space(bit ? HITACHI_AC224_ONE_SPACE : HITACHI_AC224_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
// Footer
|
||||
data->item(HITACHI_AC224_BIT_MARK, HITACHI_AC224_MIN_GAP);
|
||||
}
|
||||
transmit.perform();
|
||||
|
||||
dump_state_("Sent", remote_state_);
|
||||
}
|
||||
|
||||
void HitachiClimate::dump_state_(const char action[], uint8_t state[]) {
|
||||
for (uint16_t i = 0; i < HITACHI_AC224_STATE_LENGTH - 10; i += 10) {
|
||||
ESP_LOGV(TAG, "%s: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", action, state[i + 0], state[i + 1],
|
||||
state[i + 2], state[i + 3], state[i + 4], state[i + 5], state[i + 6], state[i + 7], state[i + 8],
|
||||
state[i + 9]);
|
||||
}
|
||||
ESP_LOGV(TAG, "%s: %02X %02X %02X %02X %02X %02X %02X %02X", action, state[20], state[21], state[22], state[23],
|
||||
state[24], state[25], state[26], state[27]);
|
||||
}
|
||||
|
||||
} // namespace hitachi_ac224
|
||||
} // namespace esphome
|
84
esphome/components/hitachi_ac224/hitachi_ac224.h
Normal file
84
esphome/components/hitachi_ac224/hitachi_ac224.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/climate_ir/climate_ir.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace hitachi_ac224 {
|
||||
|
||||
const uint16_t HITACHI_AC224_HDR_MARK = 3300;
|
||||
const uint16_t HITACHI_AC224_HDR_SPACE = 1700;
|
||||
const uint16_t HITACHI_AC224_BIT_MARK = 400;
|
||||
const uint16_t HITACHI_AC224_ONE_SPACE = 1250;
|
||||
const uint16_t HITACHI_AC224_ZERO_SPACE = 500;
|
||||
const uint32_t HITACHI_AC224_MIN_GAP = 100000; // Just a guess.
|
||||
const uint16_t HITACHI_AC224_FREQ = 38000; // Hz.
|
||||
|
||||
const uint8_t HITACHI_AC224_TEMP_BYTE = 11;
|
||||
const uint8_t HITACHI_AC224_TEMP_MIN = 16; // 16C
|
||||
const uint8_t HITACHI_AC224_TEMP_MAX = 32; // 32C
|
||||
const uint8_t HITACHI_AC224_TEMP_FAN = 64; // 64C
|
||||
const uint8_t HITACHI_AC224_TEMP_BOOST_BYTE = 9;
|
||||
const uint8_t HITACHI_AC224_TEMP_BOOST_ON = 0x90;
|
||||
const uint8_t HITACHI_AC224_TEMP_BOOST_OFF = 0x10;
|
||||
|
||||
const uint8_t HITACHI_AC224_MODE_BYTE = 10;
|
||||
const uint8_t HITACHI_AC224_MODE_AUTO = 2;
|
||||
const uint8_t HITACHI_AC224_MODE_HEAT = 3;
|
||||
const uint8_t HITACHI_AC224_MODE_COOL = 4;
|
||||
const uint8_t HITACHI_AC224_MODE_DRY = 5;
|
||||
const uint8_t HITACHI_AC224_MODE_FAN = 0xC;
|
||||
|
||||
const uint8_t HITACHI_AC224_FAN_BYTE = 13;
|
||||
const uint8_t HITACHI_AC224_FAN_AUTO = 1;
|
||||
const uint8_t HITACHI_AC224_FAN_QUIET = 2;
|
||||
const uint8_t HITACHI_AC224_FAN_LOW = 3;
|
||||
const uint8_t HITACHI_AC224_FAN_MEDIUM = 4;
|
||||
const uint8_t HITACHI_AC224_FAN_HIGH = 5;
|
||||
|
||||
const uint8_t HITACHI_AC224_POWER_BYTE = 17;
|
||||
const uint8_t HITACHI_AC224_POWER_OFFSET = 0; // Mask 0b0000000x
|
||||
|
||||
const uint8_t HITACHI_AC224_SWINGV_BYTE = 14;
|
||||
const uint8_t HITACHI_AC224_SWINGV_OFFSET = 7; // Mask 0bx0000000
|
||||
|
||||
const uint8_t HITACHI_AC224_SWINGH_BYTE = 15;
|
||||
const uint8_t HITACHI_AC224_SWINGH_OFFSET = 7; // Mask 0bx0000000
|
||||
|
||||
const uint8_t HITACHI_AC224_CHECKSUM_BYTE = 27;
|
||||
|
||||
const uint16_t HITACHI_AC224_STATE_LENGTH = 28;
|
||||
|
||||
class HitachiClimate : public climate_ir::ClimateIR {
|
||||
public:
|
||||
HitachiClimate()
|
||||
: climate_ir::ClimateIR(HITACHI_AC224_TEMP_MIN, HITACHI_AC224_TEMP_MAX, 1.0F, true, true,
|
||||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_QUIET, 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_SWING_BOTH}) {}
|
||||
|
||||
protected:
|
||||
uint8_t remote_state_[HITACHI_AC224_STATE_LENGTH]{0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x10,
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00};
|
||||
uint8_t previous_temp_{27};
|
||||
// Transmit via IR the state of this climate controller.
|
||||
void transmit_state() override;
|
||||
void set_checksum_();
|
||||
bool get_power_();
|
||||
void set_power_(bool on);
|
||||
uint8_t get_mode_();
|
||||
void set_mode_(uint8_t mode);
|
||||
void set_temp_(uint8_t celsius);
|
||||
uint8_t get_fan_();
|
||||
void set_fan_(uint8_t speed);
|
||||
void set_swing_v_(bool on);
|
||||
bool get_swing_v_();
|
||||
void set_swing_h_(bool on);
|
||||
bool get_swing_h_();
|
||||
void dump_state_(const char action[], uint8_t remote_state[]);
|
||||
};
|
||||
|
||||
} // namespace hitachi_ac224
|
||||
} // namespace esphome
|
Loading…
Reference in a new issue