mirror of
https://github.com/GrKoR/esphome_aux_ac_component.git
synced 2025-12-11 22:17:08 +03:00
formatting + horizontal louvers patch
This commit is contained in:
@@ -415,9 +415,12 @@ enum ac_louver_V : uint8_t {
|
|||||||
// Горизонтальные жалюзи. В протоколе зашита возможность двигать ими по всякому, но должна быть такая возможность на уровне железа.
|
// Горизонтальные жалюзи. В протоколе зашита возможность двигать ими по всякому, но должна быть такая возможность на уровне железа.
|
||||||
// горизонтальные жалюзи выставлять в определенное положение не вышло, протестировано.
|
// горизонтальные жалюзи выставлять в определенное положение не вышло, протестировано.
|
||||||
#define AC_LOUVERH_MASK 0b11100000
|
#define AC_LOUVERH_MASK 0b11100000
|
||||||
enum ac_louver_H : uint8_t { AC_LOUVERH_SWING_LEFTRIGHT = 0x00,
|
enum ac_louver_H : uint8_t {
|
||||||
AC_LOUVERH_OFF = 0xE0,
|
AC_LOUVERH_SWING_LEFTRIGHT = 0x00,
|
||||||
AC_LOUVERH_UNTOUCHED = 0xFF };
|
AC_LOUVERH_OFF_AUX = 0x20, // 0b00100000
|
||||||
|
AC_LOUVERH_OFF_ALTERNATIVE = 0xE0, // 0b11100000 - по коду везде кроме проверок использую его, так как у него все три бита в 1
|
||||||
|
AC_LOUVERH_UNTOUCHED = 0xFF
|
||||||
|
};
|
||||||
|
|
||||||
struct ac_louver {
|
struct ac_louver {
|
||||||
ac_louver_H louver_h;
|
ac_louver_H louver_h;
|
||||||
@@ -547,6 +550,16 @@ struct ac_command_t {
|
|||||||
|
|
||||||
typedef ac_command_t ac_state_t; // текущее состояние параметров кондея можно хранить в таком же формате, как и комманды
|
typedef ac_command_t ac_state_t; // текущее состояние параметров кондея можно хранить в таком же формате, как и комманды
|
||||||
|
|
||||||
|
// Структура для хранения последних полученных от сплита информационных пакетов в сыром виде
|
||||||
|
// Нужно до тех пор, пока весь функционал не разберем в структуру статуса.
|
||||||
|
// Используем для проверки реакции сплита на команды (так отлавливаем разные версии протокола общения wifi-модуля с кондиционером)
|
||||||
|
// Каждый пакет имеет поле msec. Если оно равно нулю, значит пакеты еще не принимались. По этому же полю можно смотреть, как давно
|
||||||
|
// принималась информация от кондиционера, делать вывод об отвале и рапортовать об ошибке.
|
||||||
|
struct ac_last_raw_data {
|
||||||
|
packet_t last_small_info_packet;
|
||||||
|
packet_t last_big_info_packet;
|
||||||
|
};
|
||||||
|
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
//************************************************ КОНЕЦ ПАРАМЕТРОВ РАБОТЫ КОНДИЦИОНЕРА **************************************************************
|
//************************************************ КОНЕЦ ПАРАМЕТРОВ РАБОТЫ КОНДИЦИОНЕРА **************************************************************
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
@@ -697,6 +710,9 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
// пакет для тестирования всякой фигни
|
// пакет для тестирования всякой фигни
|
||||||
packet_t _outTestPacket;
|
packet_t _outTestPacket;
|
||||||
|
|
||||||
|
// сырые данные последних полученных большого и маленького информационных пакетов
|
||||||
|
ac_last_raw_data _last_raw_data;
|
||||||
|
|
||||||
// последовательность пакетов текущий шаг в последовательности
|
// последовательность пакетов текущий шаг в последовательности
|
||||||
sequence_item_t _sequence[AC_SEQUENCE_MAX_LEN];
|
sequence_item_t _sequence[AC_SEQUENCE_MAX_LEN];
|
||||||
uint8_t _sequence_current_step;
|
uint8_t _sequence_current_step;
|
||||||
@@ -891,6 +907,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
cmd->temp_outbound = 0;
|
cmd->temp_outbound = 0;
|
||||||
cmd->temp_compressor = 0;
|
cmd->temp_compressor = 0;
|
||||||
cmd->realFanSpeed = AC_REAL_FAN_UNTOUCHED;
|
cmd->realFanSpeed = AC_REAL_FAN_UNTOUCHED;
|
||||||
|
cmd->invertor_power = 0;
|
||||||
|
cmd->defrost = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// очистка буфера размером AC_BUFFER_SIZE
|
// очистка буфера размером AC_BUFFER_SIZE
|
||||||
@@ -982,7 +1000,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
// но потом понял, что у нас пакеты уходят не только когда надо отвечать, но и мы можем быть инициаторами
|
// но потом понял, что у нас пакеты уходят не только когда надо отвечать, но и мы можем быть инициаторами
|
||||||
// поэтому вызов отправки тут пригодится
|
// поэтому вызов отправки тут пригодится
|
||||||
if (_outPacket.msec > 0) _setStateMachineState(ACSM_SENDING_PACKET);
|
if (_outPacket.msec > 0) _setStateMachineState(ACSM_SENDING_PACKET);
|
||||||
// иначе просто выходим
|
// больше дел нет - выходим
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1143,8 +1161,16 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case AC_PTYPE_INFO: { // информационный пакет
|
case AC_PTYPE_INFO: { // информационный пакет
|
||||||
// смотрим тип поступившего пакета по второму байту тела
|
|
||||||
_debugMsg(F("Parser: status packet received"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
_debugMsg(F("Parser: status packet received"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||||
|
// смотрим тип поступившего пакета по второму байту тела
|
||||||
|
// но вначале проверяем, что такое тело вообще есть
|
||||||
|
if ((_inPacket.body == nullptr) || (_inPacket.bytesLoaded < AC_HEADER_SIZE + 4) || (_inPacket.header->body_length < 2)) {
|
||||||
|
_debugMsg(F("Parser: packet type=0x07 without body. Error!"), ESPHOME_LOG_LEVEL_WARN, __LINE__);
|
||||||
|
_clearInPacket();
|
||||||
|
_setStateMachineState(ACSM_IDLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// теперь можно проверять второй байт тела пакета
|
||||||
switch (_inPacket.body[1]) {
|
switch (_inPacket.body[1]) {
|
||||||
case AC_CMD_STATUS_SMALL: { // маленький пакет статуса кондиционера
|
case AC_CMD_STATUS_SMALL: { // маленький пакет статуса кондиционера
|
||||||
_debugMsg(F("Parser: status packet type = small"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
_debugMsg(F("Parser: status packet type = small"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||||
@@ -1554,7 +1580,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
pack->header->start_byte = AC_PACKET_START_BYTE;
|
pack->header->start_byte = AC_PACKET_START_BYTE;
|
||||||
pack->header->wifi = AC_PACKET_ANSWER; // для исходящего пакета ставим признак ответа
|
pack->header->wifi = AC_PACKET_ANSWER; // для исходящего пакета ставим признак ответа
|
||||||
pack->header->packet_type = AC_PTYPE_CMD;
|
pack->header->packet_type = AC_PTYPE_CMD;
|
||||||
pack->header->body_length = 15; // тело команды 15 байт, как у Small status
|
pack->header->body_length = 0x0F; // тело команды 15 (0x0F) байт, как у Small status
|
||||||
pack->body = &(pack->data[AC_HEADER_SIZE]);
|
pack->body = &(pack->data[AC_HEADER_SIZE]);
|
||||||
pack->body[0] = AC_CMD_SET_PARAMS; // устанавливаем параметры
|
pack->body[0] = AC_CMD_SET_PARAMS; // устанавливаем параметры
|
||||||
pack->body[1] = 0x01; // он всегда 0x01
|
pack->body[1] = 0x01; // он всегда 0x01
|
||||||
@@ -1616,6 +1642,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
if (cmd->power != AC_POWER_UNTOUCHED) {
|
if (cmd->power != AC_POWER_UNTOUCHED) {
|
||||||
pack->body[10] = (pack->body[10] & ~AC_POWER_MASK) | cmd->power;
|
pack->body[10] = (pack->body[10] & ~AC_POWER_MASK) | cmd->power;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// просушка
|
||||||
if (cmd->clean != AC_CLEAN_UNTOUCHED) {
|
if (cmd->clean != AC_CLEAN_UNTOUCHED) {
|
||||||
pack->body[10] = (pack->body[10] & ~AC_CLEAN_MASK) | cmd->clean;
|
pack->body[10] = (pack->body[10] & ~AC_CLEAN_MASK) | cmd->clean;
|
||||||
}
|
}
|
||||||
@@ -1682,8 +1710,13 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
relevant = (relevant && (_inPacket.body[0] == 0x01));
|
relevant = (relevant && (_inPacket.body[0] == 0x01));
|
||||||
relevant = (relevant && (_inPacket.body[1] == AC_CMD_STATUS_SMALL));
|
relevant = (relevant && (_inPacket.body[1] == AC_CMD_STATUS_SMALL));
|
||||||
|
|
||||||
// если пакет подходит, значит можно переходить к следующему шагу
|
// если пакет подходит...
|
||||||
if (relevant) {
|
if (relevant) {
|
||||||
|
// ...значит можно переходить к следующему шагу
|
||||||
|
// так как пакет корректный, то его можно скопировать в последние полученные пакеты
|
||||||
|
_copyPacket(&_last_raw_data.last_small_info_packet, &_inPacket);
|
||||||
|
|
||||||
|
// отчитываемся в лог и переходим к следующему шагу
|
||||||
_debugMsg(F("Sequence [step %u]: correct small status packet received"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, _sequence_current_step);
|
_debugMsg(F("Sequence [step %u]: correct small status packet received"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, _sequence_current_step);
|
||||||
_sequence_current_step++;
|
_sequence_current_step++;
|
||||||
} else {
|
} else {
|
||||||
@@ -1734,8 +1767,13 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
relevant = (relevant && (_inPacket.body[0] == 0x01));
|
relevant = (relevant && (_inPacket.body[0] == 0x01));
|
||||||
relevant = (relevant && (_inPacket.body[1] == AC_CMD_STATUS_BIG));
|
relevant = (relevant && (_inPacket.body[1] == AC_CMD_STATUS_BIG));
|
||||||
|
|
||||||
// если пакет подходит, значит можно переходить к следующему шагу
|
// если пакет подходит...
|
||||||
if (relevant) {
|
if (relevant) {
|
||||||
|
// ...значит можно переходить к следующему шагу
|
||||||
|
// так как пакет корректный, то его можно скопировать в последние полученные пакеты
|
||||||
|
_copyPacket(&_last_raw_data.last_big_info_packet, &_inPacket);
|
||||||
|
|
||||||
|
// отчитываемся в лог и переходим к следующему шагу
|
||||||
_debugMsg(F("Sequence [step %u]: correct big status packet received"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, _sequence_current_step);
|
_debugMsg(F("Sequence [step %u]: correct big status packet received"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, _sequence_current_step);
|
||||||
_sequence_current_step++;
|
_sequence_current_step++;
|
||||||
} else {
|
} else {
|
||||||
@@ -1912,6 +1950,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
_clearInPacket();
|
_clearInPacket();
|
||||||
_clearOutPacket();
|
_clearOutPacket();
|
||||||
_clearPacket(&_outTestPacket);
|
_clearPacket(&_outTestPacket);
|
||||||
|
_clearPacket(&_last_raw_data.last_big_info_packet);
|
||||||
|
_clearPacket(&_last_raw_data.last_small_info_packet);
|
||||||
|
|
||||||
_setStateMachineState(ACSM_IDLE);
|
_setStateMachineState(ACSM_IDLE);
|
||||||
_ac_serial = parent;
|
_ac_serial = parent;
|
||||||
@@ -1961,9 +2001,9 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
void stateChanged() {
|
void stateChanged() {
|
||||||
_debugMsg(F("State changed, let's publish it."), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
_debugMsg(F("State changed, let's publish it."), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
|
||||||
|
|
||||||
// TODO: сейчас экшины рассчётные и могут не отражать реального положения дел.
|
// сейчас экшины рассчётные и могут не отражать реального положения дел, но других вариантов не придумалось
|
||||||
// В протоколе расшифрованы байты, позволяющие выводить реальный экшн. Требуется исправить.
|
if (_is_invertor) {
|
||||||
if (_is_invertor) { // анализ режима для инвертора, точнее потому что использует показания мощности инвертора
|
// анализ режима для инвертора точнее потому, что использует показания мощности инвертора
|
||||||
static uint32_t timerInv = 0;
|
static uint32_t timerInv = 0;
|
||||||
if (_current_ac_state.invertor_power == 0) { // инвертор выключен
|
if (_current_ac_state.invertor_power == 0) { // инвертор выключен
|
||||||
timerInv = millis();
|
timerInv = millis();
|
||||||
@@ -2010,7 +2050,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
this->action = climate::CLIMATE_ACTION_FAN; // другие режимы - вентиляция
|
this->action = climate::CLIMATE_ACTION_FAN; // другие режимы - вентиляция
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else { // if(_is_invertor)
|
||||||
|
// для on-off сплита рассчет экшена упрощен
|
||||||
if (_current_ac_state.realFanSpeed == AC_REAL_FAN_OFF &&
|
if (_current_ac_state.realFanSpeed == AC_REAL_FAN_OFF &&
|
||||||
_current_ac_state.power == AC_POWER_OFF) {
|
_current_ac_state.power == AC_POWER_OFF) {
|
||||||
this->action = climate::CLIMATE_ACTION_OFF; // значит кондей не работает
|
this->action = climate::CLIMATE_ACTION_OFF; // значит кондей не работает
|
||||||
@@ -2018,7 +2059,6 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
int16_t delta_temp = _current_ac_state.temp_ambient - _current_ac_state.temp_inbound; // разность температуры между комнатной и входящей
|
int16_t delta_temp = _current_ac_state.temp_ambient - _current_ac_state.temp_inbound; // разность температуры между комнатной и входящей
|
||||||
if (delta_temp > 0 && delta_temp < 2 &&
|
if (delta_temp > 0 && delta_temp < 2 &&
|
||||||
(_current_ac_state.realFanSpeed == AC_REAL_FAN_OFF ||
|
(_current_ac_state.realFanSpeed == AC_REAL_FAN_OFF ||
|
||||||
_current_ac_state.realFanSpeed == AC_REAL_FAN_MUTE ||
|
|
||||||
_current_ac_state.realFanSpeed == AC_REAL_FAN_MUTE)) {
|
_current_ac_state.realFanSpeed == AC_REAL_FAN_MUTE)) {
|
||||||
this->action = climate::CLIMATE_ACTION_DRYING; // ОСУШЕНИЕ
|
this->action = climate::CLIMATE_ACTION_DRYING; // ОСУШЕНИЕ
|
||||||
} else if (_current_ac_state.realFanSpeed != AC_REAL_FAN_OFF &&
|
} else if (_current_ac_state.realFanSpeed != AC_REAL_FAN_OFF &&
|
||||||
@@ -2214,7 +2254,15 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
if (_current_ac_state.power == AC_POWER_ON) {
|
if (_current_ac_state.power == AC_POWER_ON) {
|
||||||
if (_current_ac_state.louver.louver_h == AC_LOUVERH_SWING_LEFTRIGHT && _current_ac_state.louver.louver_v == AC_LOUVERV_OFF) {
|
if (_current_ac_state.louver.louver_h == AC_LOUVERH_SWING_LEFTRIGHT && _current_ac_state.louver.louver_v == AC_LOUVERV_OFF) {
|
||||||
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
||||||
} else if (_current_ac_state.louver.louver_h == AC_LOUVERH_OFF && _current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) {
|
} else if (_current_ac_state.louver.louver_h == AC_LOUVERH_OFF_AUX && _current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) {
|
||||||
|
// TODO: КОСТЫЛЬ!
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||||
|
} else if (_current_ac_state.louver.louver_h == AC_LOUVERH_OFF_ALTERNATIVE && _current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) {
|
||||||
|
// TODO: КОСТЫЛЬ!
|
||||||
|
// временно сделал так. Сделать нормально - это надо подумать.
|
||||||
|
// На AUX и многих других марках выключенный режим горизонтальных жалюзи равен 0x20, а на ROVEX и Royal Clima 0xE0
|
||||||
|
// Из-за этого происходил сброс на OFF во фронтенде Home Assistant. Пришлось городить это.
|
||||||
|
// Надо как-то изящнее решить эту историю
|
||||||
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||||
} else if (_current_ac_state.louver.louver_h == AC_LOUVERH_SWING_LEFTRIGHT && _current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) {
|
} else if (_current_ac_state.louver.louver_h == AC_LOUVERH_SWING_LEFTRIGHT && _current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) {
|
||||||
this->swing_mode = climate::CLIMATE_SWING_BOTH;
|
this->swing_mode = climate::CLIMATE_SWING_BOTH;
|
||||||
@@ -2623,7 +2671,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
// But the ROVEX IR-remote does not provide this features. Therefore this features haven't been tested.
|
// But the ROVEX IR-remote does not provide this features. Therefore this features haven't been tested.
|
||||||
// May be suitable for other models of AUX-based ACs.
|
// May be suitable for other models of AUX-based ACs.
|
||||||
case climate::CLIMATE_SWING_OFF:
|
case climate::CLIMATE_SWING_OFF:
|
||||||
cmd.louver.louver_h = AC_LOUVERH_OFF;
|
cmd.louver.louver_h = AC_LOUVERH_OFF_ALTERNATIVE;
|
||||||
cmd.louver.louver_v = AC_LOUVERV_OFF;
|
cmd.louver.louver_v = AC_LOUVERV_OFF;
|
||||||
hasCommand = true;
|
hasCommand = true;
|
||||||
this->swing_mode = swingmode;
|
this->swing_mode = swingmode;
|
||||||
@@ -2637,7 +2685,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case climate::CLIMATE_SWING_VERTICAL:
|
case climate::CLIMATE_SWING_VERTICAL:
|
||||||
cmd.louver.louver_h = AC_LOUVERH_OFF;
|
cmd.louver.louver_h = AC_LOUVERH_OFF_ALTERNATIVE;
|
||||||
cmd.louver.louver_v = AC_LOUVERV_SWING_UPDOWN;
|
cmd.louver.louver_v = AC_LOUVERV_SWING_UPDOWN;
|
||||||
hasCommand = true;
|
hasCommand = true;
|
||||||
this->swing_mode = swingmode;
|
this->swing_mode = swingmode;
|
||||||
|
|||||||
Reference in New Issue
Block a user