v.0.2.9: new feature - power limitation for inverter ACs; communication timeout was took out to the yaml config

v.0.2.9: new feature - power limitation for inverter ACs
This commit is contained in:
GK
2023-01-23 19:59:13 +04:00
committed by GitHub
7 changed files with 894 additions and 112 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
@@ -128,9 +129,17 @@ climate:
name: AC Defrost State
id: ac_defrost_state
internal: false
invertor_power:
name: AC Invertor Power
id: ac_invertor_power
inverter_power:
name: AC Inverter Power
id: ac_inverter_power
internal: false
inverter_power_limit_value:
name: AC Inverter Power Limit Value
id: ac_inverter_power_limit_value
internal: false
inverter_power_limit_state:
name: AC Inverter Power Limit State
id: ac_inverter_power_limit_state
internal: false
preset_reporter:
name: AC Preset Reporter
@@ -166,43 +175,77 @@ climate:
```
## Configuration variables: ##
- **name** (**Required**, string): The name of the climate device. At least one of `id` or `name` is required!
- **id** (*Optional*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Manually specify the ID used for code generation. At least one of `id` or `name` is required!
- **uart_id** (*Optional*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Manually specify the ID of the [UART Bus](https://esphome.io/components/uart.html) if you want to use multiple UART buses.
- **period** (*Optional*, [time](https://esphome.io/guides/configuration-types.html#config-time), default ``7s``): Period between status requests to the AC. `Aux_ac` will receive the new air conditioner status only after a regular request, even if you change the settings of AC using IR-remote.
- **show_action** (*Optional*, boolean, default ``true``): Whether to show current action of the device (experimental). For example, in the HEAT-COOL mode, AC hardware may be in one of the following actions:
- HEATING: AC is heating the air in the room;
- IDLE: AC is working in the FAN mode, cause the target temperature is reached;
- COOLING: AC is cooling the air.
The same thing will be in HEAT or COOL modes, with the only difference of the list of actions (IDLE + HEATING or IDLE + COOLING).
- **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.
- **internal** (*Optional*, boolean): Mark this component as internal. Internal components will not be exposed to the frontend (like Home Assistant). As opposed to default [Sensor](https://esphome.io/components/sensor/index.html#base-sensor-configuration) behaviour, this variable is **always true** except in cases where the user has set it directly.
- All other options from [Sensor](https://esphome.io/components/sensor/index.html#base-sensor-configuration).
- **outdoor_temperature** (*Optional*): Parameters of the outdoor temperature sensor. They are the same as the **indoor_temperature** (see description above).
**Attention!** When the air conditioner is turned off, the outdoor temperature is updated rarely (every 6-7 hours). This isn't a bug of the component, but a feature of the air conditioner hardware. The only way to get changes more often is to create a template sensor, the temperature of which can be changed manually. When the air conditioner is working, the value of this sensor can be copied from the **outdoor_temperature**. When the air conditioner is turned off, the temperature value should be is recalculated according to the dynamics of the **outbound_temperature** sensor (it changes frequently and shows values close to the air temperature when the air conditioner is turned off). You can't direct copy the value of **outbound_temperature** to the template sensor in AC off mode, because these temperatures are not identical.
> **Attention!** When the air conditioner is turned off, the outdoor temperature is updated rarely (every 6-7 hours). This isn't a bug of the component, but a feature of the air conditioner hardware. The only way to get changes more often is to create a template sensor, the temperature of which can be changed manually. When the air conditioner is working, the value of this sensor can be copied from the **outdoor_temperature**. When the air conditioner is turned off, the temperature value should be recalculated according to the dynamics of the **outbound_temperature** sensor (it changes frequently and shows values close to the air temperature when the air conditioner is turned off). You can't copy the value of **outbound_temperature** without changes to the template sensor in AC off mode, because these temperatures are not identical.
- **inbound_temperature** (*Optional*): Parameters of the coolant inbound temperature sensor. They are the same as the **indoor_temperature** (see description above).
- **outbound_temperature** (*Optional*): Parameters of the coolant outbound temperature sensor. They are the same as the **indoor_temperature** (see description above).
- **compressor_temperature** (*Optional*): Parameters of the compressor temperature sensor. They are the same as the **indoor_temperature** (see description above).
- **display_state** (*Optional*): The information for the HVAC display state sensor (is display ON or OFF)
- **name** (**Required**, string): The name for the display state sensor.
- **id** (*Optional*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Set the ID of this sensor for use in lambdas.
- **internal** (*Optional*, boolean): Mark this component as internal. Internal components will not be exposed to the frontend (like Home Assistant). As opposed to default [Binary Sensor](https://esphome.io/components/binary_sensor/index.html#base-binary-sensor-configuration) behavior, this variable is **always true** except in cases where the user has set it directly.
- All other options from [Binary Sensor](https://esphome.io/components/binary_sensor/index.html#base-binary-sensor-configuration).
- **defrost_state** (*Optional*): The information for the HVAC defrost function state sensor (is it ON or OFF). All settings are the same as for the **display_state** (see description above).
- **invertor_power** (*Optional*): The information for the inverter power sensor. (Yes, I know about a mistake in the name of this parameter... I'll correct it... Sometimes :) ) All settings are the same as for the **display_state** (see description above).
- **inverter_power** (*Optional*): The information for the inverter power sensor. All settings are the same as for the **indoor_temperature** (see description above).
> **ATTENTION!** The parameter name was changed in v.0.2.9 due to incorrect spelling.
- **inverter_power_limit_state** (*Optional*): Configuration of the power limit state sensor. It displays the state of the power limitation function for the inverter HVAC (is it ON or OFF). All settings are the same as for the **display_state** (see description above).
- **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).
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.
- **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``.
- **custom_fan_modes** (*Optional*, list): List of supported custom fan modes. Possible values are: ``MUTE``, ``TURBO``. No custom fan modes by default.
- **supported_presets** (*Optional*, list): List of supported presets. Possible values are: ``SLEEP``. No presets by default.
- **custom_presets** (*Optional*, list): List of supported custom presets. Possible values are: ``CLEAN``, ``HEALTH``, ``ANTIFUNGUS``. No custom presets by default.
- **supported_swing_modes** (*Optional*, list): List of supported swing modes. Possible values are: ``VERTICAL``, ``HORIZONTAL``, ``BOTH``. No swing modes by default.
- All other options from [Climate](https://esphome.io/components/climate/index.html#base-climate-configuration).
## Actions: ##
### ``aux_ac.display_on`` ###
This action turns a HVAC temperature display on when executed.
@@ -316,6 +359,32 @@ on_...:
```
- **aux_id** (**Required**, string): ID of `aux_ac` component.
### ``aux_ac.aux_ac.power_limit_off`` ###
This action disables inverter HVAC power limitation.
```yaml
on_...:
then:
- aux_ac.power_limit_off: aux_id
```
- **aux_id** (**Required**, string): ID of `aux_ac` component.
### ``aux_ac.power_limit_on`` ###
This action enables inverter HVAC power limitation and sets this limit value.
```yaml
on_...:
then:
- aux_ac.power_limit_on:
id: aux_id
limit: 46 # limits the maximum power of the inverter HVAC at 46%
```
- **aux_id** (**Required**, string): ID of `aux_ac` component.
- **limit** (**Optional**, integer): the maximum power of the inverter HVAC. If the power limitation is enabled, the inverter HVAC will limits its power.
> **Notice**, that power limitation will affect the efficiency of your HVAC. For example, a low power limit may block the possibility of the conditioner to reach user-specified room temperature, because HVAC will not have enough power for it. Keep this in mind when you are using this function.
Due to hardware limitation this value should be in the range from `30%` to `100%`. The default value for `limit` is `30%` (it will be used if `limit` is omitted in configuration).
## Simple example ##

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
@@ -126,9 +127,17 @@ climate:
name: AC Defrost State
id: ac_defrost_state
internal: false
invertor_power:
name: AC Invertor Power
id: ac_invertor_power
inverter_power:
name: AC Inverter Power
id: ac_inverter_power
internal: false
inverter_power_limit_value:
name: AC Inverter Power Limit Value
id: ac_inverter_power_limit_value
internal: false
inverter_power_limit_state:
name: AC Inverter Power Limit State
id: ac_inverter_power_limit_state
internal: false
preset_reporter:
name: AC Preset Reporter
@@ -164,41 +173,75 @@ climate:
```
## Параметры компонента: ##
- **name** (**Обязательный**, строка): Имя кондиционера. Как минимум один из параметров `id` или `name` должен быть указан!
- **id** (*Опциональный*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Укажите идентификатор кондиционера чтобы обращаться к нему из кода. Как минимум один из параметров `id` или `name` должен быть указан!
- **uart_id** (*Опциональный*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Укажите ID [шины UART](https://esphome.io/components/uart.html), к которой подключен кондиционер. Если сконфигурирована одна шина, то компонент подключит её автоматически. Если шин несколько, то лучше указать вручную.
- **period** (*Опциональный*, [время](https://esphome.io/guides/configuration-types.html#config-time), по умолчанию ``7s``): Период между запросами статуса кондиционера. `Aux_ac` получает новое состояние кондиционера только после регулярного запроса, потому что сам кондиционер об изменении параметров своеё работы не уведомляет. Поэтому нужно запрашивать его, вдруг пользователь установил иной режим работы с помощью ИК-пульта.
- **show_action** (*Опциональный*, логическое, по умолчанию ``true``): Показывать ли текущую задачу кондиционера (экспериментальная функция). Например, в режиме HEAT-COOL кондиционер может выполнять одну из следующих задач:
- НАГРЕВ: нагревает воздух в комнате;
- ПРОСТОЙ: кондиционер работает в режиме вентилятора для перемешивания воздуха в комнате, поскольку целевая температура уже достигнута;
- ОХЛАЖДЕНИЕ: кондиционер охлаждает воздух в комнате.
Аналогично будут отображаться действия кондиционера и для режимов ОТОПЛЕНИЕ и ОХЛАЖДЕНИЕ. Единственная разница будет в количестве действий: ПРОСТОЙ+НАГРЕВ для режима отопления и ПРОСТОЙ+ОХЛАЖДЕНИЕ для режима охлаждения комнаты.
- **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 для датчика для использования в лямбдах.
- **internal** (*Опциональный*, логическое): Пометить данный датчик как внутренний. Внутренний датчик не будет передаваться во фронтэнд (такой как Home Assistant). В противоположность стандартному поведению [сенсоров](https://esphome.io/components/sensor/index.html#base-sensor-configuration) этот параметр для датчика в кондиционере **всегда выставлен в true** за исключением случаев, когда пользователь не установил его в `false`. То есть по умолчанию значение сенсора не будет передаваться во фронтенд даже если указано `name` для сенсора.
- Все остальные параметры [сенсора](https://esphome.io/components/sensor/index.html#base-sensor-configuration) ESPHome.
- **outdoor_temperature** (*Опциональный*): Параметры создаваемого датчика уличной температуры воздуха, если такой датчик нужен. Параметры аналогичны датчику внутренней температуры **indoor_temperature** (см. выше).
**ВНИМАНИЕ!** Когда кондиционер выключен, температура наружного воздуха обновляется редко (раз в 6-7 часов). Это не баг компонента, а особенность работы железа кондиционера. Единственный способ получать изменения чаще - создать шаблонный сенсор, температуру которого изменять в ручную. Когда кондиционер работает, значение такого сенсора можно копировать из **outdoor_temperature**. Когда кондиционер выключен, значение температуры пересчитывать по динамике сенсора **outbound_temperature** (он изменяется часто и при выключенном кондее показывает значения близкие к температуре воздуха). Заморочки с пересчетом нужны потому, что показания сенсоров не идентичны и на графике значений шаблонного сенсора могут быть ступеньки при переходе с **outdoor_temperature** на **outbound_temperature** и обратно.
> **ВНИМАНИЕ!** Когда кондиционер выключен, температура наружного воздуха обновляется редко (раз в 6-7 часов). Это не баг компонента, а особенность работы железа кондиционера. Единственный способ получать изменения чаще - создать шаблонный сенсор, температуру которого изменять в ручную. Когда кондиционер работает, значение такого сенсора можно копировать из **outdoor_temperature**. Когда кондиционер выключен, значение температуры пересчитывать по динамике сенсора **outbound_temperature** (он изменяется часто и при выключенном кондее показывает значения близкие к температуре воздуха). Заморочки с пересчетом нужны потому, что показания сенсоров не идентичны и на графике значений шаблонного сенсора могут быть ступеньки при переходе с **outdoor_temperature** на **outbound_temperature** и обратно.
- **inbound_temperature** (*Опциональный*): Параметры создаваемого датчика температуры на подаче теплоносителя, если такой датчик нужен. Параметры аналогичны датчику внутренней температуры **indoor_temperature** (см. выше).
- **outbound_temperature** (*Опциональный*): Параметры создаваемого датчика температуры на обратке теплоносителя, если такой датчик нужен. Параметры аналогичны датчику внутренней температуры **indoor_temperature** (см. выше).
- **compressor_temperature** (*Опциональный*): Параметры создаваемого датчика температуры компрессора, если такой датчик нужен. Параметры аналогичны датчику внутренней температуры **indoor_temperature** (см. выше).
- **display_state** (*Опциональный*): Параметры создаваемого датчика дисплея (включен или выключен), если такой датчик нужен.
- **name** (**Обязательный**, строка): Имя датчика дисплея.
- **id** (*Опциональный*, [ID](https://esphome.io/guides/configuration-types.html#config-id)): Можно указать свой ID для датчика для использования в лямбдах.
- **internal** (*Опциональный*, логическое): Пометить данный датчик как внутренний. Внутренний датчик не будет передаваться во фронтэнд (такой как Home Assistant). В противоположность стандартному поведению [бинарных сенсоров](https://esphome.io/components/binary_sensor/index.html#base-binary-sensor-configuration) этот параметр для датчика в кондиционере **всегда выставлен в true** за исключением случаев, когда пользователь не установил его в `false`. То есть по умолчанию значение сенсора не будет передаваться во фронтенд даже если указано `name` для сенсора.
- Все остальные параметры [бинарного сенсора](https://esphome.io/components/binary_sensor/index.html#base-binary-sensor-configuration) ESPHome.
- **defrost_state** (*Опциональный*): Параметры создаваемого датчика состояния разморозки (включена или выключена), если такой датчик нужен. Параметры аналогичны датчику дисплея **display_state**.
- **invertor_power** (*Опциональный*): Параметры создаваемого датчика мощности инвертора, если такой датчик нужен. Параметры аналогичны датчику дисплея **display_state**.
- **inverter_power** (*Опциональный*): Параметры создаваемого датчика мощности инвертора, если такой датчик нужен. Параметры аналогичны датчику дисплея **indoor_temperature**.
> **ВНИМАНИЕ!** Название параметра было изменено в версии v.0.2.9 в рамках борьбы с безграмотностью.
- **inverter_power_limit_state** (*Опциональный*): Параметры создаваемого датчика состояния функции ограничения мощности. Показывает, включена данная функция в настоящий момент или нет. По очевидным причинам актуально только для инверторных кондиционеров, для "старт-стоп" кондиционеров всегда будет "выключен". Параметры аналогичны датчику дисплея **display_state**.
- **inverter_power_limit_value** (*Опциональный*): Параметры создаваемого датчика текущего ограничения мощности, если такой датчик нужен. Параметры аналогичны датчику внутренней температуры **indoor_temperature** (см. выше).
Сенсор отображает текущее значение ограничения максимальной мощности для инверторного кондиционера. Значение в процентах. С кондиционерами "старт-стоп" по очевидным причинам не работает, всегда показывая значение `0%`. Заданное пользователем значения лимита будет отображено только после того, как кондиционер подтвердит полученное значение и начнет с ним работать.
В силу ограничений на уровне железа лимит мощности может быть задан только в пределах от `30%` до `100%`.
- **preset_reporter** (*Опциональный*): Параметры создаваемого текстового датчика текущего активного пресета. Параметры аналогичны датчику дисплея **display_state**.
Климатические устройства ESPHome не отправляют по MQTT активный пресет (см. **supported_presets** и **custom_presets**), в котором работает устройство. Если вы используете MQTT и хотите получать информацию о пресетах, то пропишите этот датчик в конфигурации.
- **vlouver_state** (*Опциональный*): Параметры создаваемого сенсора состояния вертикальных жалюзи. Параметры аналогичны датчику дисплея **display_state**. Состояние желюзи кодируется целочисленными значениями (подробнее смотри [aux_ac.vlouver_set action](#aux_ac_._vlouver_set) ниже).
- **supported_modes** (*Опциональный*, список): Список поддерживаемых режимов работы. Возможные значения: ``HEAT_COOL``, ``COOL``, ``HEAT``, ``DRY``, ``FAN_ONLY``. Обратите внимание: некоторые производители кондиционеров указывают на пульте режим AUTO, хотя по факту этот режим не работает по расписанию и только лишь поддерживает целевую температуру. Такой режим в ESPHome называется HEAT_COOL. По умолчанию список содержит только значение ``FAN_ONLY``.
- **custom_fan_modes** (*Опциональный*, список): Список поддерживаемых дополнительных режимов вентилятора. Возможные значения: ``MUTE``, ``TURBO``. По умолчанию никакие дополнительные режимы не установлены.
- **supported_presets** (*Опциональный*, список): Список поддерживаемых базовых функций кондиционера. Возможные значения: ``SLEEP``. По умолчанию никакие базовые функции не установлены.
- **custom_presets** (*Опциональный*, список): Список поддерживаемых дополнительных функций кондиционера. Возможные значения: ``CLEAN``, ``HEALTH``, ``ANTIFUNGUS``. По умолчанию никакие дополнительные функции не установлены.
- **supported_swing_modes** (*Опциональный*, список): Список поддерживаемых режимов качания шторки. Возможные значения: ``VERTICAL``, ``HORIZONTAL``, ``BOTH``. По умолчанию устанавливается, что качание шторки кондиционером не поддерживается.
- Все остальные параметры [климатического устройства](https://esphome.io/components/climate/index.html#base-climate-configuration) ESPHome.
## Действия: ##
@@ -314,6 +357,33 @@ on_...:
```
- **aux_id** (**Обязательный**, строка): ID компонента `aux_ac`.
### ``aux_ac.power_limit_off`` ###
Отключает лимит мощности инверторного кондиционера, если такой лимит был установлен.
```yaml
on_...:
then:
- aux_ac.power_limit_off: aux_id
```
- **aux_id** (**Обязательный**, строка): ID компонента `aux_ac`.
### ``aux_ac.power_limit_on`` ###
Включает ограничение мощности для инверторного кондиционера, а также может задавать предельное значение мощности. Для кондиционеров "старт-стоп" не работает, вызов игнорируется.
```yaml
on_...:
then:
- aux_ac.power_limit_on:
id: aux_id
limit: 46 # ограничивает максимальную мощность инверторного кондиционера на уровне 46%
```
- **aux_id** (**Обязательный**, строка): ID компонента `aux_ac`.
- **limit** (**Опциональный**, целое число): задаваемое пользователем максимальное значение мощности инверторного кондиционера.
При установленном лимите кондиционер не будет превышать это значение в работе.
> **Обратите внимание**, что задание лимита повлияет на эффективность работы сплит-системы. Например, при низком значении лимита кондиционеру может не хватать мощности, чтобы охладить или нагреть комнату до заданной пользователем температуры. Либо время, затраченное кондиционером на нагрев или охлаждение, может вырасти. Имейте это ввиду при установке ограничений.
В силу ограничений на уровне железа, лимит мощности должен быть в пределах от `30%` до `100%`. Некорректные значения будут приведены к диапазону.
В случае, если включение лимита вызывается без указания параметра `limit`, то будет использовано значение по умолчанию `30%`.
## Простейший пример ##

View File

@@ -154,5 +154,33 @@ class AirConSendTestPacketAction : public Action<Ts...> {
std::vector<uint8_t> data_static_{};
};
// **************************************** POWER LIMITATION ACTIONS ****************************************
template <typename... Ts>
class AirConPowerLimitationOffAction : public Action<Ts...> {
public:
explicit AirConPowerLimitationOffAction(AirCon *ac) : ac_(ac) {}
void play(Ts... x) override { this->ac_->powerLimitationOffSequence(); }
protected:
AirCon *ac_;
};
template <typename... Ts>
class AirConPowerLimitationOnAction : public Action<Ts...> {
public:
AirConPowerLimitationOnAction(AirCon *ac) : ac_(ac) {}
TEMPLATABLE_VALUE(uint8_t, value);
void play(Ts... x) {
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

View File

@@ -26,8 +26,6 @@
#endif
#endif
// раскоментируй ключ HOLMS для вывода лога под Эксель, значение ключа - размер пакетов которые будут видны
//#define HOLMS 19
namespace esphome {
namespace aux_ac {
@@ -40,6 +38,47 @@ using climate::ClimatePreset;
using climate::ClimateSwingMode;
using climate::ClimateTraits;
//****************************************************************************************************************************************************
//**************************************************** Packet logger configuration *******************************************************************
//****************************************************************************************************************************************************
// v.0.2.9: замена директиве HOLMS
#ifdef HOLMS
#undef HOLMS
#warning "HOLMS was deprecated in v.0.2.9. Use HOLMES_x instead (see below)."
#endif
// Директива HOLMES_WORKS позволяет включить (true) или выключить (false) вывод пакетов в лог
// Причём отключение вывода пакетов не затронет вывод остальных данных
#define HOLMES_WORKS true
// Директива HOLMES_BYTE_FORMAT задаёт формат вывод каждого байта пакета в лог в формате sprintf.
// Для вывода в шестнадцатиричном виде с двумя знаками, задайте "%02X".
// Для вывода в десятичном виде с тремя знаками, задайте "%03d".
#define HOLMES_BYTE_FORMAT "%02X"
// Директива HOLMES_FILTER_LEN обеспечивает фильтрацию вывода пакетов в лог.
// Все корректные пакеты, длина тела которых короче HOLMES_FILTER_LEN, будут проигнорированы.
// Все корректные пакеты, длина тела которых HOLMES_FILTER_LEN и более, попадут в лог.
// Все данные, не являющиеся корректными пакетами, попадут в лог в любом случае. Это нужно для целей отладки.
// В протоколе встречаются пакеты с телом следующей длины: 0, 1, 2, 4, 8, 15, 23
#define HOLMES_FILTER_LEN 0
// Директива HOLMES_DELIMITER позволяет задать разделитель байт при выводе в лог
// Для "классического" вывода задайте " "
// Для вывода "под Excel" задайте ";"
#define HOLMES_DELIMITER " "
// Директивы HOLMES_x_BRACKET_OPEN и HOLMES_x_BRACKET_CLOSE задают открывающую и
// закрывающую скобки для заголовка и CRC.
// Если вместо скобок указать "", то в логе скобок не будет.
#define HOLMES_HEADER_BRACKET_OPEN "["
#define HOLMES_HEADER_BRACKET_CLOSE "]"
#define HOLMES_CRC_BRACKET_OPEN "["
#define HOLMES_CRC_BRACKET_CLOSE "]"
//****************************************************************************************************************************************************
//************************************************* Constants for ESPHome integration ****************************************************************
//****************************************************************************************************************************************************
class Constants {
public:
static const std::string AC_FIRMWARE_VERSION;
@@ -56,13 +95,23 @@ class Constants {
/// шаг изменения целевой температуры, градусы Цельсия
static const float AC_TEMPERATURE_STEP;
/// минимальное и максимальное значение мощности инвертора при установке ограничений
static const uint8_t AC_MIN_INVERTER_POWER_LIMIT;
static const uint8_t AC_MAX_INVERTER_POWER_LIMIT;
// периодичность опроса кондиционера на предмет изменения состояния
// изменение параметров с пульта не сообщается в 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.8";
const std::string Constants::AC_FIRMWARE_VERSION = "0.2.9";
// custom fan modes
const std::string Constants::MUTE = "mute";
@@ -77,8 +126,26 @@ const std::string Constants::ANTIFUNGUS = "Antifungus";
const float Constants::AC_MIN_TEMPERATURE = 16.0;
const float Constants::AC_MAX_TEMPERATURE = 32.0;
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;
// состояния конечного автомата компонента
@@ -98,17 +165,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-пакет
@@ -187,9 +243,9 @@ struct packet_big_info_body_t {
// байт 2 тела (байт 10 пакета)
// https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_21_b10
uint8_t reserv20 : 2;
bool is_invertor_periodic : 1; // флаг периодического пакета инверторного кондиционера
bool is_inverter_periodic : 1; // флаг периодического пакета инверторного кондиционера
uint8_t reserv23 : 2;
bool is_invertor : 1; // флаг инвертора
bool is_inverter : 1; // флаг инвертора
uint8_t reserv26 : 2;
// байт 3 тела (байт 11 пакета)
@@ -282,7 +338,7 @@ struct packet_big_info_body_t {
// байт 16 тела (байт 24 пакета)
// https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_21_b24
uint8_t invertor_power; // мощность инвертора (от 0 до 100) в %
uint8_t inverter_power; // мощность инвертора (от 0 до 100) в %
// байт 17 тела (байт 25 пакета)
// https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_21_b25
@@ -319,20 +375,50 @@ struct packet_big_info_body_t {
// тело малого информационного пакета
// https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11
struct packet_small_info_body_t {
// байт 8 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b08
uint8_t byte_01;
// байт 9 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b09
uint8_t cmd_answer;
// байт 10 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b10
uint8_t target_temp_int_and_v_louver;
// байт 11 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b11
uint8_t h_louver;
// байт 12 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b12
uint8_t target_temp_frac;
// байт 13 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b13
uint8_t fan_speed;
// байт 14 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b14
uint8_t fan_turbo_and_mute;
// байт 15 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b15
uint8_t mode;
// байт 16 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b16
uint8_t zero1; // всегда 0x00
// байт 17 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b17
uint8_t zero2; // всегда 0x00
// байт 18 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b18
uint8_t status;
// байт 19 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b19
uint8_t zero3; // всегда 0x00
// байт 20 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b20
uint8_t display_and_mildew;
uint8_t zero4; // всегда 0x00
// байт 21 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b21
uint8_t inverter_power_limitation_value : 7;
bool inverter_power_limitation_enable : 1;
// байт 22 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b22
uint8_t target_temp_frac2;
};
@@ -475,6 +561,11 @@ enum ac_mildew : uint8_t { AC_MILDEW_OFF = 0x00,
// GK: define убрал, т.к. считаю, что сбрасывать счетчик не надо.
// #define AC_MIN_COUNTER_MASK 0b00111111
// маски ограничения мощности для инверторного кондиционера
#define AC_INVERTER_POWER_LIMITATION_ENABLE_MASK 0b10000000
#define AC_INVERTER_POWER_LIMITATION_VALUE_MASK 0b01111111
#define AC_INVERTER_POWER_LIMITATION_VALUE_UNTOUCHED 0xFF
// положение вертикальных жалюзи для фронтенда
enum ac_vlouver_frontend : uint8_t {
AC_VLOUVER_FRONTEND_SWING = 0x00,
@@ -544,8 +635,10 @@ struct ac_command_t {
int8_t temp_outbound; // температура исходящая
int8_t temp_compressor; // температура компрессора
ac_realFan realFanSpeed; // текущая скорость вентилятора
uint8_t invertor_power; // мощность инвертора
uint8_t inverter_power; // мощность инвертора
bool defrost; // режим разморозки внешнего блока (накопление тепла + прогрев испарителя)
bool inverter_power_limitation_enable; // ограничение мощности инвертора
uint8_t inverter_power_limitation_value; // значение ограничения мощности инвертора
};
typedef ac_command_t ac_state_t; // текущее состояние параметров кондея можно хранить в таком же формате, как и комманды
@@ -662,7 +755,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// будет работать, но будет ниже, переменная устанавливается при первом получении большого пакета;
// если эта переменная установлена, то режим работы не инверторного кондиционера будет распознаваться
// как "в простое" (IDLE)
bool _is_invertor = false;
bool _is_inverter = false;
// поддерживаемые кондиционером опции
std::set<ClimateMode> _supported_modes{};
@@ -710,16 +803,12 @@ 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;
// последовательность пакетов текущий шаг в последовательности
sequence_item_t _sequence[AC_SEQUENCE_MAX_LEN];
uint8_t _sequence_current_step;
// флаг успешного выполнения стартовой последовательности команд
bool _startupSequenceComlete = false;
// нормализация показаний температуры, приведение в диапазон
float _temp_target_normalise(float temp) {
auto traits = this->get_traits();
@@ -732,6 +821,20 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return temp;
}
// нормализация лимита ограничения мощности инвертора, приведение в диапазон
uint8_t _power_limitation_value_normalise(uint8_t power_limitation_value) {
if (power_limitation_value < Constants::AC_MIN_INVERTER_POWER_LIMIT) power_limitation_value = Constants::AC_MIN_INVERTER_POWER_LIMIT;
if (power_limitation_value > Constants::AC_MAX_INVERTER_POWER_LIMIT) power_limitation_value = Constants::AC_MAX_INVERTER_POWER_LIMIT;
return power_limitation_value;
}
// последовательность пакетов текущий шаг в последовательности
sequence_item_t _sequence[AC_SEQUENCE_MAX_LEN];
uint8_t _sequence_current_step;
// флаг успешного выполнения стартовой последовательности команд
bool _startupSequenceComlete = false;
// очистка последовательности команд
void _clearSequence() {
for (uint8_t i = 0; i < AC_SEQUENCE_MAX_LEN; i++) {
@@ -907,8 +1010,10 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
cmd->temp_outbound = 0;
cmd->temp_compressor = 0;
cmd->realFanSpeed = AC_REAL_FAN_UNTOUCHED;
cmd->invertor_power = 0;
cmd->inverter_power = 0;
cmd->defrost = false;
cmd->inverter_power_limitation_enable = false;
cmd->inverter_power_limitation_value = AC_INVERTER_POWER_LIMITATION_VALUE_UNTOUCHED;
};
// очистка буфера размером AC_BUFFER_SIZE
@@ -1075,7 +1180,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();
@@ -1238,6 +1343,16 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
stateChangedFlag = stateChangedFlag || (_current_ac_state.mildew != (ac_mildew)stateByte);
_current_ac_state.mildew = (ac_mildew)stateByte;
// enable flag of power limitation for inverter ACs
bool temp = small_info_body->inverter_power_limitation_enable;
stateChangedFlag = stateChangedFlag || (_current_ac_state.inverter_power_limitation_enable != temp);
_current_ac_state.inverter_power_limitation_enable = temp;
// the limit value of power limitation for inverter ACs
stateByte = small_info_body->inverter_power_limitation_value;
stateChangedFlag = stateChangedFlag || (_current_ac_state.inverter_power_limitation_value != stateByte);
_current_ac_state.inverter_power_limitation_value = stateByte;
// уведомляем об изменении статуса сплита
if (stateChangedFlag) stateChanged();
@@ -1255,8 +1370,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
big_info_body = (packet_big_info_body_t *)(_inPacket.body);
// тип кондея (инвертор или старт стоп)
_is_invertor = big_info_body->is_invertor;
_is_inverter = big_info_body->is_inverter;
// температура воздуха в помещении по версии сплит-системы
stateFloat = big_info_body->ambient_temperature_int - 0x20 + (float)(big_info_body->ambient_temperature_frac & 0x0f) / 10.0;
stateChangedFlag = stateChangedFlag || (_current_ac_state.temp_ambient != stateFloat);
@@ -1283,15 +1398,15 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
stateChangedFlag = stateChangedFlag || (_current_ac_state.temp_compressor != stateFloat);
_current_ac_state.temp_compressor = stateFloat;
// реальная скорость проперлера
// реальная скорость пропеллера
stateFloat = big_info_body->realFanSpeed;
stateChangedFlag = stateChangedFlag || (_current_ac_state.realFanSpeed != (ac_realFan)stateFloat);
_current_ac_state.realFanSpeed = (ac_realFan)stateFloat;
// мощность инвертора
stateFloat = big_info_body->invertor_power;
stateChangedFlag = stateChangedFlag || (_current_ac_state.invertor_power != stateFloat);
_current_ac_state.invertor_power = stateFloat;
stateFloat = big_info_body->inverter_power;
stateChangedFlag = stateChangedFlag || (_current_ac_state.inverter_power != stateFloat);
_current_ac_state.inverter_power = stateFloat;
// режим разморозки
//bool temp = (big_info_body->needDefrost && big_info_body->defrostMode);
@@ -1309,8 +1424,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
case AC_CMD_SET_PARAMS: { // такой статусный пакет присылается кондиционером в ответ на команду установки параметров
// в теле пакета нет ничего примечательного
// в байтах 2 и 3 тела похоже передается CRC пакета поступившей команды, на которую сплит отвечает
// но я решил этот момент тут не проверять и не контролировать.
// в байтах 2 и 3 тела передается CRC пакета поступившей команды, на которую сплит отвечает
// я решил этот момент тут не проверять и не контролировать.
// корректную установку параметров можно определить, запросив статус кондиционера сразу после получения этой команды кондея
// в настоящий момент проверка сделана в механизме sequences
break;
@@ -1387,13 +1502,19 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
* Для нормального пакета данные выводятся с форматированием.
* line - строка, на которой произошел вызов (удобно при отладке)
**/
void _debugPrintPacket(packet_t *packet, uint8_t dbgLevel = ESPHOME_LOG_LEVEL_DEBUG, unsigned int line = 0) {
void _debugPrintPacket(packet_t *packet, uint8_t dbgLevel = ESPHOME_LOG_LEVEL_DEBUG, unsigned int line = __LINE__) {
// определяем, полноценный ли пакет нам передан
bool notAPacket = false;
// указатель заголовка всегда установден на начало буфера
notAPacket = notAPacket || (packet->crc == nullptr);
notAPacket = notAPacket || (packet->data[0] != AC_PACKET_START_BYTE);
// если пакет по длине меньше, чем указано в фильтре, то не выводим.
// если вывод пакетов отключен с помощью директивы HOLMES_WORKS, то тоже не выводим.
// "не пакеты" выводим всегда, так как от них зависит отладка багов
if ((!notAPacket) && (packet->header->body_length < HOLMES_FILTER_LEN)) return;
if ((!notAPacket) && (!HOLMES_WORKS)) return;
String st = "";
char textBuf[11];
@@ -1411,44 +1532,26 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
st += "[--] "; // преамбула для "непакета"
}
// формируем данные
#ifdef HOLMS
// если этот дефайн объявлен, то в лог попадут только пакеты больше указанного в дефайне размера
// при этом весь вывод будет в десятичном виде, данные будут разделены ";"
// и не будет выделения заголовков и CRC квадратными скобками
dbgLevel = ESPHOME_LOG_LEVEL_ERROR;
if (packet->header->body_length > HOLMS) {
for (int i = 0; i < packet->bytesLoaded; i++) {
sprintf(textBuf, "%03d;", packet->data[i]);
st += textBuf;
}
if (line == 0) line = __LINE__;
_debugMsg(st, dbgLevel, line);
}
#else
// если дефайна HOLMS нет, то выводим пакеты в HEX и все подряд
// формируем данные
for (int i = 0; i < packet->bytesLoaded; i++) {
// для нормальных пакетов надо заключить заголовок в []
if ((!notAPacket) && (i == 0)) st += "[";
// для нормальных пакетов надо заключить CRC в []
if ((!notAPacket) && (i == packet->header->body_length + AC_HEADER_SIZE)) st += "[";
// для заголовков нормальных пакетов надо отработать скобки (если они есть)
if ((!notAPacket) && (i == 0)) st += HOLMES_HEADER_BRACKET_OPEN;
// для CRC нормальных пакетов надо отработать скобки (если они есть)
if ((!notAPacket) && (i == packet->header->body_length + AC_HEADER_SIZE)) st += HOLMES_CRC_BRACKET_OPEN;
memset(textBuf, 0, 11);
sprintf(textBuf, "%02X", packet->data[i]);
sprintf(textBuf, HOLMES_BYTE_FORMAT, packet->data[i]);
st += textBuf;
// для нормальных пакетов надо заключить заголовок в []
if ((!notAPacket) && (i == AC_HEADER_SIZE - 1)) st += "]";
// для нормальных пакетов надо заключить CRC в []
if ((!notAPacket) && (i == packet->header->body_length + AC_HEADER_SIZE + 2 - 1)) st += "]";
// для заголовков нормальных пакетов надо отработать скобки (если они есть)
if ((!notAPacket) && (i == AC_HEADER_SIZE - 1)) st += HOLMES_HEADER_BRACKET_CLOSE;
// для CRC нормальных пакетов надо отработать скобки (если они есть)
if ((!notAPacket) && (i == packet->header->body_length + AC_HEADER_SIZE + 2 - 1)) st += HOLMES_CRC_BRACKET_CLOSE;
st += " ";
st += HOLMES_DELIMITER;
}
if (line == 0) line = __LINE__;
_debugMsg(st, dbgLevel, line);
#endif
}
/** расчет CRC16 для блока данных data длиной len
@@ -1604,6 +1707,13 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
}
}
// ограничение мощности инвертора
if (cmd->inverter_power_limitation_value != AC_INVERTER_POWER_LIMITATION_VALUE_UNTOUCHED) {
pack->body[13] = (pack->body[13] & ~AC_INVERTER_POWER_LIMITATION_ENABLE_MASK) | (cmd->inverter_power_limitation_enable << 7);
cmd->inverter_power_limitation_value = _power_limitation_value_normalise(cmd->inverter_power_limitation_value);
pack->body[13] = (pack->body[13] & ~AC_INVERTER_POWER_LIMITATION_VALUE_MASK) | cmd->inverter_power_limitation_value;
}
// обнулить счетчик минут с последней команды
// GK: считаю, что так делать не надо. Штатный wifi-модуль не сбрасывает счетчик минут.
// pack->body[4] &= ~ AC_MIN_COUNTER_MASK ;
@@ -1869,11 +1979,13 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
esphome::sensor::Sensor *sensor_inbound_temperature_ = nullptr;
esphome::sensor::Sensor *sensor_outbound_temperature_ = nullptr;
esphome::sensor::Sensor *sensor_compressor_temperature_ = nullptr;
esphome::sensor::Sensor *sensor_invertor_power_ = nullptr;
esphome::sensor::Sensor *sensor_inverter_power_ = nullptr;
esphome::sensor::Sensor *sensor_vlouver_state_ = nullptr;
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_inverter_power_limit_value_ = nullptr;
esphome::binary_sensor::BinarySensor *sensor_inverter_power_limit_state_ = nullptr;
// загружает на выполнение последовательность команд на включение/выключение табло с температурой
bool _displaySequence(ac_display dsp = AC_DISPLAY_ON) {
@@ -1960,6 +2072,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);
@@ -1989,8 +2102,10 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
void set_vlouver_state_sensor(sensor::Sensor *vlouver_state_sensor) { sensor_vlouver_state_ = vlouver_state_sensor; }
void set_defrost_state(binary_sensor::BinarySensor *defrost_state_sensor) { sensor_defrost_ = defrost_state_sensor; }
void set_display_sensor(binary_sensor::BinarySensor *display_sensor) { sensor_display_ = display_sensor; }
void set_invertor_power_sensor(sensor::Sensor *invertor_power_sensor) { sensor_invertor_power_ = invertor_power_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_inverter_power_limit_value_sensor(sensor::Sensor *inverter_power_limit_value_sensor) { sensor_inverter_power_limit_value_ = inverter_power_limit_value_sensor; }
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; };
bool get_has_connection() { return _has_connection; };
@@ -2004,11 +2119,12 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
void stateChanged() {
_debugMsg(F("State changed, let's publish it."), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__);
// экшины кондиционера (информация для пользователя, что кондиционер сейчас делает)
// сейчас экшины рассчётные и могут не отражать реального положения дел, но других вариантов не придумалось
if (_is_invertor) {
if (_is_inverter) {
// анализ режима для инвертора точнее потому, что использует показания мощности инвертора
static uint32_t timerInv = 0;
if (_current_ac_state.invertor_power == 0) { // инвертор выключен
if (_current_ac_state.inverter_power == 0) { // инвертор выключен
timerInv = millis();
if (_current_ac_state.realFanSpeed == AC_REAL_FAN_OFF &&
_current_ac_state.power == AC_POWER_OFF) { // внутренний кулер остановлен, кондей выключен
@@ -2053,7 +2169,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
this->action = climate::CLIMATE_ACTION_FAN; // другие режимы - вентиляция
}
}
} else { // if(_is_invertor)
} else { // if(_is_inverter)
// для on-off сплита рассчет экшена упрощен
if (_current_ac_state.realFanSpeed == AC_REAL_FAN_OFF &&
_current_ac_state.power == AC_POWER_OFF) {
@@ -2306,14 +2422,20 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
if (sensor_compressor_temperature_ != nullptr)
sensor_compressor_temperature_->publish_state(_current_ac_state.temp_compressor);
// мощность инвертора
if (sensor_invertor_power_ != nullptr)
sensor_invertor_power_->publish_state(_current_ac_state.invertor_power);
if (sensor_inverter_power_ != nullptr)
sensor_inverter_power_->publish_state(_current_ac_state.inverter_power);
// флаг режима разморозки
if (sensor_defrost_ != nullptr)
sensor_defrost_->publish_state(_current_ac_state.defrost);
// положение вертикальных жалюзи
if (sensor_vlouver_state_ != nullptr)
sensor_vlouver_state_->publish_state((float)this->getCurrentVlouverFrontendState());
// флаг включенного ограничения мощности инвертора
if (sensor_inverter_power_limit_state_ != nullptr)
sensor_inverter_power_limit_state_->publish_state(_current_ac_state.inverter_power_limitation_enable);
// значение ограничения мощности инвертора
if (sensor_inverter_power_limit_value_ != nullptr)
sensor_inverter_power_limit_value_->publish_state(_current_ac_state.inverter_power_limitation_value);
// сенсор состояния сплита
if (sensor_preset_reporter_ != nullptr) {
@@ -2341,19 +2463,23 @@ 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()));
#endif
ESP_LOGCONFIG(TAG, " [?] Is invertor %s", millis() > _update_period + 1000 ? YESNO(_is_invertor) : "pending...");
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_invertor_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_);
@@ -2965,6 +3091,56 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
return true;
}
// выключает ограничение мощности сплита
bool powerLimitationOffSequence() {
// нет смысла в последовательности, если нет коннекта с кондиционером
if (!get_has_connection()) {
_debugMsg(F("powerLimitationOffSequence: no pings from HVAC. It seems like no AC connected."), ESPHOME_LOG_LEVEL_ERROR, __LINE__);
return false;
}
if (!this->_is_inverter) return false; // если кондиционер не инверторный, то выходим
// формируем команду
ac_command_t cmd;
_clearCommand(&cmd); // не забываем очищать, а то будет мусор
cmd.inverter_power_limitation_enable = false;
cmd.inverter_power_limitation_value = this->_current_ac_state.inverter_power_limitation_value;
// добавляем команду в последовательность
if (!commandSequence(&cmd)) return false;
_debugMsg(F("powerLimitationOffSequence: loaded (value = %02X)"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, cmd.inverter_power_limitation_enable);
return true;
}
// включает ограничение мощности сплита на нужный уровень
bool powerLimitationOnSequence(uint8_t power_limit = Constants::AC_MIN_INVERTER_POWER_LIMIT) {
// нет смысла в последовательности, если нет коннекта с кондиционером
if (!get_has_connection()) {
_debugMsg(F("powerLimitationOnSequence: no pings from HVAC. It seems like no AC connected."), ESPHOME_LOG_LEVEL_ERROR, __LINE__);
return false;
}
if (!this->_is_inverter) { // если кондиционер не инверторный, то выходим
_debugMsg(F("powerLimitationOnSequence: unsupported for noninverter AC."), ESPHOME_LOG_LEVEL_WARN, __LINE__);
return false;
}
power_limit = this->_power_limitation_value_normalise(power_limit);
// формируем команду
ac_command_t cmd;
_clearCommand(&cmd); // не забываем очищать, а то будет мусор
cmd.inverter_power_limitation_enable = true;
cmd.inverter_power_limitation_value = power_limit;
// добавляем команду в последовательность
if (!commandSequence(&cmd)) return false;
_debugMsg(F("powerLimitationOnSequence: loaded (power limit = %02X)"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, power_limit);
return true;
}
// конвертирует состояние жалюзи из кодов сплита в коды для фронтенда
ac_vlouver_frontend AUXvlouverToVlouverFrontend(const ac_louver_V vLouver) {
switch (vLouver) {
@@ -3048,7 +3224,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate {
// добавляем команду в последовательность
if (!commandSequence(&cmd)) return false;
_debugMsg(F("setVLouverSequence: loaded (power = %02X)"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, vLouver);
_debugMsg(F("setVLouverSequence: loaded (vLouver = %02X)"), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__, vLouver);
return true;
}
@@ -3075,6 +3251,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

@@ -5,16 +5,18 @@ from esphome.components import climate, uart, sensor, binary_sensor, text_sensor
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.const import (
CONF_ID,
CONF_UART_ID,
CONF_PERIOD,
CONF_CUSTOM_FAN_MODES,
CONF_CUSTOM_PRESETS,
CONF_INTERNAL,
CONF_DATA,
CONF_ID,
CONF_INTERNAL,
CONF_PERIOD,
CONF_POSITION,
CONF_SUPPORTED_MODES,
CONF_SUPPORTED_SWING_MODES,
CONF_SUPPORTED_PRESETS,
CONF_TIMEOUT,
CONF_UART_ID,
UNIT_CELSIUS,
UNIT_PERCENT,
ICON_POWER,
@@ -22,7 +24,6 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_POWER_FACTOR,
STATE_CLASS_MEASUREMENT,
CONF_POSITION,
)
from esphome.components.climate import (
ClimateMode,
@@ -37,26 +38,42 @@ DEPENDENCIES = ["climate", "uart"]
AUTO_LOAD = ["sensor", "binary_sensor", "text_sensor"]
CONF_SHOW_ACTION = "show_action"
CONF_INDOOR_TEMPERATURE = "indoor_temperature"
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
ICON_OUTDOOR_TEMPERATURE = "mdi:home-thermometer-outline"
CONF_INBOUND_TEMPERATURE = "inbound_temperature"
ICON_INBOUND_TEMPERATURE = "mdi:thermometer-plus"
CONF_OUTBOUND_TEMPERATURE = "outbound_temperature"
ICON_OUTBOUND_TEMPERATURE = "mdi:thermometer-minus"
CONF_COMPRESSOR_TEMPERATURE = "compressor_temperature"
ICON_COMPRESSOR_TEMPERATURE = "mdi:thermometer-lines"
CONF_DISPLAY_STATE = "display_state"
CONF_INVERTOR_POWER = "invertor_power"
CONF_INVERTER_POWER = "inverter_power"
CONF_INVERTER_POWER_DEPRICATED = "invertor_power"
CONF_DEFROST_STATE = "defrost_state"
ICON_DEFROST = "mdi:snowflake-melt"
CONF_DISPLAY_INVERTED = "display_inverted"
ICON_DISPLAY = "mdi:clock-digital"
CONF_PRESET_REPORTER = "preset_reporter"
ICON_PRESET_REPORTER = "mdi:format-list-group"
CONF_VLOUVER_STATE = "vlouver_state"
ICON_VLOUVER_STATE = "mdi:compare-vertical"
CONF_LIMIT = "limit"
CONF_INVERTER_POWER_LIMIT_VALUE = "inverter_power_limit_value"
ICON_INVERTER_POWER_LIMIT_VALUE = "mdi:meter-electric-outline"
CONF_INVERTER_POWER_LIMIT_STATE = "inverter_power_limit_state"
ICON_INVERTER_POWER_LIMIT_STATE = "mdi:meter-electric-outline"
aux_ac_ns = cg.esphome_ns.namespace("aux_ac")
AirCon = aux_ac_ns.class_("AirCon", climate.Climate, cg.Component)
@@ -66,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
)
@@ -89,8 +106,38 @@ AirConVLouverMiddleBelowAction = aux_ac_ns.class_(
AirConVLouverBottomAction = aux_ac_ns.class_(
"AirConVLouverBottomAction", automation.Action
)
AirConVLouverSetAction = aux_ac_ns.class_(
"AirConVLouverSetAction", automation.Action
)
# power limitation actions
AirConPowerLimitationOffAction = aux_ac_ns.class_(
"AirConPowerLimitationOffAction", automation.Action
)
AirConPowerLimitationOnAction = aux_ac_ns.class_(
"AirConPowerLimitationOnAction", automation.Action
)
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}")
AirConVLouverSetAction = aux_ac_ns.class_("AirConVLouverSetAction", automation.Action)
ALLOWED_CLIMATE_MODES = {
"HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL,
@@ -134,7 +181,7 @@ def validate_raw_data(value):
def output_info(config):
"""_LOGGER.info(config)"""
"""_LOGGER.info(config.items())"""
return config
@@ -145,7 +192,12 @@ 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_INVERTOR_POWER): sensor.sensor_schema(
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."
),
cv.Optional(CONF_INVERTER_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_POWER,
accuracy_decimals=0,
@@ -156,6 +208,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
}
),
cv.Optional(CONF_INDOOR_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
icon=ICON_THERMOMETER,
@@ -240,6 +293,24 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
}
),
cv.Optional(CONF_INVERTER_POWER_LIMIT_VALUE): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_INVERTER_POWER_LIMIT_VALUE,
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_INVERTER_POWER_LIMIT_STATE): binary_sensor.binary_sensor_schema(
icon=ICON_INVERTER_POWER_LIMIT_STATE,
).extend(
{
cv.Optional(CONF_INTERNAL, default="true"): cv.boolean,
}
),
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(validate_modes),
cv.Optional(CONF_SUPPORTED_SWING_MODES): cv.ensure_list(
validate_swing_modes
@@ -258,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)
@@ -307,19 +376,30 @@ async def to_code(config):
sens = await binary_sensor.new_binary_sensor(conf)
cg.add(var.set_defrost_state(sens))
if CONF_INVERTOR_POWER in config:
conf = config[CONF_INVERTOR_POWER]
if CONF_INVERTER_POWER in config:
conf = config[CONF_INVERTER_POWER]
sens = await sensor.new_sensor(conf)
cg.add(var.set_invertor_power_sensor(sens))
cg.add(var.set_inverter_power_sensor(sens))
if CONF_PRESET_REPORTER in config:
conf = config[CONF_PRESET_REPORTER]
sens = await text_sensor.new_text_sensor(conf)
cg.add(var.set_preset_reporter_sensor(sens))
if CONF_INVERTER_POWER_LIMIT_VALUE in config:
conf = config[CONF_INVERTER_POWER_LIMIT_VALUE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_inverter_power_limit_value_sensor(sens))
if CONF_INVERTER_POWER_LIMIT_STATE in config:
conf = config[CONF_INVERTER_POWER_LIMIT_STATE]
sens = await binary_sensor.new_binary_sensor(conf)
cg.add(var.set_inverter_power_limit_state_sensor(sens))
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:
@@ -332,13 +412,13 @@ async def to_code(config):
cg.add(var.set_custom_fan_modes(config[CONF_CUSTOM_FAN_MODES]))
DISPLAY_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(AirCon),
}
)
@automation.register_action(
"aux_ac.display_off", AirConDisplayOffAction, DISPLAY_ACTION_SCHEMA
)
@@ -346,7 +426,6 @@ async def display_off_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"aux_ac.display_on", AirConDisplayOnAction, DISPLAY_ACTION_SCHEMA
)
@@ -355,13 +434,13 @@ async def display_on_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg, paren)
VLOUVER_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(AirCon),
}
)
@automation.register_action(
"aux_ac.vlouver_stop", AirConVLouverStopAction, VLOUVER_ACTION_SCHEMA
)
@@ -369,7 +448,6 @@ async def vlouver_stop_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"aux_ac.vlouver_swing", AirConVLouverSwingAction, VLOUVER_ACTION_SCHEMA
)
@@ -377,7 +455,6 @@ async def vlouver_swing_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"aux_ac.vlouver_top", AirConVLouverTopAction, VLOUVER_ACTION_SCHEMA
)
@@ -385,7 +462,6 @@ async def vlouver_top_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"aux_ac.vlouver_middle_above", AirConVLouverMiddleAboveAction, VLOUVER_ACTION_SCHEMA
)
@@ -393,7 +469,6 @@ async def vlouver_middle_above_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"aux_ac.vlouver_middle", AirConVLouverMiddleAction, VLOUVER_ACTION_SCHEMA
)
@@ -401,7 +476,6 @@ async def vlouver_middle_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"aux_ac.vlouver_middle_below", AirConVLouverMiddleBelowAction, VLOUVER_ACTION_SCHEMA
)
@@ -409,7 +483,6 @@ async def vlouver_middle_below_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"aux_ac.vlouver_bottom", AirConVLouverBottomAction, VLOUVER_ACTION_SCHEMA
)
@@ -418,6 +491,7 @@ async def vlouver_bottom_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg, paren)
VLOUVER_SET_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(AirCon),
@@ -425,7 +499,6 @@ VLOUVER_SET_ACTION_SCHEMA = cv.Schema(
}
)
@automation.register_action(
"aux_ac.vlouver_set", AirConVLouverSetAction, VLOUVER_SET_ACTION_SCHEMA
)
@@ -437,6 +510,41 @@ async def vlouver_set_to_code(config, action_id, template_arg, args):
return var
POWER_LIMITATION_OFF_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(AirCon),
}
)
@automation.register_action(
"aux_ac.power_limit_off", AirConPowerLimitationOffAction, POWER_LIMITATION_OFF_ACTION_SCHEMA
)
async def power_limit_off_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
POWER_LIMITATION_ON_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(AirCon),
cv.Optional(CONF_LIMIT, default=AC_POWER_LIMIT_MIN): validate_power_limit_range,
}
)
@automation.register_action(
"aux_ac.power_limit_on", AirConPowerLimitationOnAction, POWER_LIMITATION_ON_ACTION_SCHEMA
)
async def power_limit_on_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_LIMIT], args, int)
cg.add(var.set_value(template_))
return var
# *********************************************************************************************************
# ВАЖНО! Только для инженеров!
# Вызывайте метод aux_ac.send_packet только если понимаете, что делаете! Он не проверяет данные, а передаёт

View File

@@ -0,0 +1,162 @@
external_components:
- source: github://GrKoR/esphome_aux_ac_component@dev
components: [ aux_ac ]
refresh: 0s
substitutions:
devicename: test_ext_power_limitations
upper_devicename: Test AUX
esphome:
name: $devicename
platform: ESP8266
board: esp12e
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_pass
manual_ip:
static_ip: !secret wifi_ip
gateway: !secret wifi_gateway
subnet: !secret wifi_subnet
dns1: 8.8.8.8
dns2: 1.1.1.1
reboot_timeout: 0s
ap:
ssid: Test AUX Fallback Hotspot
password: !secret wifi_ap_pass
logger:
level: DEBUG
baud_rate: 0
api:
password: !secret api_pass
reboot_timeout: 0s
ota:
password: !secret ota_pass
web_server:
port: 80
uart:
id: ac_uart_bus
tx_pin: GPIO1
rx_pin: GPIO3
baud_rate: 4800
data_bits: 8
parity: EVEN
stop_bits: 1
sensor:
- platform: uptime
name: Uptime Sensor
climate:
- platform: aux_ac
name: $upper_devicename
id: aux_id
uart_id: ac_uart_bus
period: 7s
show_action: true
display_inverted: true
timeout: 150
indoor_temperature:
name: $upper_devicename Indoor Temperature
id: ${devicename}_indoor_temp
internal: false
display_state:
name: $upper_devicename Display State
id: ${devicename}_display_state
internal: false
outdoor_temperature:
name: $upper_devicename Outdoor Temperature
id: ${devicename}_outdoor_temp
internal: false
outbound_temperature:
name: $upper_devicename Colant Outbound Temperature
id: ${devicename}_outbound_temp
internal: false
inbound_temperature:
name: $upper_devicename Colant Inbound Temperature
id: ${devicename}_inbound_temp
internal: false
compressor_temperature:
name: $upper_devicename Compressor Temperature
id: ${devicename}_strange_temp
internal: false
defrost_state:
name: $upper_devicename Defrost State
id: ${devicename}_defrost_state
internal: false
inverter_power:
name: $upper_devicename Inverter Power
id: ${devicename}_invertor_power
internal: false
preset_reporter:
name: $upper_devicename Preset Reporter
id: ${devicename}_preset_reporter
internal: false
inverter_power_limit_value:
name: $upper_devicename Inverter Power Limit Value
id: ${devicename}_inverter_power_limit_value
internal: false
inverter_power_limit_state:
name: $upper_devicename Inverter Power Limit State
id: ${devicename}_inverter_power_limit_state
internal: false
visual:
min_temperature: 16
max_temperature: 32
temperature_step: 0.5
supported_modes:
- HEAT_COOL
- COOL
- HEAT
- DRY
- FAN_ONLY
custom_fan_modes:
- MUTE
- TURBO
supported_presets:
- SLEEP
custom_presets:
- CLEAN
- HEALTH
- ANTIFUNGUS
supported_swing_modes:
- VERTICAL
- HORIZONTAL
- BOTH
button:
- platform: template
name: ${upper_devicename} IPower Limit Off
icon: "mdi:power-plug-off-outline"
on_press:
- aux_ac.power_limit_off: aux_id
- platform: template
name: ${upper_devicename} IPower Limit On Half
icon: "mdi:fraction-one-half"
on_press:
- aux_ac.power_limit_on:
id: aux_id
limit: 50
number:
- platform: template
name: ${upper_devicename} IPower Limit Value
id: ${devicename}_ipower_limit_value
icon: "mdi:battery-unknown"
mode: "slider"
min_value: 30
max_value: 100
step: 1
set_action:
then:
- lambda: !lambda |-
id(aux_id).powerLimitationOnSequence( x );

View File

@@ -0,0 +1,162 @@
external_components:
- source:
type: local
path: ../components
substitutions:
devicename: test_local_power_limitations
upper_devicename: Test AUX
esphome:
name: $devicename
platform: ESP8266
board: esp12e
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_pass
manual_ip:
static_ip: !secret wifi_ip
gateway: !secret wifi_gateway
subnet: !secret wifi_subnet
dns1: 8.8.8.8
dns2: 1.1.1.1
reboot_timeout: 0s
ap:
ssid: Test AUX Fallback Hotspot
password: !secret wifi_ap_pass
logger:
level: DEBUG
baud_rate: 0
api:
password: !secret api_pass
reboot_timeout: 0s
ota:
password: !secret ota_pass
web_server:
port: 80
uart:
id: ac_uart_bus
tx_pin: GPIO1
rx_pin: GPIO3
baud_rate: 4800
data_bits: 8
parity: EVEN
stop_bits: 1
sensor:
- platform: uptime
name: Uptime Sensor
climate:
- platform: aux_ac
name: $upper_devicename
id: aux_id
uart_id: ac_uart_bus
period: 7s
show_action: true
display_inverted: true
timeout: 150
indoor_temperature:
name: $upper_devicename Indoor Temperature
id: ${devicename}_indoor_temp
internal: false
display_state:
name: $upper_devicename Display State
id: ${devicename}_display_state
internal: false
outdoor_temperature:
name: $upper_devicename Outdoor Temperature
id: ${devicename}_outdoor_temp
internal: false
outbound_temperature:
name: $upper_devicename Colant Outbound Temperature
id: ${devicename}_outbound_temp
internal: false
inbound_temperature:
name: $upper_devicename Colant Inbound Temperature
id: ${devicename}_inbound_temp
internal: false
compressor_temperature:
name: $upper_devicename Compressor Temperature
id: ${devicename}_strange_temp
internal: false
defrost_state:
name: $upper_devicename Defrost State
id: ${devicename}_defrost_state
internal: false
inverter_power:
name: $upper_devicename Inverter Power
id: ${devicename}_invertor_power
internal: false
preset_reporter:
name: $upper_devicename Preset Reporter
id: ${devicename}_preset_reporter
internal: false
inverter_power_limit_value:
name: $upper_devicename Inverter Power Limit Value
id: ${devicename}_inverter_power_limit_value
internal: false
inverter_power_limit_state:
name: $upper_devicename Inverter Power Limit State
id: ${devicename}_inverter_power_limit_state
internal: false
visual:
min_temperature: 16
max_temperature: 32
temperature_step: 0.5
supported_modes:
- HEAT_COOL
- COOL
- HEAT
- DRY
- FAN_ONLY
custom_fan_modes:
- MUTE
- TURBO
supported_presets:
- SLEEP
custom_presets:
- CLEAN
- HEALTH
- ANTIFUNGUS
supported_swing_modes:
- VERTICAL
- HORIZONTAL
- BOTH
button:
- platform: template
name: ${upper_devicename} IPower Limit Off
icon: "mdi:power-plug-off-outline"
on_press:
- aux_ac.power_limit_off: aux_id
- platform: template
name: ${upper_devicename} IPower Limit On Half
icon: "mdi:fraction-one-half"
on_press:
- aux_ac.power_limit_on:
id: aux_id
limit: 50
number:
- platform: template
name: ${upper_devicename} IPower Limit Value
id: ${devicename}_ipower_limit_value
icon: "mdi:battery-unknown"
mode: "slider"
min_value: 30
max_value: 100
step: 1
set_action:
then:
- lambda: !lambda |-
id(aux_id).powerLimitationOnSequence( x );