From 2d0d794a9dab9f538d65744fddbe6ecbee6c50e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Gim=C3=A9nez?= Date: Wed, 22 Jan 2020 23:38:04 +0100 Subject: [PATCH] Daikin climate ir component (#964) * Daikin ARC43XXX IR remote controller support * Format and lint fixes * Check temperature values against allowed min/max --- esphome/components/daikin/__init__.py | 0 esphome/components/daikin/climate.py | 18 ++++ esphome/components/daikin/daikin.cpp | 128 ++++++++++++++++++++++++++ esphome/components/daikin/daikin.h | 57 ++++++++++++ tests/test1.yaml | 2 + 5 files changed, 205 insertions(+) create mode 100644 esphome/components/daikin/__init__.py create mode 100644 esphome/components/daikin/climate.py create mode 100644 esphome/components/daikin/daikin.cpp create mode 100644 esphome/components/daikin/daikin.h diff --git a/esphome/components/daikin/__init__.py b/esphome/components/daikin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/daikin/climate.py b/esphome/components/daikin/climate.py new file mode 100644 index 0000000000..f1d5c7ed4a --- /dev/null +++ b/esphome/components/daikin/climate.py @@ -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'] + +daikin_ns = cg.esphome_ns.namespace('daikin') +DaikinClimate = daikin_ns.class_('DaikinClimate', climate_ir.ClimateIR) + +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(DaikinClimate), +}) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield climate_ir.register_climate_ir(var, config) diff --git a/esphome/components/daikin/daikin.cpp b/esphome/components/daikin/daikin.cpp new file mode 100644 index 0000000000..eabbb96014 --- /dev/null +++ b/esphome/components/daikin/daikin.cpp @@ -0,0 +1,128 @@ +#include "daikin.h" +#include "esphome/components/remote_base/remote_base.h" + +namespace esphome { +namespace daikin { + +static const char *TAG = "daikin.climate"; + +void DaikinClimate::transmit_state() { + uint8_t remote_state[35] = {0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, + 0x42, 0x49, 0x05, 0xA2, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00}; + + remote_state[21] = this->operation_mode_(); + remote_state[24] = this->fan_speed_(); + remote_state[22] = this->temperature_(); + + // Calculate checksum + for (int i = 16; i < 34; i++) { + remote_state[34] += remote_state[i]; + } + + auto transmit = this->transmitter_->transmit(); + auto data = transmit.get_data(); + data->set_carrier_frequency(DAIKIN_IR_FREQUENCY); + + data->mark(DAIKIN_HEADER_MARK); + data->space(DAIKIN_HEADER_SPACE); + for (int i = 0; i < 8; i++) { + for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask + data->mark(DAIKIN_BIT_MARK); + bool bit = remote_state[i] & mask; + data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE); + } + } + data->mark(DAIKIN_BIT_MARK); + data->space(DAIKIN_MESSAGE_SPACE); + data->mark(DAIKIN_HEADER_MARK); + data->space(DAIKIN_HEADER_SPACE); + + for (int i = 8; i < 16; i++) { + for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask + data->mark(DAIKIN_BIT_MARK); + bool bit = remote_state[i] & mask; + data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE); + } + } + data->mark(DAIKIN_BIT_MARK); + data->space(DAIKIN_MESSAGE_SPACE); + data->mark(DAIKIN_HEADER_MARK); + data->space(DAIKIN_HEADER_SPACE); + + for (int i = 16; i < 35; i++) { + for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask + data->mark(DAIKIN_BIT_MARK); + bool bit = remote_state[i] & mask; + data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE); + } + } + data->mark(DAIKIN_BIT_MARK); + data->space(0); + + transmit.perform(); +} + +uint8_t DaikinClimate::operation_mode_() { + uint8_t operating_mode = DAIKIN_MODE_ON; + switch (this->mode) { + case climate::CLIMATE_MODE_COOL: + operating_mode |= DAIKIN_MODE_COOL; + break; + case climate::CLIMATE_MODE_DRY: + operating_mode |= DAIKIN_MODE_DRY; + break; + case climate::CLIMATE_MODE_HEAT: + operating_mode |= DAIKIN_MODE_HEAT; + break; + case climate::CLIMATE_MODE_AUTO: + operating_mode |= DAIKIN_MODE_AUTO; + break; + case climate::CLIMATE_MODE_FAN_ONLY: + operating_mode |= DAIKIN_MODE_FAN; + break; + case climate::CLIMATE_MODE_OFF: + default: + operating_mode = DAIKIN_MODE_OFF; + break; + } + + return operating_mode; +} + +uint8_t DaikinClimate::fan_speed_() { + uint8_t fan_speed; + switch (this->fan_mode) { + case climate::CLIMATE_FAN_LOW: + fan_speed = DAIKIN_FAN_1; + break; + case climate::CLIMATE_FAN_MEDIUM: + fan_speed = DAIKIN_FAN_3; + break; + case climate::CLIMATE_FAN_HIGH: + fan_speed = DAIKIN_FAN_5; + break; + case climate::CLIMATE_FAN_AUTO: + default: + fan_speed = DAIKIN_FAN_AUTO; + } + + // If swing is enabled switch first 4 bits to 1111 + return this->swing_mode == climate::CLIMATE_SWING_VERTICAL ? fan_speed | 0xF : fan_speed; +} + +uint8_t DaikinClimate::temperature_() { + // Force special temperatures depending on the mode + switch (this->mode) { + case climate::CLIMATE_MODE_FAN_ONLY: + return 25; + case climate::CLIMATE_MODE_DRY: + return 0xc0; + default: + uint8_t temperature = (uint8_t) roundf(clamp(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX)); + return temperature << 1; + } +} + +} // namespace daikin +} // namespace esphome diff --git a/esphome/components/daikin/daikin.h b/esphome/components/daikin/daikin.h new file mode 100644 index 0000000000..ea69256701 --- /dev/null +++ b/esphome/components/daikin/daikin.h @@ -0,0 +1,57 @@ +#pragma once + +#include "esphome/components/climate_ir/climate_ir.h" + +namespace esphome { +namespace daikin { + +// Values for Daikin ARC43XXX IR Controllers +// Temperature +const uint8_t DAIKIN_TEMP_MIN = 10; // Celsius +const uint8_t DAIKIN_TEMP_MAX = 30; // Celsius + +// Modes +const uint8_t DAIKIN_MODE_AUTO = 0x00; +const uint8_t DAIKIN_MODE_COOL = 0x30; +const uint8_t DAIKIN_MODE_HEAT = 0x40; +const uint8_t DAIKIN_MODE_DRY = 0x20; +const uint8_t DAIKIN_MODE_FAN = 0x60; +const uint8_t DAIKIN_MODE_OFF = 0x00; +const uint8_t DAIKIN_MODE_ON = 0x01; + +// Fan Speed +const uint8_t DAIKIN_FAN_AUTO = 0xA0; +const uint8_t DAIKIN_FAN_1 = 0x30; +const uint8_t DAIKIN_FAN_2 = 0x40; +const uint8_t DAIKIN_FAN_3 = 0x50; +const uint8_t DAIKIN_FAN_4 = 0x60; +const uint8_t DAIKIN_FAN_5 = 0x70; + +// IR Transmission +const uint32_t DAIKIN_IR_FREQUENCY = 38000; +const uint32_t DAIKIN_HEADER_MARK = 3360; +const uint32_t DAIKIN_HEADER_SPACE = 1760; +const uint32_t DAIKIN_BIT_MARK = 360; +const uint32_t DAIKIN_ONE_SPACE = 1370; +const uint32_t DAIKIN_ZERO_SPACE = 520; +const uint32_t DAIKIN_MESSAGE_SPACE = 32300; + +class DaikinClimate : public climate_ir::ClimateIR { + public: + DaikinClimate() + : climate_ir::ClimateIR( + DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX, 1.0f, true, true, + std::vector{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, + climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH}, + std::vector{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {} + + protected: + // Transmit via IR the state of this climate controller. + void transmit_state() override; + uint8_t operation_mode_(); + uint8_t fan_speed_(); + uint8_t temperature_(); +}; + +} // namespace daikin +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index fb15bc0a6b..0533063fb8 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1190,6 +1190,8 @@ climate: name: Coolix Climate - platform: fujitsu_general name: Fujitsu General Climate + - platform: daikin + name: Daikin Climate - platform: yashima name: Yashima Climate - platform: mitsubishi