Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion README-EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ climate:
name: AC Preset Reporter
id: ac_preset_reporter
internal: false
actual_fan_speed:
name: AC Actual Fan Speed
id: ac_actual_fan_speed
internal: false
vlouver_state:
name: AC Vertical Louvers State
id: ac_vlouver_state
Expand Down Expand Up @@ -230,9 +234,25 @@ climate:
- **inverter_power_limit_value** (*Optional*): Configuration of the power limit value sensor. All settings are the same as for the **indoor_temperature** (see description above).
It reports the current value of the power limitation function for the inverter HVAC. This sensor represents the value only after the HVAC confirms the power limitation. The value is always in the range from 30% to 100%. This is the hardware limitation.

- **preset_reporter** (*Optional*): Parameters of text sensor with current preset. 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 reporting their active presets (from **supported_presets** and **custom_presets** lists) to MQTT. This behavior has been noticed at least in version 1.20.0. In case you are using MQTT and want to receive information about active preset, you should declare this sensor in your yaml.

- **actual_fan_speed** (*Optional*): Configuration of the actual fan speed sensor. This sensor reports the real fan speed as a percentage (0-100%). All settings are the same as for the **indoor_temperature** (see description above), plus the following optional percentage overrides:
- **off_percent** (*Optional*, integer 0-100): Custom percentage for OFF speed. Default: 0%
- **mute_percent** (*Optional*, integer 0-100): Custom percentage for MUTE speed.
- **low_percent** (*Optional*, integer 0-100): Custom percentage for LOW speed.
- **mid_percent** (*Optional*, integer 0-100): Custom percentage for MID speed.
- **high_percent** (*Optional*, integer 0-100): Custom percentage for HIGH speed.
- **turbo_percent** (*Optional*, integer 0-100): Custom percentage for TURBO speed.

When percentage options are not specified, defaults are calculated based on which modes are enabled in **custom_fan_modes**:
- Neither MUTE nor TURBO: OFF=0%, LOW=33%, MID=67%, HIGH=100%
- MUTE only: OFF=0%, MUTE=25%, LOW=50%, MID=75%, HIGH=100%
- TURBO only: OFF=0%, LOW=25%, MID=50%, HIGH=75%, TURBO=100%
- Both MUTE and TURBO: OFF=0%, MUTE=20%, LOW=40%, MID=60%, HIGH=80%, TURBO=100%

This value reflects the actual fan speed reported by the AC unit, which may differ from the requested fan mode.

- **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``.
Expand Down
105 changes: 105 additions & 0 deletions components/aux_ac/aux_ac.h
Original file line number Diff line number Diff line change
Expand Up @@ -2245,7 +2245,16 @@ namespace esphome
esphome::binary_sensor::BinarySensor *sensor_display_ = nullptr;
esphome::binary_sensor::BinarySensor *sensor_defrost_ = nullptr;
esphome::text_sensor::TextSensor *sensor_preset_reporter_ = nullptr;
esphome::sensor::Sensor *sensor_actual_fan_speed_ = nullptr;
esphome::sensor::Sensor *sensor_inverter_power_limit_value_ = nullptr;

// Custom fan speed percentages (-1 means use auto-calculated default)
int8_t fan_speed_off_percent_ = -1;
int8_t fan_speed_mute_percent_ = -1;
int8_t fan_speed_low_percent_ = -1;
int8_t fan_speed_mid_percent_ = -1;
int8_t fan_speed_high_percent_ = -1;
int8_t fan_speed_turbo_percent_ = -1;
esphome::binary_sensor::BinarySensor *sensor_inverter_power_limit_state_ = nullptr;

// загружает на выполнение последовательность команд на включение/выключение табло с температурой
Expand Down Expand Up @@ -2394,7 +2403,16 @@ namespace esphome
void set_display_sensor(binary_sensor::BinarySensor *display_sensor) { sensor_display_ = display_sensor; }
void set_inverter_power_sensor(sensor::Sensor *inverter_power_sensor) { sensor_inverter_power_ = inverter_power_sensor; }
void set_preset_reporter_sensor(text_sensor::TextSensor *preset_reporter_sensor) { sensor_preset_reporter_ = preset_reporter_sensor; }
void set_actual_fan_speed_sensor(sensor::Sensor *actual_fan_speed_sensor) { sensor_actual_fan_speed_ = actual_fan_speed_sensor; }
void set_inverter_power_limit_value_sensor(sensor::Sensor *inverter_power_limit_value_sensor) { sensor_inverter_power_limit_value_ = inverter_power_limit_value_sensor; }

// Fan speed percentage setters
void set_fan_speed_off_percent(int8_t percent) { fan_speed_off_percent_ = percent; }
void set_fan_speed_mute_percent(int8_t percent) { fan_speed_mute_percent_ = percent; }
void set_fan_speed_low_percent(int8_t percent) { fan_speed_low_percent_ = percent; }
void set_fan_speed_mid_percent(int8_t percent) { fan_speed_mid_percent_ = percent; }
void set_fan_speed_high_percent(int8_t percent) { fan_speed_high_percent_ = percent; }
void set_fan_speed_turbo_percent(int8_t percent) { fan_speed_turbo_percent_ = percent; }
void set_inverter_power_limit_state_sensor(binary_sensor::BinarySensor *inverter_power_limit_state_sensor) { sensor_inverter_power_limit_state_ = inverter_power_limit_state_sensor; }

bool get_hw_initialized() { return _hw_initialized; };
Expand All @@ -2416,6 +2434,90 @@ namespace esphome
return (v == AC_LOUVERV_SWING_UPDOWN);
}

// Convert real fan speed enum to percentage (0-100%)
// Uses custom percentages if configured, otherwise calculates defaults based on
// which custom_fan_modes are enabled (MUTE and/or TURBO):
// - Neither: OFF=0%, LOW=33%, MID=67%, HIGH=100%
// - MUTE only: OFF=0%, MUTE=25%, LOW=50%, MID=75%, HIGH=100%
// - TURBO only: OFF=0%, LOW=25%, MID=50%, HIGH=75%, TURBO=100%
// - Both: OFF=0%, MUTE=20%, LOW=40%, MID=60%, HIGH=80%, TURBO=100%
inline float actual_fan_speed_to_percent(ac_realFan speed) {
// Check if custom percentage is configured for this speed level
switch (speed) {
case AC_REAL_FAN_OFF:
if (fan_speed_off_percent_ >= 0) return static_cast<float>(fan_speed_off_percent_);
return 0.0f; // OFF is always 0%
case AC_REAL_FAN_MUTE:
if (fan_speed_mute_percent_ >= 0) return static_cast<float>(fan_speed_mute_percent_);
break;
case AC_REAL_FAN_LOW:
if (fan_speed_low_percent_ >= 0) return static_cast<float>(fan_speed_low_percent_);
break;
case AC_REAL_FAN_MID:
if (fan_speed_mid_percent_ >= 0) return static_cast<float>(fan_speed_mid_percent_);
break;
case AC_REAL_FAN_HIGH:
if (fan_speed_high_percent_ >= 0) return static_cast<float>(fan_speed_high_percent_);
break;
case AC_REAL_FAN_TURBO:
if (fan_speed_turbo_percent_ >= 0) return static_cast<float>(fan_speed_turbo_percent_);
break;
default:
return 0.0f;
}

// No custom percentage set, calculate default based on enabled modes
bool mute_enabled = false;
bool turbo_enabled = false;
for (const char* mode : _supported_custom_fan_modes) {
if (strcmp(mode, Constants::MUTE) == 0) mute_enabled = true;
if (strcmp(mode, Constants::TURBO) == 0) turbo_enabled = true;
}

// Return default percentage based on enabled modes
if (mute_enabled && turbo_enabled) {
// Both enabled: MUTE=20%, LOW=40%, MID=60%, HIGH=80%, TURBO=100%
switch (speed) {
case AC_REAL_FAN_MUTE: return 20.0f;
case AC_REAL_FAN_LOW: return 40.0f;
case AC_REAL_FAN_MID: return 60.0f;
case AC_REAL_FAN_HIGH: return 80.0f;
case AC_REAL_FAN_TURBO: return 100.0f;
default: return 0.0f;
}
} else if (mute_enabled) {
// MUTE only: MUTE=25%, LOW=50%, MID=75%, HIGH=100%
switch (speed) {
case AC_REAL_FAN_MUTE: return 25.0f;
case AC_REAL_FAN_LOW: return 50.0f;
case AC_REAL_FAN_MID: return 75.0f;
case AC_REAL_FAN_HIGH: return 100.0f;
case AC_REAL_FAN_TURBO: return 100.0f; // Treat as max
default: return 0.0f;
}
} else if (turbo_enabled) {
// TURBO only: LOW=25%, MID=50%, HIGH=75%, TURBO=100%
switch (speed) {
case AC_REAL_FAN_MUTE: return 25.0f; // Treat as LOW
case AC_REAL_FAN_LOW: return 25.0f;
case AC_REAL_FAN_MID: return 50.0f;
case AC_REAL_FAN_HIGH: return 75.0f;
case AC_REAL_FAN_TURBO: return 100.0f;
default: return 0.0f;
}
} else {
// Neither: LOW=33%, MID=67%, HIGH=100%
switch (speed) {
case AC_REAL_FAN_MUTE: return 33.0f; // Treat as LOW
case AC_REAL_FAN_LOW: return 33.0f;
case AC_REAL_FAN_MID: return 67.0f;
case AC_REAL_FAN_HIGH: return 100.0f;
case AC_REAL_FAN_TURBO: return 100.0f; // Treat as max
default: return 0.0f;
}
}
}

// возвращает, есть ли елементы в последовательности команд
bool hasSequence()
{
Expand Down Expand Up @@ -2792,6 +2894,9 @@ namespace esphome
// мощность инвертора
if (sensor_inverter_power_ != nullptr)
sensor_inverter_power_->publish_state(_current_ac_state.inverter_power);
// actual fan speed as percentage
if (sensor_actual_fan_speed_ != nullptr)
sensor_actual_fan_speed_->publish_state(actual_fan_speed_to_percent(_current_ac_state.realFanSpeed));
// флаг режима разморозки
if (sensor_defrost_ != nullptr)
sensor_defrost_->publish_state(_current_ac_state.defrost);
Expand Down
46 changes: 46 additions & 0 deletions components/aux_ac/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@
CONF_PRESET_REPORTER = "preset_reporter"
ICON_PRESET_REPORTER = "mdi:format-list-group"

CONF_ACTUAL_FAN_SPEED = "actual_fan_speed"
ICON_ACTUAL_FAN_SPEED = "mdi:fan"

# Fan speed percentage configuration options
CONF_OFF_PERCENT = "off_percent"
CONF_MUTE_PERCENT = "mute_percent"
CONF_LOW_PERCENT = "low_percent"
CONF_MID_PERCENT = "mid_percent"
CONF_HIGH_PERCENT = "high_percent"
CONF_TURBO_PERCENT = "turbo_percent"

CONF_VLOUVER_STATE = "vlouver_state"
ICON_VLOUVER_STATE = "mdi:compare-vertical"

Expand Down Expand Up @@ -298,6 +309,23 @@ def output_info(config):
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
}
),
cv.Optional(CONF_ACTUAL_FAN_SPEED): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_ACTUAL_FAN_SPEED,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
cv.Optional(CONF_OFF_PERCENT): cv.int_range(min=0, max=100),
cv.Optional(CONF_MUTE_PERCENT): cv.int_range(min=0, max=100),
cv.Optional(CONF_LOW_PERCENT): cv.int_range(min=0, max=100),
cv.Optional(CONF_MID_PERCENT): cv.int_range(min=0, max=100),
cv.Optional(CONF_HIGH_PERCENT): cv.int_range(min=0, max=100),
cv.Optional(CONF_TURBO_PERCENT): cv.int_range(min=0, max=100),
}
),
cv.Optional(CONF_INVERTER_POWER_LIMIT_VALUE): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_INVERTER_POWER_LIMIT_VALUE,
Expand Down Expand Up @@ -406,6 +434,24 @@ async def to_code(config):
sens = await text_sensor.new_text_sensor(conf)
cg.add(var.set_preset_reporter_sensor(sens))

if CONF_ACTUAL_FAN_SPEED in config:
conf = config[CONF_ACTUAL_FAN_SPEED]
sens = await sensor.new_sensor(conf)
cg.add(var.set_actual_fan_speed_sensor(sens))
# Set custom percentages if provided
if CONF_OFF_PERCENT in conf:
cg.add(var.set_fan_speed_off_percent(conf[CONF_OFF_PERCENT]))
if CONF_MUTE_PERCENT in conf:
cg.add(var.set_fan_speed_mute_percent(conf[CONF_MUTE_PERCENT]))
if CONF_LOW_PERCENT in conf:
cg.add(var.set_fan_speed_low_percent(conf[CONF_LOW_PERCENT]))
if CONF_MID_PERCENT in conf:
cg.add(var.set_fan_speed_mid_percent(conf[CONF_MID_PERCENT]))
if CONF_HIGH_PERCENT in conf:
cg.add(var.set_fan_speed_high_percent(conf[CONF_HIGH_PERCENT]))
if CONF_TURBO_PERCENT in conf:
cg.add(var.set_fan_speed_turbo_percent(conf[CONF_TURBO_PERCENT]))

if CONF_INVERTER_POWER_LIMIT_VALUE in config:
conf = config[CONF_INVERTER_POWER_LIMIT_VALUE]
sens = await sensor.new_sensor(conf)
Expand Down
12 changes: 12 additions & 0 deletions examples/advanced/ac_common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ climate:
name: ${upper_devicename} Preset Reporter
id: ${devicename}_preset_reporter
internal: false
actual_fan_speed:
name: ${upper_devicename} Actual Fan Speed
id: ${devicename}_actual_fan_speed
internal: false
# Optional: customize the percentage for each fan speed level
# These are the defaults when both MUTE and TURBO are enabled:
off_percent: 0
mute_percent: 20
low_percent: 40
mid_percent: 60
high_percent: 80
turbo_percent: 100
vlouver_state:
name: ${upper_devicename} VLouvers State
id: ${devicename}_vlouver_state
Expand Down