Initial commit
This commit is contained in:
0
components/tclac/__init__.py
Normal file
0
components/tclac/__init__.py
Normal file
114
components/tclac/automation.h
Normal file
114
components/tclac/automation.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "tclac.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tclac {
|
||||
|
||||
// Шаблон действия: изменение вертикальной фиксации заслонки
|
||||
template<typename... Ts> class VerticalAirflowAction : public Action<Ts...> {
|
||||
public:
|
||||
VerticalAirflowAction(tclacClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(AirflowVerticalDirection, direction)
|
||||
void play(Ts... x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: изменение горизонтальной фиксации заслонок
|
||||
template<typename... Ts> class HorizontalAirflowAction : public Action<Ts...> {
|
||||
public:
|
||||
HorizontalAirflowAction(tclacClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(AirflowHorizontalDirection, direction)
|
||||
void play(Ts... x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: изменение режима вертикального качания заслонки
|
||||
template<typename... Ts> class VerticalSwingDirectionAction : public Action<Ts...> {
|
||||
public:
|
||||
VerticalSwingDirectionAction(tclacClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(VerticalSwingDirection, direction)
|
||||
void play(Ts... x) { this->parent_->set_vertical_swing_direction(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: изменение режима горизонтального качания заслонок
|
||||
template<typename... Ts> class HorizontalSwingDirectionAction : public Action<Ts...> {
|
||||
public:
|
||||
HorizontalSwingDirectionAction(tclacClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(HorizontalSwingDirection, direction)
|
||||
void play(Ts... x) { this->parent_->set_horizontal_swing_direction(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: включение дисплея
|
||||
template<typename... Ts> class DisplayOnAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayOnAction(tclacClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_display_state(true); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: выключение дисплея
|
||||
template<typename... Ts> class DisplayOffAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayOffAction(tclacClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_display_state(false); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: включение пищалки
|
||||
template<typename... Ts> class BeeperOnAction : public Action<Ts...> {
|
||||
public:
|
||||
BeeperOnAction(tclacClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_beeper_state(true); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: выклюение пищалки
|
||||
template<typename... Ts> class BeeperOffAction : public Action<Ts...> {
|
||||
public:
|
||||
BeeperOffAction(tclacClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_beeper_state(false); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: включение индикатора модуля
|
||||
template<typename... Ts> class ModuleDisplayOnAction : public Action<Ts...> {
|
||||
public:
|
||||
ModuleDisplayOnAction(tclacClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_module_display_state(true); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
// Шаблон действия: выключение индикатора модуля
|
||||
template<typename... Ts> class ModuleDisplayOffAction : public Action<Ts...> {
|
||||
public:
|
||||
ModuleDisplayOffAction(tclacClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_module_display_state(false); }
|
||||
|
||||
protected:
|
||||
tclacClimate *parent_;
|
||||
};
|
||||
|
||||
} // namespace tclac
|
||||
} // namespace esphome
|
||||
265
components/tclac/climate.py
Normal file
265
components/tclac/climate.py
Normal file
@@ -0,0 +1,265 @@
|
||||
from esphome import automation, pins
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import climate, uart
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_LEVEL,
|
||||
CONF_BEEPER,
|
||||
CONF_MAX_TEMPERATURE,
|
||||
CONF_MIN_TEMPERATURE,
|
||||
CONF_SUPPORTED_MODES,
|
||||
CONF_SUPPORTED_PRESETS,
|
||||
CONF_SUPPORTED_SWING_MODES,
|
||||
)
|
||||
|
||||
from esphome.components.climate import (
|
||||
ClimateMode,
|
||||
ClimatePreset,
|
||||
ClimateSwingMode,
|
||||
CONF_CURRENT_TEMPERATURE,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["climate"]
|
||||
CODEOWNERS = ["@I-am-nightingale"]
|
||||
DEPENDENCIES = ["climate", "uart"]
|
||||
CONF_RX_LED = "rx_led"
|
||||
CONF_TX_LED = "tx_led"
|
||||
CONF_DISPLAY = "show_display"
|
||||
CONF_USE_OLED = "use_oled_display"
|
||||
CONF_MODULE_DISPLAY = "show_module_display"
|
||||
CONF_VERTICAL_AIRFLOW = "vertical_airflow"
|
||||
CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow"
|
||||
CONF_VERTICAL_SWING_MODE = "vertical_swing_mode"
|
||||
CONF_HORIZONTAL_SWING_MODE = "horizontal_swing_mode"
|
||||
|
||||
tclac_ns = cg.esphome_ns.namespace("tclac")
|
||||
tclacClimate = tclac_ns.class_("tclacClimate", uart.UARTDevice, climate.Climate, cg.PollingComponent)
|
||||
|
||||
AirflowVerticalDirection = tclac_ns.enum("AirflowVerticalDirection", True)
|
||||
AIRFLOW_VERTICAL_DIRECTION_OPTIONS = {
|
||||
"LAST": AirflowVerticalDirection.LAST,
|
||||
"MAX_UP": AirflowVerticalDirection.MAX_UP,
|
||||
"UP": AirflowVerticalDirection.UP,
|
||||
"CENTER": AirflowVerticalDirection.CENTER,
|
||||
"DOWN": AirflowVerticalDirection.DOWN,
|
||||
"MAX_DOWN": AirflowVerticalDirection.MAX_DOWN,
|
||||
}
|
||||
|
||||
AirflowHorizontalDirection = tclac_ns.enum("AirflowHorizontalDirection", True)
|
||||
AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = {
|
||||
"LAST": AirflowHorizontalDirection.LAST,
|
||||
"MAX_LEFT": AirflowHorizontalDirection.MAX_LEFT,
|
||||
"LEFT": AirflowHorizontalDirection.LEFT,
|
||||
"CENTER": AirflowHorizontalDirection.CENTER,
|
||||
"RIGHT": AirflowHorizontalDirection.RIGHT,
|
||||
"MAX_RIGHT": AirflowHorizontalDirection.MAX_RIGHT,
|
||||
}
|
||||
|
||||
VerticalSwingDirection = tclac_ns.enum("VerticalSwingDirection", True)
|
||||
VERTICAL_SWING_DIRECTION_OPTIONS = {
|
||||
"UP_DOWN": VerticalSwingDirection.UPDOWN,
|
||||
"UPSIDE": VerticalSwingDirection.UPSIDE,
|
||||
"DOWNSIDE": VerticalSwingDirection.DOWNSIDE,
|
||||
}
|
||||
|
||||
HorizontalSwingDirection = tclac_ns.enum("HorizontalSwingDirection", True)
|
||||
HORIZONTAL_SWING_DIRECTION_OPTIONS = {
|
||||
"LEFT_RIGHT": HorizontalSwingDirection.LEFT_RIGHT,
|
||||
"LEFTSIDE": HorizontalSwingDirection.LEFTSIDE,
|
||||
"CENTER": HorizontalSwingDirection.CENTER,
|
||||
"RIGHTSIDE": HorizontalSwingDirection.RIGHTSIDE,
|
||||
}
|
||||
|
||||
SUPPORTED_SWING_MODES_OPTIONS = {
|
||||
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
|
||||
"VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,
|
||||
"HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
|
||||
"BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH,
|
||||
}
|
||||
|
||||
SUPPORTED_CLIMATE_MODES_OPTIONS = {
|
||||
"OFF": ClimateMode.CLIMATE_MODE_OFF, # always available
|
||||
"AUTO": ClimateMode.CLIMATE_MODE_AUTO, # always available
|
||||
"COOL": ClimateMode.CLIMATE_MODE_COOL,
|
||||
"HEAT": ClimateMode.CLIMATE_MODE_HEAT,
|
||||
"DRY": ClimateMode.CLIMATE_MODE_DRY,
|
||||
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
|
||||
}
|
||||
|
||||
# Упрощаем себе житзнь и даем спискам названия покороче
|
||||
validate_presets = cv.enum(AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS, upper=True)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
climate.CLIMATE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(tclacClimate),
|
||||
cv.Optional(CONF_BEEPER, default=True): cv.boolean,
|
||||
cv.Optional(CONF_DISPLAY, default=True): cv.boolean,
|
||||
cv.Optional(CONF_RX_LED): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_TX_LED): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_MODULE_DISPLAY, default=True): cv.boolean,
|
||||
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(cv.enum(SUPPORTED_CLIMATE_MODES_OPTIONS, upper=True)),
|
||||
cv.Optional(CONF_SUPPORTED_SWING_MODES,default=["OFF","VERTICAL","HORIZONTAL","BOTH",],): cv.ensure_list(cv.enum(SUPPORTED_SWING_MODES_OPTIONS, upper=True)),
|
||||
}
|
||||
)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
BeeperOnAction = tclac_ns.class_("BeeperOnAction", automation.Action)
|
||||
BeeperOffAction = tclac_ns.class_("BeeperOffAction", automation.Action)
|
||||
DisplayOnAction = tclac_ns.class_("DisplayOnAction", automation.Action)
|
||||
DisplayOffAction = tclac_ns.class_("DisplayOffAction", automation.Action)
|
||||
ModuleDisplayOnAction = tclac_ns.class_("ModuleDisplayOnAction", automation.Action)
|
||||
ModuleDisplayOffAction = tclac_ns.class_("ModuleDisplayOffAction", automation.Action)
|
||||
VerticalAirflowAction = tclac_ns.class_("VerticalAirflowAction", automation.Action)
|
||||
HorizontalAirflowAction = tclac_ns.class_("HorizontalAirflowAction", automation.Action)
|
||||
VerticalSwingDirectionAction = tclac_ns.class_("VerticalSwingDirectionAction", automation.Action)
|
||||
HorizontalSwingDirectionAction = tclac_ns.class_("HorizontalSwingDirectionAction", automation.Action)
|
||||
|
||||
# TCLAC_ACTION_BASE_SCHEMA = cv.Schema(
|
||||
TCLAC_ACTION_BASE_SCHEMA = automation.maybe_simple_id({cv.GenerateID(CONF_ID): cv.use_id(tclacClimate),})
|
||||
|
||||
@automation.register_action(
|
||||
"climate.tclac.display_on", DisplayOnAction, cv.Schema
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.tclac.display_off", DisplayOffAction, cv.Schema
|
||||
)
|
||||
async def display_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"climate.tclac.beeper_on", BeeperOnAction, cv.Schema
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.tclac.beeper_off", BeeperOffAction, cv.Schema
|
||||
)
|
||||
async def beeper_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"climate.tclac.module_display_on", ModuleDisplayOnAction, cv.Schema
|
||||
)
|
||||
@automation.register_action(
|
||||
"climate.tclac.module_display_off", ModuleDisplayOffAction, cv.Schema
|
||||
)
|
||||
async def module_display_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
return var
|
||||
|
||||
|
||||
# Set vertical airflow direction action
|
||||
@automation.register_action(
|
||||
"climate.tclac.set_vertical_airflow",
|
||||
VerticalAirflowAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(tclacClimate),
|
||||
cv.Required(CONF_VERTICAL_AIRFLOW): cv.templatable(cv.enum(AIRFLOW_VERTICAL_DIRECTION_OPTIONS, upper=True)),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def tclac_set_vertical_airflow_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(
|
||||
config[CONF_VERTICAL_AIRFLOW], args, AirflowVerticalDirection
|
||||
)
|
||||
cg.add(var.set_direction(template_))
|
||||
return var
|
||||
|
||||
|
||||
# Set horizontal airflow direction action
|
||||
@automation.register_action(
|
||||
"climate.tclac.set_horizontal_airflow",
|
||||
HorizontalAirflowAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(tclacClimate),
|
||||
cv.Required(CONF_HORIZONTAL_AIRFLOW): cv.templatable(cv.enum(AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS, upper=True)),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def tclac_set_horizontal_airflow_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_HORIZONTAL_AIRFLOW], args, AirflowHorizontalDirection)
|
||||
cg.add(var.set_direction(template_))
|
||||
return var
|
||||
|
||||
|
||||
# Регистрация события установки вертикального качания шторки
|
||||
@automation.register_action(
|
||||
"climate.tclac.set_vertical_swing_direction",
|
||||
VerticalSwingDirectionAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(tclacClimate),
|
||||
cv.Required(CONF_VERTICAL_SWING_MODE): cv.templatable(cv.enum(VERTICAL_SWING_DIRECTION_OPTIONS, upper=True)),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def tclac_set_vertical_swing_direction_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_VERTICAL_SWING_MODE], args, VerticalSwingDirection)
|
||||
cg.add(var.set_swing_direction(template_))
|
||||
return var
|
||||
|
||||
|
||||
# Регистрация события установки горизонтального качания шторок
|
||||
@automation.register_action(
|
||||
"climate.tclac.set_horizontal_swing_direction",
|
||||
HorizontalSwingDirectionAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(tclacClimate),
|
||||
cv.Required(CONF_HORIZONTAL_SWING_MODE): cv.templatable(cv.enum(HORIZONTAL_SWING_DIRECTION_OPTIONS, upper=True)),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def tclac_set_horizontal_swing_direction_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_HORIZONTAL_SWING_MODE], args, HorizontalSwingDirection)
|
||||
cg.add(var.set_swing_direction(template_))
|
||||
return var
|
||||
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield uart.register_uart_device(var, config)
|
||||
yield climate.register_climate(var, config)
|
||||
|
||||
if CONF_SUPPORTED_MODES in config:
|
||||
cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES]))
|
||||
if CONF_SUPPORTED_SWING_MODES in config:
|
||||
cg.add(var.set_supported_swing_modes(config[CONF_SUPPORTED_SWING_MODES]))
|
||||
if CONF_BEEPER in config:
|
||||
cg.add(var.set_beeper_state(config[CONF_BEEPER]))
|
||||
if CONF_DISPLAY in config:
|
||||
cg.add(var.set_display_state(config[CONF_DISPLAY]))
|
||||
if CONF_MODULE_DISPLAY in config:
|
||||
cg.add(var.set_module_display_state(config[CONF_MODULE_DISPLAY]))
|
||||
if CONF_USE_OLED in config:
|
||||
cg.add_define("USE_OLED_DISPLAY")
|
||||
if CONF_RX_LED in config:
|
||||
cg.add_define("CONF_RX_LED")
|
||||
rx_led_pin = yield cg.gpio_pin_expression(config[CONF_RX_LED])
|
||||
cg.add(var.set_rx_led_pin(rx_led_pin))
|
||||
if CONF_TX_LED in config:
|
||||
cg.add_define("CONF_TX_LED")
|
||||
tx_led_pin = yield cg.gpio_pin_expression(config[CONF_TX_LED])
|
||||
cg.add(var.set_tx_led_pin(tx_led_pin))
|
||||
660
components/tclac/tclac.cpp
Normal file
660
components/tclac/tclac.cpp
Normal file
@@ -0,0 +1,660 @@
|
||||
#include "esphome.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "tclac.h"
|
||||
|
||||
namespace esphome{
|
||||
namespace tclac{
|
||||
|
||||
|
||||
ClimateTraits tclacClimate::traits() {
|
||||
auto traits = climate::ClimateTraits();
|
||||
|
||||
traits.set_supports_action(false);
|
||||
|
||||
traits.set_supported_modes(
|
||||
{
|
||||
climate::CLIMATE_MODE_AUTO,
|
||||
climate::CLIMATE_MODE_COOL,
|
||||
climate::CLIMATE_MODE_DRY,
|
||||
climate::CLIMATE_MODE_FAN_ONLY,
|
||||
climate::CLIMATE_MODE_HEAT,
|
||||
climate::CLIMATE_MODE_OFF
|
||||
});
|
||||
|
||||
traits.set_supported_fan_modes(
|
||||
{
|
||||
climate::CLIMATE_FAN_AUTO, // auto
|
||||
climate::CLIMATE_FAN_QUIET, // silent
|
||||
climate::CLIMATE_FAN_LOW, // |
|
||||
climate::CLIMATE_FAN_MIDDLE, // ||
|
||||
climate::CLIMATE_FAN_MEDIUM, // |||
|
||||
climate::CLIMATE_FAN_HIGH, // ||||
|
||||
climate::CLIMATE_FAN_FOCUS, // |||||
|
||||
climate::CLIMATE_FAN_DIFFUSE // POWER [7]
|
||||
});
|
||||
|
||||
//traits.set_supported_swing_modes({climate::CLIMATE_SWING_OFF,climate::CLIMATE_SWING_BOTH,climate::CLIMATE_SWING_VERTICAL,climate::CLIMATE_SWING_HORIZONTAL});
|
||||
traits.set_supported_swing_modes(this->supported_swing_modes_);
|
||||
|
||||
if (!traits.get_supported_swing_modes().empty())
|
||||
traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_OFF);
|
||||
|
||||
traits.set_visual_min_temperature(MIN_SET_TEMPERATURE);
|
||||
traits.set_visual_max_temperature(MAX_SET_TEMPERATURE);
|
||||
traits.set_visual_temperature_step(STEP_TEMPERATURE);
|
||||
traits.set_supports_current_temperature(true);
|
||||
traits.set_supports_two_point_target_temperature(false);
|
||||
|
||||
return traits;
|
||||
}
|
||||
|
||||
|
||||
void tclacClimate::setup() {
|
||||
|
||||
// Serial.begin(9600);
|
||||
// ESP_LOGD("TCL" , "Started Serial");
|
||||
#ifdef CONF_RX_LED
|
||||
this->rx_led_pin_->setup();
|
||||
this->rx_led_pin_->digital_write(false);
|
||||
#endif
|
||||
#ifdef CONF_TX_LED
|
||||
this->tx_led_pin_->setup();
|
||||
this->tx_led_pin_->digital_write(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tclacClimate::loop() {
|
||||
// Если в буфере UART что-то есть, то читаем это что-то
|
||||
if (esphome::uart::UARTDevice::available() > 0) {
|
||||
dataShow(0, true);
|
||||
dataRX[0] = esphome::uart::UARTDevice::read();
|
||||
// Если принятый байт- не заголовок (0xBB), то просто покидаем цикл
|
||||
if (dataRX[0] != 0xBB) {
|
||||
ESP_LOGD("TCL", "Wrong byte");
|
||||
dataShow(0,0);
|
||||
return;
|
||||
}
|
||||
// А вот если совпал заголовок (0xBB), то начинаем чтение по цепочке еще 4 байт
|
||||
delay(5);
|
||||
dataRX[1] = esphome::uart::UARTDevice::read();
|
||||
delay(5);
|
||||
dataRX[2] = esphome::uart::UARTDevice::read();
|
||||
delay(5);
|
||||
dataRX[3] = esphome::uart::UARTDevice::read();
|
||||
delay(5);
|
||||
dataRX[4] = esphome::uart::UARTDevice::read();
|
||||
|
||||
auto raw = getHex(dataRX, 5);
|
||||
ESP_LOGD("TCL", "first 5 byte : %s ", raw.c_str());
|
||||
|
||||
//Из первых 5 байт нам нужен пятый- он содержит длину сообщения
|
||||
esphome::uart::UARTDevice::read_array(dataRX+5, dataRX[4]+1);
|
||||
|
||||
byte check = getChecksum(dataRX, sizeof(dataRX));
|
||||
|
||||
raw = getHex(dataRX, sizeof(dataRX));
|
||||
ESP_LOGD("TCL", "RX full : %s ", raw.c_str());
|
||||
// Проверяем контрольную сумму
|
||||
if (check != dataRX[60]) {
|
||||
ESP_LOGD("TCL", "Invalid checksum %x", check);
|
||||
tclacClimate::dataShow(0,0);
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGD("TCL", "checksum OK %x", check);
|
||||
}
|
||||
tclacClimate::dataShow(0,0);
|
||||
// Прочитав все из буфера приступаем к разбору данных
|
||||
tclacClimate::readData();
|
||||
}
|
||||
}
|
||||
|
||||
void tclacClimate::update() {
|
||||
|
||||
tclacClimate::dataShow(1,1);
|
||||
//Serial.write(poll, sizeof(poll));
|
||||
this->esphome::uart::UARTDevice::write_array(poll, sizeof(poll));
|
||||
auto raw = tclacClimate::getHex(poll, sizeof(poll));
|
||||
ESP_LOGD("TCL", "chek status sended");
|
||||
tclacClimate::dataShow(1,0);
|
||||
}
|
||||
|
||||
void tclacClimate::readData() {
|
||||
|
||||
current_temperature = float((( (dataRX[17] << 8) | dataRX[18] ) / 374 - 32)/1.8);
|
||||
target_temperature = (dataRX[FAN_SPEED_POS] & SET_TEMP_MASK) + 16;
|
||||
|
||||
ESP_LOGD("TCL", "TEMP: %f ", current_temperature);
|
||||
|
||||
if (dataRX[MODE_POS] & ( 1 << 4)) {
|
||||
//Если кондиционер включен, то разбираем данные для отображения
|
||||
uint8_t modeswitch = MODE_MASK & dataRX[MODE_POS];
|
||||
uint8_t fanspeedswitch = FAN_SPEED_MASK & dataRX[FAN_SPEED_POS];
|
||||
uint8_t swingmodeswitch = SWING_MODE_MASK & dataRX[SWING_POS];
|
||||
|
||||
switch (modeswitch) {
|
||||
case MODE_AUTO:
|
||||
mode = climate::CLIMATE_MODE_AUTO;
|
||||
break;
|
||||
case MODE_COOL:
|
||||
mode = climate::CLIMATE_MODE_COOL;
|
||||
break;
|
||||
case MODE_DRY:
|
||||
mode = climate::CLIMATE_MODE_DRY;
|
||||
break;
|
||||
case MODE_FAN_ONLY:
|
||||
mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||
break;
|
||||
case MODE_HEAT:
|
||||
mode = climate::CLIMATE_MODE_HEAT;
|
||||
break;
|
||||
default:
|
||||
mode = climate::CLIMATE_MODE_AUTO;
|
||||
}
|
||||
|
||||
if ( dataRX[FAN_QUIET_POS] & FAN_QUIET) {
|
||||
fan_mode = climate::CLIMATE_FAN_QUIET;
|
||||
} else if (dataRX[MODE_POS] & FAN_DIFFUSE){
|
||||
fan_mode = climate::CLIMATE_FAN_DIFFUSE;
|
||||
} else {
|
||||
switch (fanspeedswitch) {
|
||||
case FAN_AUTO:
|
||||
fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
break;
|
||||
case FAN_LOW:
|
||||
fan_mode = climate::CLIMATE_FAN_LOW;
|
||||
break;
|
||||
case FAN_MIDDLE:
|
||||
fan_mode = climate::CLIMATE_FAN_MIDDLE;
|
||||
break;
|
||||
case FAN_MEDIUM:
|
||||
fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||
break;
|
||||
case FAN_HIGH:
|
||||
fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||
break;
|
||||
case FAN_FOCUS:
|
||||
fan_mode = climate::CLIMATE_FAN_FOCUS;
|
||||
break;
|
||||
default:
|
||||
fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
switch (swingmodeswitch) {
|
||||
case SWING_OFF:
|
||||
swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
break;
|
||||
case SWING_HORIZONTAL:
|
||||
swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
||||
break;
|
||||
case SWING_VERTICAL:
|
||||
swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||
break;
|
||||
case SWING_BOTH:
|
||||
swing_mode = climate::CLIMATE_SWING_BOTH;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Если кондиционер выключен, то все режимы показываются, как выключенные
|
||||
mode = climate::CLIMATE_MODE_OFF;
|
||||
fan_mode = climate::CLIMATE_FAN_OFF;
|
||||
swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
// Публикуем данные
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
// Climate control
|
||||
void tclacClimate::control(const ClimateCall &call) {
|
||||
|
||||
uint8_t switchvar = 0;
|
||||
|
||||
dataTX[7] = 0b00000000;//eco,display,beep,ontimerenable, offtimerenable,power,0,0
|
||||
dataTX[8] = 0b00000000;//mute,0,turbo,health,mode(4) 0=cool 1=fan 2=dry 3=heat 4=auto
|
||||
dataTX[9] = 0b00000000;//[9] = 0,0,0,0,temp(4) 31 - value
|
||||
dataTX[10] = 0b00000000;//[10] = 0,timerindicator,swingv(3),fan(3) 0=auto 1=low 2=med 3=high
|
||||
// {0,2,3,5,0};
|
||||
dataTX[11] = 0b00000000;
|
||||
dataTX[32] = 0b00000000;
|
||||
dataTX[33] = 0b00000000;
|
||||
|
||||
if (call.get_mode().has_value()){
|
||||
switchvar = call.get_mode().value();
|
||||
} else {
|
||||
switchvar = mode;
|
||||
}
|
||||
|
||||
// Включаем или отключаем пищалку в зависимости от переключателя в настройках
|
||||
if (beeper_status_){
|
||||
ESP_LOGD("TCL", "Beep mode ON");
|
||||
dataTX[7] += 0b00100000;
|
||||
} else {
|
||||
ESP_LOGD("TCL", "Beep mode OFF");
|
||||
dataTX[7] += 0b00000000;
|
||||
}
|
||||
|
||||
// Включаем или отключаем дисплей на кондиционере в зависимости от переключателя в настройках
|
||||
// Включаем дисплей только если кондиционер в одном из рабочих режимов
|
||||
|
||||
// ВНИМАНИЕ! При выключении дисплея кондиционер сам принудительно переходит в автоматический режим!
|
||||
|
||||
if ((display_status_) && (switchvar != climate::CLIMATE_MODE_OFF)){
|
||||
ESP_LOGD("TCL", "Dispaly turn ON");
|
||||
dataTX[7] += 0b01000000;
|
||||
} else {
|
||||
ESP_LOGD("TCL", "Dispaly turn OFF");
|
||||
dataTX[7] += 0b00000000;
|
||||
}
|
||||
|
||||
// Настраиваем режим работы кондиционера
|
||||
switch (switchvar) {
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
dataTX[7] += 0b00000000;
|
||||
dataTX[8] += 0b00000000;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
dataTX[7] += 0b00000100;
|
||||
dataTX[8] += 0b00001000;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
dataTX[7] += 0b00000100;
|
||||
dataTX[8] += 0b00000011;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
dataTX[7] += 0b00000100;
|
||||
dataTX[8] += 0b00000010;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
dataTX[7] += 0b00000100;
|
||||
dataTX[8] += 0b00000111;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
dataTX[7] += 0b00000100;
|
||||
dataTX[8] += 0b00000001;
|
||||
break;
|
||||
}
|
||||
|
||||
// Настраиваем режим вентилятора
|
||||
if (call.get_fan_mode().has_value()){
|
||||
switchvar = call.get_fan_mode().value();
|
||||
switch(switchvar) {
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000000;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_QUIET:
|
||||
dataTX[8] += 0b10000000;
|
||||
dataTX[10] += 0b00000000;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000001;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MIDDLE:
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000110;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000011;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000111;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_FOCUS:
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000101;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_DIFFUSE:
|
||||
dataTX[8] += 0b01000000;
|
||||
dataTX[10] += 0b00000000;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(fan_mode == climate::CLIMATE_FAN_AUTO){
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000000;
|
||||
} else if(fan_mode == climate::CLIMATE_FAN_QUIET){
|
||||
dataTX[8] += 0b10000000;
|
||||
dataTX[10] += 0b00000000;
|
||||
} else if(fan_mode == climate::CLIMATE_FAN_LOW){
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000001;
|
||||
} else if(fan_mode == climate::CLIMATE_FAN_MIDDLE){
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000110;
|
||||
} else if(fan_mode == climate::CLIMATE_FAN_MEDIUM){
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000011;
|
||||
} else if(fan_mode == climate::CLIMATE_FAN_HIGH){
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000111;
|
||||
} else if(fan_mode == climate::CLIMATE_FAN_FOCUS){
|
||||
dataTX[8] += 0b00000000;
|
||||
dataTX[10] += 0b00000101;
|
||||
} else if(fan_mode == climate::CLIMATE_FAN_DIFFUSE){
|
||||
dataTX[8] += 0b01000000;
|
||||
dataTX[10] += 0b00000000;
|
||||
}
|
||||
}
|
||||
|
||||
//Режим заслонок
|
||||
// Вертикальная заслонка
|
||||
// Качание вертикальной заслонки [10 байт, маска 00111000]:
|
||||
// 000 - Качание отключено, заслонка в последней позиции или в фиксации
|
||||
// 111 - Качание включено в выбранном режиме
|
||||
// Режим качания вертикальной заслонки (режим фиксации заслонки роли не играет, если качание включено) [32 байт, маска 00011000]:
|
||||
// 01 - качание сверху вниз, ПО УМОЛЧАНИЮ
|
||||
// 10 - качание в верхней половине
|
||||
// 11 - качание в нижней половине
|
||||
// Режим фиксации заслонки (режим качания заслонки роли не играет, если качание выключено) [32 байт, маска 00000111]:
|
||||
// 000 - нет фиксации, ПО УМОЛЧАНИЮ
|
||||
// 001 - фиксация вверху
|
||||
// 010 - фиксация между верхом и серединой
|
||||
// 011 - фиксация в середине
|
||||
// 100 - фиксация между серединой и низом
|
||||
// 101 - фиксация внизу
|
||||
// Горизонтальные заслонки
|
||||
// Качание горизонтальных заслонок [11 байт, маска 00001000]:
|
||||
// 0 - Качание отключено, заслонки в последней позиции или в фиксации
|
||||
// 1 - Качание включено в выбранном режиме
|
||||
// Режим качания горизонтальных заслонок (режим фиксации заслонок роли не играет, если качание включено) [33 байт, маска 00111000]:
|
||||
// 001 - качание слева направо, ПО УМОЛЧАНИЮ
|
||||
// 010 - качание слева
|
||||
// 011 - качание по середине
|
||||
// 100 - качание справа
|
||||
// Режим фиксации горизонтальных заслонок (режим качания заслонок роли не играет, если качание выключено) [33 байт, маска 00000111]:
|
||||
// 000 - нет фиксации, ПО УМОЛЧАНИЮ
|
||||
// 001 - фиксация слева
|
||||
// 010 - фиксация между левой стороной и серединой
|
||||
// 011 - фиксация в середине
|
||||
// 100 - фиксация между серединой и правой стороной
|
||||
// 101 - фиксация справа
|
||||
|
||||
|
||||
//Запрашиваем данные из переключателя режимов качания заслонок
|
||||
if (call.get_swing_mode().has_value()){
|
||||
switchvar = call.get_swing_mode().value();
|
||||
} else {
|
||||
// А если в переключателе пусто- заполняем значением из последнего опроса состояния. Типа, ничего не поменялось.
|
||||
switchvar = swing_mode;
|
||||
}
|
||||
|
||||
switch(switchvar) {
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
dataTX[10] += 0b00000000;
|
||||
dataTX[11] += 0b00000000;
|
||||
break;
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
dataTX[10] += 0b00111000;
|
||||
dataTX[11] += 0b00000000;
|
||||
break;
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
dataTX[10] += 0b00000000;
|
||||
dataTX[11] += 0b00001000;
|
||||
break;
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
dataTX[10] += 0b00111000;
|
||||
dataTX[11] += 0b00001000;
|
||||
break;
|
||||
}
|
||||
//Выбираем режим для качания вертикальной заслонки
|
||||
switch(vertical_swing_direction_) {
|
||||
case VerticalSwingDirection::UP_DOWN:
|
||||
dataTX[32] += 0b00001000;
|
||||
ESP_LOGD("TCL", "Vertical swing: up-down");
|
||||
break;
|
||||
case VerticalSwingDirection::UPSIDE:
|
||||
dataTX[32] += 0b00010000;
|
||||
ESP_LOGD("TCL", "Vertical swing: upper");
|
||||
break;
|
||||
case VerticalSwingDirection::DOWNSIDE:
|
||||
dataTX[32] += 0b00011000;
|
||||
ESP_LOGD("TCL", "Vertical swing: downer");
|
||||
break;
|
||||
}
|
||||
//Выбираем режим для качания горизонтальных заслонок
|
||||
switch(horizontal_swing_direction_) {
|
||||
case HorizontalSwingDirection::LEFT_RIGHT:
|
||||
dataTX[33] += 0b00001000;
|
||||
ESP_LOGD("TCL", "Horizontal swing: left-right");
|
||||
break;
|
||||
case HorizontalSwingDirection::LEFTSIDE:
|
||||
dataTX[33] += 0b00010000;
|
||||
ESP_LOGD("TCL", "Horizontal swing: lefter");
|
||||
break;
|
||||
case HorizontalSwingDirection::CENTER:
|
||||
dataTX[33] += 0b00011000;
|
||||
ESP_LOGD("TCL", "Horizontal swing: center");
|
||||
break;
|
||||
case HorizontalSwingDirection::RIGHTSIDE:
|
||||
dataTX[33] += 0b00100000;
|
||||
ESP_LOGD("TCL", "Horizontal swing: righter");
|
||||
break;
|
||||
}
|
||||
//Выбираем положение фиксации вертикальной заслонки
|
||||
switch(this->vertical_direction_) {
|
||||
case AirflowVerticalDirection::LAST:
|
||||
dataTX[32] += 0b00000000;
|
||||
ESP_LOGD("TCL", "Vertical fix: last position");
|
||||
break;
|
||||
case AirflowVerticalDirection::MAX_UP:
|
||||
dataTX[32] += 0b00000001;
|
||||
ESP_LOGD("TCL", "Vertical fix: up");
|
||||
break;
|
||||
case AirflowVerticalDirection::UP:
|
||||
dataTX[32] += 0b00000010;
|
||||
ESP_LOGD("TCL", "Vertical fix: upper");
|
||||
break;
|
||||
case AirflowVerticalDirection::CENTER:
|
||||
dataTX[32] += 0b00000011;
|
||||
ESP_LOGD("TCL", "Vertical fix: center");
|
||||
break;
|
||||
case AirflowVerticalDirection::DOWN:
|
||||
dataTX[32] += 0b00000100;
|
||||
ESP_LOGD("TCL", "Vertical fix: downer");
|
||||
break;
|
||||
case AirflowVerticalDirection::MAX_DOWN:
|
||||
dataTX[32] += 0b00000101;
|
||||
ESP_LOGD("TCL", "Vertical fix: down");
|
||||
break;
|
||||
}
|
||||
//Выбираем положение фиксации горизонтальных заслонок
|
||||
switch(this->horizontal_direction_) {
|
||||
case AirflowHorizontalDirection::LAST:
|
||||
dataTX[33] += 0b00000000;
|
||||
ESP_LOGD("TCL", "Horizontal fix: last position");
|
||||
break;
|
||||
case AirflowHorizontalDirection::MAX_LEFT:
|
||||
dataTX[33] += 0b00000001;
|
||||
ESP_LOGD("TCL", "Horizontal fix: left");
|
||||
break;
|
||||
case AirflowHorizontalDirection::LEFT:
|
||||
dataTX[33] += 0b00000010;
|
||||
ESP_LOGD("TCL", "Horizontal fix: lefter");
|
||||
break;
|
||||
case AirflowHorizontalDirection::CENTER:
|
||||
dataTX[33] += 0b00000011;
|
||||
ESP_LOGD("TCL", "Horizontal fix: center");
|
||||
break;
|
||||
case AirflowHorizontalDirection::RIGHT:
|
||||
dataTX[33] += 0b00000100;
|
||||
ESP_LOGD("TCL", "Horizontal fix: righter");
|
||||
break;
|
||||
case AirflowHorizontalDirection::MAX_RIGHT:
|
||||
dataTX[33] += 0b00000101;
|
||||
ESP_LOGD("TCL", "Horizontal fix: right");
|
||||
break;
|
||||
}
|
||||
|
||||
// Расчет и установка температуры
|
||||
if (call.get_target_temperature().has_value()) {
|
||||
dataTX[9] = 31-(int)call.get_target_temperature().value(); //0,0,0,0, temp(4)
|
||||
} else {
|
||||
dataTX[9] = 31-(int)target_temperature;
|
||||
}
|
||||
|
||||
//Собираем массив байт для отправки в кондиционер
|
||||
dataTX[0] = 0xBB; //стартовый байт заголовка
|
||||
dataTX[1] = 0x00; //стартовый байт заголовка
|
||||
dataTX[2] = 0x01; //стартовый байт заголовка
|
||||
dataTX[3] = 0x03; //0x03 - управление, 0x04 - опрос
|
||||
dataTX[4] = 0x20; //0x20 - управление, 0x19 - опрос
|
||||
dataTX[5] = 0x03; //??
|
||||
dataTX[6] = 0x01; //??
|
||||
//dataTX[7] = 0x64; //eco,display,beep,ontimerenable, offtimerenable,power,0,0
|
||||
//dataTX[8] = 0x08; //mute,0,turbo,health, mode(4) mode 01 heat, 02 dry, 03 cool, 07 fan, 08 auto, health(+16), 41=turbo-heat 43=turbo-cool (turbo = 0x40+ 0x01..0x08)
|
||||
//dataTX[9] = 0x0f; //0 -31 ; 15 - 16 0,0,0,0, temp(4) settemp 31 - x
|
||||
//dataTX[10] = 0x00; //0,timerindicator,swingv(3),fan(3) fan+swing modes //0=auto 1=low 2=med 3=high
|
||||
//dataTX[11] = 0x00; //0,offtimer(6),0
|
||||
dataTX[12] = 0x00; //fahrenheit,ontimer(6),0 cf 80=f 0=c
|
||||
dataTX[13] = 0x01; //??
|
||||
dataTX[14] = 0x00; //0,0,halfdegree,0,0,0,0,0
|
||||
dataTX[15] = 0x00; //??
|
||||
dataTX[16] = 0x00; //??
|
||||
dataTX[17] = 0x00; //??
|
||||
dataTX[18] = 0x00; //??
|
||||
dataTX[19] = 0x00; //sleep on = 1 off=0
|
||||
dataTX[20] = 0x00; //??
|
||||
dataTX[21] = 0x00; //??
|
||||
dataTX[22] = 0x00; //??
|
||||
dataTX[23] = 0x00; //??
|
||||
dataTX[24] = 0x00; //??
|
||||
dataTX[25] = 0x00; //??
|
||||
dataTX[26] = 0x00; //??
|
||||
dataTX[27] = 0x00; //??
|
||||
dataTX[28] = 0x00; //??
|
||||
dataTX[30] = 0x00; //??
|
||||
dataTX[31] = 0x00; //??
|
||||
//dataTX[32] = 0x00; //0,0,0,режим вертикального качания(2),режим вертикальной фиксации(3)
|
||||
//dataTX[33] = 0x00; //0,0,режим горизонтального качания(3),режим горизонтальной фиксации(3)
|
||||
dataTX[34] = 0x00; //??
|
||||
dataTX[35] = 0x00; //??
|
||||
dataTX[36] = 0x00; //??
|
||||
dataTX[37] = 0xFF; //Контрольная сумма
|
||||
dataTX[37] = tclacClimate::getChecksum(dataTX, sizeof(dataTX));
|
||||
|
||||
tclacClimate::sendData(dataTX, sizeof(dataTX));
|
||||
}
|
||||
|
||||
void tclacClimate::sendData(byte * message, byte size) {
|
||||
tclacClimate::dataShow(1,1);
|
||||
//Serial.write(message, size);
|
||||
this->esphome::uart::UARTDevice::write_array(message, size);
|
||||
auto raw = getHex(message, size);
|
||||
ESP_LOGD("TCL", "Message to TCL sended...");
|
||||
tclacClimate::dataShow(1,0);
|
||||
}
|
||||
|
||||
String tclacClimate::getHex(byte *message, byte size) {
|
||||
String raw;
|
||||
for (int i = 0; i < size; i++) {
|
||||
raw += "\n" + String(message[i]);
|
||||
}
|
||||
raw.toUpperCase();
|
||||
return raw;
|
||||
}
|
||||
|
||||
byte tclacClimate::getChecksum(const byte * message, size_t size) {
|
||||
byte position = size - 1;
|
||||
byte crc = 0;
|
||||
for (int i = 0; i < position; i++)
|
||||
crc ^= message[i];
|
||||
return crc;
|
||||
}
|
||||
|
||||
void tclacClimate::dataShow(bool flow, bool shine) {
|
||||
if (module_display_status_){
|
||||
if (flow == 0){
|
||||
if (shine == 1){
|
||||
#ifdef CONF_RX_LED
|
||||
this->rx_led_pin_->digital_write(true);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef CONF_RX_LED
|
||||
this->rx_led_pin_->digital_write(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (flow == 1) {
|
||||
if (shine == 1){
|
||||
#ifdef CONF_TX_LED
|
||||
this->tx_led_pin_->digital_write(true);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef CONF_TX_LED
|
||||
this->tx_led_pin_->digital_write(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Действия с данными из конфига
|
||||
|
||||
//void tclacClimate::set_supported_modes(const std::set<climate::ClimateMode> &modes) {
|
||||
// this->traits.set_supported_modes(modes);
|
||||
// this->traits.add_supported_mode(climate::CLIMATE_MODE_OFF); // Always available
|
||||
// this->traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); // Always available
|
||||
//}
|
||||
|
||||
void tclacClimate::set_beeper_state(bool state) {
|
||||
this->beeper_status_ = state;
|
||||
}
|
||||
|
||||
void tclacClimate::set_display_state(bool state) {
|
||||
this->display_status_ = state;
|
||||
}
|
||||
|
||||
#ifdef CONF_RX_LED
|
||||
void tclacClimate::set_rx_led_pin(GPIOPin *rx_led_pin) {
|
||||
this->rx_led_pin_ = rx_led_pin;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONF_TX_LED
|
||||
void tclacClimate::set_tx_led_pin(GPIOPin *tx_led_pin) {
|
||||
this->tx_led_pin_ = tx_led_pin;
|
||||
}
|
||||
#endif
|
||||
|
||||
void tclacClimate::set_module_display_state(bool state) {
|
||||
this->module_display_status_ = state;
|
||||
}
|
||||
|
||||
void tclacClimate::set_vertical_airflow(AirflowVerticalDirection direction) {
|
||||
this->vertical_direction_ = direction;
|
||||
}
|
||||
|
||||
void tclacClimate::set_horizontal_airflow(AirflowHorizontalDirection direction) {
|
||||
this->horizontal_direction_ = direction;
|
||||
}
|
||||
|
||||
void tclacClimate::set_vertical_swing_direction(VerticalSwingDirection direction) {
|
||||
this->vertical_swing_direction_ = direction;
|
||||
}
|
||||
|
||||
void tclacClimate::set_horizontal_swing_direction(HorizontalSwingDirection direction) {
|
||||
this->horizontal_swing_direction_ = direction;
|
||||
}
|
||||
|
||||
void tclacClimate::set_supported_swing_modes(const std::set<climate::ClimateSwingMode> &modes) {
|
||||
this->supported_swing_modes_ = modes;
|
||||
}
|
||||
|
||||
|
||||
// Заготовки функций запроса состояния, может пригодиться в будущем, если делать обратную связь. Очень не хочется, будет очень костыльно.
|
||||
|
||||
//bool tclacClimate::get_beeper_state() const { return this->beeper_status_; }
|
||||
//bool tclacClimate::get_display_state() const { return this->display_status_; }
|
||||
//bool tclacClimate::get_module_display_state() const { return this->module_display_status_; }
|
||||
//AirflowVerticalDirection tclacClimate::get_vertical_airflow() const { return this->vertical_direction_; };
|
||||
//AirflowHorizontalDirection tclacClimate::get_horizontal_airflow() const { return this->horizontal_direction_; }
|
||||
//VerticalSwingDirection tclacClimate::get_vertical_swing_direction() const { return this->vertical_swing_direction_; }
|
||||
//HorizontalSwingDirection tclacClimate::get_horizontal_swing_direction() const { return this->horizontal_swing_direction_; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
160
components/tclac/tclac.h
Normal file
160
components/tclac/tclac.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Create by Miguel Ángel López on 20/07/19
|
||||
* and modify by xaxexa
|
||||
* and finally made it by Solovey 13.08.2023
|
||||
**/
|
||||
|
||||
#ifndef TCL_ESP_TCL_H
|
||||
#define TCL_ESP_TCL_H
|
||||
|
||||
#include "esphome.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace tclac {
|
||||
|
||||
#define SET_TEMP_MASK 0b00001111
|
||||
|
||||
#define MODE_POS 7
|
||||
#define MODE_MASK 0b00111111
|
||||
|
||||
#define MODE_AUTO 0b00110101
|
||||
#define MODE_COOL 0b00110001
|
||||
#define MODE_DRY 0b00110011
|
||||
#define MODE_FAN_ONLY 0b00110010
|
||||
#define MODE_HEAT 0b00110100
|
||||
|
||||
#define FAN_SPEED_POS 8
|
||||
#define FAN_QUIET_POS 33
|
||||
|
||||
|
||||
#define FAN_AUTO 0b10000000 //auto
|
||||
#define FAN_QUIET 0x80 //silent
|
||||
#define FAN_LOW 0b10010000 // |
|
||||
#define FAN_MIDDLE 0b11000000 // ||
|
||||
#define FAN_MEDIUM 0b10100000 // |||
|
||||
#define FAN_HIGH 0b11010000 // ||||
|
||||
#define FAN_FOCUS 0b10110000 // |||||
|
||||
#define FAN_DIFFUSE 0b10000000 // POWER [7]
|
||||
#define FAN_SPEED_MASK 0b11110000 //FAN SPEED MASK
|
||||
|
||||
|
||||
#define SWING_POS 10
|
||||
#define SWING_OFF 0b00000000
|
||||
#define SWING_HORIZONTAL 0b00100000
|
||||
#define SWING_VERTICAL 0b01000000
|
||||
#define SWING_BOTH 0b01100000
|
||||
#define SWING_MODE_MASK 0b01100000
|
||||
|
||||
|
||||
#define MIN_SET_TEMPERATURE 16
|
||||
#define MAX_SET_TEMPERATURE 31
|
||||
#define STEP_TEMPERATURE 1
|
||||
|
||||
using climate::ClimateCall;
|
||||
using climate::ClimatePreset;
|
||||
using climate::ClimateTraits;
|
||||
using climate::ClimateMode;
|
||||
using climate::ClimateSwingMode;
|
||||
using climate::ClimateFanMode;
|
||||
|
||||
enum class AirflowVerticalDirection : uint8_t {
|
||||
LAST = 0,
|
||||
MAX_UP = 1,
|
||||
UP = 2,
|
||||
CENTER = 3,
|
||||
DOWN = 4,
|
||||
MAX_DOWN = 5,
|
||||
};
|
||||
enum class AirflowHorizontalDirection : uint8_t {
|
||||
LAST = 0,
|
||||
MAX_LEFT = 1,
|
||||
LEFT = 2,
|
||||
CENTER = 3,
|
||||
RIGHT = 4,
|
||||
MAX_RIGHT = 5,
|
||||
};
|
||||
enum class VerticalSwingDirection : uint8_t {
|
||||
UP_DOWN = 0,
|
||||
UPSIDE = 1,
|
||||
DOWNSIDE = 2,
|
||||
};
|
||||
enum class HorizontalSwingDirection : uint8_t {
|
||||
LEFT_RIGHT = 0,
|
||||
LEFTSIDE = 1,
|
||||
CENTER = 2,
|
||||
RIGHTSIDE = 3,
|
||||
};
|
||||
|
||||
class tclacClimate : public climate::Climate, public esphome::uart::UARTDevice, public PollingComponent {
|
||||
|
||||
private:
|
||||
byte checksum;
|
||||
// dataTX с управлением состоит из 38 байт
|
||||
byte dataTX[38];
|
||||
// А dataRX по прежнему из 61 байта
|
||||
byte dataRX[61];
|
||||
// Команда запроса состояния
|
||||
byte poll[8] = {0xBB,0x00,0x01,0x04,0x02,0x01,0x00,0xBD};
|
||||
// Инициализация и начальное наполнение переменных состоянй переключателей
|
||||
bool beeper_status_;
|
||||
bool display_status_;
|
||||
bool module_display_status_;
|
||||
|
||||
public:
|
||||
|
||||
tclacClimate() : PollingComponent(5 * 1000) {
|
||||
checksum = 0;
|
||||
}
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void readData();
|
||||
void control(const ClimateCall &call) override; // Climate control
|
||||
void sendData(byte * message, byte size);
|
||||
static String getHex(byte *message, byte size);
|
||||
static byte getChecksum(const byte * message, size_t size);
|
||||
void dataShow(bool flow, bool shine);
|
||||
|
||||
// Заготовки функций запроса состояния, может пригодиться в будущем, если делать обратную связь. Очень не хочется, будет очень костыльно.
|
||||
|
||||
//bool get_beeper_state() const;
|
||||
//bool get_display_state() const;
|
||||
//bool tclacClimate::get_module_display_state() const;
|
||||
//AirflowVerticalDirection get_vertical_airflow() const;
|
||||
//AirflowHorizontalDirection get_horizontal_airflow() const;
|
||||
//VerticalSwingDirection tclacClimate::get_vertical_swing_direction() const;
|
||||
//HorizontalSwingDirection tclacClimate::get_horizontal_swing_direction() const;
|
||||
|
||||
void set_beeper_state(bool state);
|
||||
void set_display_state(bool state);
|
||||
void set_rx_led_pin(GPIOPin *rx_led_pin);
|
||||
void set_tx_led_pin(GPIOPin *tx_led_pin);
|
||||
void set_module_display_state(bool state);
|
||||
void set_vertical_airflow(AirflowVerticalDirection direction);
|
||||
void set_horizontal_airflow(AirflowHorizontalDirection direction);
|
||||
void set_vertical_swing_direction(VerticalSwingDirection direction);
|
||||
void set_horizontal_swing_direction(HorizontalSwingDirection direction);
|
||||
void set_supported_modes(const std::set<esphome::climate::ClimateMode> &modes);
|
||||
void set_supported_swing_modes(const std::set<esphome::climate::ClimateSwingMode> &modes);
|
||||
|
||||
|
||||
protected:
|
||||
GPIOPin *rx_led_pin_;
|
||||
GPIOPin *tx_led_pin_;
|
||||
ClimateTraits traits() override;
|
||||
AirflowVerticalDirection vertical_direction_;
|
||||
AirflowHorizontalDirection horizontal_direction_;
|
||||
VerticalSwingDirection vertical_swing_direction_;
|
||||
std::set<ClimateSwingMode> supported_swing_modes_{};
|
||||
HorizontalSwingDirection horizontal_swing_direction_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //TCL_ESP_TCL_H
|
||||
286
packages/core.yaml
Normal file
286
packages/core.yaml
Normal file
@@ -0,0 +1,286 @@
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # НЕ РЕДАКТИРОВАТЬ!! # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
||||
# Компонент климата
|
||||
external_components:
|
||||
- source: https://github.com/I-am-nightingale/tclac.git
|
||||
type: git
|
||||
ref: main
|
||||
components: [ tclac ]
|
||||
|
||||
# Конфигурация ESPHome
|
||||
esphome:
|
||||
name: ${device_name}
|
||||
friendly_name: ${humanly_name}
|
||||
on_boot:
|
||||
priority: -100
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
if (id(beep_mode).state){
|
||||
id(${device_name}climate).set_beeper_state(true);
|
||||
} else {
|
||||
id(${device_name}climate).set_beeper_state(false);
|
||||
}
|
||||
if (id(display_mode).state){
|
||||
id(${device_name}climate).set_display_state(true);
|
||||
} else {
|
||||
id(${device_name}climate).set_display_state(false);
|
||||
}
|
||||
if (id(ledflash_mode).state){
|
||||
id(${device_name}climate).set_module_display_state(true);
|
||||
} else {
|
||||
id(${device_name}climate).set_module_display_state(false);
|
||||
}
|
||||
|
||||
if (id(vswing).active_index() == 1){
|
||||
id(${device_name}climate).set_vertical_swing_direction(esphome::tclac::VerticalSwingDirection::UP_DOWN);
|
||||
} else if (id(vswing).active_index() == 2){
|
||||
id(${device_name}climate).set_vertical_swing_direction(esphome::tclac::VerticalSwingDirection::UPSIDE);
|
||||
} else if (id(vswing).active_index() == 3){
|
||||
id(${device_name}climate).set_vertical_swing_direction(esphome::tclac::VerticalSwingDirection::DOWNSIDE);
|
||||
}
|
||||
|
||||
if (id(hswing).active_index() == 1){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::LEFT_RIGHT);
|
||||
} else if (id(hswing).active_index() == 2){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::LEFTSIDE);
|
||||
} else if (id(hswing).active_index() == 3){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::CENTER);
|
||||
} else if (id(hswing).active_index() == 4){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::RIGHTSIDE);
|
||||
}
|
||||
|
||||
if (id(vfixing).active_index() == 1){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::LAST);
|
||||
} else if (id(vfixing).active_index() == 2){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::MAX_UP);
|
||||
} else if (id(vfixing).active_index() == 3){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::UP);
|
||||
} else if (id(vfixing).active_index() == 4){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::CENTER);
|
||||
} else if (id(vfixing).active_index() == 5){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::DOWN);
|
||||
} else if (id(vfixing).active_index() == 6){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::MAX_DOWN);
|
||||
}
|
||||
|
||||
if (id(vswing).active_index() == 1){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::LAST);
|
||||
} else if (id(vswing).active_index() == 2){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::MAX_LEFT);
|
||||
} else if (id(vswing).active_index() == 3){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::LEFT);
|
||||
} else if (id(vswing).active_index() == 4){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::CENTER);
|
||||
} else if (id(vswing).active_index() == 5){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::RIGHT);
|
||||
} else if (id(vswing).active_index() == 6){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::MAX_RIGHT);
|
||||
}
|
||||
|
||||
uart:
|
||||
baud_rate: 9600
|
||||
rx_pin: ${uart_rx}
|
||||
tx_pin: ${uart_tx}
|
||||
|
||||
api:
|
||||
encryption:
|
||||
key: "${api_key}"
|
||||
|
||||
ota:
|
||||
password: "${ota_pass}"
|
||||
|
||||
wifi:
|
||||
id: "${device_name}_wifi"
|
||||
ssid: ${wifi_ssid}
|
||||
password: ${wifi_password}
|
||||
ap:
|
||||
ssid: "${device_name} Fallback Hotspot"
|
||||
password: "${recovery_pass}"
|
||||
# Если требуется ручное указание настроек сети- это здесь:
|
||||
manual_ip:
|
||||
static_ip: 192.168.1.204
|
||||
gateway: 192.168.1.1
|
||||
subnet: 255.255.255.0
|
||||
|
||||
captive_portal:
|
||||
|
||||
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # НЕ РЕДАКТИРОВАТЬ! # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
|
||||
# ОБЯЗАТЕЛЬНО отключаем логгирование через UART
|
||||
logger:
|
||||
baud_rate: 0
|
||||
|
||||
# Раздел настроек устройства:
|
||||
# - Переключатели
|
||||
switch:
|
||||
# Пищалка для подтверждения команд, по умолчанию выключена
|
||||
- name: Beeper
|
||||
platform: template
|
||||
device_class: switch
|
||||
id: beep_mode
|
||||
entity_category: config
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
optimistic: true
|
||||
on_turn_on:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
id(${device_name}climate).set_beeper_state(true);
|
||||
on_turn_off:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
id(${device_name}climate).set_beeper_state(false);
|
||||
|
||||
# Индикатор уставки температуры на корпусе внутреннего блока, по умолчанию включен
|
||||
- name: Display
|
||||
platform: template
|
||||
device_class: switch
|
||||
id: display_mode
|
||||
entity_category: config
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
optimistic: true
|
||||
on_turn_on:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
id(${device_name}climate).set_display_state(true);
|
||||
on_turn_off:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
id(${device_name}climate).set_display_state(false);
|
||||
|
||||
# Светодиодная индикация обмена данными с кондиционером, по умолчанию выключена
|
||||
- name: Module display
|
||||
platform: template
|
||||
device_class: switch
|
||||
id: ledflash_mode
|
||||
entity_category: config
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
optimistic: true
|
||||
on_turn_on:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
id(${device_name}climate).set_module_display_state(true);
|
||||
on_turn_off:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
id(${device_name}climate).set_module_display_state(false);
|
||||
|
||||
# - Выпадающие списки
|
||||
select:
|
||||
# Настройка вертикального качания
|
||||
- platform: template
|
||||
name: Vertical swing
|
||||
id: vswing
|
||||
entity_category: config
|
||||
options:
|
||||
- "Сверху вниз"
|
||||
- "В верхней половине"
|
||||
- "В нижней половине"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
set_action:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
if (id(vswing).active_index() == 1){
|
||||
id(${device_name}climate).set_vertical_swing_direction(esphome::tclac::VerticalSwingDirection::UP_DOWN);
|
||||
} else if (id(vswing).active_index() == 2){
|
||||
id(${device_name}climate).set_vertical_swing_direction(esphome::tclac::VerticalSwingDirection::UPSIDE);
|
||||
} else if (id(vswing).active_index() == 3){
|
||||
id(${device_name}climate).set_vertical_swing_direction(esphome::tclac::VerticalSwingDirection::DOWNSIDE);
|
||||
}
|
||||
|
||||
# Настройка горизонтального качания
|
||||
- platform: template
|
||||
name: Horizontal swing
|
||||
id: hswing
|
||||
entity_category: config
|
||||
options:
|
||||
- "Слева направо"
|
||||
- "В левой части"
|
||||
- "В центре"
|
||||
- "В правой части"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
set_action:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
if (x == "Слева направо"){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::LEFT_RIGHT);
|
||||
} else if (x == "В левой части"){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::LEFTSIDE);
|
||||
} else if (x == "В центре"){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::CENTER);
|
||||
} else if (x == "В правой части"){
|
||||
id(${device_name}climate).set_horizontal_swing_direction(esphome::tclac::HorizontalSwingDirection::RIGHTSIDE);
|
||||
}
|
||||
|
||||
# Настройка фиксации вертикальной заслонки
|
||||
- platform: template
|
||||
name: Vertical fixing
|
||||
id: vfixing
|
||||
entity_category: config
|
||||
options:
|
||||
- Last
|
||||
- Max_Up
|
||||
- Up
|
||||
- Center
|
||||
- Down
|
||||
- Max_Down
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
set_action:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
if (x == "Last"){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::LAST);
|
||||
} else if (x == "Max_Up"){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::MAX_UP);
|
||||
} else if (x == "Up"){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::UP);
|
||||
} else if (x == "Center"){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::CENTER);
|
||||
} else if (x == "Down"){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::DOWN);
|
||||
} else if (x == "Max_Down"){
|
||||
id(${device_name}climate).set_vertical_airflow(esphome::tclac::AirflowVerticalDirection::MAX_DOWN);
|
||||
}
|
||||
|
||||
# Настройка фиксации горизонатальных заслонок
|
||||
- platform: template
|
||||
name: Horizontal fixing
|
||||
id: hfixing
|
||||
entity_category: config
|
||||
options:
|
||||
- "Последнее положение"
|
||||
- "До упора влево"
|
||||
- "В левой половине"
|
||||
- "По середине"
|
||||
- "В правой половине"
|
||||
- "До упора вправо"
|
||||
optimistic: true
|
||||
restore_value: true
|
||||
set_action:
|
||||
then:
|
||||
lambda: !lambda |-
|
||||
if (x == "Последнее положение"){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::LAST);
|
||||
} else if (x == "До упора влево"){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::MAX_LEFT);
|
||||
} else if (x == "В левой половине"){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::LEFT);
|
||||
} else if (x == "По середине"){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::CENTER);
|
||||
} else if (x == "В правой половине"){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::RIGHT);
|
||||
} else if (x == "До упора вправо"){
|
||||
id(${device_name}climate).set_horizontal_airflow(esphome::tclac::AirflowHorizontalDirection::MAX_RIGHT);
|
||||
}
|
||||
|
||||
climate:
|
||||
- platform: tclac
|
||||
name: "${device_name} Climate"
|
||||
id: ${device_name}climate
|
||||
5
packages/leds.yaml
Normal file
5
packages/leds.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
climate:
|
||||
- platform: tclac
|
||||
id: !extend ${device_name}climate
|
||||
rx_led: ${receive_led}
|
||||
tx_led: ${transmit_led}
|
||||
78
packages/screen.yaml
Normal file
78
packages/screen.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
time:
|
||||
- platform: homeassistant
|
||||
id: my_time
|
||||
|
||||
sensor:
|
||||
- platform: wifi_signal
|
||||
id: wifi_strenght
|
||||
internal: true
|
||||
update_interval: 1s
|
||||
filters:
|
||||
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
|
||||
|
||||
font:
|
||||
- file: "gfonts://Press Start 2P"
|
||||
id: font1
|
||||
size: 16
|
||||
bpp: 1
|
||||
glyphs: '!"%()+=,-_.:°/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzАБВГДЕЖЗИКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯЫабвгдежзиклмнопрстуфхцчшщъыьэюя'
|
||||
|
||||
- file: "gfonts://Press Start 2P"
|
||||
id: font2
|
||||
size: 8
|
||||
bpp: 1
|
||||
glyphs: '!"%()+=,-_.:°/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzАБВГДЕЖЗИКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯЫабвгдежзиклмнопрстуфхцчшщъыьэюя'
|
||||
|
||||
i2c:
|
||||
sda: GPIO0
|
||||
scl: GPIO2
|
||||
frequency: 400kHz
|
||||
|
||||
display:
|
||||
- platform: ssd1306_i2c
|
||||
model: "SSD1306 128x32"
|
||||
address: 0x3C
|
||||
rotation: 180
|
||||
id: oled_disp
|
||||
lambda: |-
|
||||
if (id(${device_name}_wifi).is_connected()){
|
||||
auto time = id(my_time).now();
|
||||
id(oled_disp).strftime(0, 0, id(font2), "%H:%M", time);
|
||||
id(oled_disp).line(0, 11, 127, 11);
|
||||
|
||||
if (id(${device_name}climate).mode == CLIMATE_MODE_OFF){
|
||||
id(oled_disp).print(0, 14, id(font1), "Выключен");
|
||||
} else if (id(${device_name}climate).mode == CLIMATE_MODE_AUTO){
|
||||
id(oled_disp).print(0, 14, id(font1), "Авторежим");
|
||||
} else if (id(${device_name}climate).mode == CLIMATE_MODE_COOL){
|
||||
id(oled_disp).print(0, 14, id(font1), "Охлаждение");
|
||||
} else if (id(${device_name}climate).mode == CLIMATE_MODE_HEAT){
|
||||
id(oled_disp).print(0, 14, id(font1), "Нагревание");
|
||||
} else if (id(${device_name}climate).mode == CLIMATE_MODE_DRY){
|
||||
id(oled_disp).print(0, 14, id(font1), "Осушение");
|
||||
} else if (id(${device_name}climate).mode == CLIMATE_MODE_FAN_ONLY){
|
||||
id(oled_disp).print(0, 14, id(font1), "Вентилятор");
|
||||
}
|
||||
|
||||
if (id(beep_mode).state){
|
||||
id(oled_disp).print(60, 0, id(font2), "П");
|
||||
}
|
||||
if (id(display_mode).state){
|
||||
id(oled_disp).print(80, 0, id(font2), "Д");
|
||||
}
|
||||
if (id(wifi_strenght).state > 20){
|
||||
id(oled_disp).filled_rectangle(109, 6, 3, 2);
|
||||
}
|
||||
if (id(wifi_strenght).state > 50){
|
||||
id(oled_disp).filled_rectangle(114, 4, 3, 4);
|
||||
}
|
||||
if (id(wifi_strenght).state > 70){
|
||||
id(oled_disp).filled_rectangle(119, 2, 3, 6);
|
||||
}
|
||||
if (id(wifi_strenght).state > 85){
|
||||
id(oled_disp).filled_rectangle(124, 0, 3, 8);
|
||||
}
|
||||
}
|
||||
else {
|
||||
id(oled_disp).print(0, 0, id(font1), "Ready!");
|
||||
}
|
||||
Reference in New Issue
Block a user