From eea78531a10109cba691d8f3c08478d3ec9220ff Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Mon, 30 Dec 2019 22:02:55 -0300 Subject: [PATCH] Climate Mitsubishi (#725) * add climate * Mitsubishi updates * refactor mitsubishi to use climate_ir * lint --- esphome/components/climate_ir/climate_ir.cpp | 1 + esphome/components/climate_ir/climate_ir.h | 3 + esphome/components/mitsubishi/__init__.py | 0 esphome/components/mitsubishi/climate.py | 18 +++++ esphome/components/mitsubishi/mitsubishi.cpp | 84 ++++++++++++++++++++ esphome/components/mitsubishi/mitsubishi.h | 22 +++++ 6 files changed, 128 insertions(+) create mode 100644 esphome/components/mitsubishi/__init__.py create mode 100644 esphome/components/mitsubishi/climate.py create mode 100644 esphome/components/mitsubishi/mitsubishi.cpp create mode 100644 esphome/components/mitsubishi/mitsubishi.h diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index 8f06ff2214..92a5b2423a 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -116,6 +116,7 @@ void ClimateIR::dump_config() { ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_)); } +bool ClimateIR::on_receive(remote_base::RemoteReceiveData data) { return false; } } // namespace climate_ir } // namespace esphome diff --git a/esphome/components/climate_ir/climate_ir.h b/esphome/components/climate_ir/climate_ir.h index 7a69b19786..82bf4247b0 100644 --- a/esphome/components/climate_ir/climate_ir.h +++ b/esphome/components/climate_ir/climate_ir.h @@ -63,6 +63,9 @@ class ClimateIR : public climate::Climate, public Component, public remote_base: remote_transmitter::RemoteTransmitterComponent *transmitter_; sensor::Sensor *sensor_{nullptr}; + + /// Handle received IR Buffer, so it is optional to implement + bool on_receive(remote_base::RemoteReceiveData data) override; }; } // namespace climate_ir diff --git a/esphome/components/mitsubishi/__init__.py b/esphome/components/mitsubishi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/mitsubishi/climate.py b/esphome/components/mitsubishi/climate.py new file mode 100644 index 0000000000..933e53baf0 --- /dev/null +++ b/esphome/components/mitsubishi/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'] + +mitsubishi_ns = cg.esphome_ns.namespace('mitsubishi') +MitsubishiClimate = mitsubishi_ns.class_('MitsubishiClimate', climate_ir.ClimateIR) + +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(MitsubishiClimate), +}) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield climate_ir.register_climate_ir(var, config) diff --git a/esphome/components/mitsubishi/mitsubishi.cpp b/esphome/components/mitsubishi/mitsubishi.cpp new file mode 100644 index 0000000000..b70aa6d394 --- /dev/null +++ b/esphome/components/mitsubishi/mitsubishi.cpp @@ -0,0 +1,84 @@ +#include "mitsubishi.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mitsubishi { + +static const char *TAG = "mitsubishi.climate"; + +const uint32_t MITSUBISHI_OFF = 0x00; + +const uint8_t MITSUBISHI_COOL = 0x18; +const uint8_t MITSUBISHI_DRY = 0x10; +const uint8_t MITSUBISHI_AUTO = 0x20; +const uint8_t MITSUBISHI_HEAT = 0x08; +const uint8_t MITSUBISHI_FAN_AUTO = 0x00; + +// Pulse parameters in usec +const uint16_t MITSUBISHI_BIT_MARK = 430; +const uint16_t MITSUBISHI_ONE_SPACE = 1250; +const uint16_t MITSUBISHI_ZERO_SPACE = 390; +const uint16_t MITSUBISHI_HEADER_MARK = 3500; +const uint16_t MITSUBISHI_HEADER_SPACE = 1700; +const uint16_t MITSUBISHI_MIN_GAP = 17500; + +void MitsubishiClimate::transmit_state() { + uint32_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x48, 0x00, 0x30, + 0x58, 0x61, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00}; + + switch (this->mode) { + case climate::CLIMATE_MODE_COOL: + remote_state[6] = MITSUBISHI_COOL; + break; + case climate::CLIMATE_MODE_HEAT: + remote_state[6] = MITSUBISHI_HEAT; + break; + case climate::CLIMATE_MODE_AUTO: + remote_state[6] = MITSUBISHI_AUTO; + break; + case climate::CLIMATE_MODE_OFF: + default: + remote_state[5] = MITSUBISHI_OFF; + break; + } + + remote_state[7] = + (uint8_t) roundf(clamp(this->target_temperature, MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX) - MITSUBISHI_TEMP_MIN); + + ESP_LOGV(TAG, "Sending Mitsubishi target temp: %.1f state: %02X mode: %02X temp: %02X", this->target_temperature, + remote_state[5], remote_state[6], remote_state[7]); + + // Checksum + for (int i = 0; i < 17; i++) { + remote_state[17] += remote_state[i]; + } + + auto transmit = this->transmitter_->transmit(); + auto data = transmit.get_data(); + + data->set_carrier_frequency(38000); + // repeat twice + for (uint16_t r = 0; r < 2; r++) { + // Header + data->mark(MITSUBISHI_HEADER_MARK); + data->space(MITSUBISHI_HEADER_SPACE); + // Data + for (uint8_t i : remote_state) + for (uint8_t j = 0; j < 8; j++) { + data->mark(MITSUBISHI_BIT_MARK); + bool bit = i & (1 << j); + data->space(bit ? MITSUBISHI_ONE_SPACE : MITSUBISHI_ZERO_SPACE); + } + // Footer + if (r == 0) { + data->mark(MITSUBISHI_BIT_MARK); + data->space(MITSUBISHI_MIN_GAP); // Pause before repeating + } + } + data->mark(MITSUBISHI_BIT_MARK); + + transmit.perform(); +} + +} // namespace mitsubishi +} // namespace esphome diff --git a/esphome/components/mitsubishi/mitsubishi.h b/esphome/components/mitsubishi/mitsubishi.h new file mode 100644 index 0000000000..e6bd7b8ebe --- /dev/null +++ b/esphome/components/mitsubishi/mitsubishi.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esphome/components/climate_ir/climate_ir.h" + +namespace esphome { +namespace mitsubishi { + +// Temperature +const uint8_t MITSUBISHI_TEMP_MIN = 16; // Celsius +const uint8_t MITSUBISHI_TEMP_MAX = 31; // Celsius + +class MitsubishiClimate : public climate_ir::ClimateIR { + public: + MitsubishiClimate() : climate_ir::ClimateIR(MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX) {} + + protected: + /// Transmit via IR the state of this climate controller. + void transmit_state() override; +}; + +} // namespace mitsubishi +} // namespace esphome