mirror of
https://github.com/GrKoR/esphome_aux_ac_component.git
synced 2025-12-12 14:37:11 +03:00
Add files via upload
This commit is contained in:
65
automation.h
Normal file
65
automation.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "aux_ac.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace aux_ac {
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
class AirConDisplayOffAction : public Action<Ts...>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AirConDisplayOffAction(AirCon *ac) : ac_(ac) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->ac_->displayOffSequence(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AirCon *ac_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
class AirConDisplayOnAction : public Action<Ts...>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AirConDisplayOnAction(AirCon *ac) : ac_(ac) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->ac_->displayOnSequence(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AirCon *ac_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
class AirConSendTestPacketAction : public Action<Ts...>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AirConSendTestPacketAction(AirCon *ac) : ac_(ac) {}
|
||||||
|
void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) {
|
||||||
|
this->data_func_ = func;
|
||||||
|
this->static_ = false;
|
||||||
|
}
|
||||||
|
void set_data_static(const std::vector<uint8_t> &data) {
|
||||||
|
this->data_static_ = data;
|
||||||
|
this->static_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
if (this->static_) {
|
||||||
|
this->ac_->sendTestPacket(this->data_static_);
|
||||||
|
} else {
|
||||||
|
auto val = this->data_func_(x...);
|
||||||
|
this->ac_->sendTestPacket(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AirCon *ac_;
|
||||||
|
bool static_{false};
|
||||||
|
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
|
||||||
|
std::vector<uint8_t> data_static_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aux_ac
|
||||||
|
} // namespace esphome
|
||||||
341
climate.py
Normal file
341
climate.py
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
import logging
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import climate, uart, sensor, binary_sensor
|
||||||
|
from esphome import automation
|
||||||
|
from esphome.automation import maybe_simple_id
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_UART_ID,
|
||||||
|
CONF_PERIOD,
|
||||||
|
CONF_CUSTOM_FAN_MODES,
|
||||||
|
CONF_CUSTOM_PRESETS,
|
||||||
|
CONF_INTERNAL,
|
||||||
|
CONF_DATA,
|
||||||
|
CONF_SUPPORTED_MODES,
|
||||||
|
CONF_SUPPORTED_SWING_MODES,
|
||||||
|
CONF_SUPPORTED_PRESETS,
|
||||||
|
CONF_PRESSURE,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
UNIT_PASCAL,
|
||||||
|
ICON_POWER,
|
||||||
|
ICON_THERMOMETER,
|
||||||
|
ICON_GAS_CYLINDER,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
)
|
||||||
|
from esphome.components.climate import (
|
||||||
|
ClimateMode,
|
||||||
|
ClimatePreset,
|
||||||
|
ClimateSwingMode,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@GrKoR"]
|
||||||
|
DEPENDENCIES = ["climate", "uart"]
|
||||||
|
AUTO_LOAD = ["sensor", "binary_sensor"]
|
||||||
|
|
||||||
|
CONF_SUPPORTED_MODES = 'supported_modes'
|
||||||
|
CONF_SUPPORTED_SWING_MODES = 'supported_swing_modes'
|
||||||
|
CONF_SUPPORTED_PRESETS = 'supported_presets'
|
||||||
|
CONF_SHOW_ACTION = 'show_action'
|
||||||
|
CONF_INDOOR_TEMPERATURE = 'indoor_temperature'
|
||||||
|
CONF_OUTDOOR_TEMPERATURE = 'outdoor_temperature'
|
||||||
|
ICON_OUTDOOR_TEMPERATURE = 'mdi:home-thermometer-outline'
|
||||||
|
CONF_INBOUND_TEMPERATURE = 'inbound_temperature'
|
||||||
|
ICON_INBOUND_TEMPERATURE = 'mdi:thermometer-plus'
|
||||||
|
CONF_OUTBOUND_TEMPERATURE = 'outbound_temperature'
|
||||||
|
ICON_OUTBOUND_TEMPERATURE = 'mdi:thermometer-minus'
|
||||||
|
CONF_STRANGE_TEMPERATURE = 'strange_temperature'
|
||||||
|
ICON_STRANGE_TEMPERATURE = 'mdi:thermometer-lines'
|
||||||
|
CONF_DISPLAY_STATE = 'display_state'
|
||||||
|
CONF_INVERTOR_POWER = 'invertor_power'
|
||||||
|
CONF_DEFROST_STATE = 'defrost_state'
|
||||||
|
ICON_DEFROST = "mdi:snowflake-melt"
|
||||||
|
CONF_DISPLAY_INVERTED = 'display_inverted'
|
||||||
|
ICON_DISPLAY = "mdi:clock-digital"
|
||||||
|
CONF_STORE_SETTINGS = 'store_settings'
|
||||||
|
|
||||||
|
|
||||||
|
aux_ac_ns = cg.esphome_ns.namespace("aux_ac")
|
||||||
|
AirCon = aux_ac_ns.class_("AirCon", climate.Climate, cg.Component)
|
||||||
|
Capabilities = aux_ac_ns.namespace("Constants")
|
||||||
|
|
||||||
|
AirConDisplayOffAction = aux_ac_ns.class_("AirConDisplayOffAction", automation.Action)
|
||||||
|
AirConDisplayOnAction = aux_ac_ns.class_("AirConDisplayOnAction", automation.Action)
|
||||||
|
AirConSendTestPacketAction = aux_ac_ns.class_("AirConSendTestPacketAction", automation.Action)
|
||||||
|
|
||||||
|
ALLOWED_CLIMATE_MODES = {
|
||||||
|
"HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL,
|
||||||
|
"COOL": ClimateMode.CLIMATE_MODE_COOL,
|
||||||
|
"HEAT": ClimateMode.CLIMATE_MODE_HEAT,
|
||||||
|
"DRY": ClimateMode.CLIMATE_MODE_DRY,
|
||||||
|
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
|
||||||
|
}
|
||||||
|
validate_modes = cv.enum(ALLOWED_CLIMATE_MODES, upper=True)
|
||||||
|
|
||||||
|
ALLOWED_CLIMATE_PRESETS = {
|
||||||
|
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
||||||
|
}
|
||||||
|
validate_presets = cv.enum(ALLOWED_CLIMATE_PRESETS, upper=True)
|
||||||
|
|
||||||
|
ALLOWED_CLIMATE_SWING_MODES = {
|
||||||
|
"BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH,
|
||||||
|
"VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,
|
||||||
|
"HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
|
||||||
|
}
|
||||||
|
validate_swing_modes = cv.enum(ALLOWED_CLIMATE_SWING_MODES, upper=True)
|
||||||
|
|
||||||
|
CUSTOM_FAN_MODES = {
|
||||||
|
"MUTE": Capabilities.MUTE,
|
||||||
|
"TURBO": Capabilities.TURBO,
|
||||||
|
}
|
||||||
|
validate_custom_fan_modes = cv.enum(CUSTOM_FAN_MODES, upper=True)
|
||||||
|
|
||||||
|
CUSTOM_PRESETS = {
|
||||||
|
"CLEAN": Capabilities.CLEAN,
|
||||||
|
"HEALTH": Capabilities.HEALTH,
|
||||||
|
"ANTIFUNGUS": Capabilities.ANTIFUNGUS,
|
||||||
|
}
|
||||||
|
validate_custom_presets = cv.enum(CUSTOM_PRESETS, upper=True)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_raw_data(value):
|
||||||
|
if isinstance(value, list):
|
||||||
|
return cv.Schema([cv.hex_uint8_t])(value)
|
||||||
|
raise cv.Invalid(
|
||||||
|
"data must be a list of bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def output_info(config):
|
||||||
|
"""_LOGGER.info(config)"""
|
||||||
|
return config
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
climate.CLIMATE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AirCon),
|
||||||
|
cv.Optional(CONF_PERIOD, default="7s"): cv.time_period,
|
||||||
|
cv.Optional(CONF_SHOW_ACTION, default="true"): cv.boolean,
|
||||||
|
cv.Optional(CONF_DISPLAY_INVERTED, default="false"): cv.boolean,
|
||||||
|
cv.Optional(CONF_STORE_SETTINGS, default="false"): cv.boolean,
|
||||||
|
cv.Optional(CONF_DEFROST_STATE, default="false"): cv.boolean,
|
||||||
|
cv.Optional(CONF_INVERTOR_POWER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
icon=ICON_POWER,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_INDOOR_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_THERMOMETER,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_OUTDOOR_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_OUTDOOR_TEMPERATURE,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_INBOUND_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_INBOUND_TEMPERATURE,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_OUTBOUND_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_OUTBOUND_TEMPERATURE,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_STRANGE_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
icon=ICON_STRANGE_TEMPERATURE,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
cv.Optional(CONF_DISPLAY_STATE): binary_sensor.binary_sensor_schema(
|
||||||
|
icon=ICON_DISPLAY
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
cv.Optional(CONF_DEFROST_STATE): binary_sensor.binary_sensor_schema(
|
||||||
|
icon=ICON_DEFROST
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(validate_modes),
|
||||||
|
cv.Optional(CONF_SUPPORTED_SWING_MODES): cv.ensure_list(validate_swing_modes),
|
||||||
|
cv.Optional(CONF_SUPPORTED_PRESETS): cv.ensure_list(validate_presets),
|
||||||
|
cv.Optional(CONF_CUSTOM_PRESETS): cv.ensure_list(validate_custom_presets),
|
||||||
|
cv.Optional(CONF_CUSTOM_FAN_MODES): cv.ensure_list(validate_custom_fan_modes),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
|
output_info
|
||||||
|
)
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
"""_LOGGER.info("--------------")"""
|
||||||
|
"""_LOGGER.info(config)"""
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await climate.register_climate(var, config)
|
||||||
|
|
||||||
|
parent = await cg.get_variable(config[CONF_UART_ID])
|
||||||
|
cg.add(var.initAC(parent))
|
||||||
|
|
||||||
|
if CONF_INDOOR_TEMPERATURE in config:
|
||||||
|
conf = config[CONF_INDOOR_TEMPERATURE]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_indoor_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_OUTDOOR_TEMPERATURE in config:
|
||||||
|
conf = config[CONF_OUTDOOR_TEMPERATURE]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_outdoor_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_OUTBOUND_TEMPERATURE in config:
|
||||||
|
conf = config[CONF_OUTBOUND_TEMPERATURE]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_outbound_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_INBOUND_TEMPERATURE in config:
|
||||||
|
conf = config[CONF_INBOUND_TEMPERATURE]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_inbound_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_STRANGE_TEMPERATURE in config:
|
||||||
|
conf = config[CONF_STRANGE_TEMPERATURE]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_strange_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_DISPLAY_STATE in config:
|
||||||
|
conf = config[CONF_DISPLAY_STATE]
|
||||||
|
sens = await binary_sensor.new_binary_sensor(conf)
|
||||||
|
cg.add(var.set_display_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_DEFROST_STATE in config:
|
||||||
|
conf = config[CONF_DEFROST_STATE]
|
||||||
|
sens = await binary_sensor.new_binary_sensor(conf)
|
||||||
|
cg.add(var.set_defrost_state(sens))
|
||||||
|
|
||||||
|
if CONF_INVERTOR_POWER in config:
|
||||||
|
conf = config[CONF_INVERTOR_POWER]
|
||||||
|
sens = await sensor.new_sensor(conf)
|
||||||
|
cg.add(var.set_invertor_power_sensor(sens))
|
||||||
|
|
||||||
|
cg.add(var.set_period(config[CONF_PERIOD].total_milliseconds))
|
||||||
|
cg.add(var.set_show_action(config[CONF_SHOW_ACTION]))
|
||||||
|
cg.add(var.set_display_inverted(config[CONF_DISPLAY_INVERTED]))
|
||||||
|
cg.add(var.set_store_settings(config[CONF_STORE_SETTINGS]))
|
||||||
|
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_SUPPORTED_PRESETS in config:
|
||||||
|
cg.add(var.set_supported_presets(config[CONF_SUPPORTED_PRESETS]))
|
||||||
|
if CONF_CUSTOM_PRESETS in config:
|
||||||
|
cg.add(var.set_custom_presets(config[CONF_CUSTOM_PRESETS]))
|
||||||
|
if CONF_CUSTOM_FAN_MODES in config:
|
||||||
|
cg.add(var.set_custom_fan_modes(config[CONF_CUSTOM_FAN_MODES]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DISPLAY_ACTION_SCHEMA = maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(AirCon),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@automation.register_action("aux_ac.display_off", AirConDisplayOffAction, DISPLAY_ACTION_SCHEMA)
|
||||||
|
async def display_off_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
@automation.register_action("aux_ac.display_on", AirConDisplayOnAction, DISPLAY_ACTION_SCHEMA)
|
||||||
|
async def display_on_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
|
||||||
|
|
||||||
|
SEND_TEST_PACKET_ACTION_SCHEMA = maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(AirCon),
|
||||||
|
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# *********************************************************************************************************
|
||||||
|
# ВАЖНО! Только для инженеров!
|
||||||
|
# Вызывайте метод aux_ac.send_packet только если понимаете, что делаете! Он не проверяет данные, а передаёт
|
||||||
|
# кондиционеру всё как есть. Какой эффект получится от передачи кондиционеру рандомных байт, никто не знает.
|
||||||
|
# Вы действуете на свой страх и риск.
|
||||||
|
# *********************************************************************************************************
|
||||||
|
@automation.register_action(
|
||||||
|
"aux_ac.send_packet",
|
||||||
|
AirConSendTestPacketAction,
|
||||||
|
SEND_TEST_PACKET_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def send_packet_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)
|
||||||
|
|
||||||
|
data = config[CONF_DATA]
|
||||||
|
if isinstance(data, bytes):
|
||||||
|
data = list(data)
|
||||||
|
|
||||||
|
if cg.is_template(data):
|
||||||
|
templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
|
||||||
|
cg.add(var.set_data_template(templ))
|
||||||
|
else:
|
||||||
|
cg.add(var.set_data_static(data))
|
||||||
|
|
||||||
|
return var
|
||||||
Reference in New Issue
Block a user