From 4644ee08e22f408559c1eff68b0af40459a6ff27 Mon Sep 17 00:00:00 2001 From: Brokly Date: Thu, 26 May 2022 16:10:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BB=D0=B8=D0=BB/=D0=BF=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=B8=D0=BB.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/aux_ac/automation.h | 4 +-- components/aux_ac/aux_ac.h | 26 ++++++++--------- components/aux_ac/climate.py | 52 ++++++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/components/aux_ac/automation.h b/components/aux_ac/automation.h index 7a31105..4d137b6 100644 --- a/components/aux_ac/automation.h +++ b/components/aux_ac/automation.h @@ -17,7 +17,7 @@ namespace aux_ac { protected: AirCon *ac_; - }; + }; template class AirConDisplayOnAction : public Action @@ -59,7 +59,7 @@ namespace aux_ac { bool static_{false}; std::function(Ts...)> data_func_{}; std::vector data_static_{}; - }; + }; } // 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 1f0dd34..c8a53c3 100644 --- a/components/aux_ac/aux_ac.h +++ b/components/aux_ac/aux_ac.h @@ -452,13 +452,13 @@ enum ac_power : uint8_t { AC_POWER_OFF = 0x00, AC_POWER_ON = 0x20, AC_POWER_UNTO enum ac_clean : uint8_t { AC_CLEAN_OFF = 0x00, AC_CLEAN_ON = 0x04, AC_CLEAN_UNTOUCHED = 0xFF }; // для включения ионизатора нужно установить второй бит в байте -// по результату этот бит останется установленным +// по результату этот бит останется установленным, но кондиционер еще и установит первый бит #define AC_HEALTH_MASK 0b00000010 enum ac_health : uint8_t { AC_HEALTH_OFF = 0x00, AC_HEALTH_ON = 0x02, AC_HEALTH_UNTOUCHED = 0xFF }; // Статус ионизатора. Если бит поднят, то обнаружена ошибка ключения ионизатора #define AC_HEALTH_STATUS_MASK 0b00000001 -enum ac_health_status : uint8_t { AC_HEALTH_ERROR_NO = 0x00, AC_HEALTH_ERROR_ACT = 0x01, AC_HEALTH_STATUS_UNTOUCHED = 0xFF }; +enum ac_health_status : uint8_t { AC_HEALTH_STATUS_OFF = 0x00, AC_HEALTH_STATUS_ON = 0x01, AC_HEALTH_STATUS_UNTOUCHED = 0xFF }; // целевая температура #define AC_TEMP_TARGET_INT_PART_MASK 0b11111000 @@ -756,7 +756,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { // входящий и исходящий пакеты packet_t _inPacket; packet_t _outPacket; - + // пакет для тестирования всякой фигни packet_t _outTestPacket; @@ -915,7 +915,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { break; } } - + // заполняет структуру команды нейтральными значениями void _clearCommand(ac_command_t * cmd){ cmd->clean = AC_CLEAN_UNTOUCHED; @@ -944,7 +944,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { cmd->temp_strange = 0; cmd->realFanSpeed = AC_REAL_FAN_UNTOUCHED; }; - + // очистка буфера размером AC_BUFFER_SIZE void _clearBuffer(uint8_t * buf){ memset(buf, 0, AC_BUFFER_SIZE); @@ -1287,7 +1287,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { stateByte = small_info_body->display_and_mildew & AC_MILDEW_MASK; stateChangedFlag = stateChangedFlag || (_current_ac_state.mildew != (ac_mildew)stateByte); _current_ac_state.mildew = (ac_mildew)stateByte; - + // уведомляем об изменении статуса сплита if (stateChangedFlag) stateChanged(); @@ -2000,9 +2000,9 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { _clearPacket(&_outTestPacket); _outTestPacket.header->start_byte = AC_PACKET_START_BYTE; - _outTestPacket.header->wifi = AC_PACKET_ANSWER; - - _setStateMachineState(ACSM_IDLE); + _outTestPacket.header->wifi = AC_PACKET_ANSWER; + + _setStateMachineState(ACSM_IDLE); _ac_serial = parent; _hw_initialized = (_ac_serial != nullptr); _has_connection = false; @@ -2772,7 +2772,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { cmd.sleep = AC_SLEEP_ON; cmd.iFeel = AC_IFEEL_OFF; // для логики пресетов cmd.health = AC_HEALTH_OFF; // для логики пресетов - cmd.health_status = AC_HEALTH_ERROR_NO; + cmd.health_status = AC_HEALTH_STATUS_OFF; this->preset = preset; } else { _debugMsg(F("SLEEP preset is suitable in COOL,HEAT and AUTO modes only."), ESPHOME_LOG_LEVEL_VERBOSE, __LINE__); @@ -2782,7 +2782,7 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { // выбран пустой пресет, сбрасываем все настройки hasCommand = true; cmd.health = AC_HEALTH_OFF; // для логики пресетов - cmd.health_status = AC_HEALTH_ERROR_NO; + cmd.health_status = AC_HEALTH_STATUS_OFF; cmd.sleep = AC_SLEEP_OFF; // для логики пресетов cmd.iFeel = AC_IFEEL_OFF; // для логики пресетов this->preset = preset; @@ -2802,13 +2802,13 @@ class AirCon : public esphome::Component, public esphome::climate::Climate { hasCommand = true; cmd.iFeel = AC_IFEEL_ON; cmd.health = AC_HEALTH_OFF; // для логики пресетов - cmd.health_status = AC_HEALTH_ERROR_NO; + cmd.health_status = AC_HEALTH_STATUS_OFF; cmd.sleep = AC_SLEEP_OFF; // для логики пресетов this->custom_preset = custom_preset; } else if (custom_preset == Constants::HEALTH) { hasCommand = true; cmd.health = AC_HEALTH_ON; - cmd.health_status = AC_HEALTH_ERROR_ACT; + cmd.health_status = AC_HEALTH_STATUS_ON; cmd.iFeel = AC_IFEEL_ON; // зависимость от health cmd.fanTurbo = AC_FANTURBO_OFF; // зависимость от health cmd.fanMute = AC_FANMUTE_OFF; // зависимость от health diff --git a/components/aux_ac/climate.py b/components/aux_ac/climate.py index 5c98437..9d5796e 100644 --- a/components/aux_ac/climate.py +++ b/components/aux_ac/climate.py @@ -11,6 +11,7 @@ from esphome.const import ( CONF_CUSTOM_FAN_MODES, CONF_CUSTOM_PRESETS, CONF_INTERNAL, + CONF_DATA, CONF_SUPPORTED_MODES, CONF_SUPPORTED_SWING_MODES, CONF_SUPPORTED_PRESETS, @@ -66,6 +67,7 @@ Capabilities = aux_ac_ns.namespace("Constants") AirConDisplayOffAction = aux_ac_ns.class_("AirConDisplayOffAction", automation.Action) AirConDisplayOnAction = aux_ac_ns.class_("AirConDisplayOnAction", automation.Action) +AirConSendTestPacketAction = aux_ac_ns.class_("AirConSendTestPacketAction", automation.Action) ALLOWED_CLIMATE_MODES = { "HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL, @@ -102,6 +104,14 @@ CUSTOM_PRESETS = { } validate_custom_presets = cv.enum(CUSTOM_PRESETS, upper=True) +def validate_raw_data(value): + if isinstance(value, list): + return cv.Schema([cv.hex_uint8_t])(value) + raise cv.Invalid( + "data must be a list of bytes" + ) + + def output_info(config): """_LOGGER.info(config)""" return config @@ -283,11 +293,47 @@ DISPLAY_ACTION_SCHEMA = maybe_simple_id( ) @automation.register_action("aux_ac.display_off", AirConDisplayOffAction, DISPLAY_ACTION_SCHEMA) -async def switch_toggle_to_code(config, action_id, template_arg, args): +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) -async def switch_toggle_to_code(config, action_id, template_arg, args): +async def display_on_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) \ No newline at end of file + return cg.new_Pvariable(action_id, template_arg, paren) + + +SEND_TEST_PACKET_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(AirCon), + cv.Required(CONF_DATA): cv.templatable(validate_raw_data), + } +) + + +# ********************************************************************************************************* +# ВАЖНО! Только для инженеров! +# Вызывайте метод aux_ac.send_packet только если понимаете, что делаете! Он не проверяет данные, а передаёт +# кондиционеру всё как есть. Какой эффект получится от передачи кондиционеру рандомных байт, никто не знает. +# Вы действуете на свой страх и риск. +# ********************************************************************************************************* +@automation.register_action( + "aux_ac.send_packet", + AirConSendTestPacketAction, + SEND_TEST_PACKET_ACTION_SCHEMA +) +async def send_packet_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) + + data = config[CONF_DATA] + if isinstance(data, bytes): + data = list(data) + + if cg.is_template(data): + templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8)) + cg.add(var.set_data_template(templ)) + else: + cg.add(var.set_data_static(data)) + + return var \ No newline at end of file