From 7a4bacfb9ad9d2dbbcc4d04b82e4e444ab155228 Mon Sep 17 00:00:00 2001 From: nonitex <34610334+nonitex@users.noreply.github.com> Date: Mon, 20 Oct 2025 03:52:29 +0200 Subject: [PATCH 1/7] Preserve swing mode and vertical vane position when toggling the other axis Added helper functions to normalize louver states for consistent swing detection across different AUX models. Updated swing mode logic to utilize these helpers for improved clarity and functionality. --- components/aux_ac/aux_ac.h | 75 +++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/components/aux_ac/aux_ac.h b/components/aux_ac/aux_ac.h index 88d0015..e2e8eee 100644 --- a/components/aux_ac/aux_ac.h +++ b/components/aux_ac/aux_ac.h @@ -2416,6 +2416,22 @@ namespace esphome bool get_hw_initialized() { return _hw_initialized; }; bool get_has_connection() { return _has_connection; }; + // --- Helper functions for consistent louver interpretation --- + // Some AUX-based models use 0x20 for "horizontal off", while others (e.g., ROVEX, Royal Clima) use 0xE0. + // These helpers normalize those differences so swing detection stays consistent. + // Keeping both encodings here replaces the old workaround that caused HA to jump back to OFF + // when horizontal was swinging and vertical was fixed. + + static inline bool is_h_off(uint8_t h) { + return (h == AC_LOUVERH_OFF_AUX) || (h == AC_LOUVERH_OFF_ALTERNATIVE); + } + static inline bool is_h_swing(uint8_t h) { + return (h == AC_LOUVERH_SWING_LEFTRIGHT); + } + static inline bool is_v_swing(uint8_t v) { + return (v == AC_LOUVERV_SWING_UPDOWN); + } + // возвращает, есть ли елементы в последовательности команд bool hasSequence() { @@ -2738,29 +2754,25 @@ namespace esphome /*************************** LOUVERs ***************************/ this->swing_mode = climate::CLIMATE_SWING_OFF; - 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) - { - this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; - } - 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; - } - else if (_current_ac_state.louver.louver_h == AC_LOUVERH_SWING_LEFTRIGHT && _current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) - { + + if (_current_ac_state.power == AC_POWER_ON) { + const uint8_t h = _current_ac_state.louver.louver_h; + const uint8_t v = _current_ac_state.louver.louver_v; + + const bool hSwing = is_h_swing(h); + const bool hOff = is_h_off(h); + const bool vSwing = is_v_swing(v); + + if (hSwing && vSwing) { this->swing_mode = climate::CLIMATE_SWING_BOTH; + } else if (hSwing) { + // Horizontal swings even if vertical is fixed to a position + this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; + } else if (vSwing && hOff) { + // Vertical swings while horizontal is not swinging + this->swing_mode = climate::CLIMATE_SWING_VERTICAL; + } else { + this->swing_mode = climate::CLIMATE_SWING_OFF; } } @@ -3191,8 +3203,16 @@ namespace esphome // 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. case climate::CLIMATE_SWING_OFF: + // Stop BOTH axes, but don't disturb vertical if it was already fixed (2..6) cmd.louver.louver_h = AC_LOUVERH_OFF_ALTERNATIVE; - cmd.louver.louver_v = AC_LOUVERV_OFF; + if (_current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) { + // If vertical was swinging, stop it. + cmd.louver.louver_v = AC_LOUVERV_OFF; + } else { + // Keep existing fixed position (2..6). + cmd.louver.louver_v = _current_ac_state.louver.louver_v; + } + hasCommand = true; this->swing_mode = swingmode; break; @@ -3213,7 +3233,12 @@ namespace esphome case climate::CLIMATE_SWING_HORIZONTAL: cmd.louver.louver_h = AC_LOUVERH_SWING_LEFTRIGHT; - cmd.louver.louver_v = AC_LOUVERV_OFF; + // Stop vertical only if it was swinging; otherwise preserve prior fixed position + if (_current_ac_state.louver.louver_v == AC_LOUVERV_SWING_UPDOWN) { + cmd.louver.louver_v = AC_LOUVERV_OFF; + } else { + cmd.louver.louver_v = _current_ac_state.louver.louver_v; + } hasCommand = true; this->swing_mode = swingmode; break; @@ -3899,4 +3924,4 @@ namespace esphome }; } // namespace aux_ac -} // namespace esphome \ No newline at end of file +} // namespace esphome From 5f9d2c0c0fa0737e2f40b6fa825855b2c928cae0 Mon Sep 17 00:00:00 2001 From: GrKoR Date: Wed, 26 Nov 2025 17:38:20 -0800 Subject: [PATCH 2/7] fix: esphome 2025.11.0 breacing changes --- components/aux_ac/aux_ac.h | 219 +++++++++++++++++++++++++++++++++++-- 1 file changed, 208 insertions(+), 11 deletions(-) diff --git a/components/aux_ac/aux_ac.h b/components/aux_ac/aux_ac.h index 88d0015..20876d3 100644 --- a/components/aux_ac/aux_ac.h +++ b/components/aux_ac/aux_ac.h @@ -15,6 +15,7 @@ #include "esphome/components/uart/uart.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include "esphome/core/version.h" #ifndef USE_ARDUINO using String = std::string; @@ -43,6 +44,11 @@ namespace esphome using climate::ClimatePreset; using climate::ClimateSwingMode; using climate::ClimateTraits; +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + using climate::ClimateModeMask; + using climate::ClimateSwingModeMask; + using climate::ClimatePresetMask; +#endif //**************************************************************************************************************************************************** //**************************************************** Packet logger configuration ******************************************************************* @@ -852,12 +858,19 @@ namespace esphome // как "в простое" (IDLE) bool _is_inverter = false; - // поддерживаемые кондиционером опции +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + ClimateModeMask _supported_modes{}; + ClimateSwingModeMask _supported_swing_modes{}; + ClimatePresetMask _supported_presets{}; + std::vector _supported_custom_fan_modes{}; + std::vector _supported_custom_presets{}; +#else std::set _supported_modes{}; std::set _supported_swing_modes{}; std::set _supported_presets{}; std::set _supported_custom_presets{}; std::set _supported_custom_fan_modes{}; +#endif // The capabilities of the climate device // Шаблон параметров отображения виджета @@ -2391,11 +2404,16 @@ namespace esphome // первоначальная инициализация this->preset = climate::CLIMATE_PRESET_NONE; +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + this->clear_custom_preset_(); + this->clear_custom_fan_mode_(); +#else this->custom_preset = (std::string) ""; + this->custom_fan_mode = (std::string) ""; +#endif this->mode = climate::CLIMATE_MODE_OFF; this->action = climate::CLIMATE_ACTION_IDLE; this->fan_mode = climate::CLIMATE_FAN_LOW; - this->custom_fan_mode = (std::string) ""; }; float get_setup_priority() const override { return esphome::setup_priority::DATA; } @@ -2619,15 +2637,24 @@ namespace esphome switch (_current_ac_state.fanTurbo) { case AC_FANTURBO_ON: - // if ((_current_ac_state.mode == AC_MODE_HEAT) || (_current_ac_state.mode == AC_MODE_COOL)) { +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + this->set_custom_fan_mode_(Constants::TURBO.c_str()); +#else this->custom_fan_mode = Constants::TURBO; - //} +#endif break; case AC_FANTURBO_OFF: default: +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + if (this->has_custom_fan_mode()) { + if (strcmp(this->get_custom_fan_mode(), Constants::TURBO.c_str()) == 0) + this->clear_custom_fan_mode_(); + } +#else if (this->custom_fan_mode == Constants::TURBO) this->custom_fan_mode = (std::string) ""; +#endif break; } @@ -2639,15 +2666,24 @@ namespace esphome switch (_current_ac_state.fanMute) { case AC_FANMUTE_ON: - // if (_current_ac_state.mode == AC_MODE_FAN) { +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + this->set_custom_fan_mode_(Constants::MUTE.c_str()); +#else this->custom_fan_mode = Constants::MUTE; - //} +#endif break; case AC_FANMUTE_OFF: default: +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + if (this->has_custom_fan_mode()) { + if (strcmp(this->get_custom_fan_mode(), Constants::MUTE.c_str()) == 0) + this->clear_custom_fan_mode_(); + } +#else if (this->custom_fan_mode == Constants::MUTE) this->custom_fan_mode = (std::string) ""; +#endif break; } @@ -2659,14 +2695,25 @@ namespace esphome if (_current_ac_state.health == AC_HEALTH_ON && _current_ac_state.power == AC_POWER_ON) { +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + this->set_custom_preset_(Constants::HEALTH.c_str()); +#else this->custom_preset = Constants::HEALTH; +#endif } + // AC_HEALTH_OFF + // только в том случае, если до этого пресет был установлен +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + else if (this->has_custom_preset() && strcmp(this->get_custom_preset(), Constants::HEALTH.c_str()) == 0) + { + this->clear_custom_preset_(); + } +#else else if (this->custom_preset == Constants::HEALTH) { - // AC_HEALTH_OFF - // только в том случае, если до этого пресет был установлен this->custom_preset = (std::string) ""; } +#endif _debugMsg(F("Climate HEALTH preset: %i"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, _current_ac_state.health); @@ -2694,14 +2741,25 @@ namespace esphome if (_current_ac_state.clean == AC_CLEAN_ON && _current_ac_state.power == AC_POWER_OFF) { +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + this->set_custom_preset_(Constants::CLEAN.c_str()); +#else this->custom_preset = Constants::CLEAN; +#endif } + // AC_CLEAN_OFF + // только в том случае, если до этого пресет был установлен +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + else if (this->has_custom_preset() && strcmp(this->get_custom_preset(), Constants::CLEAN.c_str()) == 0) + { + this->clear_custom_preset_(); + } +#else else if (this->custom_preset == Constants::CLEAN) { - // AC_CLEAN_OFF - // только в том случае, если до этого пресет был установлен this->custom_preset = (std::string) ""; } +#endif _debugMsg(F("Climate CLEAN preset: %i"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, _current_ac_state.clean); @@ -2724,14 +2782,25 @@ namespace esphome switch (_current_ac_state.mildew) { case AC_MILDEW_ON: +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + this->set_custom_preset_(Constants::ANTIFUNGUS.c_str()); +#else this->custom_preset = Constants::ANTIFUNGUS; +#endif break; case AC_MILDEW_OFF: default: +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + if (this->has_custom_preset() && strcmp(this->get_custom_preset(), Constants::ANTIFUNGUS.c_str()) == 0) + { + this->clear_custom_preset_(); + } +#else if (this->custom_preset == Constants::ANTIFUNGUS) this->custom_preset = (std::string) ""; break; +#endif } _debugMsg(F("Climate ANTIFUNGUS preset: %i"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, _current_ac_state.mildew); @@ -2821,10 +2890,17 @@ namespace esphome { state_str += "SLEEP"; } +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + else if (this->has_custom_preset()) + { + state_str += this->get_custom_preset(); + } +#else else if (this->custom_preset.has_value() && this->custom_preset.value().length() > 0) { state_str += this->custom_preset.value().c_str(); } +#endif else { state_str += "NONE"; @@ -3014,6 +3090,32 @@ namespace esphome break; } } +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + else if (call.has_custom_fan_mode()) + { + const char *customfanmode = call.get_custom_fan_mode(); + + if (strcmp(customfanmode, Constants::TURBO.c_str()) == 0) + { + // TURBO fan mode is suitable in COOL and HEAT modes. + // Other modes don't accept TURBO fan mode. + hasCommand = true; + cmd.fanTurbo = AC_FANTURBO_ON; + cmd.fanMute = AC_FANMUTE_OFF; + this->set_custom_fan_mode_(customfanmode); + } + else if (strcmp(customfanmode, Constants::MUTE.c_str()) == 0) + { + // MUTE fan mode is suitable in FAN mode only for Rovex air conditioner. + // In COOL mode AC receives command without any changes. + // May be other AUX-based air conditioners do the same. + hasCommand = true; + cmd.fanMute = AC_FANMUTE_ON; + cmd.fanTurbo = AC_FANTURBO_OFF; + this->set_custom_fan_mode_(customfanmode); + } + } +#else else if (call.get_custom_fan_mode().has_value()) { std::string customfanmode = *call.get_custom_fan_mode(); @@ -3055,6 +3157,7 @@ namespace esphome //} } } +#endif // Пользователь выбрал пресет if (call.get_preset().has_value()) @@ -3105,6 +3208,80 @@ namespace esphome break; } } +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + else if (call.has_custom_preset()) + { + const char *custom_preset = call.get_custom_preset(); + + if (strcmp(custom_preset, Constants::CLEAN.c_str()) == 0) + { + // режим очистки кондиционера, включается (или должен включаться) при AC_POWER_OFF + // TODO: надо отдебажить выключение этого режима + if (cmd.power == AC_POWER_OFF or _current_ac_state.power == AC_POWER_OFF) + { + hasCommand = true; + cmd.clean = AC_CLEAN_ON; + cmd.mildew = AC_MILDEW_OFF; + this->set_custom_preset_(custom_preset); + } + else + { + _debugMsg(F("CLEAN preset is suitable in POWER_OFF mode only."), ESPHOME_LOG_LEVEL_WARN, __LINE__); + } + } + else if (strcmp(custom_preset, Constants::HEALTH.c_str()) == 0) + { + if (cmd.power == AC_POWER_ON || + _current_ac_state.power == AC_POWER_ON) + { + hasCommand = true; + cmd.health = AC_HEALTH_ON; + cmd.fanTurbo = AC_FANTURBO_OFF; + cmd.fanMute = AC_FANMUTE_OFF; + cmd.sleep = AC_SLEEP_OFF; + + if (cmd.mode == AC_MODE_COOL || + cmd.mode == AC_MODE_HEAT || + cmd.mode == AC_MODE_AUTO || + _current_ac_state.mode == AC_MODE_COOL || + _current_ac_state.mode == AC_MODE_HEAT || + _current_ac_state.mode == AC_MODE_AUTO) + { + cmd.fanSpeed = AC_FANSPEED_AUTO; // зависимость от health + } + else if (cmd.mode == AC_MODE_FAN || + _current_ac_state.mode == AC_MODE_FAN) + { + cmd.fanSpeed = AC_FANSPEED_MEDIUM; // зависимость от health + } + this->set_custom_preset_(custom_preset); + } + else + { + _debugMsg(F("HEALTH preset is suitable in POWER_ON mode only."), ESPHOME_LOG_LEVEL_WARN, __LINE__); + } + } + else if (strcmp(custom_preset, Constants::ANTIFUNGUS.c_str()) == 0) + { + // включение-выключение функции "Антиплесень". + // По факту: после выключения сплита он оставляет минут на 5 открытые жалюзи и глушит вентилятор. + // Уличный блок при этом гудит и тарахтит. Возможно, прогревается теплообменник для высыхания. + // Через некоторое время внешний блок замолкает и сплит закрывает жалюзи. + + // Brokly: + // включение-выключение функции "Антиплесень". + // у меня пульт отправляет 5 посылок и на включение и на выключение, но реагирует на эту кнопку + // только в режиме POWER_OFF + + // TODO: надо уточнить, в каких режимах штатно включается этот режим у кондиционера + cmd.mildew = AC_MILDEW_ON; + cmd.clean = AC_CLEAN_OFF; // для логики пресетов + + hasCommand = true; + this->set_custom_preset_(custom_preset); + } + } +#else else if (call.get_custom_preset().has_value()) { std::string custom_preset = *call.get_custom_preset(); @@ -3178,6 +3355,7 @@ namespace esphome this->custom_preset = custom_preset; } } +#endif // User requested swing_mode change if (call.get_swing_mode().has_value()) @@ -3781,7 +3959,13 @@ namespace esphome void set_optimistic(bool optimistic) { this->_optimistic = optimistic; } bool get_optimistic() { return this->_optimistic; } - // возможно функции get и не нужны, но вроде как должны быть +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + void set_supported_modes(ClimateModeMask modes) { this->_supported_modes = modes; } + void set_supported_swing_modes(ClimateSwingModeMask modes) { this->_supported_swing_modes = modes; } + void set_supported_presets(ClimatePresetMask presets) { this->_supported_presets = presets; } + void set_custom_presets(std::initializer_list presets) { this->_supported_custom_presets = presets; } + void set_custom_fan_modes(std::initializer_list modes) { this->_supported_custom_fan_modes = modes; } +#else void set_supported_modes(const std::set &modes) { this->_supported_modes = modes; } std::set get_supported_modes() { return this->_supported_modes; } @@ -3796,6 +3980,7 @@ namespace esphome void set_custom_fan_modes(const std::set &modes) { this->_supported_custom_fan_modes = modes; } const std::set &get_supported_custom_fan_modes() { return this->_supported_custom_fan_modes; } +#endif #if defined(PRESETS_SAVING) void set_store_settings(bool store_settings) { this->_store_settings = store_settings; } @@ -3813,8 +3998,13 @@ namespace esphome // заполнение шаблона параметров отображения виджета // GK: всё же похоже правильнее это делать тут, а не в initAC() // initAC() в формируемом питоном коде вызывается до вызова aux_ac.set_supported_***() с установленными пользователем в конфиге параметрами +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + _traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + _traits.add_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); +#else _traits.set_supports_current_temperature(true); _traits.set_supports_two_point_target_temperature(false); // if the climate device's target temperature should be split in target_temperature_low and target_temperature_high instead of just the single target_temperature +#endif _traits.set_supported_modes(this->_supported_modes); _traits.set_supported_swing_modes(this->_supported_swing_modes); @@ -3843,7 +4033,14 @@ namespace esphome //_traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_SLEEP); // if the climate device supports reporting the active current action of the device with the action property. +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) + if (this->_show_action) + { + _traits.add_feature_flags(climate::CLIMATE_SUPPORTS_ACTION); + } +#else _traits.set_supports_action(this->_show_action); +#endif }; void loop() override From 5a165d3a3dcd2eb65cf65434cab166bd906f9153 Mon Sep 17 00:00:00 2001 From: GrKoR Date: Wed, 26 Nov 2025 18:58:36 -0800 Subject: [PATCH 3/7] upd: version++ --- components/aux_ac/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/aux_ac/climate.py b/components/aux_ac/climate.py index 1a9d424..a60326d 100644 --- a/components/aux_ac/climate.py +++ b/components/aux_ac/climate.py @@ -33,7 +33,7 @@ from esphome.components.climate import ( ClimateSwingMode, ) -AUX_AC_FIRMWARE_VERSION = '0.2.17' +AUX_AC_FIRMWARE_VERSION = '0.3.1' AC_PACKET_TIMEOUT_MIN = 150 AC_PACKET_TIMEOUT_MAX = 600 AC_POWER_LIMIT_MIN = 30 From 53414d8ab48efea2d38eb22fa87abbc34ccb301b Mon Sep 17 00:00:00 2001 From: GrKoR Date: Wed, 26 Nov 2025 21:14:46 -0800 Subject: [PATCH 4/7] fix: python code generator fix for custom presets & custom fan modes --- components/aux_ac/climate.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/components/aux_ac/climate.py b/components/aux_ac/climate.py index a60326d..0b0fa2c 100644 --- a/components/aux_ac/climate.py +++ b/components/aux_ac/climate.py @@ -26,12 +26,14 @@ from esphome.const import ( DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_POWER_FACTOR, STATE_CLASS_MEASUREMENT, + __version__ ) from esphome.components.climate import ( ClimateMode, ClimatePreset, ClimateSwingMode, ) +from pkg_resources import parse_version AUX_AC_FIRMWARE_VERSION = '0.3.1' AC_PACKET_TIMEOUT_MIN = 150 @@ -127,6 +129,11 @@ AirConPowerLimitationOnAction = aux_ac_ns.class_( ) +def use_new_api(): + current_version = parse_version(__version__) + return current_version >= parse_version("2025.11.0") + + def validate_packet_timeout(value): minV = AC_PACKET_TIMEOUT_MIN maxV = AC_PACKET_TIMEOUT_MAX @@ -426,10 +433,20 @@ async def to_code(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])) + if use_new_api(): + if CONF_CUSTOM_PRESETS in config: + presets = config[CONF_CUSTOM_PRESETS] + c_str_presets = [cg.RawExpression(f"aux_ac::Constants::{p}.c_str()") for p in presets] + cg.add(var.set_custom_presets(c_str_presets)) + if CONF_CUSTOM_FAN_MODES in config: + fan_modes = config[CONF_CUSTOM_FAN_MODES] + c_str_fan_modes = [cg.RawExpression(f"aux_ac::Constants::{p}.c_str()") for p in fan_modes] + cg.add(var.set_custom_fan_modes(c_str_fan_modes)) + else: + 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( From 2e0c421c9ffdd9b6ba975f34883e49aa995d9148 Mon Sep 17 00:00:00 2001 From: GrKoR Date: Wed, 26 Nov 2025 23:28:20 -0800 Subject: [PATCH 5/7] fix: removed module dependency" --- components/aux_ac/climate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/aux_ac/climate.py b/components/aux_ac/climate.py index 0b0fa2c..117f987 100644 --- a/components/aux_ac/climate.py +++ b/components/aux_ac/climate.py @@ -33,7 +33,6 @@ from esphome.components.climate import ( ClimatePreset, ClimateSwingMode, ) -from pkg_resources import parse_version AUX_AC_FIRMWARE_VERSION = '0.3.1' AC_PACKET_TIMEOUT_MIN = 150 @@ -130,8 +129,9 @@ AirConPowerLimitationOnAction = aux_ac_ns.class_( def use_new_api(): - current_version = parse_version(__version__) - return current_version >= parse_version("2025.11.0") + esphome_current_version = tuple(map(int, __version__.split('.'))) + esphome_bc_version = tuple(map(int, "2025.11.0".split('.'))) + return esphome_current_version >= esphome_bc_version def validate_packet_timeout(value): From efa0991dd074f1cc1412ba6b6ad2b26ad2855ecd Mon Sep 17 00:00:00 2001 From: GrKoR Date: Thu, 27 Nov 2025 17:57:13 -0800 Subject: [PATCH 6/7] fix: automations play() method fixed --- components/aux_ac/automation.h | 335 +++++++++++++++++++-------------- 1 file changed, 195 insertions(+), 140 deletions(-) diff --git a/components/aux_ac/automation.h b/components/aux_ac/automation.h index 50129e7..90df143 100644 --- a/components/aux_ac/automation.h +++ b/components/aux_ac/automation.h @@ -4,183 +4,238 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" -namespace esphome { -namespace aux_ac { +namespace esphome +{ + namespace aux_ac + { -// **************************************** DISPLAY ACTIONS **************************************** -template -class AirConDisplayOffAction : public Action { - public: - explicit AirConDisplayOffAction(AirCon *ac) : ac_(ac) {} + // **************************************** DISPLAY ACTIONS **************************************** + template + class AirConDisplayOffAction : public Action + { + public: + explicit AirConDisplayOffAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->displayOffSequence(); } + void play(const Ts &...x) override + { + this->ac_->displayOffSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConDisplayOnAction : public Action { - public: - explicit AirConDisplayOnAction(AirCon *ac) : ac_(ac) {} + template + class AirConDisplayOnAction : public Action + { + public: + explicit AirConDisplayOnAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->displayOnSequence(); } + void play(const Ts &...x) override + { + this->ac_->displayOnSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -// **************************************** VERTICAL LOUVER ACTIONS **************************************** -template -class AirConVLouverSwingAction : public Action { - public: - explicit AirConVLouverSwingAction(AirCon *ac) : ac_(ac) {} + // **************************************** VERTICAL LOUVER ACTIONS **************************************** + template + class AirConVLouverSwingAction : public Action + { + public: + explicit AirConVLouverSwingAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->setVLouverSwingSequence(); } + void play(const Ts &...x) override + { + this->ac_->setVLouverSwingSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConVLouverStopAction : public Action { - public: - explicit AirConVLouverStopAction(AirCon *ac) : ac_(ac) {} + template + class AirConVLouverStopAction : public Action + { + public: + explicit AirConVLouverStopAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->setVLouverStopSequence(); } + void play(const Ts &...x) override + { + this->ac_->setVLouverStopSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConVLouverTopAction : public Action { - public: - explicit AirConVLouverTopAction(AirCon *ac) : ac_(ac) {} + template + class AirConVLouverTopAction : public Action + { + public: + explicit AirConVLouverTopAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->setVLouverTopSequence(); } + void play(const Ts &...x) override + { + this->ac_->setVLouverTopSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConVLouverMiddleAboveAction : public Action { - public: - explicit AirConVLouverMiddleAboveAction(AirCon *ac) : ac_(ac) {} + template + class AirConVLouverMiddleAboveAction : public Action + { + public: + explicit AirConVLouverMiddleAboveAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->setVLouverMiddleAboveSequence(); } + void play(const Ts &...x) override + { + this->ac_->setVLouverMiddleAboveSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConVLouverMiddleAction : public Action { - public: - explicit AirConVLouverMiddleAction(AirCon *ac) : ac_(ac) {} + template + class AirConVLouverMiddleAction : public Action + { + public: + explicit AirConVLouverMiddleAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->setVLouverMiddleSequence(); } + void play(const Ts &...x) override + { + this->ac_->setVLouverMiddleSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConVLouverMiddleBelowAction : public Action { - public: - explicit AirConVLouverMiddleBelowAction(AirCon *ac) : ac_(ac) {} + template + class AirConVLouverMiddleBelowAction : public Action + { + public: + explicit AirConVLouverMiddleBelowAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->setVLouverMiddleBelowSequence(); } + void play(const Ts &...x) override + { + this->ac_->setVLouverMiddleBelowSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConVLouverBottomAction : public Action { - public: - explicit AirConVLouverBottomAction(AirCon *ac) : ac_(ac) {} + template + class AirConVLouverBottomAction : public Action + { + public: + explicit AirConVLouverBottomAction(AirCon *ac) : ac_(ac) {} - void play(Ts... x) override { this->ac_->setVLouverBottomSequence(); } + void play(const Ts &...x) override + { + this->ac_->setVLouverBottomSequence(); + } - protected: - AirCon *ac_; -}; + protected: + AirCon *ac_; + }; -template -class AirConVLouverSetAction : public Action { - public: - AirConVLouverSetAction(AirCon *ac) : ac_(ac) {} - TEMPLATABLE_VALUE(uint8_t, value); + template + class AirConVLouverSetAction : public Action + { + public: + TEMPLATABLE_VALUE(uint8_t, value); - void play(Ts... x) { - vlpos_ = this->value_.value(x...); - this->ac_->setVLouverFrontendSequence((ac_vlouver_frontend)vlpos_); - } + AirConVLouverSetAction(AirCon *ac) : ac_(ac) {}; - protected: - AirCon *ac_; - uint8_t vlpos_; -}; + void play(const Ts &...x) override + { + vlpos_ = this->value_.value(x...); + this->ac_->setVLouverFrontendSequence((ac_vlouver_frontend)vlpos_); + }; -// **************************************** SEND TEST PACKET ACTION **************************************** -template -class AirConSendTestPacketAction : public Action { - public: - explicit AirConSendTestPacketAction(AirCon *ac) : ac_(ac) {} - void set_data_template(std::function(Ts...)> func) { - this->data_func_ = func; - this->static_ = false; - } - void set_data_static(const std::vector &data) { - this->data_static_ = data; - this->static_ = true; - } + protected: + AirCon *ac_; + uint8_t vlpos_; + }; - 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); - } - } + // **************************************** SEND TEST PACKET ACTION **************************************** + template + class AirConSendTestPacketAction : public Action + { + public: + explicit AirConSendTestPacketAction(AirCon *ac) : ac_(ac) {} + void set_data_template(std::function(Ts...)> func) + { + this->data_func_ = func; + this->static_ = false; + } + void set_data_static(const std::vector &data) + { + this->data_static_ = data; + this->static_ = true; + } - protected: - AirCon *ac_; - bool static_{false}; - std::function(Ts...)> data_func_{}; - std::vector data_static_{}; -}; + void play(const Ts &...x) override + { + if (this->static_) + { + this->ac_->sendTestPacket(this->data_static_); + } + else + { + auto val = this->data_func_(x...); + this->ac_->sendTestPacket(val); + } + } -// **************************************** POWER LIMITATION ACTIONS **************************************** -template -class AirConPowerLimitationOffAction : public Action { - public: - explicit AirConPowerLimitationOffAction(AirCon *ac) : ac_(ac) {} + protected: + AirCon *ac_; + bool static_{false}; + std::function(Ts...)> data_func_{}; + std::vector data_static_{}; + }; - void play(Ts... x) override { this->ac_->powerLimitationOffSequence(); } + // **************************************** POWER LIMITATION ACTIONS **************************************** + template + class AirConPowerLimitationOffAction : public Action + { + public: + explicit AirConPowerLimitationOffAction(AirCon *ac) : ac_(ac) {} - protected: - AirCon *ac_; -}; + void play(const Ts &...x) override + { + this->ac_->powerLimitationOffSequence(); + } -template -class AirConPowerLimitationOnAction : public Action { - public: - AirConPowerLimitationOnAction(AirCon *ac) : ac_(ac) {} - TEMPLATABLE_VALUE(uint8_t, value); + protected: + AirCon *ac_; + }; - void play(Ts... x) { - this->pwr_lim_ = this->value_.value(x...); - this->ac_->powerLimitationOnSequence(this->pwr_lim_); - } + template + class AirConPowerLimitationOnAction : public Action + { + public: + TEMPLATABLE_VALUE(uint8_t, value); - protected: - AirCon *ac_; - uint8_t pwr_lim_; -}; + AirConPowerLimitationOnAction(AirCon *ac) : ac_(ac) {}; -} // namespace aux_ac -} // namespace esphome \ No newline at end of file + void play(const Ts &...x) override + { + this->pwr_lim_ = this->value_.value(x...); + this->ac_->powerLimitationOnSequence(this->pwr_lim_); + } + + protected: + AirCon *ac_; + uint8_t pwr_lim_; + }; + + } // namespace aux_ac +} // namespace esphome \ No newline at end of file From fc56fb79661d94cbd0499d6f5ec3241863eefe37 Mon Sep 17 00:00:00 2001 From: qbus00 Date: Fri, 28 Nov 2025 15:01:25 +0100 Subject: [PATCH 7/7] fix: remove TWO_POINT_TARGET_TEMPERATURE flag for ESPHome 2025.11+ --- components/aux_ac/aux_ac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/aux_ac/aux_ac.h b/components/aux_ac/aux_ac.h index 14645c1..4b7fb7b 100644 --- a/components/aux_ac/aux_ac.h +++ b/components/aux_ac/aux_ac.h @@ -4025,7 +4025,7 @@ namespace esphome // initAC() в формируемом питоном коде вызывается до вызова aux_ac.set_supported_***() с установленными пользователем в конфиге параметрами #if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) _traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); - _traits.add_feature_flags(climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + // NOT setting CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE - this device uses single target temperature #else _traits.set_supports_current_temperature(true); _traits.set_supports_two_point_target_temperature(false); // if the climate device's target temperature should be split in target_temperature_low and target_temperature_high instead of just the single target_temperature