+ new yaml parameter: packet timeout

This commit is contained in:
GrKoR
2023-01-13 22:57:12 +04:00
parent 23e3045e13
commit 6fbb5b6bf8
6 changed files with 80 additions and 28 deletions

View File

@@ -99,6 +99,7 @@ climate:
period: 7s
show_action: true
display_inverted: false
timeout: 150
indoor_temperature:
name: AC Indoor Temperature
id: ac_indoor_temp
@@ -191,6 +192,11 @@ climate:
- **display_inverted** (*Optional*, boolean, default ``false``): It configures display driver logic level. As it turned out in the issue [#31](https://github.com/GrKoR/esphome_aux_ac_component/issues/31), different models of conditioners manage display different way. Rovex ACs powers off display by bit `1` in command packet and power it on by bit `0`. Many other conditioners do this vice versa.
- **timeout** (*Optional*, unsigned integer, default ``150``): Packet timeout for `aux_ac` data receiver.
In the most common use of `aux_ac`, it isn't necessary to change this value. This keyword is optional, so you may omit it.
The only situation when you can play with timeout is heavily loaded ESP. When you are using your ESP for many hard tasks, it is possible that `aux_ac` does not have enough time to receive AC responses. In this case, you can slightly raise the timeout value. But the best solution would be to remove some of the tasks from the ESP.
The timeout is limited to a range from `150` to `600` milliseconds. Other values are possible only with source code modification. But I don't recommend that.
- **indoor_temperature** (*Optional*): Parameters of the room air temperature sensor.
- **name** (**Required**, string): The name for the temperature sensor.
- **id** (*Optional*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Set the ID of this sensor for use in lambdas.

View File

@@ -97,6 +97,7 @@ climate:
period: 7s
show_action: true
display_inverted: false
timeout: 150
indoor_temperature:
name: AC Indoor Temperature
id: ac_indoor_temp
@@ -189,6 +190,11 @@ climate:
- **display_inverted** (*Опциональный*, логическое, по умолчанию ``false``): Настраивает способ управления дисплеем. Как выяснилось (issue [#31](https://github.com/GrKoR/esphome_aux_ac_component/issues/31)), включение-выключение дисплея обрабатывается кондиционерами по разному. Кондиционеры Rovex включают дисплей по `0` в соответствующем бите команды и выключают по биту `1`. Многие другие модели кондиционеров поступают наоборот.
- **timeout** (*Опциональный*, неотрицательное целое, по умолчанию ``150``): Таймаут получения пакета для ресивера данных `aux_ac`.
Чаще всего вам это значение никогда не понадобится. Поскольку этот параметр опционален, то его можно смело пропустить, если нет необходимости менять таймауты.
Единственная ситуация, когда вам может пригодиться этот параметр, - это сильно загруженная ESP. Если по какой-то неподдающейся логике причине вы кроме `aux_ac` нагрузили свою ESP кучей дополнительных ресурсоемких задач, то у компонента может просто не хватать времени для оперативного приёма ответов от кондиционера. В этом в логе будут сообщения о том, что последовательность команд была прервана по таймауту. Чтобы это исправить, лучше, конечно, немного разгрузить ESP. Если это вам не подходит, тогда можно увеличить таймаут.
Значение таймаута в прошивке ограничено диапазоном от `150` до `600` миллисекунд. Устанавливать значения выше можно только отредактировав исходные коды компонента. Но сильно задирать таймаут не стоит. Кондиционер периодически рассылает пакеты без запроса со стороны `aux_ac` и это приводит к сбою в отправке команды.
- **indoor_temperature** (*Опциональный*): Параметры создаваемого датчика температуры воздуха, если такой датчик нужен
- **name** (**Обязательный**, строка): Имя датчика температуры.
- **id** (*Опциональный*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Можно указать свой ID для датчика для использования в лямбдах.

View File

@@ -64,9 +64,15 @@ class Constants {
// изменение параметров с пульта не сообщается в UART, поэтому надо запрашивать состояние, чтобы быть в курсе
// значение в миллисекундах
static const uint32_t AC_STATES_REQUEST_INTERVAL;
// границы допустимого диапазона таймаута загрузки пакета
// таймаут загрузки - через такое количиство миллисекунд конечный автомат перейдет из
// состояния ACSM_RECEIVING_PACKET в ACSM_IDLE, если пакет не будет загружен
static const uint32_t AC_PACKET_TIMEOUT_MAX;
static const uint32_t AC_PACKET_TIMEOUT_MIN;
};
const std::string Constants::AC_FIRMWARE_VERSION = "0.2.9-dev.3+";
const std::string Constants::AC_FIRMWARE_VERSION = "0.2.9-dev.4";
// custom fan modes
const std::string Constants::MUTE = "mute";
@@ -84,6 +90,19 @@ const float Constants::AC_TEMPERATURE_STEP = 0.5;
const uint8_t Constants::AC_MIN_INVERTER_POWER_LIMIT = 30; // 30%
const uint8_t Constants::AC_MAX_INVERTER_POWER_LIMIT = 100; // 100%
const uint32_t Constants::AC_STATES_REQUEST_INTERVAL = 7000;
// таймаут загрузки пакета
// По расчетам выходит:
// - получение и обработка посимвольно не должна длиться дольше 600 мсек.
// - получение и обработка пакетов целиком не должна длиться дольше 150 мсек.
// Мы будем обрабатывать пакетами, поэтому 150.
// Растягивать приём пакетов очередью команд нельзя, так как кондиционер иногда посылает
// информационные пакеты без запроса. Такие пакеты будут рушить последовательность команд,
// команды будут теряться. От такой коллизии мы не защищены в любом случае. Но чем меньше таймаут,
// тем меньше шансов на коллизию.
// Из этих соображений выбраны границы диапазона (_MIN и _MAX значения).
const uint32_t Constants::AC_PACKET_TIMEOUT_MAX = 600;
const uint32_t Constants::AC_PACKET_TIMEOUT_MIN = 150;
class AirCon;
@@ -104,17 +123,6 @@ enum acsm_state : uint8_t {
// поэтому буффер увеличен
#define AC_BUFFER_SIZE 35
/**
* таймаут загрузки пакета
*
* через такое количиство миллисекунд конечный автомат перейдет из состояния ACSM_RECEIVING_PACKET в ACSM_IDLE, если пакет не будет загружен
* По расчетам выходит:
* - получение и обработка посимвольно не должна длиться дольше 600 мсек.
* - получение и обработка пакетов целиком не должна длиться дольше 150 мсек.
* Мы будем обрабатывать пакетами.
**/
#define AC_PACKET_TIMEOUT 150
// типы пакетов
// https://github.com/GrKoR/AUX_HVAC_Protocol#packet_types
#define AC_PTYPE_PING 0x01 // ping-пакет
@@ -753,6 +761,9 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// пакет для тестирования всякой фигни
packet_t _outTestPacket;
// таймаут загрузки пакета, по дефолту минимальный
uint32_t _packet_timeout = Constants::AC_PACKET_TIMEOUT_MIN;
// сырые данные последних полученных большого и маленького информационных пакетов
ac_last_raw_data _last_raw_data;
@@ -1127,7 +1138,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
}
// если пакет не загружен, а время вышло, то надо вернуться в IDLE
if (millis() - _inPacket.msec >= AC_PACKET_TIMEOUT) {
if (millis() - _inPacket.msec >= this->_packet_timeout) {
_debugMsg(F("Receiver: packet timed out!"), ESPHOME_LOG_LEVEL_WARN, __LINE__);
_debugPrintPacket(&_inPacket, ESPHOME_LOG_LEVEL_WARN, __LINE__);
_clearInPacket();
@@ -2031,6 +2042,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
_ac_serial = parent;
_hw_initialized = (_ac_serial != nullptr);
_has_connection = false;
_packet_timeout = Constants::AC_PACKET_TIMEOUT_MIN;
// заполняем структуру состояния начальными значениями
_clearCommand((ac_command_t *)&_current_ac_state);
@@ -2421,6 +2433,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
ESP_LOGCONFIG(TAG, " [x] Period: %dms", this->get_period());
ESP_LOGCONFIG(TAG, " [x] Show action: %s", TRUEFALSE(this->get_show_action()));
ESP_LOGCONFIG(TAG, " [x] Display inverted: %s", TRUEFALSE(this->get_display_inverted()));
ESP_LOGCONFIG(TAG, " [x] Packet timeout: %dms", this->get_packet_timeout());
#if defined(PRESETS_SAVING)
ESP_LOGCONFIG(TAG, " [x] Save settings %s", TRUEFALSE(this->get_store_settings()));
@@ -2428,17 +2441,18 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
ESP_LOGCONFIG(TAG, " [?] Is inverter %s", millis() > _update_period + 1000 ? YESNO(_is_inverter) : "pending...");
LOG_SENSOR(" ", "Inverter Power", this->sensor_inverter_power_);
LOG_SENSOR(" ", "Inverter Power Limit Value", this->sensor_inverter_power_limit_value_);
LOG_BINARY_SENSOR(" ", "Inverter Power Limit State", this->sensor_inverter_power_limit_state_);
LOG_SENSOR(" ", "Indoor Temperature", this->sensor_indoor_temperature_);
LOG_SENSOR(" ", "Outdoor Temperature", this->sensor_outdoor_temperature_);
LOG_SENSOR(" ", "Inbound Temperature", this->sensor_inbound_temperature_);
LOG_SENSOR(" ", "Outbound Temperature", this->sensor_outbound_temperature_);
LOG_SENSOR(" ", "Compressor Temperature", this->sensor_compressor_temperature_);
LOG_SENSOR(" ", "Inverter Power", this->sensor_inverter_power_);
LOG_BINARY_SENSOR(" ", "Defrost Status", this->sensor_defrost_);
LOG_BINARY_SENSOR(" ", "Display", this->sensor_display_);
LOG_TEXT_SENSOR(" ", "Preset Reporter", this->sensor_preset_reporter_);
LOG_SENSOR(" ", "Inverter Power Limit Value", this->sensor_inverter_power_limit_value_);
LOG_BINARY_SENSOR(" ", "Inverter Power Limit State", this->sensor_inverter_power_limit_state_);
this->dump_traits_(TAG);
}
@@ -3207,6 +3221,13 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
void set_display_inverted(bool display_inverted) { this->_display_inverted = display_inverted; }
bool get_display_inverted() { return this->_display_inverted; }
void set_packet_timeout(uint32_t ms) {
if (ms < Constants::AC_PACKET_TIMEOUT_MIN) ms = Constants::AC_PACKET_TIMEOUT_MIN;
if (ms > Constants::AC_PACKET_TIMEOUT_MAX) ms = Constants::AC_PACKET_TIMEOUT_MIN;
this->_packet_timeout = ms;
}
uint32_t get_packet_timeout() { return this->_packet_timeout; }
// возможно функции get и не нужны, но вроде как должны быть
void set_supported_modes(const std::set<ClimateMode> &modes) { this->_supported_modes = modes; }
std::set<ClimateMode> get_supported_modes() { return this->_supported_modes; }

View File

@@ -15,6 +15,7 @@ from esphome.const import (
CONF_SUPPORTED_MODES,
CONF_SUPPORTED_SWING_MODES,
CONF_SUPPORTED_PRESETS,
CONF_TIMEOUT,
CONF_UART_ID,
UNIT_CELSIUS,
UNIT_PERCENT,
@@ -82,7 +83,7 @@ Capabilities = aux_ac_ns.namespace("Constants")
AirConDisplayOffAction = aux_ac_ns.class_("AirConDisplayOffAction", automation.Action)
AirConDisplayOnAction = aux_ac_ns.class_("AirConDisplayOnAction", automation.Action)
# test packet
# test packet action
AirConSendTestPacketAction = aux_ac_ns.class_(
"AirConSendTestPacketAction", automation.Action
)
@@ -118,6 +119,26 @@ AirConPowerLimitationOnAction = aux_ac_ns.class_(
)
AC_PACKET_TIMEOUT_MIN = 150
AC_PACKET_TIMEOUT_MAX = 600
def validate_packet_timeout(value):
minV = AC_PACKET_TIMEOUT_MIN
maxV = AC_PACKET_TIMEOUT_MAX
if value in range(minV, maxV+1):
return cv.Schema(cv.uint32_t)(value)
raise cv.Invalid(f"Timeout should be in range: {minV}..{maxV}.")
AC_POWER_LIMIT_MIN = 30
AC_POWER_LIMIT_MAX = 100
def validate_power_limit_range(value):
minV = AC_POWER_LIMIT_MIN
maxV = AC_POWER_LIMIT_MAX
if value in range(minV, maxV+1):
return cv.Schema(cv.uint32_t)(value)
raise cv.Invalid(f"Power limit should be in range: {minV}..{maxV}")
ALLOWED_CLIMATE_MODES = {
"HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL,
"COOL": ClimateMode.CLIMATE_MODE_COOL,
@@ -160,7 +181,7 @@ def validate_raw_data(value):
def output_info(config):
"""_LOGGER.info(config)"""
"""_LOGGER.info(config.items())"""
return config
@@ -171,6 +192,7 @@ CONFIG_SCHEMA = cv.All(
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_TIMEOUT, default=AC_PACKET_TIMEOUT_MIN): validate_packet_timeout,
cv.Optional(CONF_INVERTER_POWER_DEPRICATED): cv.invalid(
"The name of sensor was changed in v.0.2.9 from 'invertor_power' to 'inverter_power'. Update your config please."
@@ -307,8 +329,6 @@ CONFIG_SCHEMA = cv.All(
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)
@@ -379,6 +399,7 @@ async def to_code(config):
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_packet_timeout(config[CONF_TIMEOUT]))
if CONF_SUPPORTED_MODES in config:
cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES]))
if CONF_SUPPORTED_SWING_MODES in config:
@@ -508,9 +529,7 @@ async def power_limit_off_to_code(config, action_id, template_arg, args):
POWER_LIMITATION_ON_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(AirCon),
cv.Optional(CONF_LIMIT, default=Capabilities.AC_MIN_INVERTER_POWER_LIMIT): cv.templatable(
cv.int_range(30, 100)
),
cv.Optional(CONF_LIMIT, default=AC_POWER_LIMIT_MIN): validate_power_limit_range,
}
)

View File

@@ -61,6 +61,7 @@ climate:
period: 7s
show_action: true
display_inverted: true
timeout: 150
indoor_temperature:
name: $upper_devicename Indoor Temperature
id: ${devicename}_indoor_temp
@@ -90,7 +91,7 @@ climate:
id: ${devicename}_defrost_state
internal: false
inverter_power:
name: $upper_devicename Invertor Power
name: $upper_devicename Inverter Power
id: ${devicename}_invertor_power
internal: false
preset_reporter:
@@ -144,8 +145,7 @@ button:
- aux_ac.power_limit_on:
id: aux_id
limit: 50
number:
- platform: template
name: ${upper_devicename} IPower Limit Value

View File

@@ -61,6 +61,7 @@ climate:
period: 7s
show_action: true
display_inverted: true
timeout: 150
indoor_temperature:
name: $upper_devicename Indoor Temperature
id: ${devicename}_indoor_temp
@@ -144,8 +145,7 @@ button:
- aux_ac.power_limit_on:
id: aux_id
limit: 50
number:
- platform: template
name: ${upper_devicename} IPower Limit Value