mirror of
https://github.com/GrKoR/esphome_aux_ac_component.git
synced 2025-12-18 13:38:19 +03:00
fixed uart connection, cmd sequence refactoring
This commit is contained in:
@@ -429,15 +429,19 @@ enum sequence_packet_type_t : uint8_t {
|
||||
AC_SPT_SENT_PACKET = 0x02 // отправленный пакет
|
||||
};
|
||||
|
||||
// элемент последовательности
|
||||
/** элемент последовательности
|
||||
* Поля item_type, func, timeout и cmd устанавливаются ручками и задают параметры выполнения шага последовательности.
|
||||
* Поля msec, packet_type и packet заполняются движком при обработке последовательности.
|
||||
**/
|
||||
struct sequence_item_t {
|
||||
sequence_item_type_t item_type; // тип элемента последовательности
|
||||
bool (AirCon::*func)(); // указатель на функцию, отрабатывающую шаг последовательности
|
||||
uint16_t timeout; // допустимый таймаут в ожидании пакета (применим только для входящих пакетов)
|
||||
ac_command_t cmd; // новое состояние сплита, нужно для передачи кондиционеру команд
|
||||
//******* поля ниже заполняются функциями обработки последовательности ***********
|
||||
uint32_t msec; // время старта текущего шага последовательности (для входящего пакета и паузы)
|
||||
sequence_packet_type_t packet_type; // тип пакета (входящий, исходящий или вовсе не пакет)
|
||||
packet_t packet; // данные пакета
|
||||
ac_command_t cmd; // новое состояние сплита, нужно для передачи кондиционеру команд
|
||||
};
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
@@ -470,7 +474,21 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
// флаг подключения к UART
|
||||
bool _hw_initialized = false;
|
||||
// указатель на UART, по которому общаемся с кондиционером
|
||||
esphome::uart::UARTDevice *_ac_serial;
|
||||
esphome::uart::UARTComponent *_ac_serial;
|
||||
|
||||
// UART wrappers: peek
|
||||
int peek() {
|
||||
uint8_t data;
|
||||
if (!_ac_serial->peek_byte(&data)) return -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
// UART wrappers: read
|
||||
int read() {
|
||||
uint8_t data;
|
||||
if (!_ac_serial->read_byte(&data)) return -1;
|
||||
return data;
|
||||
}
|
||||
|
||||
// флаг обмена пакетами с кондиционером (если проходят пинги, значит есть коннект)
|
||||
bool _has_connection = false;
|
||||
@@ -508,7 +526,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
// возвращает индекс первого пустого шага последовательности команд
|
||||
uint8_t _getNextFreeSequenceStep(){
|
||||
for (size_t i = 0; i < AC_SEQUENCE_MAX_LEN; i++) {
|
||||
if (_sequence[i].item_type != AC_SIT_NONE){
|
||||
if (_sequence[i].item_type == AC_SIT_NONE){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -521,6 +539,42 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return (AC_SEQUENCE_MAX_LEN - _getNextFreeSequenceStep());
|
||||
}
|
||||
|
||||
// добавляет шаг в последовательность команд
|
||||
// возвращает false, если не нашлось места для шага
|
||||
bool _addSequenceStep(const sequence_item_type_t item_type, bool (AirCon::*func)() = nullptr, ac_command_t *cmd = nullptr, uint16_t timeout = AC_SEQUENCE_DEFAULT_TIMEOUT){
|
||||
if (!_hasFreeSequenceStep()) return false; // если места нет, то уходим
|
||||
if (item_type == AC_SIT_NONE) return false; // глупость какая-то, уходим
|
||||
if ((item_type == AC_SIT_FUNC) && (func == nullptr)) return false; // должна быть передана функция для такого типа шага
|
||||
if ((item_type != AC_SIT_DELAY) && (item_type != AC_SIT_FUNC)){
|
||||
// какой-то неизвестный тип
|
||||
_debugMsg(F("_addSequenceStep: unknown sequence item type = %u"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, item_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t step = _getNextFreeSequenceStep();
|
||||
|
||||
_sequence[step].item_type = item_type;
|
||||
|
||||
// если задержка нулевая, то присваиваем дефолтную задержку
|
||||
if (timeout == 0) timeout = AC_SEQUENCE_DEFAULT_TIMEOUT;
|
||||
_sequence[step].timeout = timeout;
|
||||
|
||||
_sequence[step].func = func;
|
||||
if (cmd != nullptr) _sequence[step].cmd = *cmd; // так как в структуре команды только простые типы, то можно вот так присваивать
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// додбавляет в последовательность шаг с задержкой
|
||||
bool _addSequenceDelayStep(uint16_t timeout){
|
||||
return this->_addSequenceStep(AC_SIT_DELAY, nullptr, nullptr, timeout);
|
||||
}
|
||||
|
||||
// добавляет в последовательность функциональный шаг
|
||||
bool _addSequenceFuncStep(bool (AirCon::*func)(), ac_command_t *cmd = nullptr, uint16_t timeout = AC_SEQUENCE_DEFAULT_TIMEOUT){
|
||||
return this->_addSequenceStep(AC_SIT_FUNC, func, cmd, timeout);
|
||||
}
|
||||
|
||||
// выполняет всю логику очередного шага последовательности команд
|
||||
void _doSequence(){
|
||||
if (!hasSequence()) return;
|
||||
@@ -555,7 +609,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
|
||||
// если время вышло, то отчитываемся в лог и очищаем последовательность
|
||||
if (millis() - _sequence[_sequence_current_step].msec >= _sequence[_sequence_current_step].timeout) {
|
||||
_debugMsg(F("Sequence [step %u]: step timed out (%u ms)"), ESPHOME_LOG_LEVEL_WARN, __LINE__, _sequence_current_step, millis() - _sequence[_sequence_current_step].msec);
|
||||
_debugMsg(F("Sequence [step %u]: step timed out (it took %u ms instead of %u ms)"), ESPHOME_LOG_LEVEL_WARN, __LINE__, _sequence_current_step, millis() - _sequence[_sequence_current_step].msec, _sequence[_sequence_current_step].timeout);
|
||||
_clearSequence();
|
||||
return;
|
||||
}
|
||||
@@ -713,10 +767,11 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
};
|
||||
|
||||
|
||||
if (_ac_serial->peek() == AC_PACKET_START_BYTE) {
|
||||
if (this->peek() == AC_PACKET_START_BYTE) {
|
||||
// если во входящий пакет что-то уже загружено, значит это какие-то ошибочные данные или неизвестные пакеты
|
||||
// надо эту инфу вывалить в лог
|
||||
if (_inPacket.bytesLoaded > 0){
|
||||
_debugMsg(F("Start byte received but there are some unparsed bytes in the buffer:"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugPrintPacket(&_inPacket, ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
}
|
||||
_clearInPacket();
|
||||
@@ -739,10 +794,11 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
while (_ac_serial->available() > 0)
|
||||
{
|
||||
// если наткнулись на старт пакета, то выходим из while
|
||||
if (_ac_serial->peek() == AC_PACKET_START_BYTE) break;
|
||||
// если какие-то данные были загружены в буфер, то они будут выгружены в лог при загрузке нового пакета
|
||||
if (this->peek() == AC_PACKET_START_BYTE) break;
|
||||
|
||||
// читаем байт в буфер входящего пакета
|
||||
_inPacket.data[_inPacket.bytesLoaded] = _ac_serial->read();
|
||||
_inPacket.data[_inPacket.bytesLoaded] = this->read();
|
||||
_inPacket.bytesLoaded++;
|
||||
|
||||
// если буфер уже полон, надо его вывалить в лог и очистить
|
||||
@@ -767,7 +823,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return;
|
||||
}
|
||||
|
||||
_inPacket.data[_inPacket.bytesLoaded] = _ac_serial->read();
|
||||
_inPacket.data[_inPacket.bytesLoaded] = this->read();
|
||||
_inPacket.bytesLoaded++;
|
||||
|
||||
// данных достаточно для заголовка
|
||||
@@ -870,7 +926,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
|
||||
case AC_PTYPE_CMD: { // команда сплиту; модуль отправляет такие команды, когда что-то хочет от сплита
|
||||
// сплит такие команды отправлять не должен, поэтому жалуемся в лог
|
||||
_debugMsg(F("Parser: packet type=0x06 received. This isn't expected."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
_debugMsg(F("Parser: packet type=0x06 received from HVAC. This isn't expected."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
// очищаем пакет
|
||||
_clearInPacket();
|
||||
_setStateMachineState(ACSM_IDLE);
|
||||
@@ -1501,10 +1557,10 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
|
||||
AirCon(){ initAC(); };
|
||||
|
||||
AirCon(esphome::uart::UARTDevice *parent) { initAC(parent); };
|
||||
AirCon(esphome::uart::UARTComponent *parent) { initAC(parent); };
|
||||
|
||||
// инициализация объекта
|
||||
void initAC(esphome::uart::UARTDevice *parent = nullptr){
|
||||
void initAC(esphome::uart::UARTComponent *parent = nullptr){
|
||||
_dataMillis = millis();
|
||||
_clearInPacket();
|
||||
_clearOutPacket();
|
||||
@@ -2099,23 +2155,19 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t step = _getNextFreeSequenceStep();
|
||||
|
||||
/**************************************************************************************/
|
||||
//step++; // - getSmallInfo
|
||||
_sequence[step].item_type = AC_SIT_FUNC;
|
||||
_sequence[step].func = &AirCon::sq_requestSmallStatus;
|
||||
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
|
||||
/*************************************** getSmallInfo request ***********************************************/
|
||||
if (!_addSequenceFuncStep(&AirCon::sq_requestSmallStatus)) {
|
||||
_debugMsg(F("getStatusSmall: getSmallInfo request sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
return false;
|
||||
}
|
||||
/*************************************** getSmallInfo control ***********************************************/
|
||||
if (!_addSequenceFuncStep(&AirCon::sq_controlSmallStatus)) {
|
||||
_debugMsg(F("getStatusSmall: getSmallInfo control sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
return false;
|
||||
}
|
||||
/**************************************************************************************/
|
||||
|
||||
/**************************************************************************************/
|
||||
step++; // - control getSmallInfo
|
||||
_sequence[step].item_type = AC_SIT_FUNC;
|
||||
_sequence[step].func = &AirCon::sq_controlSmallStatus;
|
||||
//_sequence[step].timeout = 1000;
|
||||
/**************************************************************************************/
|
||||
|
||||
_debugMsg(F("getStatusSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugMsg(F("getStatusSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2132,23 +2184,19 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t step = _getNextFreeSequenceStep();
|
||||
|
||||
/**************************************************************************************/
|
||||
//step++; // - getBigInfo
|
||||
_sequence[step].item_type = AC_SIT_FUNC;
|
||||
_sequence[step].func = &AirCon::sq_requestBigStatus;
|
||||
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
|
||||
/*************************************** getBigInfo request ***********************************************/
|
||||
if (!_addSequenceFuncStep(&AirCon::sq_requestBigStatus)) {
|
||||
_debugMsg(F("getStatusSmall: getBigInfo request sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
return false;
|
||||
}
|
||||
/*************************************** getBigInfo control ***********************************************/
|
||||
if (!_addSequenceFuncStep(&AirCon::sq_controlBigStatus)) {
|
||||
_debugMsg(F("getStatusSmall: getBigInfo control sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
return false;
|
||||
}
|
||||
/**************************************************************************************/
|
||||
|
||||
/**************************************************************************************/
|
||||
step++; // - control getSmallInfo
|
||||
_sequence[step].item_type = AC_SIT_FUNC;
|
||||
_sequence[step].func = &AirCon::sq_controlBigStatus;
|
||||
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
|
||||
/**************************************************************************************/
|
||||
|
||||
_debugMsg(F("getStatusBig: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugMsg(F("getStatusBig: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2170,7 +2218,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return false;
|
||||
}
|
||||
|
||||
_debugMsg(F("getStatusBigAndSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugMsg(F("getStatusBigAndSmall: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2194,7 +2242,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return false;
|
||||
};
|
||||
|
||||
_debugMsg(F("startupSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugMsg(F("startupSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2222,22 +2270,16 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t step = _getNextFreeSequenceStep();
|
||||
|
||||
/**************************************************************************************/
|
||||
//step++; // - set params
|
||||
_sequence[step].item_type = AC_SIT_FUNC;
|
||||
_sequence[step].func = &AirCon::sq_requestDoCommand;
|
||||
// так как в структуре команды нет указателей, то простое присваивание возможно
|
||||
_sequence[step].cmd = *cmd;
|
||||
//_sequence[step].timeout = 0; // пусть будет таймаут по-умолчанию
|
||||
/**************************************************************************************/
|
||||
|
||||
/**************************************************************************************/
|
||||
step++; // - control of params setting
|
||||
_sequence[step].item_type = AC_SIT_FUNC;
|
||||
_sequence[step].func = &AirCon::sq_controlDoCommand;
|
||||
//_sequence[step].timeout = 1000;
|
||||
/*************************************** set params request ***********************************************/
|
||||
if (!_addSequenceFuncStep(&AirCon::sq_requestDoCommand, cmd)) {
|
||||
_debugMsg(F("getStatusSmall: getBigInfo request sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
return false;
|
||||
}
|
||||
/*************************************** set params control ***********************************************/
|
||||
if (!_addSequenceFuncStep(&AirCon::sq_controlDoCommand)) {
|
||||
_debugMsg(F("getStatusSmall: getBigInfo control sequence step fail."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
return false;
|
||||
}
|
||||
/**************************************************************************************/
|
||||
|
||||
// добавление финального запроса маленького статусного пакета в последовательность команд
|
||||
@@ -2246,7 +2288,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
return false;
|
||||
}
|
||||
|
||||
_debugMsg(F("commandSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugMsg(F("commandSequence: loaded to sequence"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2266,7 +2308,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
// добавляем команду в последовательность
|
||||
if (!commandSequence(&cmd)) return false;
|
||||
|
||||
_debugMsg(F("powerSequence: loaded (power = %02X)"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, pwr);
|
||||
_debugMsg(F("powerSequence: loaded (power = %02X)"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, pwr);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2284,14 +2326,6 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
};
|
||||
|
||||
void loop() override {
|
||||
if ((millis()-_dataMillis) > 3000){
|
||||
_dataMillis = millis();
|
||||
_debugMsg(F("current status:"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugMsg(F(" _hw_initialized = %02X"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_hw_initialized);
|
||||
_debugMsg(F(" _has_connection = %02X"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_has_connection);
|
||||
_debugMsg(F(" _ac_state = %02X"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_ac_state);
|
||||
}
|
||||
|
||||
if (!get_hw_initialized()) return;
|
||||
|
||||
/// отрабатываем состояния конечного автомата
|
||||
@@ -2321,14 +2355,22 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
||||
if ((millis()-_dataMillis) > _update_period){
|
||||
_dataMillis = millis();
|
||||
|
||||
_debugMsg(F("update period:"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__);
|
||||
_debugMsg(F(" _hw_initialized = %02X)"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_hw_initialized);
|
||||
_debugMsg(F(" _has_connection = %02X)"), ESPHOME_LOG_LEVEL_DEBUG, __LINE__, this->_has_connection);
|
||||
// обычный wifi-модуль запрашивает маленький пакет статуса
|
||||
// но нам никто не мешает запрашивать и большой и маленький, чтобы чаще обновлять комнатную температуру
|
||||
// делаем этот запросом только в случае, если есть коннект с кондиционером
|
||||
if (get_has_connection()) getStatusBigAndSmall();
|
||||
}
|
||||
|
||||
/*
|
||||
// это экспериментальная секция для отладки функционала
|
||||
static uint32_t debug_millis = millis();
|
||||
if (millis()-debug_millis > 10000){
|
||||
debug_millis = millis();
|
||||
//_debugMsg(F("Test!"), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||
//if (_current_ac_state.power == AC_POWER_OFF) powerSequence(AC_POWER_ON);
|
||||
//else powerSequence(AC_POWER_OFF);
|
||||
}
|
||||
*/
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -90,6 +90,10 @@ async def to_code(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))
|
||||
|
||||
cg.add(var.set_period(config[CONF_PERIOD].total_milliseconds))
|
||||
cg.add(var.set_show_action(config[CONF_SHOW_ACTION]))
|
||||
if CONF_SUPPORTED_MODES in config:
|
||||
|
||||
Reference in New Issue
Block a user