mirror of
https://github.com/esphome/esphome.git
synced 2024-11-28 12:46:22 +01:00
Add ZH/LT-01 climate component with IR receiver option (#4333)
Co-authored-by: Chris Feenstra <chris@cfeenstra.nl> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
da9c2f2256
commit
b4765fb5fb
@ -345,4 +345,5 @@ esphome/components/xiaomi_mhoc401/* @vevsvevs
|
|||||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||||
esphome/components/xl9535/* @mreditor97
|
esphome/components/xl9535/* @mreditor97
|
||||||
esphome/components/xpt2046/* @nielsnl68 @numo68
|
esphome/components/xpt2046/* @nielsnl68 @numo68
|
||||||
|
esphome/components/zhlt01/* @cfeenstra1024
|
||||||
esphome/components/zio_ultrasonic/* @kahrendt
|
esphome/components/zio_ultrasonic/* @kahrendt
|
||||||
|
0
esphome/components/zhlt01/__init__.py
Normal file
0
esphome/components/zhlt01/__init__.py
Normal file
19
esphome/components/zhlt01/climate.py
Normal file
19
esphome/components/zhlt01/climate.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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"]
|
||||||
|
CODEOWNERS = ["@cfeenstra1024"]
|
||||||
|
|
||||||
|
zhlt01_ns = cg.esphome_ns.namespace("zhlt01")
|
||||||
|
ZHLT01Climate = zhlt01_ns.class_("ZHLT01Climate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||||
|
{cv.GenerateID(): cv.declare_id(ZHLT01Climate)}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await climate_ir.register_climate_ir(var, config)
|
238
esphome/components/zhlt01/zhlt01.cpp
Normal file
238
esphome/components/zhlt01/zhlt01.cpp
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
#include "zhlt01.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace zhlt01 {
|
||||||
|
|
||||||
|
static const char *const TAG = "zhlt01.climate";
|
||||||
|
|
||||||
|
void ZHLT01Climate::transmit_state() {
|
||||||
|
uint8_t ir_message[12] = {0};
|
||||||
|
|
||||||
|
// Byte 1 : Timer
|
||||||
|
ir_message[1] = 0x00; // Timer off
|
||||||
|
|
||||||
|
// Byte 3 : Turbo mode
|
||||||
|
if (this->preset.value() == climate::CLIMATE_PRESET_BOOST) {
|
||||||
|
ir_message[3] = AC1_FAN_TURBO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Byte 5 : Last pressed button
|
||||||
|
ir_message[5] = 0x00; // fixed as power button
|
||||||
|
|
||||||
|
// Byte 7 : Power | Swing | Fan
|
||||||
|
// -- Power
|
||||||
|
if (this->mode == climate::CLIMATE_MODE_OFF) {
|
||||||
|
ir_message[7] = AC1_POWER_OFF;
|
||||||
|
} else {
|
||||||
|
ir_message[7] = AC1_POWER_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Swing
|
||||||
|
switch (this->swing_mode) {
|
||||||
|
case climate::CLIMATE_SWING_OFF:
|
||||||
|
ir_message[7] |= AC1_HDIR_FIXED | AC1_VDIR_FIXED;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||||
|
ir_message[7] |= AC1_HDIR_SWING | AC1_VDIR_FIXED;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_VERTICAL:
|
||||||
|
ir_message[7] |= AC1_HDIR_FIXED | AC1_VDIR_SWING;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_SWING_BOTH:
|
||||||
|
ir_message[7] |= AC1_HDIR_SWING | AC1_VDIR_SWING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Fan
|
||||||
|
switch (this->preset.value()) {
|
||||||
|
case climate::CLIMATE_PRESET_BOOST:
|
||||||
|
ir_message[7] |= AC1_FAN3;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_PRESET_SLEEP:
|
||||||
|
ir_message[7] |= AC1_FAN_SILENT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
switch (this->fan_mode.value()) {
|
||||||
|
case climate::CLIMATE_FAN_LOW:
|
||||||
|
ir_message[7] |= AC1_FAN1;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_MEDIUM:
|
||||||
|
ir_message[7] |= AC1_FAN2;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_HIGH:
|
||||||
|
ir_message[7] |= AC1_FAN3;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_AUTO:
|
||||||
|
ir_message[7] |= AC1_FAN_AUTO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Byte 9 : AC Mode | Temperature
|
||||||
|
// -- AC Mode
|
||||||
|
switch (this->mode) {
|
||||||
|
case climate::CLIMATE_MODE_AUTO:
|
||||||
|
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||||
|
ir_message[9] = AC1_MODE_AUTO;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_COOL:
|
||||||
|
ir_message[9] = AC1_MODE_COOL;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_HEAT:
|
||||||
|
ir_message[9] = AC1_MODE_HEAT;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_DRY:
|
||||||
|
ir_message[9] = AC1_MODE_DRY;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||||
|
ir_message[9] = AC1_MODE_FAN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Temperature
|
||||||
|
ir_message[9] |= (uint8_t) (this->target_temperature - 16.0f);
|
||||||
|
|
||||||
|
// Byte 11 : Remote control ID
|
||||||
|
ir_message[11] = 0xD5;
|
||||||
|
|
||||||
|
// Set checksum bytes
|
||||||
|
for (int i = 0; i < 12; i += 2) {
|
||||||
|
ir_message[i] = ~ir_message[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the code
|
||||||
|
auto transmit = this->transmitter_->transmit();
|
||||||
|
auto *data = transmit.get_data();
|
||||||
|
|
||||||
|
data->set_carrier_frequency(38000); // 38 kHz PWM
|
||||||
|
|
||||||
|
// Header
|
||||||
|
data->mark(AC1_HDR_MARK);
|
||||||
|
data->space(AC1_HDR_SPACE);
|
||||||
|
|
||||||
|
// Data
|
||||||
|
for (uint8_t i : ir_message) {
|
||||||
|
for (uint8_t j = 0; j < 8; j++) {
|
||||||
|
data->mark(AC1_BIT_MARK);
|
||||||
|
bool bit = i & (1 << j);
|
||||||
|
data->space(bit ? AC1_ONE_SPACE : AC1_ZERO_SPACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
data->mark(AC1_BIT_MARK);
|
||||||
|
data->space(0);
|
||||||
|
|
||||||
|
transmit.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZHLT01Climate::on_receive(remote_base::RemoteReceiveData data) {
|
||||||
|
// Validate header
|
||||||
|
if (!data.expect_item(AC1_HDR_MARK, AC1_HDR_SPACE)) {
|
||||||
|
ESP_LOGV(TAG, "Header fail");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode IR message
|
||||||
|
uint8_t ir_message[12] = {0};
|
||||||
|
// Read all bytes
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
// Read bit
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
if (data.expect_item(AC1_BIT_MARK, AC1_ONE_SPACE)) {
|
||||||
|
ir_message[i] |= 1 << j;
|
||||||
|
} else if (!data.expect_item(AC1_BIT_MARK, AC1_ZERO_SPACE)) {
|
||||||
|
ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGVV(TAG, "Byte %d %02X", i, ir_message[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate footer
|
||||||
|
if (!data.expect_mark(AC1_BIT_MARK)) {
|
||||||
|
ESP_LOGV(TAG, "Footer fail");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checksum
|
||||||
|
for (int i = 0; i < 12; i += 2) {
|
||||||
|
if (ir_message[i] != (uint8_t) (~ir_message[i + 1])) {
|
||||||
|
ESP_LOGV(TAG, "Byte %d checksum incorrect (%02X != %02X)", i, ir_message[i], (uint8_t) (~ir_message[i + 1]));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate remote control ID
|
||||||
|
if (ir_message[11] != 0xD5) {
|
||||||
|
ESP_LOGV(TAG, "Invalid remote control ID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All is good to go
|
||||||
|
|
||||||
|
if ((ir_message[7] & AC1_POWER_ON) == 0) {
|
||||||
|
this->mode = climate::CLIMATE_MODE_OFF;
|
||||||
|
} else {
|
||||||
|
// Vertical swing
|
||||||
|
if ((ir_message[7] & 0x0C) == AC1_VDIR_FIXED) {
|
||||||
|
if ((ir_message[7] & 0x10) == AC1_HDIR_FIXED) {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||||
|
} else {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((ir_message[7] & 0x10) == AC1_HDIR_FIXED) {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||||
|
} else {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_BOTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preset + Fan speed
|
||||||
|
if ((ir_message[3] & AC1_FAN_TURBO) == AC1_FAN_TURBO) {
|
||||||
|
this->preset = climate::CLIMATE_PRESET_BOOST;
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||||
|
} else if ((ir_message[7] & 0xE1) == AC1_FAN_SILENT) {
|
||||||
|
this->preset = climate::CLIMATE_PRESET_SLEEP;
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||||
|
} else if ((ir_message[7] & 0xE1) == AC1_FAN_AUTO) {
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||||
|
} else if ((ir_message[7] & 0xE1) == AC1_FAN1) {
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||||
|
} else if ((ir_message[7] & 0xE1) == AC1_FAN2) {
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||||
|
} else if ((ir_message[7] & 0xE1) == AC1_FAN3) {
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AC Mode
|
||||||
|
if ((ir_message[9] & 0xE0) == AC1_MODE_COOL) {
|
||||||
|
this->mode = climate::CLIMATE_MODE_COOL;
|
||||||
|
} else if ((ir_message[9] & 0xE0) == AC1_MODE_HEAT) {
|
||||||
|
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||||
|
} else if ((ir_message[9] & 0xE0) == AC1_MODE_DRY) {
|
||||||
|
this->mode = climate::CLIMATE_MODE_DRY;
|
||||||
|
} else if ((ir_message[9] & 0xE0) == AC1_MODE_FAN) {
|
||||||
|
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||||
|
} else {
|
||||||
|
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taregt Temperature
|
||||||
|
this->target_temperature = (ir_message[9] & 0x1F) + 16.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->publish_state();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace zhlt01
|
||||||
|
} // namespace esphome
|
167
esphome/components/zhlt01/zhlt01.h
Normal file
167
esphome/components/zhlt01/zhlt01.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/climate_ir/climate_ir.h"
|
||||||
|
|
||||||
|
/***********************************************************************************
|
||||||
|
* SOURCE
|
||||||
|
***********************************************************************************
|
||||||
|
* The IR codes and the functional description below were taken from
|
||||||
|
* 'arduino-heatpumpir/ZHLT01HeatpumpIR.h' as can be found on GitHub
|
||||||
|
* https://github.com/ToniA/arduino-heatpumpir/blob/master/ZHLT01HeatpumpIR.h
|
||||||
|
*
|
||||||
|
************************************************************************************
|
||||||
|
* Airconditional remote control encoder for:
|
||||||
|
*
|
||||||
|
* ZH/LT-01 Remote control https://www.google.com/search?q=zh/lt-01
|
||||||
|
*
|
||||||
|
* The ZH/LT-01 remote control is used for many locally branded Split
|
||||||
|
* airconditioners, so it is better to name this protocol by the name of the
|
||||||
|
* REMOTE rather then the name of the Airconditioner. For this project I used
|
||||||
|
* a 2014 model Eurom-airconditioner, which is Dutch-branded and sold in
|
||||||
|
* the Netherlands at Hornbach.
|
||||||
|
*
|
||||||
|
* For airco-brands:
|
||||||
|
* Eurom
|
||||||
|
* Chigo
|
||||||
|
* Tristar
|
||||||
|
* Tecnomaster
|
||||||
|
* Elgin
|
||||||
|
* Geant
|
||||||
|
* Tekno
|
||||||
|
* Topair
|
||||||
|
* Proma
|
||||||
|
* Sumikura
|
||||||
|
* JBS
|
||||||
|
* Turbo Air
|
||||||
|
* Nakatomy
|
||||||
|
* Celestial Air
|
||||||
|
* Ager
|
||||||
|
* Blueway
|
||||||
|
* Airlux
|
||||||
|
* Etc.
|
||||||
|
*
|
||||||
|
***********************************************************************************
|
||||||
|
* SUMMARY FUNCTIONAL DESCRIPTION
|
||||||
|
***********************************************************************************
|
||||||
|
* The remote sends a 12 Byte message which contains all possible settings every
|
||||||
|
* time.
|
||||||
|
*
|
||||||
|
* Byte 11 (and 10) contain the remote control identifier and are always 0xD5 and
|
||||||
|
* 0x2A respectively for the ZH/LT-01 remote control.
|
||||||
|
* Every UNeven Byte (01,03,05,07 and 09) holds command data
|
||||||
|
* Every EVEN Byte (00,02,04,06,08 and 10) holds a checksum of the corresponding
|
||||||
|
* command-, or identifier-byte by _inverting_ the bits, for example:
|
||||||
|
*
|
||||||
|
* The identifier byte[11] = 0xD5 = B1101 0101
|
||||||
|
* The checksum byte[10] = 0x2A = B0010 1010
|
||||||
|
*
|
||||||
|
* So, you can check the message by:
|
||||||
|
* - inverting the bits of the checksum byte with the corresponding command-, or
|
||||||
|
* identifier byte, they should be the same, or
|
||||||
|
* - Summing up the checksum byte and the corresponding command-, or identifier byte,
|
||||||
|
* they should always add up to 0xFF = B11111111 = 255
|
||||||
|
*
|
||||||
|
* Control bytes:
|
||||||
|
* [01] - Timer (1-24 hours, Off)
|
||||||
|
* Time is hardcoded to OFF
|
||||||
|
*
|
||||||
|
* [03] - LAMP ON/OFF, TURBO ON/OFF, HOLD ON/OFF
|
||||||
|
* Lamp and Hold are hardcoded to OFF
|
||||||
|
* Turbo is used for the BOOST preset
|
||||||
|
*
|
||||||
|
* [05] - Indicates which button the user _pressed_ on the remote control
|
||||||
|
* Hardcoded to POWER-button
|
||||||
|
*
|
||||||
|
* [07] - POWER ON/OFF, FAN AUTO/3/2/1, SLEEP ON/OFF, AIRFLOW ON/OFF,
|
||||||
|
* VERTICAL SWING/WIND/FIXED
|
||||||
|
* SLEEP is used for preset SLEEP
|
||||||
|
* Vertical Swing supports Fixed, Swing and "Wind". The Wind option
|
||||||
|
* is ignored in this implementation
|
||||||
|
*
|
||||||
|
* [09] - MODE AUTO/COOL/VENT/DRY/HEAT, TEMPERATURE (16 - 32°C)
|
||||||
|
*
|
||||||
|
***********************************************************************************/
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace zhlt01 {
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* TIMINGS
|
||||||
|
* Space: Not used
|
||||||
|
* Header Mark: 6100 us
|
||||||
|
* Header Space: 7400 us
|
||||||
|
* Bit Mark: 500 us
|
||||||
|
* Zero Space: 600 us
|
||||||
|
* One Space: 1800 us
|
||||||
|
*
|
||||||
|
* Note : These timings are slightly different than those of ZHLT01HeatpumpIR
|
||||||
|
* The values below were measured by taking the average of 2 different
|
||||||
|
* remote controls each sending 10 commands
|
||||||
|
*******************************************************************************/
|
||||||
|
static const uint32_t AC1_HDR_MARK = 6100;
|
||||||
|
static const uint32_t AC1_HDR_SPACE = 7400;
|
||||||
|
static const uint32_t AC1_BIT_MARK = 500;
|
||||||
|
static const uint32_t AC1_ZERO_SPACE = 600;
|
||||||
|
static const uint32_t AC1_ONE_SPACE = 1800;
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
*
|
||||||
|
* ZHLT01 codes
|
||||||
|
*
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
// Power
|
||||||
|
static const uint8_t AC1_POWER_OFF = 0x00;
|
||||||
|
static const uint8_t AC1_POWER_ON = 0x02;
|
||||||
|
|
||||||
|
// Operating Modes
|
||||||
|
static const uint8_t AC1_MODE_AUTO = 0x00;
|
||||||
|
static const uint8_t AC1_MODE_COOL = 0x20;
|
||||||
|
static const uint8_t AC1_MODE_DRY = 0x40;
|
||||||
|
static const uint8_t AC1_MODE_FAN = 0x60;
|
||||||
|
static const uint8_t AC1_MODE_HEAT = 0x80;
|
||||||
|
|
||||||
|
// Fan control
|
||||||
|
static const uint8_t AC1_FAN_AUTO = 0x00;
|
||||||
|
static const uint8_t AC1_FAN_SILENT = 0x01;
|
||||||
|
static const uint8_t AC1_FAN1 = 0x60;
|
||||||
|
static const uint8_t AC1_FAN2 = 0x40;
|
||||||
|
static const uint8_t AC1_FAN3 = 0x20;
|
||||||
|
static const uint8_t AC1_FAN_TURBO = 0x08;
|
||||||
|
|
||||||
|
// Vertical Swing
|
||||||
|
static const uint8_t AC1_VDIR_WIND = 0x00; // "Natural Wind", ignore
|
||||||
|
static const uint8_t AC1_VDIR_SWING = 0x04; // Swing
|
||||||
|
static const uint8_t AC1_VDIR_FIXED = 0x08; // Fixed
|
||||||
|
|
||||||
|
// Horizontal Swing
|
||||||
|
static const uint8_t AC1_HDIR_SWING = 0x00; // Swing
|
||||||
|
static const uint8_t AC1_HDIR_FIXED = 0x10; // Fixed
|
||||||
|
|
||||||
|
// Temperature range
|
||||||
|
static const float AC1_TEMP_MIN = 16.0f;
|
||||||
|
static const float AC1_TEMP_MAX = 32.0f;
|
||||||
|
static const float AC1_TEMP_INC = 1.0f;
|
||||||
|
|
||||||
|
class ZHLT01Climate : public climate_ir::ClimateIR {
|
||||||
|
public:
|
||||||
|
ZHLT01Climate()
|
||||||
|
: climate_ir::ClimateIR(
|
||||||
|
AC1_TEMP_MIN, AC1_TEMP_MAX, AC1_TEMP_INC, 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_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL,
|
||||||
|
climate::CLIMATE_SWING_BOTH},
|
||||||
|
{climate::CLIMATE_PRESET_NONE, climate::CLIMATE_PRESET_SLEEP, climate::CLIMATE_PRESET_BOOST}) {}
|
||||||
|
|
||||||
|
void setup() override { climate_ir::ClimateIR::setup(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Transmit via IR the state of this climate controller.
|
||||||
|
void transmit_state() override;
|
||||||
|
/// Handle received IR Buffer
|
||||||
|
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace zhlt01
|
||||||
|
} // namespace esphome
|
@ -2297,6 +2297,8 @@ climate:
|
|||||||
heat_mode: extended
|
heat_mode: extended
|
||||||
- platform: whynter
|
- platform: whynter
|
||||||
name: Whynter
|
name: Whynter
|
||||||
|
- platform: zhlt01
|
||||||
|
name: ZH/LT-01 Climate
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: climate_custom
|
- id: climate_custom
|
||||||
|
Loading…
Reference in New Issue
Block a user