diff --git a/components/aux_ac/automation.h b/components/aux_ac/automation.h index 21bd374..50129e7 100644 --- a/components/aux_ac/automation.h +++ b/components/aux_ac/automation.h @@ -154,5 +154,33 @@ class AirConSendTestPacketAction : public Action { std::vector data_static_{}; }; +// **************************************** POWER LIMITATION ACTIONS **************************************** +template +class AirConPowerLimitationOffAction : public Action { + public: + explicit AirConPowerLimitationOffAction(AirCon *ac) : ac_(ac) {} + + void play(Ts... x) override { this->ac_->powerLimitationOffSequence(); } + + protected: + AirCon *ac_; +}; + +template +class AirConPowerLimitationOnAction : public Action { + 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 \ No newline at end of file diff --git a/components/aux_ac/aux_ac.h b/components/aux_ac/aux_ac.h index 639484b..3d2e6dd 100644 --- a/components/aux_ac/aux_ac.h +++ b/components/aux_ac/aux_ac.h @@ -56,13 +56,17 @@ 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; }; -const std::string Constants::AC_FIRMWARE_VERSION = "0.2.8"; +const std::string Constants::AC_FIRMWARE_VERSION = "0.2.9 dev"; // custom fan modes const std::string Constants::MUTE = "mute"; @@ -77,6 +81,8 @@ 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; class AirCon; @@ -319,20 +325,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 + bool inverter_power_limitation_enable : 1; + uint8_t inverter_power_limitation_value : 7; + + // байт 22 пакета: https://github.com/GrKoR/AUX_HVAC_Protocol#packet_cmd_11_b22 uint8_t target_temp_frac2; }; @@ -475,6 +511,10 @@ 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 + // положение вертикальных жалюзи для фронтенда enum ac_vlouver_frontend : uint8_t { AC_VLOUVER_FRONTEND_SWING = 0x00, @@ -546,6 +586,8 @@ struct ac_command_t { ac_realFan realFanSpeed; // текущая скорость вентилятора uint8_t invertor_power; // мощность инвертора bool defrost; // режим разморозки внешнего блока (накопление тепла + прогрев испарителя) + bool inverter_power_limitation_enable; // ограничение мощности инвертора + uint8_t inverter_power_limitation_value; // значение ограничения мощности инвертора }; typedef ac_command_t ac_state_t; // текущее состояние параметров кондея можно хранить в таком же формате, как и комманды @@ -713,13 +755,6 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { // сырые данные последних полученных большого и маленького информационных пакетов 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 +767,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++) { @@ -909,6 +958,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { cmd->realFanSpeed = AC_REAL_FAN_UNTOUCHED; cmd->invertor_power = 0; cmd->defrost = false; + cmd->inverter_power_limitation_enable =false; + cmd->inverter_power_limitation_value = 0; }; // очистка буфера размером AC_BUFFER_SIZE @@ -1238,6 +1289,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(); @@ -1256,7 +1317,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { // тип кондея (инвертор или старт стоп) _is_invertor = big_info_body->is_invertor; - + // температура воздуха в помещении по версии сплит-системы 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); @@ -1309,8 +1370,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { case AC_CMD_SET_PARAMS: { // такой статусный пакет присылается кондиционером в ответ на команду установки параметров // в теле пакета нет ничего примечательного - // в байтах 2 и 3 тела похоже передается CRC пакета поступившей команды, на которую сплит отвечает - // но я решил этот момент тут не проверять и не контролировать. + // в байтах 2 и 3 тела передается CRC пакета поступившей команды, на которую сплит отвечает + // я решил этот момент тут не проверять и не контролировать. // корректную установку параметров можно определить, запросив статус кондиционера сразу после получения этой команды кондея // в настоящий момент проверка сделана в механизме sequences break; @@ -1604,6 +1665,11 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { } } + // ограничение мощности инвертора + pack->body[13] = (pack->body[13] & ~AC_INVERTER_POWER_LIMITATION_ENABLE_MASK) | cmd->inverter_power_limitation_enable; + 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 ; @@ -1874,6 +1940,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { 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_invertor_power_limit_value_ = nullptr; + esphome::binary_sensor::BinarySensor *sensor_invertor_power_limit_state_ = nullptr; // загружает на выполнение последовательность команд на включение/выключение табло с температурой bool _displaySequence(ac_display dsp = AC_DISPLAY_ON) { @@ -1991,6 +2059,8 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { 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_preset_reporter_sensor(text_sensor::TextSensor *preset_reporter_sensor) { sensor_preset_reporter_ = preset_reporter_sensor; } + void set_invertor_power_limit_value_sensor(sensor::Sensor *invertor_power_limit_value_sensor) { sensor_invertor_power_limit_value_ = invertor_power_limit_value_sensor; } + void set_invertor_power_limit_state_sensor(binary_sensor::BinarySensor *invertor_power_limit_state_sensor) { sensor_invertor_power_limit_state_ = invertor_power_limit_state_sensor; } bool get_hw_initialized() { return _hw_initialized; }; bool get_has_connection() { return _has_connection; }; @@ -2314,6 +2384,12 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { // положение вертикальных жалюзи if (sensor_vlouver_state_ != nullptr) sensor_vlouver_state_->publish_state((float)this->getCurrentVlouverFrontendState()); + // флаг включенного ограничения мощности инвертора + if (sensor_invertor_power_limit_state_ != nullptr) + sensor_invertor_power_limit_state_->publish_state(_current_ac_state.inverter_power_limitation_enable); + // значение ограничения мощности инвертора + if (sensor_invertor_power_limit_value_ != nullptr) + sensor_invertor_power_limit_value_->publish_state(_current_ac_state.inverter_power_limitation_value); // сенсор состояния сплита if (sensor_preset_reporter_ != nullptr) { @@ -2965,6 +3041,53 @@ 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_invertor) return false; // если кондиционер не инверторный, то выходим + + // формируем команду + ac_command_t cmd; + _clearCommand(&cmd); // не забываем очищать, а то будет мусор + cmd.inverter_power_limitation_value = this->_current_ac_state.inverter_power_limitation_value; + cmd.inverter_power_limitation_enable = false; + // добавляем команду в последовательность + 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_invertor) 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 +3171,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; } diff --git a/components/aux_ac/climate.py b/components/aux_ac/climate.py index 68e3612..a0caae7 100644 --- a/components/aux_ac/climate.py +++ b/components/aux_ac/climate.py @@ -5,16 +5,17 @@ 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_UART_ID, UNIT_CELSIUS, UNIT_PERCENT, ICON_POWER, @@ -22,7 +23,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 +37,41 @@ 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_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) @@ -89,8 +104,18 @@ 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 +) -AirConVLouverSetAction = aux_ac_ns.class_("AirConVLouverSetAction", automation.Action) ALLOWED_CLIMATE_MODES = { "HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL, @@ -240,6 +265,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 @@ -316,6 +359,16 @@ async def to_code(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_invertor_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_invertor_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])) @@ -332,13 +385,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 +399,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 +407,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 +421,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 +428,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 +435,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 +442,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 +449,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 +456,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 +464,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 +472,6 @@ VLOUVER_SET_ACTION_SCHEMA = cv.Schema( } ) - @automation.register_action( "aux_ac.vlouver_set", AirConVLouverSetAction, VLOUVER_SET_ACTION_SCHEMA ) @@ -437,6 +483,43 @@ 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=Capabilities.AC_MIN_INVERTER_POWER_LIMIT): cv.templatable( + cv.int_range(30, 100) + ), + } +) + +@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 только если понимаете, что делаете! Он не проверяет данные, а передаёт diff --git a/tests/test-ext-power-limit.yaml b/tests/test-ext-power-limit.yaml new file mode 100644 index 0000000..2bdcc82 --- /dev/null +++ b/tests/test-ext-power-limit.yaml @@ -0,0 +1,162 @@ +external_components: + - source: github://GrKoR/esphome_aux_ac_component@dev + components: [ aux_ac ] + refresh: 0s + +substitutions: + devicename: test_local_airflow_dir + 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 + 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 + invertor_power: + name: $upper_devicename Invertor 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 ); + diff --git a/tests/test-local-power-limit.yaml b/tests/test-local-power-limit.yaml new file mode 100644 index 0000000..3c12f30 --- /dev/null +++ b/tests/test-local-power-limit.yaml @@ -0,0 +1,162 @@ +external_components: + - source: + type: local + path: ../components + +substitutions: + devicename: test_local_airflow_dir + 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 + 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 + invertor_power: + name: $upper_devicename Invertor 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 ); +