Добавить сенсор "Положение жалюзей"

Fixes #51
This commit is contained in:
GrKoR
2022-06-13 20:50:44 +03:00
parent 2acd934bbe
commit 809fddf13a
6 changed files with 235 additions and 27 deletions

View File

@@ -123,6 +123,10 @@ climate:
name: AC Preset Reporter
id: ac_preset_reporter
internal: false
vlouver_state:
name: AC Vertical Louvers State
id: ac_vlouver_state
internal: false
visual:
min_temperature: 16
max_temperature: 32
@@ -177,6 +181,7 @@ climate:
- **invertor_power** (*Optional*): The information for the invertor power sensor. All settings are the same as for the **display_state** (see description above).
- **preset_reporter** (*Optional*): Parameters of text sensor with current preset. All settings are the same as for the **display_state** (see description above).
ESPHome Climate devices are not report their active presets (from **supported_presets** and **custom_presets** lists) to MQTT. In case you are using mqtt and want to receive information about active preset you should declare this sensor in your yaml.
- **vlouver_state** (*Optional*): Parameters of vertical louvers state sensor. All settings are the same as for the **display_state** (see description above). The state of the vertical louvers is encoded by the integer value (see [aux_ac.vlouver_set action](#aux_ac_._vlouver_set) below).
- **supported_modes** (*Optional*, list): List of supported modes. Possible values are: ``HEAT_COOL``, ``COOL``, ``HEAT``, ``DRY``, ``FAN_ONLY``. Please note: some manufacturers call AUTO mode instead of HEAT_COOL. Defaults to ``FAN_ONLY``.
- **custom_fan_modes** (*Optional*, list): List of supported custom fan modes. Possible values are: ``MUTE``, ``TURBO``. No custom fan modes by default.
- **supported_presets** (*Optional*, list): List of supported presets. Possible values are: ``SLEEP``. No presets by default.
@@ -205,6 +210,28 @@ on_...:
```
- **aux_id** (**Requared**, string): ID of `aux_ac` component.
### ``aux_ac.vlouver_set`` ###
This action moves HVAC vertical louvers to the specified position.
The position is encoded by the following values:
- `0`: the vertical louvers are in `SWING` mode (they are moving up and down);
- `1`: the louvers are stopped in a user position;
- `2`: the louvers are in the topmost position;
- `3`: the louvers are in one step above middle position;
- `4`: the louvers are in the middle position;
- `5`: the louvers are in one step below middle position;
- `6`: the louvers are in the lowest position.
```yaml
on_...:
then:
- aux_ac.vlouver_set:
id: aux_id
position: 3 # moves the louvers to the middle position
```
- **aux_id** (**Requared**, string): ID of `aux_ac` component.
- **position** (**Requared**, integer): position of the vertical louvers.
### ``aux_ac.vlouver_stop`` ###
This action stops vertical swing of louvers.

View File

@@ -132,6 +132,10 @@ climate:
name: AC Preset Reporter
id: ac_preset_reporter
internal: false
vlouver_state:
name: AC Vertical Louvers State
id: ac_vlouver_state
internal: false
visual:
min_temperature: 16
max_temperature: 32
@@ -186,6 +190,7 @@ climate:
- **invertor_power** (*Опциональный*): Параметры создаваемого датчика мощности инвертора, если такой датчик нужен. Параметры аналогичны датчику дисплея **display_state**.
- **preset_reporter** (*Опциональный*): Параметры создаваемого текстового датчика текущего активного пресета. Параметры аналогичны датчику дисплея **display_state**.
Климатические устройства ESPHome не отправляют по MQTT активный пресет (см. **supported_presets** и **custom_presets**), в котором работает устройство. Если вы используете MQTT и хотите получать информацию о пресетах, то пропишите этот датчик в конфигурации.
- **vlouver_state** (*Опциональный*): Параметры создаваемого сенсора состояния вертикальных жалюзи. Параметры аналогичны датчику дисплея **display_state**. Состояние желюзи кодируется целочисленными значениями (подробнее смотри [aux_ac.vlouver_set action](#aux_ac_._vlouver_set) ниже).
- **supported_modes** (*Опциональный*, список): Список поддерживаемых режимов работы. Возможные значения: ``HEAT_COOL``, ``COOL``, ``HEAT``, ``DRY``, ``FAN_ONLY``. Обратите внимание: некоторые производители кондиционеров указывают на пульте режим AUTO, хотя по факту этот режим не работает по расписанию и только лишь поддерживает целевую температуру. Такой режим в ESPHome называется HEAT_COOL. По умолчанию список содержит только значение ``FAN_ONLY``.
- **custom_fan_modes** (*Опциональный*, список): Список поддерживаемых дополнительных режимов вентилятора. Возможные значения: ``MUTE``, ``TURBO``. По умолчанию никакие дополнительные режимы не установлены.
- **supported_presets** (*Опциональный*, список): Список поддерживаемых базовых функций кондиционера. Возможные значения: ``SLEEP``. По умолчанию никакие базовые функции не установлены.
@@ -214,6 +219,28 @@ on_...:
```
- **aux_id** (**Обязательный**, строка): ID компонента `aux_ac`.
### ``aux_ac.vlouver_set`` ###
Переводит жалюзи в указанное состояние.
Состояние кодируется следующими целочисленными значениями:
- `0`: жалюзи находятся в состоянии `SWING` (качаются вверх-вниз);
- `1`: жалюзи остановлены в каком-то пользовательском положении;
- `2`: жалюзи установлены в верхнее положение;
- `3`: жалюзи установдены в положение на шаг выше среднего;
- `4`: жалюзи установены в среднее положение;
- `5`: жалюзи установдены в положение на шаг ниже среднего;
- `6`: жалюзи установлены в нижнее положение.
```yaml
on_...:
then:
- aux_ac.vlouver_set:
id: aux_id
position: 3 # устанавливаем жалюзи в среднее положение
```
- **aux_id** (**Обязательный**, строка): ID компонента `aux_ac`.
- **position** (**Обязательный**, целое число): состояние вертикальных жалюзи.
### ``aux_ac.vlouver_stop`` ###
Остановка вертикального движения жалюзи кондиционера. Если жалюзи качались в вертикальном направлении, то можно их остановить в нужном положении.

View File

@@ -119,6 +119,22 @@ namespace aux_ac {
AirCon *ac_;
};
template<typename... Ts>
class AirConVLouverSetAction : public Action<Ts...>
{
public:
AirConVLouverSetAction(AirCon *ac) : ac_(ac) {}
TEMPLATABLE_VALUE(uint8_t, value);
void play(Ts... x) {
vlpos_ = this->value_.value(x...);
this->ac_->setVLouverFrontendSequence( (ac_vlouver_frontend)vlpos_);
}
protected:
AirCon *ac_;
uint8_t vlpos_;
};
// **************************************** SEND TEST PACKET ACTION ****************************************

View File

@@ -63,7 +63,7 @@ public:
static const uint32_t AC_STATES_REQUEST_INTERVAL;
};
const std::string Constants::AC_FIRMWARE_VERSION = "0.2.5";
const std::string Constants::AC_FIRMWARE_VERSION = "0.2.6";
const char *const Constants::TAG = "AirCon";
// custom fan modes
@@ -440,6 +440,18 @@ enum ac_mildew : uint8_t { AC_MILDEW_OFF = 0x00, AC_MILDEW_ON = 0x08, AC_MILDEW_
// GK: define убрал, т.к. считаю, что сбрасывать счетчик не надо.
// #define AC_MIN_COUNTER_MASK 0b00111111
// положение вертикальных жалюзи для фронтенда
enum ac_vlouver_frontend : uint8_t {
AC_VLOUVER_FRONTEND_SWING = 0x00,
AC_VLOUVER_FRONTEND_STOP = 0x01,
AC_VLOUVER_FRONTEND_TOP = 0x02,
AC_VLOUVER_FRONTEND_MIDDLE_ABOVE = 0x03,
AC_VLOUVER_FRONTEND_MIDDLE = 0x04,
AC_VLOUVER_FRONTEND_MIDDLE_BELOW = 0x05,
AC_VLOUVER_FRONTEND_BOTTOM = 0x06,
};
/** команда для кондиционера
*
* ВАЖНО! В коде используется копирование команд простым присваиванием.
@@ -1793,13 +1805,10 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
esphome::sensor::Sensor *sensor_inbound_temperature_ = nullptr;
esphome::sensor::Sensor *sensor_outbound_temperature_ = nullptr;
esphome::sensor::Sensor *sensor_compressor_temperature_ = nullptr;
// текущая мощность компрессора
esphome::sensor::Sensor *sensor_invertor_power_ = nullptr;
// бинарный сенсор, отображающий состояние дисплея
esphome::sensor::Sensor *sensor_vlouver_state_ = nullptr;
esphome::binary_sensor::BinarySensor *sensor_display_ = nullptr;
// бинарный сенсор состония разморозки
esphome::binary_sensor::BinarySensor *sensor_defrost_ = nullptr;
// текстовый сенсор, отображающий текущий режим работы сплита
esphome::text_sensor::TextSensor *sensor_preset_reporter_ = nullptr;
@@ -1913,6 +1922,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
void set_inbound_temperature_sensor(sensor::Sensor *temperature_sensor) { sensor_inbound_temperature_ = temperature_sensor; }
void set_outbound_temperature_sensor(sensor::Sensor *temperature_sensor) { sensor_outbound_temperature_ = temperature_sensor; }
void set_compressor_temperature_sensor(sensor::Sensor *temperature_sensor) { sensor_compressor_temperature_ = temperature_sensor; }
void set_vlouver_state_sensor(sensor::Sensor *vlouver_state_sensor){ sensor_vlouver_state_ = vlouver_state_sensor; }
void set_defrost_state(binary_sensor::BinarySensor *defrost_state_sensor) { sensor_defrost_ = defrost_state_sensor; }
void set_display_sensor(binary_sensor::BinarySensor *display_sensor) { sensor_display_ = display_sensor; }
void set_invertor_power_sensor(sensor::Sensor *invertor_power_sensor) { sensor_invertor_power_ = invertor_power_sensor; }
@@ -2241,6 +2251,9 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// флаг режима разморозки
if (sensor_defrost_ != nullptr)
sensor_defrost_->publish_state(_current_ac_state.defrost);
// положение вертикальных жалюзи
if (sensor_vlouver_state_ != nullptr)
sensor_vlouver_state_->publish_state( (float) this->getCurrentVlouverFrontendState() );
// сенсор состояния сплита
if (sensor_preset_reporter_ != nullptr) {
@@ -2408,6 +2421,20 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
}
}
if ((this->sensor_vlouver_state_) != nullptr) {
ESP_LOGCONFIG(Constants::TAG, "%s%s '%s'", " ", LOG_STR_LITERAL("Vertical louvers state"), (this->sensor_vlouver_state_)->get_name().c_str());
ESP_LOGCONFIG(Constants::TAG, "%s Accuracy Decimals: %d", " ", (this->sensor_vlouver_state_)->get_accuracy_decimals());
if (!(this->sensor_vlouver_state_)->get_icon().empty()) {
ESP_LOGCONFIG(Constants::TAG, "%s Icon: '%s'", " ", (this->sensor_vlouver_state_)->get_icon().c_str());
}
if (!(this->sensor_vlouver_state_)->unique_id().empty()) {
ESP_LOGV(Constants::TAG, "%s Unique ID: '%s'", " ", (this->sensor_vlouver_state_)->unique_id().c_str());
}
if ((this->sensor_vlouver_state_)->get_force_update()) {
ESP_LOGV(Constants::TAG, "%s Force Update: YES", " ");
}
}
if ((this->sensor_defrost_) != nullptr) {
ESP_LOGCONFIG(Constants::TAG, "%s%s '%s'", " ", LOG_STR_LITERAL("Defrost status"), (this->sensor_defrost_)->get_name().c_str());
if (!(this->sensor_defrost_)->get_device_class().empty()) {
@@ -3059,7 +3086,72 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return true;
}
// устанавливает жалюзи в нужное положение
// конвертирует состояние жалюзи из кодов сплита в коды для фронтенда
ac_vlouver_frontend AUXvlouverToVlouverFrontend(const ac_louver_V vLouver){
switch (vLouver) {
case AC_LOUVERV_SWING_UPDOWN:
return AC_VLOUVER_FRONTEND_SWING;
case AC_LOUVERV_OFF:
return AC_VLOUVER_FRONTEND_STOP;
case AC_LOUVERV_SWING_TOP:
return AC_VLOUVER_FRONTEND_TOP;
case AC_LOUVERV_SWING_MIDDLE_ABOVE:
return AC_VLOUVER_FRONTEND_MIDDLE_ABOVE;
case AC_LOUVERV_SWING_MIDDLE:
return AC_VLOUVER_FRONTEND_MIDDLE;
case AC_LOUVERV_SWING_MIDDLE_BELOW:
return AC_VLOUVER_FRONTEND_MIDDLE_BELOW;
case AC_LOUVERV_SWING_BOTTOM:
return AC_VLOUVER_FRONTEND_BOTTOM;
default:
_debugMsg(F("AUXvlouverToVlouverFrontend: unknown vertical louver state = %u"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, _current_ac_state.louver.louver_v);
return AC_VLOUVER_FRONTEND_STOP; //
}
}
// возвращает текущее положение шторок в кодах для фронтенда
ac_vlouver_frontend getCurrentVlouverFrontendState(){
return AUXvlouverToVlouverFrontend(_current_ac_state.louver.louver_v);
}
// конвертирует состояние жалюзи из кодов для фронтенда в коды сплита
ac_louver_V vlouverFrontendToAUXvlouver(const ac_vlouver_frontend vLouver){
switch (vLouver) {
case AC_VLOUVER_FRONTEND_SWING:
return AC_LOUVERV_SWING_UPDOWN;
case AC_VLOUVER_FRONTEND_STOP:
return AC_LOUVERV_OFF;
case AC_VLOUVER_FRONTEND_TOP:
return AC_LOUVERV_SWING_TOP;
case AC_VLOUVER_FRONTEND_MIDDLE_ABOVE:
return AC_LOUVERV_SWING_MIDDLE_ABOVE;
case AC_VLOUVER_FRONTEND_MIDDLE:
return AC_LOUVERV_SWING_MIDDLE;
case AC_VLOUVER_FRONTEND_MIDDLE_BELOW:
return AC_LOUVERV_SWING_MIDDLE_BELOW;
case AC_VLOUVER_FRONTEND_BOTTOM:
return AC_LOUVERV_SWING_BOTTOM;
default:
_debugMsg(F("vlouverFrontendToAUXvlouver: unknown vertical louver state = %u"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, _current_ac_state.louver.louver_v);
return AC_LOUVERV_OFF; //
}
}
// устанавливает жалюзи в нужное положение по коду сплита
bool setVLouverSequence(const ac_louver_V vLouver){
// нет смысла в последовательности, если нет коннекта с кондиционером
if (!get_has_connection()) {
@@ -3081,6 +3173,11 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return true;
}
// устанавливает жалюзи в нужное положение по коду для фронтенда
bool setVLouverFrontendSequence(const ac_vlouver_frontend vLouver){
return setVLouverSequence(vlouverFrontendToAUXvlouver(vLouver));
}
// установка жалюзи в определенные положения
bool setVLouverSwingSequence() { return setVLouverSequence(AC_LOUVERV_SWING_UPDOWN); }
bool setVLouverStopSequence() { return setVLouverSequence(AC_LOUVERV_OFF); }

View File

@@ -22,6 +22,7 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_POWER_FACTOR,
STATE_CLASS_MEASUREMENT,
CONF_POSITION,
)
from esphome.components.climate import (
ClimateMode,
@@ -53,14 +54,22 @@ CONF_DISPLAY_INVERTED = 'display_inverted'
ICON_DISPLAY = "mdi:clock-digital"
CONF_PRESET_REPORTER = "preset_reporter"
ICON_PRESET_REPORTER = "mdi:format-list-group"
CONF_VLOUVER_STATE = "vlouver_state"
ICON_VLOUVER_STATE = "mdi:compare-vertical"
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")
# Display actions
AirConDisplayOffAction = aux_ac_ns.class_("AirConDisplayOffAction", automation.Action)
AirConDisplayOnAction = aux_ac_ns.class_("AirConDisplayOnAction", automation.Action)
# test packet
AirConSendTestPacketAction = aux_ac_ns.class_("AirConSendTestPacketAction", automation.Action)
# vertical louvers actions
AirConVLouverSwingAction = aux_ac_ns.class_("AirConVLouverSwingAction", automation.Action)
AirConVLouverStopAction = aux_ac_ns.class_("AirConVLouverStopAction", automation.Action)
AirConVLouverTopAction = aux_ac_ns.class_("AirConVLouverTopAction", automation.Action)
@@ -68,7 +77,8 @@ AirConVLouverMiddleAboveAction = aux_ac_ns.class_("AirConVLouverMiddleAboveActio
AirConVLouverMiddleAction = aux_ac_ns.class_("AirConVLouverMiddleAction", automation.Action)
AirConVLouverMiddleBelowAction = aux_ac_ns.class_("AirConVLouverMiddleBelowAction", automation.Action)
AirConVLouverBottomAction = aux_ac_ns.class_("AirConVLouverBottomAction", automation.Action)
AirConSendTestPacketAction = aux_ac_ns.class_("AirConSendTestPacketAction", automation.Action)
AirConVLouverSetAction = aux_ac_ns.class_("AirConVLouverSetAction", automation.Action)
ALLOWED_CLIMATE_MODES = {
"HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL,
@@ -190,6 +200,14 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
}
),
cv.Optional(CONF_VLOUVER_STATE): sensor.sensor_schema(
icon=ICON_VLOUVER_STATE,
accuracy_decimals=0,
).extend(
{
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
}
),
cv.Optional(CONF_DISPLAY_STATE): binary_sensor.binary_sensor_schema(
icon=ICON_DISPLAY,
).extend(
@@ -258,6 +276,11 @@ async def to_code(config):
sens = await sensor.new_sensor(conf)
cg.add(var.set_compressor_temperature_sensor(sens))
if CONF_VLOUVER_STATE in config:
conf = config[CONF_VLOUVER_STATE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_vlouver_state_sensor(sens))
if CONF_DISPLAY_STATE in config:
conf = config[CONF_DISPLAY_STATE]
sens = await binary_sensor.new_binary_sensor(conf)
@@ -353,6 +376,21 @@ async def vlouver_bottom_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)
VLOUVER_SET_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(AirCon),
cv.Required(CONF_POSITION): cv.templatable(cv.int_range(0, 6)),
}
)
@automation.register_action("aux_ac.vlouver_set", AirConVLouverSetAction, VLOUVER_SET_ACTION_SCHEMA)
async def vlouver_set_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_POSITION], args, int)
cg.add(var.set_value(template_))
return var
# *********************************************************************************************************
# ВАЖНО! Только для инженеров!
@@ -367,11 +405,7 @@ SEND_TEST_PACKET_ACTION_SCHEMA = maybe_simple_id(
}
)
@automation.register_action(
"aux_ac.send_packet",
AirConSendTestPacketAction,
SEND_TEST_PACKET_ACTION_SCHEMA
)
@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)

View File

@@ -100,6 +100,10 @@ climate:
name: ${upper_devicename} Preset Reporter
id: ${devicename}_preset_reporter
internal: false
vlouver_state:
name: ${upper_devicename} VLouvers State
id: ${devicename}_vlouver_state
internal: false
visual:
min_temperature: 16
max_temperature: 32
@@ -194,15 +198,18 @@ button:
number:
- platform: template
name: ${upper_devicename} Vertical Louver
name: ${upper_devicename} Vertical Louvers
id: ${devicename}_vlouver
icon: "mdi:circle-small"
mode: "slider"
min_value: 0
max_value: 6
step: 1
update_interval: 2s
lambda: |-
return id(${devicename}_vlouver_state).state;
set_action:
then:
- lambda: !lambda |-
if (x == 6) x = 7; // 6 is incorrect louver position, 7 is stopped louver
id(aux_id).setVLouverSequence( static_cast<esphome::aux_ac::ac_louver_V>(x) );
- aux_ac.vlouver_set:
id: aux_id
position: !lambda "return x;"