mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Merge branch 'dev' into jesserockz-2023-284
This commit is contained in:
		
							
								
								
									
										11
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								CODEOWNERS
									
									
									
									
									
								
							| @@ -54,7 +54,7 @@ esphome/components/bp1658cj/* @Cossid | ||||
| esphome/components/bp5758d/* @Cossid | ||||
| esphome/components/button/* @esphome/core | ||||
| esphome/components/canbus/* @danielschramm @mvturnho | ||||
| esphome/components/cap1188/* @MrEditor97 | ||||
| esphome/components/cap1188/* @mreditor97 | ||||
| esphome/components/captive_portal/* @OttoWinter | ||||
| esphome/components/ccs811/* @habbie | ||||
| esphome/components/cd74hc4067/* @asoehlke | ||||
| @@ -87,7 +87,7 @@ esphome/components/ens210/* @itn3rd77 | ||||
| esphome/components/esp32/* @esphome/core | ||||
| esphome/components/esp32_ble/* @jesserockz | ||||
| esphome/components/esp32_ble_client/* @jesserockz | ||||
| esphome/components/esp32_ble_server/* @jesserockz | ||||
| esphome/components/esp32_ble_server/* @clydebarrow @jesserockz | ||||
| esphome/components/esp32_camera_web_server/* @ayufan | ||||
| esphome/components/esp32_can/* @Sympatron | ||||
| esphome/components/esp32_improv/* @jesserockz | ||||
| @@ -133,7 +133,7 @@ esphome/components/i2s_audio/speaker/* @jesserockz | ||||
| esphome/components/ili9xxx/* @nielsnl68 | ||||
| esphome/components/improv_base/* @esphome/core | ||||
| esphome/components/improv_serial/* @esphome/core | ||||
| esphome/components/ina260/* @MrEditor97 | ||||
| esphome/components/ina260/* @mreditor97 | ||||
| esphome/components/inkbird_ibsth1_mini/* @fkirill | ||||
| esphome/components/inkplate6/* @jesserockz | ||||
| esphome/components/integration/* @OttoWinter | ||||
| @@ -145,7 +145,7 @@ esphome/components/key_collector/* @ssieb | ||||
| esphome/components/key_provider/* @ssieb | ||||
| esphome/components/kuntze/* @ssieb | ||||
| esphome/components/lcd_menu/* @numo68 | ||||
| esphome/components/ld2410/* @sebcaps | ||||
| esphome/components/ld2410/* @regevbr @sebcaps | ||||
| esphome/components/ledc/* @OttoWinter | ||||
| esphome/components/light/* @esphome/core | ||||
| esphome/components/lilygo_t5_47/touchscreen/* @jesserockz | ||||
| @@ -169,7 +169,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho | ||||
| esphome/components/mcp3204/* @rsumner | ||||
| esphome/components/mcp4728/* @berfenger | ||||
| esphome/components/mcp47a1/* @jesserockz | ||||
| esphome/components/mcp9600/* @MrEditor97 | ||||
| esphome/components/mcp9600/* @mreditor97 | ||||
| esphome/components/mcp9808/* @k7hpn | ||||
| esphome/components/md5/* @esphome/core | ||||
| esphome/components/mdns/* @esphome/core | ||||
| @@ -213,6 +213,7 @@ esphome/components/pid/* @OttoWinter | ||||
| esphome/components/pipsolar/* @andreashergert1984 | ||||
| esphome/components/pm1006/* @habbie | ||||
| esphome/components/pmsa003i/* @sjtrny | ||||
| esphome/components/pmwcs3/* @SeByDocKy | ||||
| esphome/components/pn532/* @OttoWinter @jesserockz | ||||
| esphome/components/pn532_i2c/* @OttoWinter @jesserockz | ||||
| esphome/components/pn532_spi/* @OttoWinter @jesserockz | ||||
|   | ||||
| @@ -28,14 +28,15 @@ RUN \ | ||||
|         git=1:2.30.2-1+deb11u2 \ | ||||
|         curl=7.74.0-1.3+deb11u7 \ | ||||
|         openssh-client=1:8.4p1-5+deb11u1 \ | ||||
|         python3-cffi=1.14.5-1; \ | ||||
|         python3-cffi=1.14.5-1 \ | ||||
|         libcairo2=1.16.0-5; \ | ||||
|     if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||
|         apt-get install -y --no-install-recommends \ | ||||
|           build-essential=12.9 \ | ||||
|           python3-dev=3.9.2-3 \ | ||||
|           zlib1g-dev=1:1.2.11.dfsg-2+deb11u2 \ | ||||
|           libjpeg-dev=1:2.0.6-4 \ | ||||
|           libcairo2=1.16.0-5; \ | ||||
|           libfreetype-dev=2.10.4+dfsg-1+deb11u1; \ | ||||
|     fi; \ | ||||
|     rm -rf \ | ||||
|         /tmp/* \ | ||||
| @@ -60,7 +61,7 @@ RUN \ | ||||
|     # Ubuntu python3-pip is missing wheel | ||||
|     pip3 install --no-cache-dir \ | ||||
|         wheel==0.37.1 \ | ||||
|         platformio==6.1.7 \ | ||||
|         platformio==6.1.10 \ | ||||
|     # Change some platformio settings | ||||
|     && platformio settings set enable_telemetry No \ | ||||
|     && platformio settings set check_platformio_interval 1000000 \ | ||||
|   | ||||
| @@ -7,7 +7,9 @@ from esphome.core import CORE | ||||
| from esphome.components.esp32 import get_esp32_variant | ||||
| from esphome.components.esp32.const import ( | ||||
|     VARIANT_ESP32, | ||||
|     VARIANT_ESP32C2, | ||||
|     VARIANT_ESP32C3, | ||||
|     VARIANT_ESP32C6, | ||||
|     VARIANT_ESP32H2, | ||||
|     VARIANT_ESP32S2, | ||||
|     VARIANT_ESP32S3, | ||||
| @@ -70,6 +72,22 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = { | ||||
|         3: adc1_channel_t.ADC1_CHANNEL_3, | ||||
|         4: adc1_channel_t.ADC1_CHANNEL_4, | ||||
|     }, | ||||
|     VARIANT_ESP32C2: { | ||||
|         0: adc1_channel_t.ADC1_CHANNEL_0, | ||||
|         1: adc1_channel_t.ADC1_CHANNEL_1, | ||||
|         2: adc1_channel_t.ADC1_CHANNEL_2, | ||||
|         3: adc1_channel_t.ADC1_CHANNEL_3, | ||||
|         4: adc1_channel_t.ADC1_CHANNEL_4, | ||||
|     }, | ||||
|     VARIANT_ESP32C6: { | ||||
|         0: adc1_channel_t.ADC1_CHANNEL_0, | ||||
|         1: adc1_channel_t.ADC1_CHANNEL_1, | ||||
|         2: adc1_channel_t.ADC1_CHANNEL_2, | ||||
|         3: adc1_channel_t.ADC1_CHANNEL_3, | ||||
|         4: adc1_channel_t.ADC1_CHANNEL_4, | ||||
|         5: adc1_channel_t.ADC1_CHANNEL_5, | ||||
|         6: adc1_channel_t.ADC1_CHANNEL_6, | ||||
|     }, | ||||
|     VARIANT_ESP32H2: { | ||||
|         0: adc1_channel_t.ADC1_CHANNEL_0, | ||||
|         1: adc1_channel_t.ADC1_CHANNEL_1, | ||||
|   | ||||
| @@ -9,7 +9,7 @@ CONF_ALLOW_MULTIPLE_TOUCHES = "allow_multiple_touches" | ||||
|  | ||||
| DEPENDENCIES = ["i2c"] | ||||
| AUTO_LOAD = ["binary_sensor", "output"] | ||||
| CODEOWNERS = ["@MrEditor97"] | ||||
| CODEOWNERS = ["@mreditor97"] | ||||
|  | ||||
| cap1188_ns = cg.esphome_ns.namespace("cap1188") | ||||
| CONF_CAP1188_ID = "cap1188_id" | ||||
|   | ||||
| @@ -38,7 +38,7 @@ void CurrentBasedCover::control(const CoverCall &call) { | ||||
|   } | ||||
|   if (call.get_position().has_value()) { | ||||
|     auto pos = *call.get_position(); | ||||
|     if (pos == this->position) { | ||||
|     if (fabsf(this->position - pos) < 0.01) { | ||||
|       // already at target | ||||
|     } else { | ||||
|       auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; | ||||
|   | ||||
| @@ -12,11 +12,15 @@ | ||||
| #include <esp_heap_caps.h> | ||||
| #include <esp_system.h> | ||||
|  | ||||
| #if ESP_IDF_VERSION_MAJOR >= 4 | ||||
| #include <esp32/rom/rtc.h> | ||||
| #include <esp_chip_info.h> | ||||
| #else | ||||
| #include <rom/rtc.h> | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
| #include <esp32/rom/rtc.h> | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32C3) | ||||
| #include <esp32c3/rom/rtc.h> | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32S2) | ||||
| #include <esp32s2/rom/rtc.h> | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32S3) | ||||
| #include <esp32s3/rom/rtc.h> | ||||
| #endif | ||||
|  | ||||
| #endif  // USE_ESP32 | ||||
| @@ -109,13 +113,19 @@ void DebugComponent::dump_config() { | ||||
|   esp_chip_info_t info; | ||||
|   esp_chip_info(&info); | ||||
|   const char *model; | ||||
|   switch (info.model) { | ||||
|     case CHIP_ESP32: | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
|   model = "ESP32"; | ||||
|       break; | ||||
|     default: | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32C3) | ||||
|   model = "ESP32-C3"; | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32S2) | ||||
|   model = "ESP32-S2"; | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|   model = "ESP32-S3"; | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32H2) | ||||
|   model = "ESP32-H2"; | ||||
| #else | ||||
|   model = "UNKNOWN"; | ||||
|   } | ||||
| #endif | ||||
|   std::string features; | ||||
|   if (info.features & CHIP_FEATURE_EMB_FLASH) { | ||||
|     features += "EMB_FLASH,"; | ||||
| @@ -157,18 +167,26 @@ void DebugComponent::dump_config() { | ||||
|     case POWERON_RESET: | ||||
|       reset_reason = "Power On Reset"; | ||||
|       break; | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
|     case SW_RESET: | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|     case RTC_SW_SYS_RESET: | ||||
| #endif | ||||
|       reset_reason = "Software Reset Digital Core"; | ||||
|       break; | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
|     case OWDT_RESET: | ||||
|       reset_reason = "Watch Dog Reset Digital Core"; | ||||
|       break; | ||||
| #endif | ||||
|     case DEEPSLEEP_RESET: | ||||
|       reset_reason = "Deep Sleep Reset Digital Core"; | ||||
|       break; | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
|     case SDIO_RESET: | ||||
|       reset_reason = "SLC Module Reset Digital Core"; | ||||
|       break; | ||||
| #endif | ||||
|     case TG0WDT_SYS_RESET: | ||||
|       reset_reason = "Timer Group 0 Watch Dog Reset Digital Core"; | ||||
|       break; | ||||
| @@ -181,24 +199,61 @@ void DebugComponent::dump_config() { | ||||
|     case INTRUSION_RESET: | ||||
|       reset_reason = "Intrusion Reset CPU"; | ||||
|       break; | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
|     case TGWDT_CPU_RESET: | ||||
|       reset_reason = "Timer Group Reset CPU"; | ||||
|       break; | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|     case TG0WDT_CPU_RESET: | ||||
|       reset_reason = "Timer Group 0 Reset CPU"; | ||||
|       break; | ||||
| #endif | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
|     case SW_CPU_RESET: | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|     case RTC_SW_CPU_RESET: | ||||
| #endif | ||||
|       reset_reason = "Software Reset CPU"; | ||||
|       break; | ||||
|     case RTCWDT_CPU_RESET: | ||||
|       reset_reason = "RTC Watch Dog Reset CPU"; | ||||
|       break; | ||||
| #if defined(USE_ESP32_VARIANT_ESP32) | ||||
|     case EXT_CPU_RESET: | ||||
|       reset_reason = "External CPU Reset"; | ||||
|       break; | ||||
| #endif | ||||
|     case RTCWDT_BROWN_OUT_RESET: | ||||
|       reset_reason = "Voltage Unstable Reset"; | ||||
|       break; | ||||
|     case RTCWDT_RTC_RESET: | ||||
|       reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; | ||||
|       break; | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|     case TG1WDT_CPU_RESET: | ||||
|       reset_reason = "Timer Group 1 Reset CPU"; | ||||
|       break; | ||||
|     case SUPER_WDT_RESET: | ||||
|       reset_reason = "Super Watchdog Reset Digital Core And RTC Module"; | ||||
|       break; | ||||
|     case GLITCH_RTC_RESET: | ||||
|       reset_reason = "Glitch Reset Digital Core And RTC Module"; | ||||
|       break; | ||||
|     case EFUSE_RESET: | ||||
|       reset_reason = "eFuse Reset Digital Core"; | ||||
|       break; | ||||
| #endif | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|     case USB_UART_CHIP_RESET: | ||||
|       reset_reason = "USB UART Reset Digital Core"; | ||||
|       break; | ||||
|     case USB_JTAG_CHIP_RESET: | ||||
|       reset_reason = "USB JTAG Reset Digital Core"; | ||||
|       break; | ||||
|     case POWER_GLITCH_RESET: | ||||
|       reset_reason = "Power Glitch Reset Digital Core And RTC Module"; | ||||
|       break; | ||||
| #endif | ||||
|     default: | ||||
|       reset_reason = "Unknown Reset Reason"; | ||||
|   } | ||||
|   | ||||
| @@ -22,6 +22,8 @@ from esphome.components.esp32.const import ( | ||||
|     VARIANT_ESP32C3, | ||||
|     VARIANT_ESP32S2, | ||||
|     VARIANT_ESP32S3, | ||||
|     VARIANT_ESP32C2, | ||||
|     VARIANT_ESP32C6, | ||||
| ) | ||||
|  | ||||
| WAKEUP_PINS = { | ||||
| @@ -94,6 +96,8 @@ WAKEUP_PINS = { | ||||
|         20, | ||||
|         21, | ||||
|     ], | ||||
|     VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5], | ||||
|     VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7], | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -44,7 +44,6 @@ from .const import (  # noqa | ||||
|     KEY_SDKCONFIG_OPTIONS, | ||||
|     KEY_SUBMODULES, | ||||
|     KEY_VARIANT, | ||||
|     VARIANT_ESP32C3, | ||||
|     VARIANT_FRIENDLY, | ||||
|     VARIANTS, | ||||
| ) | ||||
|   | ||||
| @@ -14,13 +14,17 @@ KEY_SUBMODULES = "submodules" | ||||
| VARIANT_ESP32 = "ESP32" | ||||
| VARIANT_ESP32S2 = "ESP32S2" | ||||
| VARIANT_ESP32S3 = "ESP32S3" | ||||
| VARIANT_ESP32C2 = "ESP32C2" | ||||
| VARIANT_ESP32C3 = "ESP32C3" | ||||
| VARIANT_ESP32C6 = "ESP32C6" | ||||
| VARIANT_ESP32H2 = "ESP32H2" | ||||
| VARIANTS = [ | ||||
|     VARIANT_ESP32, | ||||
|     VARIANT_ESP32S2, | ||||
|     VARIANT_ESP32S3, | ||||
|     VARIANT_ESP32C2, | ||||
|     VARIANT_ESP32C3, | ||||
|     VARIANT_ESP32C6, | ||||
|     VARIANT_ESP32H2, | ||||
| ] | ||||
|  | ||||
| @@ -28,7 +32,9 @@ VARIANT_FRIENDLY = { | ||||
|     VARIANT_ESP32: "ESP32", | ||||
|     VARIANT_ESP32S2: "ESP32-S2", | ||||
|     VARIANT_ESP32S3: "ESP32-S3", | ||||
|     VARIANT_ESP32C2: "ESP32-C2", | ||||
|     VARIANT_ESP32C3: "ESP32-C3", | ||||
|     VARIANT_ESP32C6: "ESP32-C6", | ||||
|     VARIANT_ESP32H2: "ESP32-H2", | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,9 +10,7 @@ | ||||
| #include <esp_timer.h> | ||||
| #include <soc/rtc.h> | ||||
|  | ||||
| #if ESP_IDF_VERSION_MAJOR >= 4 | ||||
| #include <hal/cpu_hal.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ARDUINO | ||||
| #include <esp32-hal.h> | ||||
| @@ -55,15 +53,7 @@ void arch_init() { | ||||
| void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); } | ||||
|  | ||||
| uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } | ||||
| uint32_t arch_get_cpu_cycle_count() { | ||||
| #if ESP_IDF_VERSION_MAJOR >= 4 | ||||
|   return cpu_hal_get_cycle_count(); | ||||
| #else | ||||
|   uint32_t ccount; | ||||
|   __asm__ __volatile__("esync; rsr %0,ccount" : "=a"(ccount)); | ||||
|   return ccount; | ||||
| #endif | ||||
| } | ||||
| uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); } | ||||
| uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); } | ||||
|  | ||||
| #ifdef USE_ESP_IDF | ||||
|   | ||||
| @@ -26,6 +26,8 @@ from .const import ( | ||||
|     VARIANT_ESP32C3, | ||||
|     VARIANT_ESP32S2, | ||||
|     VARIANT_ESP32S3, | ||||
|     VARIANT_ESP32C2, | ||||
|     VARIANT_ESP32C6, | ||||
|     VARIANT_ESP32H2, | ||||
|     esp32_ns, | ||||
| ) | ||||
| @@ -35,6 +37,8 @@ from .gpio_esp32 import esp32_validate_gpio_pin, esp32_validate_supports | ||||
| from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports | ||||
| from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports | ||||
| from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports | ||||
| from .gpio_esp32_c2 import esp32_c2_validate_gpio_pin, esp32_c2_validate_supports | ||||
| from .gpio_esp32_c6 import esp32_c6_validate_gpio_pin, esp32_c6_validate_supports | ||||
| from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports | ||||
|  | ||||
|  | ||||
| @@ -95,6 +99,14 @@ _esp32_validations = { | ||||
|         pin_validation=esp32_s3_validate_gpio_pin, | ||||
|         usage_validation=esp32_s3_validate_supports, | ||||
|     ), | ||||
|     VARIANT_ESP32C2: ESP32ValidationFunctions( | ||||
|         pin_validation=esp32_c2_validate_gpio_pin, | ||||
|         usage_validation=esp32_c2_validate_supports, | ||||
|     ), | ||||
|     VARIANT_ESP32C6: ESP32ValidationFunctions( | ||||
|         pin_validation=esp32_c6_validate_gpio_pin, | ||||
|         usage_validation=esp32_c6_validate_supports, | ||||
|     ), | ||||
|     VARIANT_ESP32H2: ESP32ValidationFunctions( | ||||
|         pin_validation=esp32_h2_validate_gpio_pin, | ||||
|         usage_validation=esp32_h2_validate_supports, | ||||
|   | ||||
							
								
								
									
										37
									
								
								esphome/components/esp32/gpio_esp32_c2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								esphome/components/esp32/gpio_esp32_c2.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import logging | ||||
|  | ||||
| from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER | ||||
|  | ||||
| import esphome.config_validation as cv | ||||
|  | ||||
| _ESP32C2_STRAPPING_PINS = {8, 9} | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def esp32_c2_validate_gpio_pin(value): | ||||
|     if value < 0 or value > 20: | ||||
|         raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)") | ||||
|     if value in _ESP32C2_STRAPPING_PINS: | ||||
|         _LOGGER.warning( | ||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" | ||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" | ||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", | ||||
|             value, | ||||
|         ) | ||||
|  | ||||
|     return value | ||||
|  | ||||
|  | ||||
| def esp32_c2_validate_supports(value): | ||||
|     num = value[CONF_NUMBER] | ||||
|     mode = value[CONF_MODE] | ||||
|     is_input = mode[CONF_INPUT] | ||||
|  | ||||
|     if num < 0 or num > 20: | ||||
|         raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)") | ||||
|  | ||||
|     if is_input: | ||||
|         # All ESP32 pins support input mode | ||||
|         pass | ||||
|     return value | ||||
							
								
								
									
										50
									
								
								esphome/components/esp32/gpio_esp32_c6.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								esphome/components/esp32/gpio_esp32_c6.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| import logging | ||||
|  | ||||
| from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER | ||||
|  | ||||
| import esphome.config_validation as cv | ||||
|  | ||||
| _ESP32C6_SPI_PSRAM_PINS = { | ||||
|     24: "SPICS0", | ||||
|     25: "SPIQ", | ||||
|     26: "SPIWP", | ||||
|     27: "VDD_SPI", | ||||
|     28: "SPIHD", | ||||
|     29: "SPICLK", | ||||
|     30: "SPID", | ||||
| } | ||||
|  | ||||
| _ESP32C6_STRAPPING_PINS = {8, 9, 15} | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| def esp32_c6_validate_gpio_pin(value): | ||||
|     if value < 0 or value > 23: | ||||
|         raise cv.Invalid(f"Invalid pin number: {value} (must be 0-23)") | ||||
|     if value in _ESP32C6_SPI_PSRAM_PINS: | ||||
|         raise cv.Invalid( | ||||
|             f"This pin cannot be used on ESP32-C6s and is already used by the SPI/PSRAM interface (function: {_ESP32C6_SPI_PSRAM_PINS[value]})" | ||||
|         ) | ||||
|     if value in _ESP32C6_STRAPPING_PINS: | ||||
|         _LOGGER.warning( | ||||
|             "GPIO%d is a Strapping PIN and should be avoided.\n" | ||||
|             "Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n" | ||||
|             "See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins", | ||||
|             value, | ||||
|         ) | ||||
|  | ||||
|     return value | ||||
|  | ||||
|  | ||||
| def esp32_c6_validate_supports(value): | ||||
|     num = value[CONF_NUMBER] | ||||
|     mode = value[CONF_MODE] | ||||
|     is_input = mode[CONF_INPUT] | ||||
|  | ||||
|     if num < 0 or num > 23: | ||||
|         raise cv.Invalid(f"Invalid pin number: {value} (must be 0-23)") | ||||
|     if is_input: | ||||
|         # All ESP32 pins support input mode | ||||
|         pass | ||||
|     return value | ||||
| @@ -42,9 +42,15 @@ void BLEAdvertising::remove_service_uuid(ESPBTUUID uuid) { | ||||
|                                  this->advertising_uuids_.end()); | ||||
| } | ||||
|  | ||||
| void BLEAdvertising::set_manufacturer_data(uint8_t *data, uint16_t size) { | ||||
|   this->advertising_data_.p_manufacturer_data = data; | ||||
|   this->advertising_data_.manufacturer_len = size; | ||||
| void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) { | ||||
|   delete[] this->advertising_data_.p_manufacturer_data; | ||||
|   this->advertising_data_.p_manufacturer_data = nullptr; | ||||
|   this->advertising_data_.manufacturer_len = data.size(); | ||||
|   if (!data.empty()) { | ||||
|     // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) | ||||
|     this->advertising_data_.p_manufacturer_data = new uint8_t[data.size()]; | ||||
|     memcpy(this->advertising_data_.p_manufacturer_data, data.data(), data.size()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void BLEAdvertising::start() { | ||||
| @@ -74,10 +80,14 @@ void BLEAdvertising::start() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (this->scan_response_) { | ||||
|     memcpy(&this->scan_response_data_, &this->advertising_data_, sizeof(esp_ble_adv_data_t)); | ||||
|     this->scan_response_data_.set_scan_rsp = true; | ||||
|     this->scan_response_data_.include_name = true; | ||||
|     this->scan_response_data_.include_txpower = true; | ||||
|     this->scan_response_data_.min_interval = 0; | ||||
|     this->scan_response_data_.max_interval = 0; | ||||
|     this->scan_response_data_.manufacturer_len = 0; | ||||
|     this->scan_response_data_.appearance = 0; | ||||
|     this->scan_response_data_.flag = 0; | ||||
|     err = esp_ble_gap_config_adv_data(&this->scan_response_data_); | ||||
| @@ -85,6 +95,7 @@ void BLEAdvertising::start() { | ||||
|       ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %d", err); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (this->advertising_data_.service_uuid_len > 0) { | ||||
|     delete[] this->advertising_data_.p_service_uuid; | ||||
|   | ||||
| @@ -20,7 +20,7 @@ class BLEAdvertising { | ||||
|   void remove_service_uuid(ESPBTUUID uuid); | ||||
|   void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; } | ||||
|   void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; } | ||||
|   void set_manufacturer_data(uint8_t *data, uint16_t size); | ||||
|   void set_manufacturer_data(const std::vector<uint8_t> &data); | ||||
|  | ||||
|   void start(); | ||||
|   void stop(); | ||||
|   | ||||
| @@ -6,11 +6,12 @@ from esphome.core import CORE | ||||
| from esphome.components.esp32 import add_idf_sdkconfig_option | ||||
|  | ||||
| AUTO_LOAD = ["esp32_ble"] | ||||
| CODEOWNERS = ["@jesserockz"] | ||||
| CODEOWNERS = ["@jesserockz", "@clydebarrow"] | ||||
| CONFLICTS_WITH = ["esp32_ble_beacon"] | ||||
| DEPENDENCIES = ["esp32"] | ||||
|  | ||||
| CONF_MANUFACTURER = "manufacturer" | ||||
| CONF_MANUFACTURER_DATA = "manufacturer_data" | ||||
|  | ||||
| esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server") | ||||
| BLEServer = esp32_ble_server_ns.class_( | ||||
| @@ -27,6 +28,7 @@ CONFIG_SCHEMA = cv.Schema( | ||||
|         cv.GenerateID(): cv.declare_id(BLEServer), | ||||
|         cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE), | ||||
|         cv.Optional(CONF_MANUFACTURER, default="ESPHome"): cv.string, | ||||
|         cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.hex_uint8_t]), | ||||
|         cv.Optional(CONF_MODEL): cv.string, | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
| @@ -42,6 +44,8 @@ async def to_code(config): | ||||
|     cg.add(var.set_parent(parent)) | ||||
|  | ||||
|     cg.add(var.set_manufacturer(config[CONF_MANUFACTURER])) | ||||
|     if CONF_MANUFACTURER_DATA in config: | ||||
|         cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA])) | ||||
|     if CONF_MODEL in config: | ||||
|         cg.add(var.set_model(config[CONF_MODEL])) | ||||
|     cg.add_define("USE_ESP32_BLE_SERVER") | ||||
|   | ||||
| @@ -68,6 +68,7 @@ void BLEServer::loop() { | ||||
|       if (this->device_information_service_->is_running()) { | ||||
|         this->state_ = RUNNING; | ||||
|         this->can_proceed_ = true; | ||||
|         this->restart_advertising_(); | ||||
|         ESP_LOGD(TAG, "BLE server setup successfully"); | ||||
|       } else if (!this->device_information_service_->is_starting()) { | ||||
|         this->device_information_service_->start(); | ||||
| @@ -77,6 +78,13 @@ void BLEServer::loop() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| void BLEServer::restart_advertising_() { | ||||
|   if (this->state_ == RUNNING) { | ||||
|     esp32_ble::global_ble->get_advertising()->set_manufacturer_data(this->manufacturer_data_); | ||||
|     esp32_ble::global_ble->get_advertising()->start(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool BLEServer::create_device_characteristics_() { | ||||
|   if (this->model_.has_value()) { | ||||
|     BLECharacteristic *model = | ||||
|   | ||||
| @@ -45,6 +45,10 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES | ||||
|  | ||||
|   void set_manufacturer(const std::string &manufacturer) { this->manufacturer_ = manufacturer; } | ||||
|   void set_model(const std::string &model) { this->model_ = model; } | ||||
|   void set_manufacturer_data(const std::vector<uint8_t> &data) { | ||||
|     this->manufacturer_data_ = data; | ||||
|     this->restart_advertising_(); | ||||
|   } | ||||
|  | ||||
|   std::shared_ptr<BLEService> create_service(const uint8_t *uuid, bool advertise = false); | ||||
|   std::shared_ptr<BLEService> create_service(uint16_t uuid, bool advertise = false); | ||||
| @@ -63,6 +67,7 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES | ||||
|  | ||||
|  protected: | ||||
|   bool create_device_characteristics_(); | ||||
|   void restart_advertising_(); | ||||
|  | ||||
|   void add_client_(uint16_t conn_id, void *client) { | ||||
|     this->clients_.insert(std::pair<uint16_t, void *>(conn_id, client)); | ||||
| @@ -73,6 +78,7 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES | ||||
|  | ||||
|   std::string manufacturer_; | ||||
|   optional<std::string> model_; | ||||
|   std::vector<uint8_t> manufacturer_data_; | ||||
|   esp_gatt_if_t gatts_if_{0}; | ||||
|   bool registered_{false}; | ||||
|  | ||||
|   | ||||
| @@ -63,6 +63,7 @@ RMT_CHANNELS = { | ||||
|     esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], | ||||
|     esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3], | ||||
|     esp32.const.VARIANT_ESP32C3: [0, 1], | ||||
|     esp32.const.VARIANT_ESP32C6: [0, 1], | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,11 +8,7 @@ | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| #if ESP_IDF_VERSION_MAJOR >= 4 | ||||
| #include <driver/touch_sensor.h> | ||||
| #else | ||||
| #include <driver/touch_pad.h> | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace esp32_touch { | ||||
|   | ||||
| @@ -14,7 +14,10 @@ from esphome.const import ( | ||||
|     CONF_MIN_TEMPERATURE, | ||||
|     CONF_PROTOCOL, | ||||
|     CONF_SUPPORTED_MODES, | ||||
|     CONF_SUPPORTED_PRESETS, | ||||
|     CONF_SUPPORTED_SWING_MODES, | ||||
|     CONF_TARGET_TEMPERATURE, | ||||
|     CONF_TEMPERATURE_STEP, | ||||
|     CONF_VISUAL, | ||||
|     CONF_WIFI, | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
| @@ -23,25 +26,29 @@ from esphome.const import ( | ||||
|     UNIT_CELSIUS, | ||||
| ) | ||||
| from esphome.components.climate import ( | ||||
|     ClimateSwingMode, | ||||
|     ClimateMode, | ||||
|     ClimatePreset, | ||||
|     ClimateSwingMode, | ||||
|     CONF_CURRENT_TEMPERATURE, | ||||
| ) | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| PROTOCOL_MIN_TEMPERATURE = 16.0 | ||||
| PROTOCOL_MAX_TEMPERATURE = 30.0 | ||||
| PROTOCOL_TEMPERATURE_STEP = 1.0 | ||||
| PROTOCOL_TARGET_TEMPERATURE_STEP = 1.0 | ||||
| PROTOCOL_CURRENT_TEMPERATURE_STEP = 0.5 | ||||
|  | ||||
| CODEOWNERS = ["@paveldn"] | ||||
| AUTO_LOAD = ["sensor"] | ||||
| DEPENDENCIES = ["climate", "uart"] | ||||
| CONF_WIFI_SIGNAL = "wifi_signal" | ||||
| CONF_ANSWER_TIMEOUT = "answer_timeout" | ||||
| CONF_DISPLAY = "display" | ||||
| CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" | ||||
| CONF_VERTICAL_AIRFLOW = "vertical_airflow" | ||||
| CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow" | ||||
|  | ||||
|  | ||||
| PROTOCOL_HON = "HON" | ||||
| PROTOCOL_SMARTAIR2 = "SMARTAIR2" | ||||
| PROTOCOLS_SUPPORTED = [PROTOCOL_HON, PROTOCOL_SMARTAIR2] | ||||
| @@ -82,13 +89,24 @@ SUPPORTED_SWING_MODES_OPTIONS = { | ||||
|  | ||||
| SUPPORTED_CLIMATE_MODES_OPTIONS = { | ||||
|     "OFF": ClimateMode.CLIMATE_MODE_OFF,  # always available | ||||
|     "AUTO": ClimateMode.CLIMATE_MODE_AUTO,  # always available | ||||
|     "HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL,  # always available | ||||
|     "COOL": ClimateMode.CLIMATE_MODE_COOL, | ||||
|     "HEAT": ClimateMode.CLIMATE_MODE_HEAT, | ||||
|     "DRY": ClimateMode.CLIMATE_MODE_DRY, | ||||
|     "FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY, | ||||
| } | ||||
|  | ||||
| SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = { | ||||
|     "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, | ||||
|     "COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT, | ||||
| } | ||||
|  | ||||
| SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = { | ||||
|     "ECO": ClimatePreset.CLIMATE_PRESET_ECO, | ||||
|     "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, | ||||
|     "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP, | ||||
| } | ||||
|  | ||||
|  | ||||
| def validate_visual(config): | ||||
|     if CONF_VISUAL in config: | ||||
| @@ -109,10 +127,29 @@ def validate_visual(config): | ||||
|                 ) | ||||
|         else: | ||||
|             config[CONF_VISUAL][CONF_MAX_TEMPERATURE] = PROTOCOL_MAX_TEMPERATURE | ||||
|         if CONF_TEMPERATURE_STEP in visual_config: | ||||
|             temp_step = config[CONF_VISUAL][CONF_TEMPERATURE_STEP][ | ||||
|                 CONF_TARGET_TEMPERATURE | ||||
|             ] | ||||
|             if ((int)(temp_step * 2)) / 2 != temp_step: | ||||
|                 raise cv.Invalid( | ||||
|                     f"Configured visual temperature step {temp_step} is wrong, it should be a multiple of 0.5" | ||||
|                 ) | ||||
|         else: | ||||
|             config[CONF_VISUAL][CONF_TEMPERATURE_STEP] = ( | ||||
|                 { | ||||
|                     CONF_TARGET_TEMPERATURE: PROTOCOL_TARGET_TEMPERATURE_STEP, | ||||
|                     CONF_CURRENT_TEMPERATURE: PROTOCOL_CURRENT_TEMPERATURE_STEP, | ||||
|                 }, | ||||
|             ) | ||||
|     else: | ||||
|         config[CONF_VISUAL] = { | ||||
|             CONF_MIN_TEMPERATURE: PROTOCOL_MIN_TEMPERATURE, | ||||
|             CONF_MAX_TEMPERATURE: PROTOCOL_MAX_TEMPERATURE, | ||||
|             CONF_TEMPERATURE_STEP: { | ||||
|                 CONF_TARGET_TEMPERATURE: PROTOCOL_TARGET_TEMPERATURE_STEP, | ||||
|                 CONF_CURRENT_TEMPERATURE: PROTOCOL_CURRENT_TEMPERATURE_STEP, | ||||
|             }, | ||||
|         } | ||||
|     return config | ||||
|  | ||||
| @@ -132,6 +169,11 @@ BASE_CONFIG_SCHEMA = ( | ||||
|                     "BOTH", | ||||
|                 ], | ||||
|             ): cv.ensure_list(cv.enum(SUPPORTED_SWING_MODES_OPTIONS, upper=True)), | ||||
|             cv.Optional(CONF_WIFI_SIGNAL, default=False): cv.boolean, | ||||
|             cv.Optional(CONF_DISPLAY): cv.boolean, | ||||
|             cv.Optional( | ||||
|                 CONF_ANSWER_TIMEOUT, | ||||
|             ): cv.positive_time_period_milliseconds, | ||||
|         } | ||||
|     ) | ||||
|     .extend(uart.UART_DEVICE_SCHEMA) | ||||
| @@ -144,13 +186,26 @@ CONFIG_SCHEMA = cv.All( | ||||
|             PROTOCOL_SMARTAIR2: BASE_CONFIG_SCHEMA.extend( | ||||
|                 { | ||||
|                     cv.GenerateID(): cv.declare_id(Smartair2Climate), | ||||
|                     cv.Optional( | ||||
|                         CONF_SUPPORTED_PRESETS, | ||||
|                         default=list( | ||||
|                             SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS.keys() | ||||
|                         ), | ||||
|                     ): cv.ensure_list( | ||||
|                         cv.enum(SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS, upper=True) | ||||
|                     ), | ||||
|                 } | ||||
|             ), | ||||
|             PROTOCOL_HON: BASE_CONFIG_SCHEMA.extend( | ||||
|                 { | ||||
|                     cv.GenerateID(): cv.declare_id(HonClimate), | ||||
|                     cv.Optional(CONF_WIFI_SIGNAL, default=True): cv.boolean, | ||||
|                     cv.Optional(CONF_BEEPER, default=True): cv.boolean, | ||||
|                     cv.Optional( | ||||
|                         CONF_SUPPORTED_PRESETS, | ||||
|                         default=list(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS.keys()), | ||||
|                     ): cv.ensure_list( | ||||
|                         cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True) | ||||
|                     ), | ||||
|                     cv.Optional(CONF_OUTDOOR_TEMPERATURE): sensor.sensor_schema( | ||||
|                         unit_of_measurement=UNIT_CELSIUS, | ||||
|                         icon=ICON_THERMOMETER, | ||||
| @@ -354,10 +409,11 @@ async def to_code(config): | ||||
|     await uart.register_uart_device(var, config) | ||||
|     await climate.register_climate(var, config) | ||||
|  | ||||
|     if (CONF_WIFI_SIGNAL in config) and (config[CONF_WIFI_SIGNAL]): | ||||
|     cg.add(var.set_send_wifi(config[CONF_WIFI_SIGNAL])) | ||||
|     if CONF_BEEPER in config: | ||||
|         cg.add(var.set_beeper_state(config[CONF_BEEPER])) | ||||
|     if CONF_DISPLAY in config: | ||||
|         cg.add(var.set_display_state(config[CONF_DISPLAY])) | ||||
|     if CONF_OUTDOOR_TEMPERATURE in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_OUTDOOR_TEMPERATURE]) | ||||
|         cg.add(var.set_outdoor_temperature_sensor(sens)) | ||||
| @@ -365,5 +421,9 @@ async def to_code(config): | ||||
|         cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES])) | ||||
|     if CONF_SUPPORTED_SWING_MODES in config: | ||||
|         cg.add(var.set_supported_swing_modes(config[CONF_SUPPORTED_SWING_MODES])) | ||||
|     if CONF_SUPPORTED_PRESETS in config: | ||||
|         cg.add(var.set_supported_presets(config[CONF_SUPPORTED_PRESETS])) | ||||
|     if CONF_ANSWER_TIMEOUT in config: | ||||
|         cg.add(var.set_answer_timeout(config[CONF_ANSWER_TIMEOUT])) | ||||
|     # https://github.com/paveldn/HaierProtocol | ||||
|     cg.add_library("pavlodn/HaierProtocol", "0.9.18") | ||||
|     cg.add_library("pavlodn/HaierProtocol", "0.9.20") | ||||
|   | ||||
| @@ -2,6 +2,9 @@ | ||||
| #include <string> | ||||
| #include "esphome/components/climate/climate.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #ifdef USE_WIFI | ||||
| #include "esphome/components/wifi/wifi_component.h" | ||||
| #endif | ||||
| #include "haier_base.h" | ||||
|  | ||||
| using namespace esphome::climate; | ||||
| @@ -24,14 +27,15 @@ constexpr size_t NO_COMMAND = 0xFF;  // Indicate that there is no command suppli | ||||
| const char *HaierClimateBase::phase_to_string_(ProtocolPhases phase) { | ||||
|   static const char *phase_names[] = { | ||||
|       "SENDING_INIT_1", | ||||
|       "WAITING_ANSWER_INIT_1", | ||||
|       "WAITING_INIT_1_ANSWER", | ||||
|       "SENDING_INIT_2", | ||||
|       "WAITING_ANSWER_INIT_2", | ||||
|       "WAITING_INIT_2_ANSWER", | ||||
|       "SENDING_FIRST_STATUS_REQUEST", | ||||
|       "WAITING_FIRST_STATUS_ANSWER", | ||||
|       "SENDING_ALARM_STATUS_REQUEST", | ||||
|       "WAITING_ALARM_STATUS_ANSWER", | ||||
|       "IDLE", | ||||
|       "UNKNOWN", | ||||
|       "SENDING_STATUS_REQUEST", | ||||
|       "WAITING_STATUS_ANSWER", | ||||
|       "SENDING_UPDATE_SIGNAL_REQUEST", | ||||
| @@ -63,11 +67,12 @@ HaierClimateBase::HaierClimateBase() | ||||
|       forced_publish_(false), | ||||
|       forced_request_status_(false), | ||||
|       first_control_attempt_(false), | ||||
|       reset_protocol_request_(false) { | ||||
|       reset_protocol_request_(false), | ||||
|       send_wifi_signal_(true) { | ||||
|   this->traits_ = climate::ClimateTraits(); | ||||
|   this->traits_.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT, | ||||
|                                      climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_DRY, | ||||
|                                      climate::CLIMATE_MODE_AUTO}); | ||||
|                                      climate::CLIMATE_MODE_HEAT_COOL}); | ||||
|   this->traits_.set_supported_fan_modes( | ||||
|       {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH}); | ||||
|   this->traits_.set_supported_swing_modes({climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, | ||||
| @@ -77,7 +82,7 @@ HaierClimateBase::HaierClimateBase() | ||||
|  | ||||
| HaierClimateBase::~HaierClimateBase() {} | ||||
|  | ||||
| void HaierClimateBase::set_phase_(ProtocolPhases phase) { | ||||
| void HaierClimateBase::set_phase(ProtocolPhases phase) { | ||||
|   if (this->protocol_phase_ != phase) { | ||||
| #if (HAIER_LOG_LEVEL > 4) | ||||
|     ESP_LOGV(TAG, "Phase transition: %s => %s", phase_to_string_(this->protocol_phase_), phase_to_string_(phase)); | ||||
| @@ -109,10 +114,27 @@ bool HaierClimateBase::is_control_message_interval_exceeded_(std::chrono::steady | ||||
|   return this->check_timeout_(now, this->last_request_timestamp_, CONTROL_MESSAGES_INTERVAL_MS); | ||||
| } | ||||
|  | ||||
| bool HaierClimateBase::is_protocol_initialisation_interval_exceded_(std::chrono::steady_clock::time_point now) { | ||||
| bool HaierClimateBase::is_protocol_initialisation_interval_exceeded_(std::chrono::steady_clock::time_point now) { | ||||
|   return this->check_timeout_(now, this->last_request_timestamp_, PROTOCOL_INITIALIZATION_INTERVAL); | ||||
| } | ||||
|  | ||||
| #ifdef USE_WIFI | ||||
| haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_(uint8_t message_type) { | ||||
|   static uint8_t wifi_status_data[4] = {0x00, 0x00, 0x00, 0x00}; | ||||
|   if (wifi::global_wifi_component->is_connected()) { | ||||
|     wifi_status_data[1] = 0; | ||||
|     int8_t rssi = wifi::global_wifi_component->wifi_rssi(); | ||||
|     wifi_status_data[3] = uint8_t((128 + rssi) / 1.28f); | ||||
|     ESP_LOGD(TAG, "WiFi signal is: %ddBm => %d%%", rssi, wifi_status_data[3]); | ||||
|   } else { | ||||
|     ESP_LOGD(TAG, "WiFi is not connected"); | ||||
|     wifi_status_data[1] = 1; | ||||
|     wifi_status_data[3] = 0; | ||||
|   } | ||||
|   return haier_protocol::HaierMessage(message_type, wifi_status_data, sizeof(wifi_status_data)); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool HaierClimateBase::get_display_state() const { return this->display_status_; } | ||||
|  | ||||
| void HaierClimateBase::set_display_state(bool state) { | ||||
| @@ -136,18 +158,31 @@ void HaierClimateBase::send_power_on_command() { this->action_request_ = ActionR | ||||
| void HaierClimateBase::send_power_off_command() { this->action_request_ = ActionRequest::TURN_POWER_OFF; } | ||||
|  | ||||
| void HaierClimateBase::toggle_power() { this->action_request_ = ActionRequest::TOGGLE_POWER; } | ||||
|  | ||||
| void HaierClimateBase::set_supported_swing_modes(const std::set<climate::ClimateSwingMode> &modes) { | ||||
|   this->traits_.set_supported_swing_modes(modes); | ||||
|   this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_OFF);       // Always available | ||||
|   this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);  // Always available | ||||
|   if (!modes.empty()) | ||||
|     this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_OFF); | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::set_answer_timeout(uint32_t timeout) { | ||||
|   this->answer_timeout_ = std::chrono::milliseconds(timeout); | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::set_supported_modes(const std::set<climate::ClimateMode> &modes) { | ||||
|   this->traits_.set_supported_modes(modes); | ||||
|   this->traits_.add_supported_mode(climate::CLIMATE_MODE_OFF);        // Always available | ||||
|   this->traits_.add_supported_mode(climate::CLIMATE_MODE_AUTO);  // Always available | ||||
|   this->traits_.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);  // Always available | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::set_supported_presets(const std::set<climate::ClimatePreset> &presets) { | ||||
|   this->traits_.set_supported_presets(presets); | ||||
|   if (!presets.empty()) | ||||
|     this->traits_.add_supported_preset(climate::CLIMATE_PRESET_NONE); | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::set_send_wifi(bool send_wifi) { this->send_wifi_signal_ = send_wifi; } | ||||
|  | ||||
| haier_protocol::HandlerError HaierClimateBase::answer_preprocess_(uint8_t request_message_type, | ||||
|                                                                   uint8_t expected_request_message_type, | ||||
|                                                                   uint8_t answer_message_type, | ||||
| @@ -155,9 +190,9 @@ haier_protocol::HandlerError HaierClimateBase::answer_preprocess_(uint8_t reques | ||||
|                                                                   ProtocolPhases expected_phase) { | ||||
|   haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK; | ||||
|   if ((expected_request_message_type != NO_COMMAND) && (request_message_type != expected_request_message_type)) | ||||
|     result = haier_protocol::HandlerError::UNSUPORTED_MESSAGE; | ||||
|     result = haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||
|   if ((expected_answer_message_type != NO_COMMAND) && (answer_message_type != expected_answer_message_type)) | ||||
|     result = haier_protocol::HandlerError::UNSUPORTED_MESSAGE; | ||||
|     result = haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||
|   if ((expected_phase != ProtocolPhases::UNKNOWN) && (expected_phase != this->protocol_phase_)) | ||||
|     result = haier_protocol::HandlerError::UNEXPECTED_MESSAGE; | ||||
|   if (is_message_invalid(answer_message_type)) | ||||
| @@ -172,9 +207,9 @@ haier_protocol::HandlerError HaierClimateBase::timeout_default_handler_(uint8_t | ||||
|   ESP_LOGW(TAG, "Answer timeout for command %02X, phase %d", request_type, (int) this->protocol_phase_); | ||||
| #endif | ||||
|   if (this->protocol_phase_ > ProtocolPhases::IDLE) { | ||||
|     this->set_phase_(ProtocolPhases::IDLE); | ||||
|     this->set_phase(ProtocolPhases::IDLE); | ||||
|   } else { | ||||
|     this->set_phase_(ProtocolPhases::SENDING_INIT_1); | ||||
|     this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|   } | ||||
|   return haier_protocol::HandlerError::HANDLER_OK; | ||||
| } | ||||
| @@ -183,8 +218,8 @@ void HaierClimateBase::setup() { | ||||
|   ESP_LOGI(TAG, "Haier initialization..."); | ||||
|   // Set timestamp here to give AC time to boot | ||||
|   this->last_request_timestamp_ = std::chrono::steady_clock::now(); | ||||
|   this->set_phase_(ProtocolPhases::SENDING_INIT_1); | ||||
|   this->set_answers_handlers(); | ||||
|   this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|   this->set_handlers(); | ||||
|   this->haier_protocol_.set_default_timeout_handler( | ||||
|       std::bind(&esphome::haier::HaierClimateBase::timeout_default_handler_, this, std::placeholders::_1)); | ||||
| } | ||||
| @@ -212,7 +247,7 @@ void HaierClimateBase::loop() { | ||||
|       this->set_force_send_control_(false); | ||||
|       if (this->hvac_settings_.valid) | ||||
|         this->hvac_settings_.reset(); | ||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); | ||||
|       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|       return; | ||||
|     } else { | ||||
|       // No need to reset protocol if we didn't pass initialization phase | ||||
| @@ -229,7 +264,7 @@ void HaierClimateBase::loop() { | ||||
|       this->process_pending_action(); | ||||
|     } else if (this->hvac_settings_.valid || this->force_send_control_) { | ||||
|       ESP_LOGV(TAG, "Control packet is pending..."); | ||||
|       this->set_phase_(ProtocolPhases::SENDING_CONTROL); | ||||
|       this->set_phase(ProtocolPhases::SENDING_CONTROL); | ||||
|     } | ||||
|   } | ||||
|   this->process_phase(now); | ||||
| @@ -243,10 +278,10 @@ void HaierClimateBase::process_pending_action() { | ||||
|   } | ||||
|   switch (request) { | ||||
|     case ActionRequest::TURN_POWER_ON: | ||||
|       this->set_phase_(ProtocolPhases::SENDING_POWER_ON_COMMAND); | ||||
|       this->set_phase(ProtocolPhases::SENDING_POWER_ON_COMMAND); | ||||
|       break; | ||||
|     case ActionRequest::TURN_POWER_OFF: | ||||
|       this->set_phase_(ProtocolPhases::SENDING_POWER_OFF_COMMAND); | ||||
|       this->set_phase(ProtocolPhases::SENDING_POWER_OFF_COMMAND); | ||||
|       break; | ||||
|     case ActionRequest::TOGGLE_POWER: | ||||
|     case ActionRequest::NO_ACTION: | ||||
| @@ -303,7 +338,11 @@ void HaierClimateBase::set_force_send_control_(bool status) { | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::send_message_(const haier_protocol::HaierMessage &command, bool use_crc) { | ||||
|   if (this->answer_timeout_.has_value()) { | ||||
|     this->haier_protocol_.send_message(command, use_crc, this->answer_timeout_.value()); | ||||
|   } else { | ||||
|     this->haier_protocol_.send_message(command, use_crc); | ||||
|   } | ||||
|   this->last_request_timestamp_ = std::chrono::steady_clock::now(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -44,6 +44,7 @@ class HaierClimateBase : public esphome::Component, | ||||
|   void reset_protocol() { this->reset_protocol_request_ = true; }; | ||||
|   void set_supported_modes(const std::set<esphome::climate::ClimateMode> &modes); | ||||
|   void set_supported_swing_modes(const std::set<esphome::climate::ClimateSwingMode> &modes); | ||||
|   void set_supported_presets(const std::set<esphome::climate::ClimatePreset> &presets); | ||||
|   size_t available() noexcept override { return esphome::uart::UARTDevice::available(); }; | ||||
|   size_t read_array(uint8_t *data, size_t len) noexcept override { | ||||
|     return esphome::uart::UARTDevice::read_array(data, len) ? len : 0; | ||||
| @@ -52,39 +53,41 @@ class HaierClimateBase : public esphome::Component, | ||||
|     esphome::uart::UARTDevice::write_array(data, len); | ||||
|   }; | ||||
|   bool can_send_message() const { return haier_protocol_.get_outgoing_queue_size() == 0; }; | ||||
|   void set_answer_timeout(uint32_t timeout); | ||||
|   void set_send_wifi(bool send_wifi); | ||||
|  | ||||
|  protected: | ||||
|   enum class ProtocolPhases { | ||||
|     UNKNOWN = -1, | ||||
|     // INITIALIZATION | ||||
|     SENDING_INIT_1 = 0, | ||||
|     WAITING_ANSWER_INIT_1 = 1, | ||||
|     WAITING_INIT_1_ANSWER = 1, | ||||
|     SENDING_INIT_2 = 2, | ||||
|     WAITING_ANSWER_INIT_2 = 3, | ||||
|     WAITING_INIT_2_ANSWER = 3, | ||||
|     SENDING_FIRST_STATUS_REQUEST = 4, | ||||
|     WAITING_FIRST_STATUS_ANSWER = 5, | ||||
|     SENDING_ALARM_STATUS_REQUEST = 6, | ||||
|     WAITING_ALARM_STATUS_ANSWER = 7, | ||||
|     // FUNCTIONAL STATE | ||||
|     IDLE = 8, | ||||
|     SENDING_STATUS_REQUEST = 9, | ||||
|     WAITING_STATUS_ANSWER = 10, | ||||
|     SENDING_UPDATE_SIGNAL_REQUEST = 11, | ||||
|     WAITING_UPDATE_SIGNAL_ANSWER = 12, | ||||
|     SENDING_SIGNAL_LEVEL = 13, | ||||
|     WAITING_SIGNAL_LEVEL_ANSWER = 14, | ||||
|     SENDING_CONTROL = 15, | ||||
|     WAITING_CONTROL_ANSWER = 16, | ||||
|     SENDING_POWER_ON_COMMAND = 17, | ||||
|     WAITING_POWER_ON_ANSWER = 18, | ||||
|     SENDING_POWER_OFF_COMMAND = 19, | ||||
|     WAITING_POWER_OFF_ANSWER = 20, | ||||
|     SENDING_STATUS_REQUEST = 10, | ||||
|     WAITING_STATUS_ANSWER = 11, | ||||
|     SENDING_UPDATE_SIGNAL_REQUEST = 12, | ||||
|     WAITING_UPDATE_SIGNAL_ANSWER = 13, | ||||
|     SENDING_SIGNAL_LEVEL = 14, | ||||
|     WAITING_SIGNAL_LEVEL_ANSWER = 15, | ||||
|     SENDING_CONTROL = 16, | ||||
|     WAITING_CONTROL_ANSWER = 17, | ||||
|     SENDING_POWER_ON_COMMAND = 18, | ||||
|     WAITING_POWER_ON_ANSWER = 19, | ||||
|     SENDING_POWER_OFF_COMMAND = 20, | ||||
|     WAITING_POWER_OFF_ANSWER = 21, | ||||
|     NUM_PROTOCOL_PHASES | ||||
|   }; | ||||
| #if (HAIER_LOG_LEVEL > 4) | ||||
|   const char *phase_to_string_(ProtocolPhases phase); | ||||
| #endif | ||||
|   virtual void set_answers_handlers() = 0; | ||||
|   virtual void set_handlers() = 0; | ||||
|   virtual void process_phase(std::chrono::steady_clock::time_point now) = 0; | ||||
|   virtual haier_protocol::HaierMessage get_control_message() = 0; | ||||
|   virtual bool is_message_invalid(uint8_t message_type) = 0; | ||||
| @@ -99,14 +102,17 @@ class HaierClimateBase : public esphome::Component, | ||||
|   // Helper functions | ||||
|   void set_force_send_control_(bool status); | ||||
|   void send_message_(const haier_protocol::HaierMessage &command, bool use_crc); | ||||
|   void set_phase_(ProtocolPhases phase); | ||||
|   virtual void set_phase(ProtocolPhases phase); | ||||
|   bool check_timeout_(std::chrono::steady_clock::time_point now, std::chrono::steady_clock::time_point tpoint, | ||||
|                       size_t timeout); | ||||
|   bool is_message_interval_exceeded_(std::chrono::steady_clock::time_point now); | ||||
|   bool is_status_request_interval_exceeded_(std::chrono::steady_clock::time_point now); | ||||
|   bool is_control_message_timeout_exceeded_(std::chrono::steady_clock::time_point now); | ||||
|   bool is_control_message_interval_exceeded_(std::chrono::steady_clock::time_point now); | ||||
|   bool is_protocol_initialisation_interval_exceded_(std::chrono::steady_clock::time_point now); | ||||
|   bool is_protocol_initialisation_interval_exceeded_(std::chrono::steady_clock::time_point now); | ||||
| #ifdef USE_WIFI | ||||
|   haier_protocol::HaierMessage get_wifi_signal_message_(uint8_t message_type); | ||||
| #endif | ||||
|  | ||||
|   struct HvacSettings { | ||||
|     esphome::optional<esphome::climate::ClimateMode> mode; | ||||
| @@ -136,6 +142,9 @@ class HaierClimateBase : public esphome::Component, | ||||
|   std::chrono::steady_clock::time_point last_valid_status_timestamp_;  // For protocol timeout | ||||
|   std::chrono::steady_clock::time_point last_status_request_;          // To request AC status | ||||
|   std::chrono::steady_clock::time_point control_request_timestamp_;    // To send control message | ||||
|   optional<std::chrono::milliseconds> answer_timeout_;                 // Message answer timeout | ||||
|   bool send_wifi_signal_; | ||||
|   std::chrono::steady_clock::time_point last_signal_request_;  // To send WiFI signal level | ||||
| }; | ||||
|  | ||||
| }  // namespace haier | ||||
|   | ||||
| @@ -2,9 +2,6 @@ | ||||
| #include <string> | ||||
| #include "esphome/components/climate/climate.h" | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #ifdef USE_WIFI | ||||
| #include "esphome/components/wifi/wifi_component.h" | ||||
| #endif | ||||
| #include "hon_climate.h" | ||||
| #include "hon_packet.h" | ||||
|  | ||||
| @@ -58,14 +55,7 @@ HonClimate::HonClimate() | ||||
|       hvac_functions_{false, false, false, false, false}, | ||||
|       use_crc_(hvac_functions_[2]), | ||||
|       active_alarms_{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||||
|       outdoor_sensor_(nullptr), | ||||
|       send_wifi_signal_(true) { | ||||
|   this->traits_.set_supported_presets({ | ||||
|       climate::CLIMATE_PRESET_NONE, | ||||
|       climate::CLIMATE_PRESET_ECO, | ||||
|       climate::CLIMATE_PRESET_BOOST, | ||||
|       climate::CLIMATE_PRESET_SLEEP, | ||||
|   }); | ||||
|       outdoor_sensor_(nullptr) { | ||||
|   this->fan_mode_speed_ = (uint8_t) hon_protocol::FanMode::FAN_MID; | ||||
|   this->other_modes_fan_speed_ = (uint8_t) hon_protocol::FanMode::FAN_AUTO; | ||||
| } | ||||
| @@ -121,17 +111,22 @@ void HonClimate::start_steri_cleaning() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| void HonClimate::set_send_wifi(bool send_wifi) { this->send_wifi_signal_ = send_wifi; } | ||||
|  | ||||
| haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(uint8_t request_type, uint8_t message_type, | ||||
|                                                                             const uint8_t *data, size_t data_size) { | ||||
|   // Should check this before preprocess | ||||
|   if (message_type == (uint8_t) hon_protocol::FrameType::INVALID) { | ||||
|     ESP_LOGW(TAG, "It looks like your ESPHome Haier climate configuration is wrong. You should use the smartAir2 " | ||||
|                   "protocol instead of hOn"); | ||||
|     this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|     return haier_protocol::HandlerError::INVALID_ANSWER; | ||||
|   } | ||||
|   haier_protocol::HandlerError result = this->answer_preprocess_( | ||||
|       request_type, (uint8_t) hon_protocol::FrameType::GET_DEVICE_VERSION, message_type, | ||||
|       (uint8_t) hon_protocol::FrameType::GET_DEVICE_VERSION_RESPONSE, ProtocolPhases::WAITING_ANSWER_INIT_1); | ||||
|       (uint8_t) hon_protocol::FrameType::GET_DEVICE_VERSION_RESPONSE, ProtocolPhases::WAITING_INIT_1_ANSWER); | ||||
|   if (result == haier_protocol::HandlerError::HANDLER_OK) { | ||||
|     if (data_size < sizeof(hon_protocol::DeviceVersionAnswer)) { | ||||
|       // Wrong structure | ||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); | ||||
|       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|       return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; | ||||
|     } | ||||
|     // All OK | ||||
| @@ -152,10 +147,10 @@ haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(uint | ||||
|     this->hvac_functions_[3] = (answr->functions[1] & 0x08) != 0;  // multiple AC support | ||||
|     this->hvac_functions_[4] = (answr->functions[1] & 0x20) != 0;  // roles support | ||||
|     this->hvac_hardware_info_available_ = true; | ||||
|     this->set_phase_(ProtocolPhases::SENDING_INIT_2); | ||||
|     this->set_phase(ProtocolPhases::SENDING_INIT_2); | ||||
|     return result; | ||||
|   } else { | ||||
|     this->set_phase_((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|     this->set_phase((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|                                                                     : ProtocolPhases::SENDING_INIT_1); | ||||
|     return result; | ||||
|   } | ||||
| @@ -165,12 +160,12 @@ haier_protocol::HandlerError HonClimate::get_device_id_answer_handler_(uint8_t r | ||||
|                                                                        const uint8_t *data, size_t data_size) { | ||||
|   haier_protocol::HandlerError result = this->answer_preprocess_( | ||||
|       request_type, (uint8_t) hon_protocol::FrameType::GET_DEVICE_ID, message_type, | ||||
|       (uint8_t) hon_protocol::FrameType::GET_DEVICE_ID_RESPONSE, ProtocolPhases::WAITING_ANSWER_INIT_2); | ||||
|       (uint8_t) hon_protocol::FrameType::GET_DEVICE_ID_RESPONSE, ProtocolPhases::WAITING_INIT_2_ANSWER); | ||||
|   if (result == haier_protocol::HandlerError::HANDLER_OK) { | ||||
|     this->set_phase_(ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||
|     this->set_phase(ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||
|     return result; | ||||
|   } else { | ||||
|     this->set_phase_((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|     this->set_phase((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|                                                                     : ProtocolPhases::SENDING_INIT_1); | ||||
|     return result; | ||||
|   } | ||||
| @@ -185,7 +180,7 @@ haier_protocol::HandlerError HonClimate::status_handler_(uint8_t request_type, u | ||||
|     result = this->process_status_message_(data, data_size); | ||||
|     if (result != haier_protocol::HandlerError::HANDLER_OK) { | ||||
|       ESP_LOGW(TAG, "Error %d while parsing Status packet", (int) result); | ||||
|       this->set_phase_((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|       this->set_phase((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|                                                                       : ProtocolPhases::SENDING_INIT_1); | ||||
|     } else { | ||||
|       if (data_size >= sizeof(hon_protocol::HaierPacketControl) + 2) { | ||||
| @@ -196,13 +191,13 @@ haier_protocol::HandlerError HonClimate::status_handler_(uint8_t request_type, u | ||||
|       } | ||||
|       if (this->protocol_phase_ == ProtocolPhases::WAITING_FIRST_STATUS_ANSWER) { | ||||
|         ESP_LOGI(TAG, "First HVAC status received"); | ||||
|         this->set_phase_(ProtocolPhases::SENDING_ALARM_STATUS_REQUEST); | ||||
|         this->set_phase(ProtocolPhases::SENDING_ALARM_STATUS_REQUEST); | ||||
|       } else if ((this->protocol_phase_ == ProtocolPhases::WAITING_STATUS_ANSWER) || | ||||
|                  (this->protocol_phase_ == ProtocolPhases::WAITING_POWER_ON_ANSWER) || | ||||
|                  (this->protocol_phase_ == ProtocolPhases::WAITING_POWER_OFF_ANSWER)) { | ||||
|         this->set_phase_(ProtocolPhases::IDLE); | ||||
|         this->set_phase(ProtocolPhases::IDLE); | ||||
|       } else if (this->protocol_phase_ == ProtocolPhases::WAITING_CONTROL_ANSWER) { | ||||
|         this->set_phase_(ProtocolPhases::IDLE); | ||||
|         this->set_phase(ProtocolPhases::IDLE); | ||||
|         this->set_force_send_control_(false); | ||||
|         if (this->hvac_settings_.valid) | ||||
|           this->hvac_settings_.reset(); | ||||
| @@ -210,7 +205,7 @@ haier_protocol::HandlerError HonClimate::status_handler_(uint8_t request_type, u | ||||
|     } | ||||
|     return result; | ||||
|   } else { | ||||
|     this->set_phase_((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|     this->set_phase((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|                                                                     : ProtocolPhases::SENDING_INIT_1); | ||||
|     return result; | ||||
|   } | ||||
| @@ -225,10 +220,10 @@ haier_protocol::HandlerError HonClimate::get_management_information_answer_handl | ||||
|                                message_type, (uint8_t) hon_protocol::FrameType::GET_MANAGEMENT_INFORMATION_RESPONSE, | ||||
|                                ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER); | ||||
|   if (result == haier_protocol::HandlerError::HANDLER_OK) { | ||||
|     this->set_phase_(ProtocolPhases::SENDING_SIGNAL_LEVEL); | ||||
|     this->set_phase(ProtocolPhases::SENDING_SIGNAL_LEVEL); | ||||
|     return result; | ||||
|   } else { | ||||
|     this->set_phase_(ProtocolPhases::IDLE); | ||||
|     this->set_phase(ProtocolPhases::IDLE); | ||||
|     return result; | ||||
|   } | ||||
| } | ||||
| @@ -239,7 +234,7 @@ haier_protocol::HandlerError HonClimate::report_network_status_answer_handler_(u | ||||
|   haier_protocol::HandlerError result = | ||||
|       this->answer_preprocess_(request_type, (uint8_t) hon_protocol::FrameType::REPORT_NETWORK_STATUS, message_type, | ||||
|                                (uint8_t) hon_protocol::FrameType::CONFIRM, ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER); | ||||
|   this->set_phase_(ProtocolPhases::IDLE); | ||||
|   this->set_phase(ProtocolPhases::IDLE); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| @@ -248,24 +243,24 @@ haier_protocol::HandlerError HonClimate::get_alarm_status_answer_handler_(uint8_ | ||||
|   if (request_type == (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS) { | ||||
|     if (message_type != (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS_RESPONSE) { | ||||
|       // Unexpected answer to request | ||||
|       this->set_phase_(ProtocolPhases::IDLE); | ||||
|       return haier_protocol::HandlerError::UNSUPORTED_MESSAGE; | ||||
|       this->set_phase(ProtocolPhases::IDLE); | ||||
|       return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||
|     } | ||||
|     if (this->protocol_phase_ != ProtocolPhases::WAITING_ALARM_STATUS_ANSWER) { | ||||
|       // Don't expect this answer now | ||||
|       this->set_phase_(ProtocolPhases::IDLE); | ||||
|       this->set_phase(ProtocolPhases::IDLE); | ||||
|       return haier_protocol::HandlerError::UNEXPECTED_MESSAGE; | ||||
|     } | ||||
|     memcpy(this->active_alarms_, data + 2, 8); | ||||
|     this->set_phase_(ProtocolPhases::IDLE); | ||||
|     this->set_phase(ProtocolPhases::IDLE); | ||||
|     return haier_protocol::HandlerError::HANDLER_OK; | ||||
|   } else { | ||||
|     this->set_phase_(ProtocolPhases::IDLE); | ||||
|     return haier_protocol::HandlerError::UNSUPORTED_MESSAGE; | ||||
|     this->set_phase(ProtocolPhases::IDLE); | ||||
|     return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void HonClimate::set_answers_handlers() { | ||||
| void HonClimate::set_handlers() { | ||||
|   // Set handlers | ||||
|   this->haier_protocol_.set_answer_handler( | ||||
|       (uint8_t) (hon_protocol::FrameType::GET_DEVICE_VERSION), | ||||
| @@ -311,7 +306,7 @@ void HonClimate::dump_config() { | ||||
| void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|   switch (this->protocol_phase_) { | ||||
|     case ProtocolPhases::SENDING_INIT_1: | ||||
|       if (this->can_send_message() && this->is_protocol_initialisation_interval_exceded_(now)) { | ||||
|       if (this->can_send_message() && this->is_protocol_initialisation_interval_exceeded_(now)) { | ||||
|         this->hvac_hardware_info_available_ = false; | ||||
|         // Indicate device capabilities: | ||||
|         // bit 0 - if 1 module support interactive mode | ||||
| @@ -323,24 +318,24 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|         static const haier_protocol::HaierMessage DEVICE_VERSION_REQUEST( | ||||
|             (uint8_t) hon_protocol::FrameType::GET_DEVICE_VERSION, module_capabilities, sizeof(module_capabilities)); | ||||
|         this->send_message_(DEVICE_VERSION_REQUEST, this->use_crc_); | ||||
|         this->set_phase_(ProtocolPhases::WAITING_ANSWER_INIT_1); | ||||
|         this->set_phase(ProtocolPhases::WAITING_INIT_1_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_INIT_2: | ||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||
|         static const haier_protocol::HaierMessage DEVICEID_REQUEST((uint8_t) hon_protocol::FrameType::GET_DEVICE_ID); | ||||
|         this->send_message_(DEVICEID_REQUEST, this->use_crc_); | ||||
|         this->set_phase_(ProtocolPhases::WAITING_ANSWER_INIT_2); | ||||
|         this->set_phase(ProtocolPhases::WAITING_INIT_2_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: | ||||
|     case ProtocolPhases::SENDING_STATUS_REQUEST: | ||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||
|         static const haier_protocol::HaierMessage STATUS_REQUEST( | ||||
|             (uint8_t) hon_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcomandsControl::GET_USER_DATA); | ||||
|             (uint8_t) hon_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::GET_USER_DATA); | ||||
|         this->send_message_(STATUS_REQUEST, this->use_crc_); | ||||
|         this->last_status_request_ = now; | ||||
|         this->set_phase_((ProtocolPhases) ((uint8_t) this->protocol_phase_ + 1)); | ||||
|         this->set_phase((ProtocolPhases) ((uint8_t) this->protocol_phase_ + 1)); | ||||
|       } | ||||
|       break; | ||||
| #ifdef USE_WIFI | ||||
| @@ -350,26 +345,14 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|             (uint8_t) hon_protocol::FrameType::GET_MANAGEMENT_INFORMATION); | ||||
|         this->send_message_(UPDATE_SIGNAL_REQUEST, this->use_crc_); | ||||
|         this->last_signal_request_ = now; | ||||
|         this->set_phase_(ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER); | ||||
|         this->set_phase(ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_SIGNAL_LEVEL: | ||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||
|         static uint8_t wifi_status_data[4] = {0x00, 0x00, 0x00, 0x00}; | ||||
|         if (wifi::global_wifi_component->is_connected()) { | ||||
|           wifi_status_data[1] = 0; | ||||
|           int8_t rssi = wifi::global_wifi_component->wifi_rssi(); | ||||
|           wifi_status_data[3] = uint8_t((128 + rssi) / 1.28f); | ||||
|           ESP_LOGD(TAG, "WiFi signal is: %ddBm => %d%%", rssi, wifi_status_data[3]); | ||||
|         } else { | ||||
|           ESP_LOGD(TAG, "WiFi is not connected"); | ||||
|           wifi_status_data[1] = 1; | ||||
|           wifi_status_data[3] = 0; | ||||
|         } | ||||
|         haier_protocol::HaierMessage wifi_status_request((uint8_t) hon_protocol::FrameType::REPORT_NETWORK_STATUS, | ||||
|                                                          wifi_status_data, sizeof(wifi_status_data)); | ||||
|         this->send_message_(wifi_status_request, this->use_crc_); | ||||
|         this->set_phase_(ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER); | ||||
|         this->send_message_(this->get_wifi_signal_message_((uint8_t) hon_protocol::FrameType::REPORT_NETWORK_STATUS), | ||||
|                             this->use_crc_); | ||||
|         this->set_phase(ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: | ||||
| @@ -380,7 +363,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|     case ProtocolPhases::SENDING_SIGNAL_LEVEL: | ||||
|     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: | ||||
|     case ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER: | ||||
|       this->set_phase_(ProtocolPhases::IDLE); | ||||
|       this->set_phase(ProtocolPhases::IDLE); | ||||
|       break; | ||||
| #endif | ||||
|     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: | ||||
| @@ -388,7 +371,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|         static const haier_protocol::HaierMessage ALARM_STATUS_REQUEST( | ||||
|             (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS); | ||||
|         this->send_message_(ALARM_STATUS_REQUEST, this->use_crc_); | ||||
|         this->set_phase_(ProtocolPhases::WAITING_ALARM_STATUS_ANSWER); | ||||
|         this->set_phase(ProtocolPhases::WAITING_ALARM_STATUS_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_CONTROL: | ||||
| @@ -403,12 +386,12 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|           this->hvac_settings_.reset(); | ||||
|         this->forced_request_status_ = true; | ||||
|         this->forced_publish_ = true; | ||||
|         this->set_phase_(ProtocolPhases::IDLE); | ||||
|         this->set_phase(ProtocolPhases::IDLE); | ||||
|       } else if (this->can_send_message() && this->is_control_message_interval_exceeded_(now)) { | ||||
|         haier_protocol::HaierMessage control_message = get_control_message(); | ||||
|         this->send_message_(control_message, this->use_crc_); | ||||
|         ESP_LOGI(TAG, "Control packet sent"); | ||||
|         this->set_phase_(ProtocolPhases::WAITING_CONTROL_ANSWER); | ||||
|         this->set_phase(ProtocolPhases::WAITING_CONTROL_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_POWER_ON_COMMAND: | ||||
| @@ -418,17 +401,17 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|         if (this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND) | ||||
|           pwr_cmd_buf[1] = 0x01; | ||||
|         haier_protocol::HaierMessage power_cmd((uint8_t) hon_protocol::FrameType::CONTROL, | ||||
|                                                ((uint16_t) hon_protocol::SubcomandsControl::SET_SINGLE_PARAMETER) + 1, | ||||
|                                                ((uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER) + 1, | ||||
|                                                pwr_cmd_buf, sizeof(pwr_cmd_buf)); | ||||
|         this->send_message_(power_cmd, this->use_crc_); | ||||
|         this->set_phase_(this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND | ||||
|         this->set_phase(this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND | ||||
|                             ? ProtocolPhases::WAITING_POWER_ON_ANSWER | ||||
|                             : ProtocolPhases::WAITING_POWER_OFF_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|  | ||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_1: | ||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_2: | ||||
|     case ProtocolPhases::WAITING_INIT_1_ANSWER: | ||||
|     case ProtocolPhases::WAITING_INIT_2_ANSWER: | ||||
|     case ProtocolPhases::WAITING_FIRST_STATUS_ANSWER: | ||||
|     case ProtocolPhases::WAITING_ALARM_STATUS_ANSWER: | ||||
|     case ProtocolPhases::WAITING_STATUS_ANSWER: | ||||
| @@ -438,14 +421,14 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|       break; | ||||
|     case ProtocolPhases::IDLE: { | ||||
|       if (this->forced_request_status_ || this->is_status_request_interval_exceeded_(now)) { | ||||
|         this->set_phase_(ProtocolPhases::SENDING_STATUS_REQUEST); | ||||
|         this->set_phase(ProtocolPhases::SENDING_STATUS_REQUEST); | ||||
|         this->forced_request_status_ = false; | ||||
|       } | ||||
| #ifdef USE_WIFI | ||||
|       else if (this->send_wifi_signal_ && | ||||
|                (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_signal_request_).count() > | ||||
|                 SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) | ||||
|         this->set_phase_(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); | ||||
|         this->set_phase(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); | ||||
| #endif | ||||
|     } break; | ||||
|     default: | ||||
| @@ -456,7 +439,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
| #else | ||||
|       ESP_LOGE(TAG, "Wrong protocol handler state: %d, resetting communication", (int) this->protocol_phase_); | ||||
| #endif | ||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); | ||||
|       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| @@ -475,7 +458,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | ||||
|         case CLIMATE_MODE_OFF: | ||||
|           out_data->ac_power = 0; | ||||
|           break; | ||||
|         case CLIMATE_MODE_AUTO: | ||||
|         case CLIMATE_MODE_HEAT_COOL: | ||||
|           out_data->ac_power = 1; | ||||
|           out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::AUTO; | ||||
|           out_data->fan_mode = this->other_modes_fan_speed_; | ||||
| @@ -551,11 +534,12 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | ||||
|       } | ||||
|     } | ||||
|     if (climate_control.target_temperature.has_value()) { | ||||
|       out_data->set_point = | ||||
|           climate_control.target_temperature.value() - 16;  // set the temperature at our offset, subtract 16. | ||||
|       float target_temp = climate_control.target_temperature.value(); | ||||
|       out_data->set_point = ((int) target_temp) - 16;  // set the temperature at our offset, subtract 16. | ||||
|       out_data->half_degree = (target_temp - ((int) target_temp) >= 0.49) ? 1 : 0; | ||||
|     } | ||||
|     if (out_data->ac_power == 0) { | ||||
|       // If AC is off - no presets alowed | ||||
|       // If AC is off - no presets allowed | ||||
|       out_data->quiet_mode = 0; | ||||
|       out_data->fast_mode = 0; | ||||
|       out_data->sleep_mode = 0; | ||||
| @@ -631,7 +615,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | ||||
|       break; | ||||
|   } | ||||
|   return haier_protocol::HaierMessage((uint8_t) hon_protocol::FrameType::CONTROL, | ||||
|                                       (uint16_t) hon_protocol::SubcomandsControl::SET_GROUP_PARAMETERS, | ||||
|                                       (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS, | ||||
|                                       control_out_buffer, sizeof(hon_protocol::HaierPacketControl)); | ||||
| } | ||||
|  | ||||
| @@ -669,7 +653,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | ||||
|   { | ||||
|     // Target temperature | ||||
|     float old_target_temperature = this->target_temperature; | ||||
|     this->target_temperature = packet.control.set_point + 16.0f; | ||||
|     this->target_temperature = packet.control.set_point + 16.0f + ((packet.control.half_degree == 1) ? 0.5f : 0.0f); | ||||
|     should_publish = should_publish || (old_target_temperature != this->target_temperature); | ||||
|   } | ||||
|   { | ||||
| @@ -747,7 +731,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | ||||
|     if (new_cleaning != this->cleaning_status_) { | ||||
|       ESP_LOGD(TAG, "Cleaning status change: %d => %d", (uint8_t) this->cleaning_status_, (uint8_t) new_cleaning); | ||||
|       if (new_cleaning == CleaningState::NO_CLEANING) { | ||||
|         // Turnuin AC off after cleaning | ||||
|         // Turning AC off after cleaning | ||||
|         this->action_request_ = ActionRequest::TURN_POWER_OFF; | ||||
|       } | ||||
|       this->cleaning_status_ = new_cleaning; | ||||
| @@ -774,7 +758,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | ||||
|           this->mode = CLIMATE_MODE_FAN_ONLY; | ||||
|           break; | ||||
|         case (uint8_t) hon_protocol::ConditioningMode::AUTO: | ||||
|           this->mode = CLIMATE_MODE_AUTO; | ||||
|           this->mode = CLIMATE_MODE_HEAT_COOL; | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
| @@ -837,7 +821,7 @@ void HonClimate::process_pending_action() { | ||||
|     case ActionRequest::START_SELF_CLEAN: | ||||
|     case ActionRequest::START_STERI_CLEAN: | ||||
|       // Will reset action with control message sending | ||||
|       this->set_phase_(ProtocolPhases::SENDING_CONTROL); | ||||
|       this->set_phase(ProtocolPhases::SENDING_CONTROL); | ||||
|       break; | ||||
|     default: | ||||
|       HaierClimateBase::process_pending_action(); | ||||
|   | ||||
| @@ -48,10 +48,9 @@ class HonClimate : public HaierClimateBase { | ||||
|   CleaningState get_cleaning_status() const; | ||||
|   void start_self_cleaning(); | ||||
|   void start_steri_cleaning(); | ||||
|   void set_send_wifi(bool send_wifi); | ||||
|  | ||||
|  protected: | ||||
|   void set_answers_handlers() override; | ||||
|   void set_handlers() override; | ||||
|   void process_phase(std::chrono::steady_clock::time_point now) override; | ||||
|   haier_protocol::HaierMessage get_control_message() override; | ||||
|   bool is_message_invalid(uint8_t message_type) override; | ||||
| @@ -87,8 +86,6 @@ class HonClimate : public HaierClimateBase { | ||||
|   bool &use_crc_; | ||||
|   uint8_t active_alarms_[8]; | ||||
|   esphome::sensor::Sensor *outdoor_sensor_; | ||||
|   bool send_wifi_signal_; | ||||
|   std::chrono::steady_clock::time_point last_signal_request_;  // To send WiFI signal level | ||||
| }; | ||||
|  | ||||
| }  // namespace haier | ||||
|   | ||||
| @@ -56,7 +56,7 @@ struct HaierPacketControl { | ||||
|   uint8_t ten_degree : 1;           // 10 degree status | ||||
|   uint8_t display_status : 1;       // If 0 disables AC's display | ||||
|   uint8_t half_degree : 1;          // Use half degree | ||||
|   uint8_t intelegence_status : 1;  // Intelligence status | ||||
|   uint8_t intelligence_status : 1;  // Intelligence status | ||||
|   uint8_t pmv_status : 1;           // Comfort/PMV status | ||||
|   uint8_t use_fahrenheit : 1;       // Use Fahrenheit instead of Celsius | ||||
|   uint8_t : 1; | ||||
| @@ -153,7 +153,7 @@ enum class FrameType : uint8_t { | ||||
|                    // <-> device, required) | ||||
|   REPORT = 0x06,   // Report frame (module <-> device, interactive, required) | ||||
|   STOP_FAULT_ALARM = 0x09,             // Stop fault alarm frame (module -> device, interactive, required) | ||||
|   SYSTEM_DOWNLIK = 0x11,               // System downlink frame (module -> device, optional) | ||||
|   SYSTEM_DOWNLINK = 0x11,              // System downlink frame (module -> device, optional) | ||||
|   DEVICE_UPLINK = 0x12,                // Device uplink frame (module <- device , interactive, optional) | ||||
|   SYSTEM_QUERY = 0x13,                 // System query frame (module -> device, optional) | ||||
|   SYSTEM_QUERY_RESPONSE = 0x14,        // System query response frame (module <- device , optional) | ||||
| @@ -210,7 +210,7 @@ enum class FrameType : uint8_t { | ||||
|   WAKE_UP = 0xFE,  // Request to wake up (module <-> device, optional) | ||||
| }; | ||||
|  | ||||
| enum class SubcomandsControl : uint16_t { | ||||
| enum class SubcommandsControl : uint16_t { | ||||
|   GET_PARAMETERS = 0x4C01,  // Request specific parameters (packet content: parameter ID1 + parameter ID2 + ...) | ||||
|   GET_USER_DATA = 0x4D01,   // Request all user data from device (packet content: None) | ||||
|   GET_BIG_DATA = 0x4DFE,    // Request big data information from device (packet content: None) | ||||
|   | ||||
| @@ -11,15 +11,10 @@ namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| static const char *const TAG = "haier.climate"; | ||||
| constexpr size_t SIGNAL_LEVEL_UPDATE_INTERVAL_MS = 10000; | ||||
|  | ||||
| Smartair2Climate::Smartair2Climate() | ||||
|     : last_status_message_(new uint8_t[sizeof(smartair2_protocol::HaierPacketControl)]) { | ||||
|   this->traits_.set_supported_presets({ | ||||
|       climate::CLIMATE_PRESET_NONE, | ||||
|       climate::CLIMATE_PRESET_BOOST, | ||||
|       climate::CLIMATE_PRESET_COMFORT, | ||||
|   }); | ||||
| } | ||||
|     : last_status_message_(new uint8_t[sizeof(smartair2_protocol::HaierPacketControl)]), timeouts_counter_(0) {} | ||||
|  | ||||
| haier_protocol::HandlerError Smartair2Climate::status_handler_(uint8_t request_type, uint8_t message_type, | ||||
|                                                                const uint8_t *data, size_t data_size) { | ||||
| @@ -30,7 +25,7 @@ haier_protocol::HandlerError Smartair2Climate::status_handler_(uint8_t request_t | ||||
|     result = this->process_status_message_(data, data_size); | ||||
|     if (result != haier_protocol::HandlerError::HANDLER_OK) { | ||||
|       ESP_LOGW(TAG, "Error %d while parsing Status packet", (int) result); | ||||
|       this->set_phase_((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|       this->set_phase((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|                                                                       : ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||
|     } else { | ||||
|       if (data_size >= sizeof(smartair2_protocol::HaierPacketControl) + 2) { | ||||
| @@ -41,11 +36,11 @@ haier_protocol::HandlerError Smartair2Climate::status_handler_(uint8_t request_t | ||||
|       } | ||||
|       if (this->protocol_phase_ == ProtocolPhases::WAITING_FIRST_STATUS_ANSWER) { | ||||
|         ESP_LOGI(TAG, "First HVAC status received"); | ||||
|         this->set_phase_(ProtocolPhases::IDLE); | ||||
|         this->set_phase(ProtocolPhases::IDLE); | ||||
|       } else if (this->protocol_phase_ == ProtocolPhases::WAITING_STATUS_ANSWER) { | ||||
|         this->set_phase_(ProtocolPhases::IDLE); | ||||
|         this->set_phase(ProtocolPhases::IDLE); | ||||
|       } else if (this->protocol_phase_ == ProtocolPhases::WAITING_CONTROL_ANSWER) { | ||||
|         this->set_phase_(ProtocolPhases::IDLE); | ||||
|         this->set_phase(ProtocolPhases::IDLE); | ||||
|         this->set_force_send_control_(false); | ||||
|         if (this->hvac_settings_.valid) | ||||
|           this->hvac_settings_.reset(); | ||||
| @@ -53,17 +48,82 @@ haier_protocol::HandlerError Smartair2Climate::status_handler_(uint8_t request_t | ||||
|     } | ||||
|     return result; | ||||
|   } else { | ||||
|     this->set_phase_((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|     this->set_phase((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE | ||||
|                                                                     : ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||
|     return result; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void Smartair2Climate::set_answers_handlers() { | ||||
| haier_protocol::HandlerError Smartair2Climate::get_device_version_answer_handler_(uint8_t request_type, | ||||
|                                                                                   uint8_t message_type, | ||||
|                                                                                   const uint8_t *data, | ||||
|                                                                                   size_t data_size) { | ||||
|   if (request_type != (uint8_t) smartair2_protocol::FrameType::GET_DEVICE_VERSION) | ||||
|     return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||
|   if (ProtocolPhases::WAITING_INIT_1_ANSWER != this->protocol_phase_) | ||||
|     return haier_protocol::HandlerError::UNEXPECTED_MESSAGE; | ||||
|   // Invalid packet is expected answer | ||||
|   if ((message_type == (uint8_t) smartair2_protocol::FrameType::GET_DEVICE_VERSION_RESPONSE) && (data_size >= 39) && | ||||
|       ((data[37] & 0x04) != 0)) { | ||||
|     ESP_LOGW(TAG, "It looks like your ESPHome Haier climate configuration is wrong. You should use the hOn protocol " | ||||
|                   "instead of smartAir2"); | ||||
|   } | ||||
|   this->set_phase(ProtocolPhases::SENDING_INIT_2); | ||||
|   return haier_protocol::HandlerError::HANDLER_OK; | ||||
| } | ||||
|  | ||||
| haier_protocol::HandlerError Smartair2Climate::report_network_status_answer_handler_(uint8_t request_type, | ||||
|                                                                                      uint8_t message_type, | ||||
|                                                                                      const uint8_t *data, | ||||
|                                                                                      size_t data_size) { | ||||
|   haier_protocol::HandlerError result = this->answer_preprocess_( | ||||
|       request_type, (uint8_t) smartair2_protocol::FrameType::REPORT_NETWORK_STATUS, message_type, | ||||
|       (uint8_t) smartair2_protocol::FrameType::CONFIRM, ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER); | ||||
|   this->set_phase(ProtocolPhases::IDLE); | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| haier_protocol::HandlerError Smartair2Climate::initial_messages_timeout_handler_(uint8_t message_type) { | ||||
|   if (this->protocol_phase_ >= ProtocolPhases::IDLE) | ||||
|     return HaierClimateBase::timeout_default_handler_(message_type); | ||||
|   this->timeouts_counter_++; | ||||
|   ESP_LOGI(TAG, "Answer timeout for command %02X, phase %d, timeout counter %d", message_type, | ||||
|            (int) this->protocol_phase_, this->timeouts_counter_); | ||||
|   if (this->timeouts_counter_ >= 3) { | ||||
|     ProtocolPhases new_phase = (ProtocolPhases) ((int) this->protocol_phase_ + 1); | ||||
|     if (new_phase >= ProtocolPhases::SENDING_ALARM_STATUS_REQUEST) | ||||
|       new_phase = ProtocolPhases::SENDING_INIT_1; | ||||
|     this->set_phase(new_phase); | ||||
|   } else { | ||||
|     // Returning to the previous state to try again | ||||
|     this->set_phase((ProtocolPhases) ((int) this->protocol_phase_ - 1)); | ||||
|   } | ||||
|   return haier_protocol::HandlerError::HANDLER_OK; | ||||
| } | ||||
|  | ||||
| void Smartair2Climate::set_handlers() { | ||||
|   // Set handlers | ||||
|   this->haier_protocol_.set_answer_handler( | ||||
|       (uint8_t) (smartair2_protocol::FrameType::GET_DEVICE_VERSION), | ||||
|       std::bind(&Smartair2Climate::get_device_version_answer_handler_, this, std::placeholders::_1, | ||||
|                 std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); | ||||
|   this->haier_protocol_.set_answer_handler( | ||||
|       (uint8_t) (smartair2_protocol::FrameType::CONTROL), | ||||
|       std::bind(&Smartair2Climate::status_handler_, this, std::placeholders::_1, std::placeholders::_2, | ||||
|                 std::placeholders::_3, std::placeholders::_4)); | ||||
|   this->haier_protocol_.set_answer_handler( | ||||
|       (uint8_t) (smartair2_protocol::FrameType::REPORT_NETWORK_STATUS), | ||||
|       std::bind(&Smartair2Climate::report_network_status_answer_handler_, this, std::placeholders::_1, | ||||
|                 std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); | ||||
|   this->haier_protocol_.set_timeout_handler( | ||||
|       (uint8_t) (smartair2_protocol::FrameType::GET_DEVICE_ID), | ||||
|       std::bind(&Smartair2Climate::initial_messages_timeout_handler_, this, std::placeholders::_1)); | ||||
|   this->haier_protocol_.set_timeout_handler( | ||||
|       (uint8_t) (smartair2_protocol::FrameType::GET_DEVICE_VERSION), | ||||
|       std::bind(&Smartair2Climate::initial_messages_timeout_handler_, this, std::placeholders::_1)); | ||||
|   this->haier_protocol_.set_timeout_handler( | ||||
|       (uint8_t) (smartair2_protocol::FrameType::CONTROL), | ||||
|       std::bind(&Smartair2Climate::initial_messages_timeout_handler_, this, std::placeholders::_1)); | ||||
| } | ||||
|  | ||||
| void Smartair2Climate::dump_config() { | ||||
| @@ -74,39 +134,62 @@ void Smartair2Climate::dump_config() { | ||||
| void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) { | ||||
|   switch (this->protocol_phase_) { | ||||
|     case ProtocolPhases::SENDING_INIT_1: | ||||
|       this->set_phase_(ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||
|       break; | ||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_1: | ||||
|     case ProtocolPhases::SENDING_INIT_2: | ||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_2: | ||||
|     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: | ||||
|     case ProtocolPhases::WAITING_ALARM_STATUS_ANSWER: | ||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST: | ||||
|     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: | ||||
|     case ProtocolPhases::SENDING_SIGNAL_LEVEL: | ||||
|     case ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER: | ||||
|       this->set_phase_(ProtocolPhases::IDLE); | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: | ||||
|       if (this->can_send_message() && this->is_protocol_initialisation_interval_exceded_(now)) { | ||||
|         static const haier_protocol::HaierMessage STATUS_REQUEST((uint8_t) smartair2_protocol::FrameType::CONTROL, | ||||
|                                                                  0x4D01); | ||||
|         this->send_message_(STATUS_REQUEST, false); | ||||
|         this->last_status_request_ = now; | ||||
|         this->set_phase_(ProtocolPhases::WAITING_FIRST_STATUS_ANSWER); | ||||
|       if (this->can_send_message() && | ||||
|           (((this->timeouts_counter_ == 0) && (this->is_protocol_initialisation_interval_exceeded_(now))) || | ||||
|            ((this->timeouts_counter_ > 0) && (this->is_message_interval_exceeded_(now))))) { | ||||
|         // Indicate device capabilities: | ||||
|         // bit 0 - if 1 module support interactive mode | ||||
|         // bit 1 - if 1 module support controller-device mode | ||||
|         // bit 2 - if 1 module support crc | ||||
|         // bit 3 - if 1 module support multiple devices | ||||
|         // bit 4..bit 15 - not used | ||||
|         uint8_t module_capabilities[2] = {0b00000000, 0b00000111}; | ||||
|         static const haier_protocol::HaierMessage DEVICE_VERSION_REQUEST( | ||||
|             (uint8_t) smartair2_protocol::FrameType::GET_DEVICE_VERSION, module_capabilities, | ||||
|             sizeof(module_capabilities)); | ||||
|         this->send_message_(DEVICE_VERSION_REQUEST, false); | ||||
|         this->set_phase(ProtocolPhases::WAITING_INIT_1_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_INIT_2: | ||||
|     case ProtocolPhases::WAITING_INIT_2_ANSWER: | ||||
|       this->set_phase(ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: | ||||
|     case ProtocolPhases::SENDING_STATUS_REQUEST: | ||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||
|         static const haier_protocol::HaierMessage STATUS_REQUEST((uint8_t) smartair2_protocol::FrameType::CONTROL, | ||||
|                                                                  0x4D01); | ||||
|         this->send_message_(STATUS_REQUEST, false); | ||||
|         this->last_status_request_ = now; | ||||
|         this->set_phase_(ProtocolPhases::WAITING_STATUS_ANSWER); | ||||
|         this->set_phase((ProtocolPhases) ((uint8_t) this->protocol_phase_ + 1)); | ||||
|       } | ||||
|       break; | ||||
| #ifdef USE_WIFI | ||||
|     case ProtocolPhases::SENDING_SIGNAL_LEVEL: | ||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||
|         this->send_message_( | ||||
|             this->get_wifi_signal_message_((uint8_t) smartair2_protocol::FrameType::REPORT_NETWORK_STATUS), false); | ||||
|         this->last_signal_request_ = now; | ||||
|         this->set_phase(ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER: | ||||
|       break; | ||||
| #else | ||||
|     case ProtocolPhases::SENDING_SIGNAL_LEVEL: | ||||
|     case ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER: | ||||
|       this->set_phase(ProtocolPhases::IDLE); | ||||
|       break; | ||||
| #endif | ||||
|     case ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST: | ||||
|     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: | ||||
|       this->set_phase(ProtocolPhases::SENDING_SIGNAL_LEVEL); | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: | ||||
|     case ProtocolPhases::WAITING_ALARM_STATUS_ANSWER: | ||||
|       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_CONTROL: | ||||
|       if (this->first_control_attempt_) { | ||||
|         this->control_request_timestamp_ = now; | ||||
| @@ -119,14 +202,14 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) | ||||
|           this->hvac_settings_.reset(); | ||||
|         this->forced_request_status_ = true; | ||||
|         this->forced_publish_ = true; | ||||
|         this->set_phase_(ProtocolPhases::IDLE); | ||||
|         this->set_phase(ProtocolPhases::IDLE); | ||||
|       } else if (this->can_send_message() && this->is_control_message_interval_exceeded_( | ||||
|                                                  now))  // Using CONTROL_MESSAGES_INTERVAL_MS to speedup requests | ||||
|       { | ||||
|         haier_protocol::HaierMessage control_message = get_control_message(); | ||||
|         this->send_message_(control_message, false); | ||||
|         ESP_LOGI(TAG, "Control packet sent"); | ||||
|         this->set_phase_(ProtocolPhases::WAITING_CONTROL_ANSWER); | ||||
|         this->set_phase(ProtocolPhases::WAITING_CONTROL_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::SENDING_POWER_ON_COMMAND: | ||||
| @@ -136,11 +219,12 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) | ||||
|             (uint8_t) smartair2_protocol::FrameType::CONTROL, | ||||
|             this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND ? 0x4D02 : 0x4D03); | ||||
|         this->send_message_(power_cmd, false); | ||||
|         this->set_phase_(this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND | ||||
|         this->set_phase(this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND | ||||
|                             ? ProtocolPhases::WAITING_POWER_ON_ANSWER | ||||
|                             : ProtocolPhases::WAITING_POWER_OFF_ANSWER); | ||||
|       } | ||||
|       break; | ||||
|     case ProtocolPhases::WAITING_INIT_1_ANSWER: | ||||
|     case ProtocolPhases::WAITING_FIRST_STATUS_ANSWER: | ||||
|     case ProtocolPhases::WAITING_STATUS_ANSWER: | ||||
|     case ProtocolPhases::WAITING_CONTROL_ANSWER: | ||||
| @@ -149,14 +233,25 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) | ||||
|       break; | ||||
|     case ProtocolPhases::IDLE: { | ||||
|       if (this->forced_request_status_ || this->is_status_request_interval_exceeded_(now)) { | ||||
|         this->set_phase_(ProtocolPhases::SENDING_STATUS_REQUEST); | ||||
|         this->set_phase(ProtocolPhases::SENDING_STATUS_REQUEST); | ||||
|         this->forced_request_status_ = false; | ||||
|       } | ||||
| #ifdef USE_WIFI | ||||
|       else if (this->send_wifi_signal_ && | ||||
|                (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_signal_request_).count() > | ||||
|                 SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) | ||||
|         this->set_phase(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); | ||||
| #endif | ||||
|     } break; | ||||
|     default: | ||||
|       // Shouldn't get here | ||||
| #if (HAIER_LOG_LEVEL > 4) | ||||
|       ESP_LOGE(TAG, "Wrong protocol handler state: %s (%d), resetting communication", | ||||
|                phase_to_string_(this->protocol_phase_), (int) this->protocol_phase_); | ||||
| #else | ||||
|       ESP_LOGE(TAG, "Wrong protocol handler state: %d, resetting communication", (int) this->protocol_phase_); | ||||
|       this->set_phase_(ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||
| #endif | ||||
|       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||
|       break; | ||||
|   } | ||||
| } | ||||
| @@ -175,7 +270,7 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() { | ||||
|           out_data->ac_power = 0; | ||||
|           break; | ||||
|  | ||||
|         case CLIMATE_MODE_AUTO: | ||||
|         case CLIMATE_MODE_HEAT_COOL: | ||||
|           out_data->ac_power = 1; | ||||
|           out_data->ac_mode = (uint8_t) smartair2_protocol::ConditioningMode::AUTO; | ||||
|           out_data->fan_mode = this->other_modes_fan_speed_; | ||||
| @@ -256,11 +351,12 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() { | ||||
|       } | ||||
|     } | ||||
|     if (climate_control.target_temperature.has_value()) { | ||||
|       out_data->set_point = | ||||
|           climate_control.target_temperature.value() - 16;  // set the temperature at our offset, subtract 16. | ||||
|       float target_temp = climate_control.target_temperature.value(); | ||||
|       out_data->set_point = target_temp - 16;  // set the temperature with offset 16 | ||||
|       out_data->half_degree = (target_temp - ((int) target_temp) >= 0.49) ? 1 : 0; | ||||
|     } | ||||
|     if (out_data->ac_power == 0) { | ||||
|       // If AC is off - no presets alowed | ||||
|       // If AC is off - no presets allowed | ||||
|       out_data->turbo_mode = 0; | ||||
|       out_data->quiet_mode = 0; | ||||
|     } else if (climate_control.preset.has_value()) { | ||||
| @@ -312,7 +408,7 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin | ||||
|   { | ||||
|     // Target temperature | ||||
|     float old_target_temperature = this->target_temperature; | ||||
|     this->target_temperature = packet.control.set_point + 16.0f; | ||||
|     this->target_temperature = packet.control.set_point + 16.0f + ((packet.control.half_degree == 1) ? 0.5f : 0.0f); | ||||
|     should_publish = should_publish || (old_target_temperature != this->target_temperature); | ||||
|   } | ||||
|   { | ||||
| @@ -333,7 +429,7 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin | ||||
|     } | ||||
|     switch (packet.control.fan_mode) { | ||||
|       case (uint8_t) smartair2_protocol::FanMode::FAN_AUTO: | ||||
|         // Somtimes AC reports in fan only mode that fan speed is auto | ||||
|         // Sometimes AC reports in fan only mode that fan speed is auto | ||||
|         // but never accept this value back | ||||
|         if (packet.control.ac_mode != (uint8_t) smartair2_protocol::ConditioningMode::FAN) { | ||||
|           this->fan_mode = CLIMATE_FAN_AUTO; | ||||
| @@ -391,7 +487,7 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin | ||||
|           this->mode = CLIMATE_MODE_FAN_ONLY; | ||||
|           break; | ||||
|         case (uint8_t) smartair2_protocol::ConditioningMode::AUTO: | ||||
|           this->mode = CLIMATE_MODE_AUTO; | ||||
|           this->mode = CLIMATE_MODE_HEAT_COOL; | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
| @@ -453,5 +549,15 @@ bool Smartair2Climate::is_message_invalid(uint8_t message_type) { | ||||
|   return message_type == (uint8_t) smartair2_protocol::FrameType::INVALID; | ||||
| } | ||||
|  | ||||
| void Smartair2Climate::set_phase(HaierClimateBase::ProtocolPhases phase) { | ||||
|   int old_phase = (int) this->protocol_phase_; | ||||
|   int new_phase = (int) phase; | ||||
|   int min_p = std::min(old_phase, new_phase); | ||||
|   int max_p = std::max(old_phase, new_phase); | ||||
|   if ((min_p % 2 != 0) || (max_p - min_p > 1)) | ||||
|     this->timeouts_counter_ = 0; | ||||
|   HaierClimateBase::set_phase(phase); | ||||
| } | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -15,16 +15,25 @@ class Smartair2Climate : public HaierClimateBase { | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   void set_answers_handlers() override; | ||||
|   void set_handlers() override; | ||||
|   void process_phase(std::chrono::steady_clock::time_point now) override; | ||||
|   haier_protocol::HaierMessage get_control_message() override; | ||||
|   bool is_message_invalid(uint8_t message_type) override; | ||||
|   // Answers handlers | ||||
|   void set_phase(HaierClimateBase::ProtocolPhases phase) override; | ||||
|   // Answer and timeout handlers | ||||
|   haier_protocol::HandlerError status_handler_(uint8_t request_type, uint8_t message_type, const uint8_t *data, | ||||
|                                                size_t data_size); | ||||
|   haier_protocol::HandlerError get_device_version_answer_handler_(uint8_t request_type, uint8_t message_type, | ||||
|                                                                   const uint8_t *data, size_t data_size); | ||||
|   haier_protocol::HandlerError get_device_id_answer_handler_(uint8_t request_type, uint8_t message_type, | ||||
|                                                              const uint8_t *data, size_t data_size); | ||||
|   haier_protocol::HandlerError report_network_status_answer_handler_(uint8_t request_type, uint8_t message_type, | ||||
|                                                                      const uint8_t *data, size_t data_size); | ||||
|   haier_protocol::HandlerError initial_messages_timeout_handler_(uint8_t message_type); | ||||
|   // Helper functions | ||||
|   haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size); | ||||
|   std::unique_ptr<uint8_t[]> last_status_message_; | ||||
|   unsigned int timeouts_counter_; | ||||
| }; | ||||
|  | ||||
| }  // namespace haier | ||||
|   | ||||
| @@ -53,7 +53,7 @@ struct HaierPacketControl { | ||||
|   uint8_t : 2; | ||||
|   uint8_t health_mode : 1;  // Health mode on or off | ||||
|   uint8_t compressor : 1;   // Compressor on or off ??? | ||||
|   uint8_t : 1; | ||||
|   uint8_t half_degree : 1;  // Use half degree | ||||
|   uint8_t ten_degree : 1;   // 10 degree status (only work in heat mode) | ||||
|   uint8_t : 0; | ||||
|   // 28 | ||||
| @@ -88,6 +88,9 @@ enum class FrameType : uint8_t { | ||||
|   INVALID = 0x03, | ||||
|   CONFIRM = 0x05, | ||||
|   GET_DEVICE_VERSION = 0x61, | ||||
|   GET_DEVICE_VERSION_RESPONSE = 0x62, | ||||
|   GET_DEVICE_ID = 0x70, | ||||
|   GET_DEVICE_ID_RESPONSE = 0x71, | ||||
|   REPORT_NETWORK_STATUS = 0xF7, | ||||
|   NO_COMMAND = 0xFF, | ||||
| }; | ||||
|   | ||||
| @@ -33,6 +33,7 @@ PROTOCOLS = { | ||||
|     "greeya": Protocol.PROTOCOL_GREEYAA, | ||||
|     "greeyan": Protocol.PROTOCOL_GREEYAN, | ||||
|     "greeyac": Protocol.PROTOCOL_GREEYAC, | ||||
|     "greeyt": Protocol.PROTOCOL_GREEYT, | ||||
|     "hisense_aud": Protocol.PROTOCOL_HISENSE_AUD, | ||||
|     "hitachi": Protocol.PROTOCOL_HITACHI, | ||||
|     "hyundai": Protocol.PROTOCOL_HYUNDAI, | ||||
| @@ -115,7 +116,7 @@ def to_code(config): | ||||
|     cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE])) | ||||
|     cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) | ||||
|  | ||||
|     cg.add_library("tonia/HeatpumpIR", "1.0.20") | ||||
|     cg.add_library("tonia/HeatpumpIR", "1.0.23") | ||||
|  | ||||
|     if CORE.is_esp8266 or CORE.is_esp32: | ||||
|         cg.add_library("crankyoldgit/IRremoteESP8266", "2.7.12") | ||||
|   | ||||
| @@ -27,6 +27,7 @@ const std::map<Protocol, std::function<HeatpumpIR *()>> PROTOCOL_CONSTRUCTOR_MAP | ||||
|     {PROTOCOL_GREEYAA, []() { return new GreeYAAHeatpumpIR(); }},                            // NOLINT | ||||
|     {PROTOCOL_GREEYAN, []() { return new GreeYANHeatpumpIR(); }},                            // NOLINT | ||||
|     {PROTOCOL_GREEYAC, []() { return new GreeYACHeatpumpIR(); }},                            // NOLINT | ||||
|     {PROTOCOL_GREEYT, []() { return new GreeYTHeatpumpIR(); }},                              // NOLINT | ||||
|     {PROTOCOL_HISENSE_AUD, []() { return new HisenseHeatpumpIR(); }},                        // NOLINT | ||||
|     {PROTOCOL_HITACHI, []() { return new HitachiHeatpumpIR(); }},                            // NOLINT | ||||
|     {PROTOCOL_HYUNDAI, []() { return new HyundaiHeatpumpIR(); }},                            // NOLINT | ||||
|   | ||||
| @@ -27,6 +27,7 @@ enum Protocol { | ||||
|   PROTOCOL_GREEYAA, | ||||
|   PROTOCOL_GREEYAN, | ||||
|   PROTOCOL_GREEYAC, | ||||
|   PROTOCOL_GREEYT, | ||||
|   PROTOCOL_HISENSE_AUD, | ||||
|   PROTOCOL_HITACHI, | ||||
|   PROTOCOL_HYUNDAI, | ||||
|   | ||||
| @@ -23,13 +23,13 @@ CONFIG_SCHEMA = ( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(HTU21DComponent), | ||||
|             cv.Required(CONF_TEMPERATURE): sensor.sensor_schema( | ||||
|             cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||
|                 unit_of_measurement=UNIT_CELSIUS, | ||||
|                 accuracy_decimals=1, | ||||
|                 device_class=DEVICE_CLASS_TEMPERATURE, | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Required(CONF_HUMIDITY): sensor.sensor_schema( | ||||
|             cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( | ||||
|                 unit_of_measurement=UNIT_PERCENT, | ||||
|                 accuracy_decimals=1, | ||||
|                 device_class=DEVICE_CLASS_HUMIDITY, | ||||
|   | ||||
| @@ -202,12 +202,14 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b | ||||
|       return ERROR_UNKNOWN; | ||||
|     } | ||||
|   } | ||||
|   if (stop) { | ||||
|     err = i2c_master_stop(cmd); | ||||
|     if (err != ESP_OK) { | ||||
|       ESP_LOGVV(TAG, "TX to %02X master stop failed: %s", address, esp_err_to_name(err)); | ||||
|       i2c_cmd_link_delete(cmd); | ||||
|       return ERROR_UNKNOWN; | ||||
|     } | ||||
|   } | ||||
|   err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); | ||||
|   i2c_cmd_link_delete(cmd); | ||||
|   if (err == ESP_FAIL) { | ||||
|   | ||||
| @@ -51,7 +51,7 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud | ||||
| #endif | ||||
|   void set_external_dac_channels(uint8_t channels) { this->external_dac_channels_ = channels; } | ||||
|  | ||||
|   void start(); | ||||
|   void start() override; | ||||
|   void stop() override; | ||||
|  | ||||
|   size_t play(const uint8_t *data, size_t length) override; | ||||
|   | ||||
| @@ -16,7 +16,7 @@ from esphome.const import ( | ||||
| ) | ||||
|  | ||||
| DEPENDENCIES = ["i2c"] | ||||
| CODEOWNERS = ["@MrEditor97"] | ||||
| CODEOWNERS = ["@mreditor97"] | ||||
|  | ||||
| ina260_ns = cg.esphome_ns.namespace("ina260") | ||||
| INA260Component = ina260_ns.class_( | ||||
|   | ||||
| @@ -1,113 +1,64 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import uart | ||||
| from esphome.const import CONF_ID, CONF_TIMEOUT | ||||
| from esphome.const import CONF_ID, CONF_THROTTLE, CONF_TIMEOUT, CONF_PASSWORD | ||||
| from esphome import automation | ||||
| from esphome.automation import maybe_simple_id | ||||
|  | ||||
| DEPENDENCIES = ["uart"] | ||||
| CODEOWNERS = ["@sebcaps"] | ||||
| CODEOWNERS = ["@sebcaps", "@regevbr"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| ld2410_ns = cg.esphome_ns.namespace("ld2410") | ||||
| LD2410Component = ld2410_ns.class_("LD2410Component", cg.Component, uart.UARTDevice) | ||||
| LD2410Restart = ld2410_ns.class_("LD2410Restart", automation.Action) | ||||
|  | ||||
| CONF_LD2410_ID = "ld2410_id" | ||||
|  | ||||
| CONF_MAX_MOVE_DISTANCE = "max_move_distance" | ||||
| CONF_MAX_STILL_DISTANCE = "max_still_distance" | ||||
| CONF_G0_MOVE_THRESHOLD = "g0_move_threshold" | ||||
| CONF_G0_STILL_THRESHOLD = "g0_still_threshold" | ||||
| CONF_G1_MOVE_THRESHOLD = "g1_move_threshold" | ||||
| CONF_G1_STILL_THRESHOLD = "g1_still_threshold" | ||||
| CONF_G2_MOVE_THRESHOLD = "g2_move_threshold" | ||||
| CONF_G2_STILL_THRESHOLD = "g2_still_threshold" | ||||
| CONF_G3_MOVE_THRESHOLD = "g3_move_threshold" | ||||
| CONF_G3_STILL_THRESHOLD = "g3_still_threshold" | ||||
| CONF_G4_MOVE_THRESHOLD = "g4_move_threshold" | ||||
| CONF_G4_STILL_THRESHOLD = "g4_still_threshold" | ||||
| CONF_G5_MOVE_THRESHOLD = "g5_move_threshold" | ||||
| CONF_G5_STILL_THRESHOLD = "g5_still_threshold" | ||||
| CONF_G6_MOVE_THRESHOLD = "g6_move_threshold" | ||||
| CONF_G6_STILL_THRESHOLD = "g6_still_threshold" | ||||
| CONF_G7_MOVE_THRESHOLD = "g7_move_threshold" | ||||
| CONF_G7_STILL_THRESHOLD = "g7_still_threshold" | ||||
| CONF_G8_MOVE_THRESHOLD = "g8_move_threshold" | ||||
| CONF_G8_STILL_THRESHOLD = "g8_still_threshold" | ||||
| CONF_STILL_THRESHOLDS = [f"g{x}_still_threshold" for x in range(9)] | ||||
| CONF_MOVE_THRESHOLDS = [f"g{x}_move_threshold" for x in range(9)] | ||||
|  | ||||
| DISTANCES = [0.75, 1.5, 2.25, 3, 3.75, 4.5, 5.25, 6] | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(LD2410Component), | ||||
|             cv.Optional(CONF_MAX_MOVE_DISTANCE, default="4.5m"): cv.All( | ||||
|                 cv.distance, cv.one_of(*DISTANCES, float=True) | ||||
|         cv.Optional(CONF_THROTTLE, default="1000ms"): cv.All( | ||||
|             cv.positive_time_period_milliseconds, | ||||
|             cv.Range(min=cv.TimePeriod(milliseconds=1)), | ||||
|         ), | ||||
|             cv.Optional(CONF_MAX_STILL_DISTANCE, default="4.5m"): cv.All( | ||||
|                 cv.distance, cv.one_of(*DISTANCES, float=True) | ||||
|         cv.Optional(CONF_MAX_MOVE_DISTANCE): cv.invalid( | ||||
|             f"The '{CONF_MAX_MOVE_DISTANCE}' option has been moved to the '{CONF_MAX_MOVE_DISTANCE}'" | ||||
|             f" number component" | ||||
|         ), | ||||
|             cv.Optional(CONF_TIMEOUT, default="5s"): cv.All( | ||||
|                 cv.positive_time_period_seconds, | ||||
|                 cv.Range(max=cv.TimePeriod(seconds=32767)), | ||||
|         cv.Optional(CONF_MAX_STILL_DISTANCE): cv.invalid( | ||||
|             f"The '{CONF_MAX_STILL_DISTANCE}' option has been moved to the '{CONF_MAX_STILL_DISTANCE}'" | ||||
|             f" number component" | ||||
|         ), | ||||
|             cv.Optional(CONF_G0_MOVE_THRESHOLD, default=50): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|         cv.Optional(CONF_TIMEOUT): cv.invalid( | ||||
|             f"The '{CONF_TIMEOUT}' option has been moved to the '{CONF_TIMEOUT}'" | ||||
|             f" number component" | ||||
|         ), | ||||
|             cv.Optional(CONF_G0_STILL_THRESHOLD, default=0): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|     } | ||||
| ) | ||||
|  | ||||
| for i in range(9): | ||||
|     CONFIG_SCHEMA = CONFIG_SCHEMA.extend( | ||||
|         cv.Schema( | ||||
|             { | ||||
|                 cv.Optional(CONF_MOVE_THRESHOLDS[i]): cv.invalid( | ||||
|                     f"The '{CONF_MOVE_THRESHOLDS[i]}' option has been moved to the '{CONF_MOVE_THRESHOLDS[i]}'" | ||||
|                     f" number component" | ||||
|                 ), | ||||
|             cv.Optional(CONF_G1_MOVE_THRESHOLD, default=50): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G1_STILL_THRESHOLD, default=0): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G2_MOVE_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G2_STILL_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G3_MOVE_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G3_STILL_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G4_MOVE_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G4_STILL_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G5_MOVE_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G5_STILL_THRESHOLD, default=40): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G6_MOVE_THRESHOLD, default=30): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G6_STILL_THRESHOLD, default=15): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G7_MOVE_THRESHOLD, default=30): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G7_STILL_THRESHOLD, default=15): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G8_MOVE_THRESHOLD, default=30): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|             ), | ||||
|             cv.Optional(CONF_G8_STILL_THRESHOLD, default=15): cv.int_range( | ||||
|                 min=0, max=100 | ||||
|                 cv.Optional(CONF_STILL_THRESHOLDS[i]): cv.invalid( | ||||
|                     f"The '{CONF_STILL_THRESHOLDS[i]}' option has been moved to the '{CONF_STILL_THRESHOLDS[i]}'" | ||||
|                     f" number component" | ||||
|                 ), | ||||
|             } | ||||
|         ) | ||||
|     .extend(uart.UART_DEVICE_SCHEMA) | ||||
|     .extend(cv.COMPONENT_SCHEMA) | ||||
|     ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     CONFIG_SCHEMA.extend(uart.UART_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) | ||||
| ) | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( | ||||
| @@ -123,31 +74,7 @@ async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|     await uart.register_uart_device(var, config) | ||||
|     cg.add(var.set_timeout(config[CONF_TIMEOUT])) | ||||
|     cg.add(var.set_max_move_distance(int(config[CONF_MAX_MOVE_DISTANCE] / 0.75))) | ||||
|     cg.add(var.set_max_still_distance(int(config[CONF_MAX_STILL_DISTANCE] / 0.75))) | ||||
|     cg.add( | ||||
|         var.set_range_config( | ||||
|             config[CONF_G0_MOVE_THRESHOLD], | ||||
|             config[CONF_G0_STILL_THRESHOLD], | ||||
|             config[CONF_G1_MOVE_THRESHOLD], | ||||
|             config[CONF_G1_STILL_THRESHOLD], | ||||
|             config[CONF_G2_MOVE_THRESHOLD], | ||||
|             config[CONF_G2_STILL_THRESHOLD], | ||||
|             config[CONF_G3_MOVE_THRESHOLD], | ||||
|             config[CONF_G3_STILL_THRESHOLD], | ||||
|             config[CONF_G4_MOVE_THRESHOLD], | ||||
|             config[CONF_G4_STILL_THRESHOLD], | ||||
|             config[CONF_G5_MOVE_THRESHOLD], | ||||
|             config[CONF_G5_STILL_THRESHOLD], | ||||
|             config[CONF_G6_MOVE_THRESHOLD], | ||||
|             config[CONF_G6_STILL_THRESHOLD], | ||||
|             config[CONF_G7_MOVE_THRESHOLD], | ||||
|             config[CONF_G7_STILL_THRESHOLD], | ||||
|             config[CONF_G8_MOVE_THRESHOLD], | ||||
|             config[CONF_G8_STILL_THRESHOLD], | ||||
|         ) | ||||
|     ) | ||||
|     cg.add(var.set_throttle(config[CONF_THROTTLE])) | ||||
|  | ||||
|  | ||||
| CALIBRATION_ACTION_SCHEMA = maybe_simple_id( | ||||
| @@ -155,3 +82,28 @@ CALIBRATION_ACTION_SCHEMA = maybe_simple_id( | ||||
|         cv.Required(CONF_ID): cv.use_id(LD2410Component), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| # Actions | ||||
| BluetoothPasswordSetAction = ld2410_ns.class_( | ||||
|     "BluetoothPasswordSetAction", automation.Action | ||||
| ) | ||||
|  | ||||
|  | ||||
| BLUETOOTH_PASSWORD_SET_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_ID): cv.use_id(LD2410Component), | ||||
|         cv.Required(CONF_PASSWORD): cv.templatable(cv.string_strict), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "bluetooth_password.set", BluetoothPasswordSetAction, BLUETOOTH_PASSWORD_SET_SCHEMA | ||||
| ) | ||||
| async def bluetooth_password_set_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_PASSWORD], args, cg.std_string) | ||||
|     cg.add(var.set_password(template_)) | ||||
|     return var | ||||
|   | ||||
							
								
								
									
										22
									
								
								esphome/components/ld2410/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/ld2410/automation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| template<typename... Ts> class BluetoothPasswordSetAction : public Action<Ts...> { | ||||
|  public: | ||||
|   explicit BluetoothPasswordSetAction(LD2410Component *ld2410_comp) : ld2410_comp_(ld2410_comp) {} | ||||
|   TEMPLATABLE_VALUE(std::string, password) | ||||
|  | ||||
|   void play(Ts... x) override { this->ld2410_comp_->set_bluetooth_password(this->password_.value(x...)); } | ||||
|  | ||||
|  protected: | ||||
|   LD2410Component *ld2410_comp_; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -1,36 +1,55 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import binary_sensor | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import DEVICE_CLASS_MOTION, DEVICE_CLASS_OCCUPANCY | ||||
| from esphome.const import ( | ||||
|     DEVICE_CLASS_MOTION, | ||||
|     DEVICE_CLASS_OCCUPANCY, | ||||
|     DEVICE_CLASS_PRESENCE, | ||||
|     ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|     ICON_MOTION_SENSOR, | ||||
|     ICON_ACCOUNT, | ||||
| ) | ||||
| from . import CONF_LD2410_ID, LD2410Component | ||||
|  | ||||
| DEPENDENCIES = ["ld2410"] | ||||
| CONF_HAS_TARGET = "has_target" | ||||
| CONF_HAS_MOVING_TARGET = "has_moving_target" | ||||
| CONF_HAS_STILL_TARGET = "has_still_target" | ||||
| CONF_OUT_PIN_PRESENCE_STATUS = "out_pin_presence_status" | ||||
|  | ||||
| CONFIG_SCHEMA = { | ||||
|     cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||
|     cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema( | ||||
|         device_class=DEVICE_CLASS_OCCUPANCY | ||||
|         device_class=DEVICE_CLASS_OCCUPANCY, | ||||
|         icon=ICON_ACCOUNT, | ||||
|     ), | ||||
|     cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema( | ||||
|         device_class=DEVICE_CLASS_MOTION | ||||
|         device_class=DEVICE_CLASS_MOTION, | ||||
|         icon=ICON_MOTION_SENSOR, | ||||
|     ), | ||||
|     cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema( | ||||
|         device_class=DEVICE_CLASS_OCCUPANCY | ||||
|         device_class=DEVICE_CLASS_OCCUPANCY, | ||||
|         icon=ICON_MOTION_SENSOR, | ||||
|     ), | ||||
|     cv.Optional(CONF_OUT_PIN_PRESENCE_STATUS): binary_sensor.binary_sensor_schema( | ||||
|         device_class=DEVICE_CLASS_PRESENCE, | ||||
|         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|         icon=ICON_ACCOUNT, | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||
|     if CONF_HAS_TARGET in config: | ||||
|         sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_TARGET]) | ||||
|         cg.add(ld2410_component.set_target_sensor(sens)) | ||||
|     if CONF_HAS_MOVING_TARGET in config: | ||||
|         sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_MOVING_TARGET]) | ||||
|         cg.add(ld2410_component.set_moving_target_sensor(sens)) | ||||
|     if CONF_HAS_STILL_TARGET in config: | ||||
|         sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_STILL_TARGET]) | ||||
|         cg.add(ld2410_component.set_still_target_sensor(sens)) | ||||
|     if has_target_config := config.get(CONF_HAS_TARGET): | ||||
|         sens = await binary_sensor.new_binary_sensor(has_target_config) | ||||
|         cg.add(ld2410_component.set_target_binary_sensor(sens)) | ||||
|     if has_moving_target_config := config.get(CONF_HAS_MOVING_TARGET): | ||||
|         sens = await binary_sensor.new_binary_sensor(has_moving_target_config) | ||||
|         cg.add(ld2410_component.set_moving_target_binary_sensor(sens)) | ||||
|     if has_still_target_config := config.get(CONF_HAS_STILL_TARGET): | ||||
|         sens = await binary_sensor.new_binary_sensor(has_still_target_config) | ||||
|         cg.add(ld2410_component.set_still_target_binary_sensor(sens)) | ||||
|     if out_pin_presence_status_config := config.get(CONF_OUT_PIN_PRESENCE_STATUS): | ||||
|         sens = await binary_sensor.new_binary_sensor(out_pin_presence_status_config) | ||||
|         cg.add(ld2410_component.set_out_pin_presence_status_binary_sensor(sens)) | ||||
|   | ||||
							
								
								
									
										57
									
								
								esphome/components/ld2410/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								esphome/components/ld2410/button/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import button | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     DEVICE_CLASS_RESTART, | ||||
|     ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
|     ICON_RESTART, | ||||
|     ICON_RESTART_ALERT, | ||||
|     ICON_DATABASE, | ||||
| ) | ||||
| from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns | ||||
|  | ||||
| QueryButton = ld2410_ns.class_("QueryButton", button.Button) | ||||
| ResetButton = ld2410_ns.class_("ResetButton", button.Button) | ||||
| RestartButton = ld2410_ns.class_("RestartButton", button.Button) | ||||
|  | ||||
| CONF_FACTORY_RESET = "factory_reset" | ||||
| CONF_RESTART = "restart" | ||||
| CONF_QUERY_PARAMS = "query_params" | ||||
|  | ||||
| CONFIG_SCHEMA = { | ||||
|     cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||
|     cv.Optional(CONF_FACTORY_RESET): button.button_schema( | ||||
|         ResetButton, | ||||
|         device_class=DEVICE_CLASS_RESTART, | ||||
|         entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|         icon=ICON_RESTART_ALERT, | ||||
|     ), | ||||
|     cv.Optional(CONF_RESTART): button.button_schema( | ||||
|         RestartButton, | ||||
|         device_class=DEVICE_CLASS_RESTART, | ||||
|         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|         icon=ICON_RESTART, | ||||
|     ), | ||||
|     cv.Optional(CONF_QUERY_PARAMS): button.button_schema( | ||||
|         QueryButton, | ||||
|         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|         icon=ICON_DATABASE, | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||
|     if factory_reset_config := config.get(CONF_FACTORY_RESET): | ||||
|         b = await button.new_button(factory_reset_config) | ||||
|         await cg.register_parented(b, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_reset_button(b)) | ||||
|     if restart_config := config.get(CONF_RESTART): | ||||
|         b = await button.new_button(restart_config) | ||||
|         await cg.register_parented(b, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_restart_button(b)) | ||||
|     if query_params_config := config.get(CONF_QUERY_PARAMS): | ||||
|         b = await button.new_button(query_params_config) | ||||
|         await cg.register_parented(b, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_query_button(b)) | ||||
							
								
								
									
										9
									
								
								esphome/components/ld2410/button/query_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/ld2410/button/query_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| #include "query_button.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void QueryButton::press_action() { this->parent_->read_all_info(); } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/button/query_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/button/query_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/button/button.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class QueryButton : public button::Button, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   QueryButton() = default; | ||||
|  | ||||
|  protected: | ||||
|   void press_action() override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										9
									
								
								esphome/components/ld2410/button/reset_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/ld2410/button/reset_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| #include "reset_button.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void ResetButton::press_action() { this->parent_->factory_reset(); } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/button/reset_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/button/reset_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/button/button.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class ResetButton : public button::Button, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   ResetButton() = default; | ||||
|  | ||||
|  protected: | ||||
|   void press_action() override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										9
									
								
								esphome/components/ld2410/button/restart_button.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								esphome/components/ld2410/button/restart_button.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| #include "restart_button.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/button/restart_button.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/button/restart_button.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/button/button.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class RestartButton : public button::Button, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   RestartButton() = default; | ||||
|  | ||||
|  protected: | ||||
|   void press_action() override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -1,5 +1,13 @@ | ||||
| #include "ld2410.h" | ||||
|  | ||||
| #include <utility> | ||||
| #ifdef USE_NUMBER | ||||
| #include "esphome/components/number/number.h" | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #endif | ||||
|  | ||||
| #define highbyte(val) (uint8_t)((val) >> 8) | ||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | ||||
|  | ||||
| @@ -8,48 +16,97 @@ namespace ld2410 { | ||||
|  | ||||
| static const char *const TAG = "ld2410"; | ||||
|  | ||||
| LD2410Component::LD2410Component() {} | ||||
|  | ||||
| void LD2410Component::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "LD2410:"); | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   LOG_BINARY_SENSOR("  ", "HasTargetSensor", this->target_binary_sensor_); | ||||
|   LOG_BINARY_SENSOR("  ", "MovingSensor", this->moving_binary_sensor_); | ||||
|   LOG_BINARY_SENSOR("  ", "StillSensor", this->still_binary_sensor_); | ||||
|   LOG_BINARY_SENSOR("  ", "TargetBinarySensor", this->target_binary_sensor_); | ||||
|   LOG_BINARY_SENSOR("  ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_); | ||||
|   LOG_BINARY_SENSOR("  ", "StillTargetBinarySensor", this->still_target_binary_sensor_); | ||||
|   LOG_BINARY_SENSOR("  ", "OutPinPresenceStatusBinarySensor", this->out_pin_presence_status_binary_sensor_); | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
|   LOG_SWITCH("  ", "EngineeringModeSwitch", this->engineering_mode_switch_); | ||||
|   LOG_SWITCH("  ", "BluetoothSwitch", this->bluetooth_switch_); | ||||
| #endif | ||||
| #ifdef USE_BUTTON | ||||
|   LOG_BUTTON("  ", "ResetButton", this->reset_button_); | ||||
|   LOG_BUTTON("  ", "RestartButton", this->restart_button_); | ||||
|   LOG_BUTTON("  ", "QueryButton", this->query_button_); | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
|   LOG_SENSOR("  ", "Moving Distance", this->moving_target_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "Still Distance", this->still_target_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "Moving Energy", this->moving_target_energy_sensor_); | ||||
|   LOG_SENSOR("  ", "Still Energy", this->still_target_energy_sensor_); | ||||
|   LOG_SENSOR("  ", "Detection Distance", this->detection_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "LightSensor", this->light_sensor_); | ||||
|   LOG_SENSOR("  ", "MovingTargetDistanceSensor", this->moving_target_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "StillTargetDistanceSensor", this->still_target_distance_sensor_); | ||||
|   LOG_SENSOR("  ", "MovingTargetEnergySensor", this->moving_target_energy_sensor_); | ||||
|   LOG_SENSOR("  ", "StillTargetEnergySensor", this->still_target_energy_sensor_); | ||||
|   LOG_SENSOR("  ", "DetectionDistanceSensor", this->detection_distance_sensor_); | ||||
|   for (sensor::Sensor *s : this->gate_still_sensors_) { | ||||
|     LOG_SENSOR("  ", "NthGateStillSesnsor", s); | ||||
|   } | ||||
|   for (sensor::Sensor *s : this->gate_move_sensors_) { | ||||
|     LOG_SENSOR("  ", "NthGateMoveSesnsor", s); | ||||
|   } | ||||
| #endif | ||||
|   this->set_config_mode_(true); | ||||
|   this->get_version_(); | ||||
|   this->set_config_mode_(false); | ||||
|   ESP_LOGCONFIG(TAG, "  Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2], | ||||
|                 this->version_[3], this->version_[4], this->version_[5]); | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|   LOG_TEXT_SENSOR("  ", "VersionTextSensor", this->version_text_sensor_); | ||||
|   LOG_TEXT_SENSOR("  ", "MacTextSensor", this->mac_text_sensor_); | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   LOG_SELECT("  ", "LightFunctionSelect", this->light_function_select_); | ||||
|   LOG_SELECT("  ", "OutPinLevelSelect", this->out_pin_level_select_); | ||||
|   LOG_SELECT("  ", "DistanceResolutionSelect", this->distance_resolution_select_); | ||||
|   LOG_SELECT("  ", "BaudRateSelect", this->baud_rate_select_); | ||||
| #endif | ||||
| #ifdef USE_NUMBER | ||||
|   LOG_NUMBER("  ", "LightThresholdNumber", this->light_threshold_number_); | ||||
|   LOG_NUMBER("  ", "MaxStillDistanceGateNumber", this->max_still_distance_gate_number_); | ||||
|   LOG_NUMBER("  ", "MaxMoveDistanceGateNumber", this->max_move_distance_gate_number_); | ||||
|   LOG_NUMBER("  ", "TimeoutNumber", this->timeout_number_); | ||||
|   for (number::Number *n : this->gate_still_threshold_numbers_) { | ||||
|     LOG_NUMBER("  ", "Still Thresholds Number", n); | ||||
|   } | ||||
|   for (number::Number *n : this->gate_move_threshold_numbers_) { | ||||
|     LOG_NUMBER("  ", "Move Thresholds Number", n); | ||||
|   } | ||||
| #endif | ||||
|   this->read_all_info(); | ||||
|   ESP_LOGCONFIG(TAG, "  Throttle_ : %ums", this->throttle_); | ||||
|   ESP_LOGCONFIG(TAG, "  MAC Address : %s", const_cast<char *>(this->mac_.c_str())); | ||||
|   ESP_LOGCONFIG(TAG, "  Firmware Version : %s", const_cast<char *>(this->version_.c_str())); | ||||
| } | ||||
|  | ||||
| void LD2410Component::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up LD2410..."); | ||||
|   this->set_config_mode_(true); | ||||
|   this->set_max_distances_timeout_(this->max_move_distance_, this->max_still_distance_, this->timeout_); | ||||
|   // Configure Gates sensitivity | ||||
|   this->set_gate_threshold_(0, this->rg0_move_threshold_, this->rg0_still_threshold_); | ||||
|   this->set_gate_threshold_(1, this->rg1_move_threshold_, this->rg1_still_threshold_); | ||||
|   this->set_gate_threshold_(2, this->rg2_move_threshold_, this->rg2_still_threshold_); | ||||
|   this->set_gate_threshold_(3, this->rg3_move_threshold_, this->rg3_still_threshold_); | ||||
|   this->set_gate_threshold_(4, this->rg4_move_threshold_, this->rg4_still_threshold_); | ||||
|   this->set_gate_threshold_(5, this->rg5_move_threshold_, this->rg5_still_threshold_); | ||||
|   this->set_gate_threshold_(6, this->rg6_move_threshold_, this->rg6_still_threshold_); | ||||
|   this->set_gate_threshold_(7, this->rg7_move_threshold_, this->rg7_still_threshold_); | ||||
|   this->set_gate_threshold_(8, this->rg8_move_threshold_, this->rg8_still_threshold_); | ||||
|   this->get_version_(); | ||||
|   this->set_config_mode_(false); | ||||
|   ESP_LOGCONFIG(TAG, "Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2], | ||||
|                 this->version_[3], this->version_[4], this->version_[5]); | ||||
|   this->read_all_info(); | ||||
|   ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast<char *>(this->mac_.c_str())); | ||||
|   ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast<char *>(this->version_.c_str())); | ||||
|   ESP_LOGCONFIG(TAG, "LD2410 setup complete."); | ||||
| } | ||||
|  | ||||
| void LD2410Component::read_all_info() { | ||||
|   this->set_config_mode_(true); | ||||
|   this->get_version_(); | ||||
|   this->get_mac_(); | ||||
|   this->get_distance_resolution_(); | ||||
|   this->get_light_control_(); | ||||
|   this->query_parameters_(); | ||||
|   this->set_config_mode_(false); | ||||
| #ifdef USE_SELECT | ||||
|   const auto baud_rate = std::to_string(this->parent_->get_baud_rate()); | ||||
|   if (this->baud_rate_select_ != nullptr && this->baud_rate_select_->state != baud_rate) { | ||||
|     this->baud_rate_select_->publish_state(baud_rate); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void LD2410Component::restart_and_read_all_info() { | ||||
|   this->set_config_mode_(true); | ||||
|   this->restart_(); | ||||
|   this->set_timeout(1000, [this]() { this->read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2410Component::loop() { | ||||
|   const int max_line_length = 80; | ||||
|   static uint8_t buffer[max_line_length]; | ||||
| @@ -59,9 +116,8 @@ void LD2410Component::loop() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| void LD2410Component::send_command_(uint8_t command, uint8_t *command_value, int command_value_len) { | ||||
|   // lastCommandSuccess->publish_state(false); | ||||
|  | ||||
| void LD2410Component::send_command_(uint8_t command, const uint8_t *command_value, int command_value_len) { | ||||
|   ESP_LOGV(TAG, "Sending COMMAND %02X", command); | ||||
|   // frame start bytes | ||||
|   this->write_array(CMD_FRAME_HEADER, 4); | ||||
|   // length bytes | ||||
| @@ -96,11 +152,26 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) { | ||||
|     return;  // data head=0xAA, data end=0x55, crc=0x00 | ||||
|  | ||||
|   /* | ||||
|       Data Type: 6th | ||||
|     Reduce data update rate to prevent home assistant database size grow fast | ||||
|   */ | ||||
|   int32_t current_millis = millis(); | ||||
|   if (current_millis - last_periodic_millis_ < this->throttle_) | ||||
|     return; | ||||
|   last_periodic_millis_ = current_millis; | ||||
|  | ||||
|   /* | ||||
|     Data Type: 7th | ||||
|     0x01: Engineering mode | ||||
|     0x02: Normal mode | ||||
|   */ | ||||
|     // char data_type = buffer[DATA_TYPES]; | ||||
|   bool engineering_mode = buffer[DATA_TYPES] == 0x01; | ||||
| #ifdef USE_SWITCH | ||||
|   if (this->engineering_mode_switch_ != nullptr && | ||||
|       current_millis - last_engineering_mode_change_millis_ > this->throttle_) { | ||||
|     this->engineering_mode_switch_->publish_state(engineering_mode); | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   /* | ||||
|     Target states: 9th | ||||
|     0x00 = No target | ||||
| @@ -108,27 +179,15 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) { | ||||
|     0x02 = Still targets | ||||
|     0x03 = Moving+Still targets | ||||
|   */ | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   char target_state = buffer[TARGET_STATES]; | ||||
|   if (this->target_binary_sensor_ != nullptr) { | ||||
|     this->target_binary_sensor_->publish_state(target_state != 0x00); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   /* | ||||
|     Reduce data update rate to prevent home assistant database size grow fast | ||||
|   */ | ||||
|   int32_t current_millis = millis(); | ||||
|   if (current_millis - last_periodic_millis < 1000) | ||||
|     return; | ||||
|   last_periodic_millis = current_millis; | ||||
|  | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   if (this->moving_binary_sensor_ != nullptr) { | ||||
|     this->moving_binary_sensor_->publish_state(CHECK_BIT(target_state, 0)); | ||||
|   if (this->moving_target_binary_sensor_ != nullptr) { | ||||
|     this->moving_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 0)); | ||||
|   } | ||||
|   if (this->still_binary_sensor_ != nullptr) { | ||||
|     this->still_binary_sensor_->publish_state(CHECK_BIT(target_state, 1)); | ||||
|   if (this->still_target_binary_sensor_ != nullptr) { | ||||
|     this->still_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 1)); | ||||
|   } | ||||
| #endif | ||||
|   /* | ||||
| @@ -164,26 +223,126 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) { | ||||
|     if (this->detection_distance_sensor_->get_state() != new_detect_distance) | ||||
|       this->detection_distance_sensor_->publish_state(new_detect_distance); | ||||
|   } | ||||
|   if (engineering_mode) { | ||||
|     /* | ||||
|       Moving distance range: 18th byte | ||||
|       Still distance range: 19th byte | ||||
|       Moving enery: 20~28th bytes | ||||
|     */ | ||||
|     for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) { | ||||
|       sensor::Sensor *s = this->gate_move_sensors_[i]; | ||||
|       if (s != nullptr) { | ||||
|         s->publish_state(buffer[MOVING_SENSOR_START + i]); | ||||
|       } | ||||
|     } | ||||
|     /* | ||||
|       Still energy: 29~37th bytes | ||||
|     */ | ||||
|     for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_still_sensors_.size(); i++) { | ||||
|       sensor::Sensor *s = this->gate_still_sensors_[i]; | ||||
|       if (s != nullptr) { | ||||
|         s->publish_state(buffer[STILL_SENSOR_START + i]); | ||||
|       } | ||||
|     } | ||||
|     /* | ||||
|       Light sensor: 38th bytes | ||||
|     */ | ||||
|     if (this->light_sensor_ != nullptr) { | ||||
|       int new_light_sensor = buffer[LIGHT_SENSOR]; | ||||
|       if (this->light_sensor_->get_state() != new_light_sensor) | ||||
|         this->light_sensor_->publish_state(new_light_sensor); | ||||
|     } | ||||
|   } else { | ||||
|     for (auto *s : this->gate_move_sensors_) { | ||||
|       if (s != nullptr && !std::isnan(s->get_state())) { | ||||
|         s->publish_state(NAN); | ||||
|       } | ||||
|     } | ||||
|     for (auto *s : this->gate_still_sensors_) { | ||||
|       if (s != nullptr && !std::isnan(s->get_state())) { | ||||
|         s->publish_state(NAN); | ||||
|       } | ||||
|     } | ||||
|     if (this->light_sensor_ != nullptr && !std::isnan(this->light_sensor_->get_state())) { | ||||
|       this->light_sensor_->publish_state(NAN); | ||||
|     } | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   if (engineering_mode) { | ||||
|     if (this->out_pin_presence_status_binary_sensor_ != nullptr) { | ||||
|       this->out_pin_presence_status_binary_sensor_->publish_state(buffer[OUT_PIN_SENSOR] == 0x01); | ||||
|     } | ||||
|   } else { | ||||
|     if (this->out_pin_presence_status_binary_sensor_ != nullptr) { | ||||
|       this->out_pin_presence_status_binary_sensor_->publish_state(false); | ||||
|     } | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | ||||
|   ESP_LOGV(TAG, "Handling ACK DATA for COMMAND"); | ||||
| const char VERSION_FMT[] = "%u.%02X.%02X%02X%02X%02X"; | ||||
|  | ||||
| std::string format_version(uint8_t *buffer) { | ||||
|   std::string::size_type version_size = 256; | ||||
|   std::string version; | ||||
|   do { | ||||
|     version.resize(version_size + 1); | ||||
|     version_size = std::snprintf(&version[0], version.size(), VERSION_FMT, buffer[13], buffer[12], buffer[17], | ||||
|                                  buffer[16], buffer[15], buffer[14]); | ||||
|   } while (version_size + 1 > version.size()); | ||||
|   version.resize(version_size); | ||||
|   return version; | ||||
| } | ||||
|  | ||||
| const char MAC_FMT[] = "%02X:%02X:%02X:%02X:%02X:%02X"; | ||||
|  | ||||
| const std::string UNKNOWN_MAC("unknown"); | ||||
| const std::string NO_MAC("08:05:04:03:02:01"); | ||||
|  | ||||
| std::string format_mac(uint8_t *buffer) { | ||||
|   std::string::size_type mac_size = 256; | ||||
|   std::string mac; | ||||
|   do { | ||||
|     mac.resize(mac_size + 1); | ||||
|     mac_size = std::snprintf(&mac[0], mac.size(), MAC_FMT, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], | ||||
|                              buffer[15]); | ||||
|   } while (mac_size + 1 > mac.size()); | ||||
|   mac.resize(mac_size); | ||||
|   if (mac == NO_MAC) { | ||||
|     return UNKNOWN_MAC; | ||||
|   } | ||||
|   return mac; | ||||
| } | ||||
|  | ||||
| #ifdef USE_NUMBER | ||||
| std::function<void(void)> set_number_value(number::Number *n, float value) { | ||||
|   float normalized_value = value * 1.0; | ||||
|   if (n != nullptr && (!n->has_state() || n->state != normalized_value)) { | ||||
|     n->state = normalized_value; | ||||
|     return [n, normalized_value]() { n->publish_state(normalized_value); }; | ||||
|   } | ||||
|   return []() {}; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | ||||
|   ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); | ||||
|   if (len < 10) { | ||||
|     ESP_LOGE(TAG, "Error with last command : incorrect length"); | ||||
|     return; | ||||
|     return true; | ||||
|   } | ||||
|   if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {  // check 4 frame start bytes | ||||
|     ESP_LOGE(TAG, "Error with last command : incorrect Header"); | ||||
|     return; | ||||
|     return true; | ||||
|   } | ||||
|   if (buffer[COMMAND_STATUS] != 0x01) { | ||||
|     ESP_LOGE(TAG, "Error with last command : status != 0x01"); | ||||
|     return; | ||||
|     return true; | ||||
|   } | ||||
|   if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) { | ||||
|     ESP_LOGE(TAG, "Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]); | ||||
|     return; | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   switch (buffer[COMMAND]) { | ||||
| @@ -193,49 +352,127 @@ void LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | ||||
|     case lowbyte(CMD_DISABLE_CONF): | ||||
|       ESP_LOGV(TAG, "Handled Disabled conf command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_BAUD_RATE): | ||||
|       ESP_LOGV(TAG, "Handled baud rate change command"); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->baud_rate_select_ != nullptr) { | ||||
|         ESP_LOGE(TAG, "Change baud rate component config to %s and reinstall", this->baud_rate_select_->state.c_str()); | ||||
|       } | ||||
| #endif | ||||
|       break; | ||||
|     case lowbyte(CMD_VERSION): | ||||
|       ESP_LOGV(TAG, "FW Version is: %u.%u.%u%u%u%u", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15], | ||||
|                buffer[14]); | ||||
|       this->version_[0] = buffer[13]; | ||||
|       this->version_[1] = buffer[12]; | ||||
|       this->version_[2] = buffer[17]; | ||||
|       this->version_[3] = buffer[16]; | ||||
|       this->version_[4] = buffer[15]; | ||||
|       this->version_[5] = buffer[14]; | ||||
|  | ||||
|       this->version_ = format_version(buffer); | ||||
|       ESP_LOGV(TAG, "FW Version is: %s", const_cast<char *>(this->version_.c_str())); | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|       if (this->version_text_sensor_ != nullptr) { | ||||
|         this->version_text_sensor_->publish_state(this->version_); | ||||
|       } | ||||
| #endif | ||||
|       break; | ||||
|     case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): { | ||||
|       std::string distance_resolution = | ||||
|           DISTANCE_RESOLUTION_INT_TO_ENUM.at(this->two_byte_to_int_(buffer[10], buffer[11])); | ||||
|       ESP_LOGV(TAG, "Distance resolution is: %s", const_cast<char *>(distance_resolution.c_str())); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->distance_resolution_select_ != nullptr && | ||||
|           this->distance_resolution_select_->state != distance_resolution) { | ||||
|         this->distance_resolution_select_->publish_state(distance_resolution); | ||||
|       } | ||||
| #endif | ||||
|     } break; | ||||
|     case lowbyte(CMD_QUERY_LIGHT_CONTROL): { | ||||
|       this->light_function_ = LIGHT_FUNCTION_INT_TO_ENUM.at(buffer[10]); | ||||
|       this->light_threshold_ = buffer[11] * 1.0; | ||||
|       this->out_pin_level_ = OUT_PIN_LEVEL_INT_TO_ENUM.at(buffer[12]); | ||||
|       ESP_LOGV(TAG, "Light function is: %s", const_cast<char *>(this->light_function_.c_str())); | ||||
|       ESP_LOGV(TAG, "Light threshold is: %f", this->light_threshold_); | ||||
|       ESP_LOGV(TAG, "Out pin level is: %s", const_cast<char *>(this->out_pin_level_.c_str())); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->light_function_select_ != nullptr && this->light_function_select_->state != this->light_function_) { | ||||
|         this->light_function_select_->publish_state(this->light_function_); | ||||
|       } | ||||
|       if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->state != this->out_pin_level_) { | ||||
|         this->out_pin_level_select_->publish_state(this->out_pin_level_); | ||||
|       } | ||||
| #endif | ||||
| #ifdef USE_NUMBER | ||||
|       if (this->light_threshold_number_ != nullptr && | ||||
|           (!this->light_threshold_number_->has_state() || | ||||
|            this->light_threshold_number_->state != this->light_threshold_)) { | ||||
|         this->light_threshold_number_->publish_state(this->light_threshold_); | ||||
|       } | ||||
| #endif | ||||
|     } break; | ||||
|     case lowbyte(CMD_MAC): | ||||
|       if (len < 20) { | ||||
|         return false; | ||||
|       } | ||||
|       this->mac_ = format_mac(buffer); | ||||
|       ESP_LOGV(TAG, "MAC Address is: %s", const_cast<char *>(this->mac_.c_str())); | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|       if (this->mac_text_sensor_ != nullptr) { | ||||
|         this->mac_text_sensor_->publish_state(this->mac_); | ||||
|       } | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
|       if (this->bluetooth_switch_ != nullptr) { | ||||
|         this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC); | ||||
|       } | ||||
| #endif | ||||
|       break; | ||||
|     case lowbyte(CMD_GATE_SENS): | ||||
|       ESP_LOGV(TAG, "Handled sensitivity command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_BLUETOOTH): | ||||
|       ESP_LOGV(TAG, "Handled bluetooth command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_DISTANCE_RESOLUTION): | ||||
|       ESP_LOGV(TAG, "Handled set distance resolution command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_LIGHT_CONTROL): | ||||
|       ESP_LOGV(TAG, "Handled set light control command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_BT_PASSWORD): | ||||
|       ESP_LOGV(TAG, "Handled set bluetooth password command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_QUERY):  // Query parameters response | ||||
|     { | ||||
|       if (buffer[10] != 0xAA) | ||||
|         return;  // value head=0xAA | ||||
|         return true;  // value head=0xAA | ||||
| #ifdef USE_NUMBER | ||||
|       /* | ||||
|         Moving distance range: 13th byte | ||||
|         Still distance range: 14th byte | ||||
|       */ | ||||
|       // TODO | ||||
|       // maxMovingDistanceRange->publish_state(buffer[12]); | ||||
|       // maxStillDistanceRange->publish_state(buffer[13]); | ||||
|       std::vector<std::function<void(void)>> updates; | ||||
|       updates.push_back(set_number_value(this->max_move_distance_gate_number_, buffer[12])); | ||||
|       updates.push_back(set_number_value(this->max_still_distance_gate_number_, buffer[13])); | ||||
|       /* | ||||
|         Moving Sensitivities: 15~23th bytes | ||||
|       */ | ||||
|       for (std::vector<number::Number *>::size_type i = 0; i != this->gate_move_threshold_numbers_.size(); i++) { | ||||
|         updates.push_back(set_number_value(this->gate_move_threshold_numbers_[i], buffer[14 + i])); | ||||
|       } | ||||
|       /* | ||||
|         Still Sensitivities: 24~32th bytes | ||||
|       */ | ||||
|       for (int i = 0; i < 9; i++) { | ||||
|         moving_sensitivities[i] = buffer[14 + i]; | ||||
|       } | ||||
|       for (int i = 0; i < 9; i++) { | ||||
|         still_sensitivities[i] = buffer[23 + i]; | ||||
|       for (std::vector<number::Number *>::size_type i = 0; i != this->gate_still_threshold_numbers_.size(); i++) { | ||||
|         updates.push_back(set_number_value(this->gate_still_threshold_numbers_[i], buffer[23 + i])); | ||||
|       } | ||||
|       /* | ||||
|         None Duration: 33~34th bytes | ||||
|       */ | ||||
|       // noneDuration->publish_state(this->two_byte_to_int_(buffer[32], buffer[33])); | ||||
|       updates.push_back(set_number_value(this->timeout_number_, this->two_byte_to_int_(buffer[32], buffer[33]))); | ||||
|       for (auto &update : updates) { | ||||
|         update(); | ||||
|       } | ||||
| #endif | ||||
|     } break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void LD2410Component::readline_(int readch, uint8_t *buffer, int len) { | ||||
| @@ -256,8 +493,11 @@ void LD2410Component::readline_(int readch, uint8_t *buffer, int len) { | ||||
|       } else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 && | ||||
|                  buffer[pos - 1] == 0x01) { | ||||
|         ESP_LOGV(TAG, "Will handle ACK Data"); | ||||
|         this->handle_ack_data_(buffer, pos); | ||||
|         if (this->handle_ack_data_(buffer, pos)) { | ||||
|           pos = 0;  // Reset position index ready for next time | ||||
|         } else { | ||||
|           ESP_LOGV(TAG, "ACK Data incomplete"); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -269,21 +509,85 @@ void LD2410Component::set_config_mode_(bool enable) { | ||||
|   this->send_command_(cmd, enable ? cmd_value : nullptr, 2); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_bluetooth(bool enable) { | ||||
|   this->set_config_mode_(true); | ||||
|   uint8_t enable_cmd_value[2] = {0x01, 0x00}; | ||||
|   uint8_t disable_cmd_value[2] = {0x00, 0x00}; | ||||
|   this->send_command_(CMD_BLUETOOTH, enable ? enable_cmd_value : disable_cmd_value, 2); | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_distance_resolution(const std::string &state) { | ||||
|   this->set_config_mode_(true); | ||||
|   uint8_t cmd_value[2] = {DISTANCE_RESOLUTION_ENUM_TO_INT.at(state), 0x00}; | ||||
|   this->send_command_(CMD_SET_DISTANCE_RESOLUTION, cmd_value, 2); | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_baud_rate(const std::string &state) { | ||||
|   this->set_config_mode_(true); | ||||
|   uint8_t cmd_value[2] = {BAUD_RATE_ENUM_TO_INT.at(state), 0x00}; | ||||
|   this->send_command_(CMD_SET_BAUD_RATE, cmd_value, 2); | ||||
|   this->set_timeout(200, [this]() { this->restart_(); }); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_bluetooth_password(const std::string &password) { | ||||
|   if (password.length() != 6) { | ||||
|     ESP_LOGE(TAG, "set_bluetooth_password(): invalid password length, must be exactly 6 chars '%s'", password.c_str()); | ||||
|     return; | ||||
|   } | ||||
|   this->set_config_mode_(true); | ||||
|   uint8_t cmd_value[6]; | ||||
|   std::copy(password.begin(), password.end(), std::begin(cmd_value)); | ||||
|   this->send_command_(CMD_BT_PASSWORD, cmd_value, 6); | ||||
|   this->set_config_mode_(false); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_engineering_mode(bool enable) { | ||||
|   this->set_config_mode_(true); | ||||
|   last_engineering_mode_change_millis_ = millis(); | ||||
|   uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG; | ||||
|   this->send_command_(cmd, nullptr, 0); | ||||
|   this->set_config_mode_(false); | ||||
| } | ||||
|  | ||||
| void LD2410Component::factory_reset() { | ||||
|   this->set_config_mode_(true); | ||||
|   this->send_command_(CMD_RESET, nullptr, 0); | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
| } | ||||
|  | ||||
| void LD2410Component::restart_() { this->send_command_(CMD_RESTART, nullptr, 0); } | ||||
|  | ||||
| void LD2410Component::query_parameters_() { this->send_command_(CMD_QUERY, nullptr, 0); } | ||||
| void LD2410Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); } | ||||
| void LD2410Component::get_mac_() { | ||||
|   uint8_t cmd_value[2] = {0x01, 0x00}; | ||||
|   this->send_command_(CMD_MAC, cmd_value, 2); | ||||
| } | ||||
| void LD2410Component::get_distance_resolution_() { this->send_command_(CMD_QUERY_DISTANCE_RESOLUTION, nullptr, 0); } | ||||
|  | ||||
| void LD2410Component::set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range, | ||||
|                                                  uint16_t timeout) { | ||||
| void LD2410Component::get_light_control_() { this->send_command_(CMD_QUERY_LIGHT_CONTROL, nullptr, 0); } | ||||
|  | ||||
| #ifdef USE_NUMBER | ||||
| void LD2410Component::set_max_distances_timeout() { | ||||
|   if (!this->max_move_distance_gate_number_->has_state() || !this->max_still_distance_gate_number_->has_state() || | ||||
|       !this->timeout_number_->has_state()) { | ||||
|     return; | ||||
|   } | ||||
|   int max_moving_distance_gate_range = static_cast<int>(this->max_move_distance_gate_number_->state); | ||||
|   int max_still_distance_gate_range = static_cast<int>(this->max_still_distance_gate_number_->state); | ||||
|   int timeout = static_cast<int>(this->timeout_number_->state); | ||||
|   uint8_t value[18] = {0x00, | ||||
|                        0x00, | ||||
|                        lowbyte(max_moving_distance_range), | ||||
|                        highbyte(max_moving_distance_range), | ||||
|                        lowbyte(max_moving_distance_gate_range), | ||||
|                        highbyte(max_moving_distance_gate_range), | ||||
|                        0x00, | ||||
|                        0x00, | ||||
|                        0x01, | ||||
|                        0x00, | ||||
|                        lowbyte(max_still_distance_range), | ||||
|                        highbyte(max_still_distance_range), | ||||
|                        lowbyte(max_still_distance_gate_range), | ||||
|                        highbyte(max_still_distance_gate_range), | ||||
|                        0x00, | ||||
|                        0x00, | ||||
|                        0x02, | ||||
| @@ -292,10 +596,25 @@ void LD2410Component::set_max_distances_timeout_(uint8_t max_moving_distance_ran | ||||
|                        highbyte(timeout), | ||||
|                        0x00, | ||||
|                        0x00}; | ||||
|   this->set_config_mode_(true); | ||||
|   this->send_command_(CMD_MAXDIST_DURATION, value, 18); | ||||
|   delay(50);  // NOLINT | ||||
|   this->query_parameters_(); | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
|   this->set_config_mode_(false); | ||||
| } | ||||
| void LD2410Component::set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens) { | ||||
|  | ||||
| void LD2410Component::set_gate_threshold(uint8_t gate) { | ||||
|   number::Number *motionsens = this->gate_move_threshold_numbers_[gate]; | ||||
|   number::Number *stillsens = this->gate_still_threshold_numbers_[gate]; | ||||
|  | ||||
|   if (!motionsens->has_state() || !stillsens->has_state()) { | ||||
|     return; | ||||
|   } | ||||
|   int motion = static_cast<int>(motionsens->state); | ||||
|   int still = static_cast<int>(stillsens->state); | ||||
|  | ||||
|   this->set_config_mode_(true); | ||||
|   // reference | ||||
|   // https://drive.google.com/drive/folders/1p4dhbEJA3YubyIjIIC7wwVsSo8x29Fq-?spm=a2g0o.detail.1000023.17.93465697yFwVxH | ||||
|   //   Send data: configure the motion sensitivity of distance gate 3 to 40, and the static sensitivity of 40 | ||||
| @@ -306,10 +625,56 @@ void LD2410Component::set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint | ||||
|   // 02 00 (still sensitivtiy) | ||||
|   // 28 00 00 00 (value) | ||||
|   uint8_t value[18] = {0x00, 0x00, lowbyte(gate),   highbyte(gate),   0x00, 0x00, | ||||
|                        0x01, 0x00, lowbyte(motionsens), highbyte(motionsens), 0x00, 0x00, | ||||
|                        0x02, 0x00, lowbyte(stillsens),  highbyte(stillsens),  0x00, 0x00}; | ||||
|                        0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00, | ||||
|                        0x02, 0x00, lowbyte(still),  highbyte(still),  0x00, 0x00}; | ||||
|   this->send_command_(CMD_GATE_SENS, value, 18); | ||||
|   delay(50);  // NOLINT | ||||
|   this->query_parameters_(); | ||||
|   this->set_config_mode_(false); | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_gate_still_threshold_number(int gate, number::Number *n) { | ||||
|   this->gate_still_threshold_numbers_[gate] = n; | ||||
| } | ||||
|  | ||||
| void LD2410Component::set_gate_move_threshold_number(int gate, number::Number *n) { | ||||
|   this->gate_move_threshold_numbers_[gate] = n; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| void LD2410Component::set_light_out_control() { | ||||
| #ifdef USE_NUMBER | ||||
|   if (this->light_threshold_number_ != nullptr && this->light_threshold_number_->has_state()) { | ||||
|     this->light_threshold_ = this->light_threshold_number_->state; | ||||
|   } | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   if (this->light_function_select_ != nullptr && this->light_function_select_->has_state()) { | ||||
|     this->light_function_ = this->light_function_select_->state; | ||||
|   } | ||||
|   if (this->out_pin_level_select_ != nullptr && this->out_pin_level_select_->has_state()) { | ||||
|     this->out_pin_level_ = this->out_pin_level_select_->state; | ||||
|   } | ||||
| #endif | ||||
|   if (this->light_function_.empty() || this->out_pin_level_.empty() || this->light_threshold_ < 0) { | ||||
|     return; | ||||
|   } | ||||
|   this->set_config_mode_(true); | ||||
|   uint8_t light_function = LIGHT_FUNCTION_ENUM_TO_INT.at(this->light_function_); | ||||
|   uint8_t light_threshold = static_cast<uint8_t>(this->light_threshold_); | ||||
|   uint8_t out_pin_level = OUT_PIN_LEVEL_ENUM_TO_INT.at(this->out_pin_level_); | ||||
|   uint8_t value[4] = {light_function, light_threshold, out_pin_level, 0x00}; | ||||
|   this->send_command_(CMD_SET_LIGHT_CONTROL, value, 4); | ||||
|   delay(50);  // NOLINT | ||||
|   this->get_light_control_(); | ||||
|   this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); | ||||
|   this->set_config_mode_(false); | ||||
| } | ||||
|  | ||||
| #ifdef USE_SENSOR | ||||
| void LD2410Component::set_gate_move_sensor(int gate, sensor::Sensor *s) { this->gate_move_sensors_[gate] = s; } | ||||
| void LD2410Component::set_gate_still_sensor(int gate, sensor::Sensor *s) { this->gate_still_sensors_[gate] = s; } | ||||
| #endif | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -7,10 +7,27 @@ | ||||
| #ifdef USE_SENSOR | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #endif | ||||
| #ifdef USE_NUMBER | ||||
| #include "esphome/components/number/number.h" | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #endif | ||||
| #ifdef USE_BUTTON | ||||
| #include "esphome/components/button/button.h" | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
| #include "esphome/components/select/select.h" | ||||
| #endif | ||||
| #ifdef USE_TEXT_SENSOR | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
| #endif | ||||
| #include "esphome/components/uart/uart.h" | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #include <map> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| @@ -19,10 +36,63 @@ namespace ld2410 { | ||||
| // Commands | ||||
| static const uint8_t CMD_ENABLE_CONF = 0x00FF; | ||||
| static const uint8_t CMD_DISABLE_CONF = 0x00FE; | ||||
| static const uint8_t CMD_ENABLE_ENG = 0x0062; | ||||
| static const uint8_t CMD_DISABLE_ENG = 0x0063; | ||||
| static const uint8_t CMD_MAXDIST_DURATION = 0x0060; | ||||
| static const uint8_t CMD_QUERY = 0x0061; | ||||
| static const uint8_t CMD_GATE_SENS = 0x0064; | ||||
| static const uint8_t CMD_VERSION = 0x00A0; | ||||
| static const uint8_t CMD_QUERY_DISTANCE_RESOLUTION = 0x00AB; | ||||
| static const uint8_t CMD_SET_DISTANCE_RESOLUTION = 0x00AA; | ||||
| static const uint8_t CMD_QUERY_LIGHT_CONTROL = 0x00AE; | ||||
| static const uint8_t CMD_SET_LIGHT_CONTROL = 0x00AD; | ||||
| static const uint8_t CMD_SET_BAUD_RATE = 0x00A1; | ||||
| static const uint8_t CMD_BT_PASSWORD = 0x00A9; | ||||
| static const uint8_t CMD_MAC = 0x00A5; | ||||
| static const uint8_t CMD_RESET = 0x00A2; | ||||
| static const uint8_t CMD_RESTART = 0x00A3; | ||||
| static const uint8_t CMD_BLUETOOTH = 0x00A4; | ||||
|  | ||||
| enum BaudRateStructure : uint8_t { | ||||
|   BAUD_RATE_9600 = 1, | ||||
|   BAUD_RATE_19200 = 2, | ||||
|   BAUD_RATE_38400 = 3, | ||||
|   BAUD_RATE_57600 = 4, | ||||
|   BAUD_RATE_115200 = 5, | ||||
|   BAUD_RATE_230400 = 6, | ||||
|   BAUD_RATE_256000 = 7, | ||||
|   BAUD_RATE_460800 = 8 | ||||
| }; | ||||
|  | ||||
| static const std::map<std::string, uint8_t> BAUD_RATE_ENUM_TO_INT{ | ||||
|     {"9600", BAUD_RATE_9600},     {"19200", BAUD_RATE_19200},   {"38400", BAUD_RATE_38400}, | ||||
|     {"57600", BAUD_RATE_57600},   {"115200", BAUD_RATE_115200}, {"230400", BAUD_RATE_230400}, | ||||
|     {"256000", BAUD_RATE_256000}, {"460800", BAUD_RATE_460800}}; | ||||
|  | ||||
| enum DistanceResolutionStructure : uint8_t { DISTANCE_RESOLUTION_0_2 = 0x01, DISTANCE_RESOLUTION_0_75 = 0x00 }; | ||||
|  | ||||
| static const std::map<std::string, uint8_t> DISTANCE_RESOLUTION_ENUM_TO_INT{{"0.2m", DISTANCE_RESOLUTION_0_2}, | ||||
|                                                                             {"0.75m", DISTANCE_RESOLUTION_0_75}}; | ||||
| static const std::map<uint8_t, std::string> DISTANCE_RESOLUTION_INT_TO_ENUM{{DISTANCE_RESOLUTION_0_2, "0.2m"}, | ||||
|                                                                             {DISTANCE_RESOLUTION_0_75, "0.75m"}}; | ||||
|  | ||||
| enum LightFunctionStructure : uint8_t { | ||||
|   LIGHT_FUNCTION_OFF = 0x00, | ||||
|   LIGHT_FUNCTION_BELOW = 0x01, | ||||
|   LIGHT_FUNCTION_ABOVE = 0x02 | ||||
| }; | ||||
|  | ||||
| static const std::map<std::string, uint8_t> LIGHT_FUNCTION_ENUM_TO_INT{ | ||||
|     {"off", LIGHT_FUNCTION_OFF}, {"below", LIGHT_FUNCTION_BELOW}, {"above", LIGHT_FUNCTION_ABOVE}}; | ||||
| static const std::map<uint8_t, std::string> LIGHT_FUNCTION_INT_TO_ENUM{ | ||||
|     {LIGHT_FUNCTION_OFF, "off"}, {LIGHT_FUNCTION_BELOW, "below"}, {LIGHT_FUNCTION_ABOVE, "above"}}; | ||||
|  | ||||
| enum OutPinLevelStructure : uint8_t { OUT_PIN_LEVEL_LOW = 0x00, OUT_PIN_LEVEL_HIGH = 0x01 }; | ||||
|  | ||||
| static const std::map<std::string, uint8_t> OUT_PIN_LEVEL_ENUM_TO_INT{{"low", OUT_PIN_LEVEL_LOW}, | ||||
|                                                                       {"high", OUT_PIN_LEVEL_HIGH}}; | ||||
| static const std::map<uint8_t, std::string> OUT_PIN_LEVEL_INT_TO_ENUM{{OUT_PIN_LEVEL_LOW, "low"}, | ||||
|                                                                       {OUT_PIN_LEVEL_HIGH, "high"}}; | ||||
|  | ||||
| // Commands values | ||||
| static const uint8_t CMD_MAX_MOVE_VALUE = 0x0000; | ||||
| @@ -44,7 +114,7 @@ Target states: 9th byte | ||||
|     Detect distance: 16~17th bytes | ||||
| */ | ||||
| enum PeriodicDataStructure : uint8_t { | ||||
|   DATA_TYPES = 5, | ||||
|   DATA_TYPES = 6, | ||||
|   TARGET_STATES = 8, | ||||
|   MOVING_TARGET_LOW = 9, | ||||
|   MOVING_TARGET_HIGH = 10, | ||||
| @@ -54,6 +124,10 @@ enum PeriodicDataStructure : uint8_t { | ||||
|   STILL_ENERGY = 14, | ||||
|   DETECT_DISTANCE_LOW = 15, | ||||
|   DETECT_DISTANCE_HIGH = 16, | ||||
|   MOVING_SENSOR_START = 19, | ||||
|   STILL_SENSOR_START = 28, | ||||
|   LIGHT_SENSOR = 37, | ||||
|   OUT_PIN_SENSOR = 38, | ||||
| }; | ||||
| enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 }; | ||||
|  | ||||
| @@ -66,80 +140,97 @@ class LD2410Component : public Component, public uart::UARTDevice { | ||||
|   SUB_SENSOR(still_target_distance) | ||||
|   SUB_SENSOR(moving_target_energy) | ||||
|   SUB_SENSOR(still_target_energy) | ||||
|   SUB_SENSOR(light) | ||||
|   SUB_SENSOR(detection_distance) | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   SUB_BINARY_SENSOR(target) | ||||
|   SUB_BINARY_SENSOR(moving_target) | ||||
|   SUB_BINARY_SENSOR(still_target) | ||||
|   SUB_BINARY_SENSOR(out_pin_presence_status) | ||||
| #endif | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|   SUB_TEXT_SENSOR(version) | ||||
|   SUB_TEXT_SENSOR(mac) | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   SUB_SELECT(distance_resolution) | ||||
|   SUB_SELECT(baud_rate) | ||||
|   SUB_SELECT(light_function) | ||||
|   SUB_SELECT(out_pin_level) | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
|   SUB_SWITCH(engineering_mode) | ||||
|   SUB_SWITCH(bluetooth) | ||||
| #endif | ||||
| #ifdef USE_BUTTON | ||||
|   SUB_BUTTON(reset) | ||||
|   SUB_BUTTON(restart) | ||||
|   SUB_BUTTON(query) | ||||
| #endif | ||||
| #ifdef USE_NUMBER | ||||
|   SUB_NUMBER(max_still_distance_gate) | ||||
|   SUB_NUMBER(max_move_distance_gate) | ||||
|   SUB_NUMBER(timeout) | ||||
|   SUB_NUMBER(light_threshold) | ||||
| #endif | ||||
|  | ||||
|  public: | ||||
|   LD2410Component(); | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
|   void loop() override; | ||||
|  | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   void set_target_sensor(binary_sensor::BinarySensor *sens) { this->target_binary_sensor_ = sens; }; | ||||
|   void set_moving_target_sensor(binary_sensor::BinarySensor *sens) { this->moving_binary_sensor_ = sens; }; | ||||
|   void set_still_target_sensor(binary_sensor::BinarySensor *sens) { this->still_binary_sensor_ = sens; }; | ||||
|   void set_light_out_control(); | ||||
| #ifdef USE_NUMBER | ||||
|   void set_gate_still_threshold_number(int gate, number::Number *n); | ||||
|   void set_gate_move_threshold_number(int gate, number::Number *n); | ||||
|   void set_max_distances_timeout(); | ||||
|   void set_gate_threshold(uint8_t gate); | ||||
| #endif | ||||
|  | ||||
|   void set_timeout(uint16_t value) { this->timeout_ = value; }; | ||||
|   void set_max_move_distance(uint8_t value) { this->max_move_distance_ = value; }; | ||||
|   void set_max_still_distance(uint8_t value) { this->max_still_distance_ = value; }; | ||||
|   void set_range_config(int rg0_move, int rg0_still, int rg1_move, int rg1_still, int rg2_move, int rg2_still, | ||||
|                         int rg3_move, int rg3_still, int rg4_move, int rg4_still, int rg5_move, int rg5_still, | ||||
|                         int rg6_move, int rg6_still, int rg7_move, int rg7_still, int rg8_move, int rg8_still) { | ||||
|     this->rg0_move_threshold_ = rg0_move; | ||||
|     this->rg0_still_threshold_ = rg0_still; | ||||
|     this->rg1_move_threshold_ = rg1_move; | ||||
|     this->rg1_still_threshold_ = rg1_still; | ||||
|     this->rg2_move_threshold_ = rg2_move; | ||||
|     this->rg2_still_threshold_ = rg2_still; | ||||
|     this->rg3_move_threshold_ = rg3_move; | ||||
|     this->rg3_still_threshold_ = rg3_still; | ||||
|     this->rg4_move_threshold_ = rg4_move; | ||||
|     this->rg4_still_threshold_ = rg4_still; | ||||
|     this->rg5_move_threshold_ = rg5_move; | ||||
|     this->rg5_still_threshold_ = rg5_still; | ||||
|     this->rg6_move_threshold_ = rg6_move; | ||||
|     this->rg6_still_threshold_ = rg6_still; | ||||
|     this->rg7_move_threshold_ = rg7_move; | ||||
|     this->rg7_still_threshold_ = rg7_still; | ||||
|     this->rg8_move_threshold_ = rg8_move; | ||||
|     this->rg8_still_threshold_ = rg8_still; | ||||
|   }; | ||||
|   int moving_sensitivities[9] = {0}; | ||||
|   int still_sensitivities[9] = {0}; | ||||
|  | ||||
|   int32_t last_periodic_millis = millis(); | ||||
| #ifdef USE_SENSOR | ||||
|   void set_gate_move_sensor(int gate, sensor::Sensor *s); | ||||
|   void set_gate_still_sensor(int gate, sensor::Sensor *s); | ||||
| #endif | ||||
|   void set_throttle(uint16_t value) { this->throttle_ = value; }; | ||||
|   void set_bluetooth_password(const std::string &password); | ||||
|   void set_engineering_mode(bool enable); | ||||
|   void read_all_info(); | ||||
|   void restart_and_read_all_info(); | ||||
|   void set_bluetooth(bool enable); | ||||
|   void set_distance_resolution(const std::string &state); | ||||
|   void set_baud_rate(const std::string &state); | ||||
|   void factory_reset(); | ||||
|  | ||||
|  protected: | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   binary_sensor::BinarySensor *target_binary_sensor_{nullptr}; | ||||
|   binary_sensor::BinarySensor *moving_binary_sensor_{nullptr}; | ||||
|   binary_sensor::BinarySensor *still_binary_sensor_{nullptr}; | ||||
| #endif | ||||
|  | ||||
|   std::vector<uint8_t> rx_buffer_; | ||||
|   int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; } | ||||
|   void send_command_(uint8_t command_str, uint8_t *command_value, int command_value_len); | ||||
|  | ||||
|   void set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range, | ||||
|                                   uint16_t timeout); | ||||
|   void set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens); | ||||
|   void send_command_(uint8_t command_str, const uint8_t *command_value, int command_value_len); | ||||
|   void set_config_mode_(bool enable); | ||||
|   void handle_periodic_data_(uint8_t *buffer, int len); | ||||
|   void handle_ack_data_(uint8_t *buffer, int len); | ||||
|   bool handle_ack_data_(uint8_t *buffer, int len); | ||||
|   void readline_(int readch, uint8_t *buffer, int len); | ||||
|   void query_parameters_(); | ||||
|   void get_version_(); | ||||
|   void get_mac_(); | ||||
|   void get_distance_resolution_(); | ||||
|   void get_light_control_(); | ||||
|   void restart_(); | ||||
|  | ||||
|   uint16_t timeout_; | ||||
|   uint8_t max_move_distance_; | ||||
|   uint8_t max_still_distance_; | ||||
|  | ||||
|   uint8_t version_[6]; | ||||
|   uint8_t rg0_move_threshold_, rg0_still_threshold_, rg1_move_threshold_, rg1_still_threshold_, rg2_move_threshold_, | ||||
|       rg2_still_threshold_, rg3_move_threshold_, rg3_still_threshold_, rg4_move_threshold_, rg4_still_threshold_, | ||||
|       rg5_move_threshold_, rg5_still_threshold_, rg6_move_threshold_, rg6_still_threshold_, rg7_move_threshold_, | ||||
|       rg7_still_threshold_, rg8_move_threshold_, rg8_still_threshold_; | ||||
|   int32_t last_periodic_millis_ = millis(); | ||||
|   int32_t last_engineering_mode_change_millis_ = millis(); | ||||
|   uint16_t throttle_; | ||||
|   std::string version_; | ||||
|   std::string mac_; | ||||
|   std::string out_pin_level_; | ||||
|   std::string light_function_; | ||||
|   float light_threshold_ = -1; | ||||
| #ifdef USE_NUMBER | ||||
|   std::vector<number::Number *> gate_still_threshold_numbers_ = std::vector<number::Number *>(9); | ||||
|   std::vector<number::Number *> gate_move_threshold_numbers_ = std::vector<number::Number *>(9); | ||||
| #endif | ||||
| #ifdef USE_SENSOR | ||||
|   std::vector<sensor::Sensor *> gate_still_sensors_ = std::vector<sensor::Sensor *>(9); | ||||
|   std::vector<sensor::Sensor *> gate_move_sensors_ = std::vector<sensor::Sensor *>(9); | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
|   | ||||
							
								
								
									
										128
									
								
								esphome/components/ld2410/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								esphome/components/ld2410/number/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import number | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_TIMEOUT, | ||||
|     DEVICE_CLASS_DISTANCE, | ||||
|     DEVICE_CLASS_SIGNAL_STRENGTH, | ||||
|     DEVICE_CLASS_ILLUMINANCE, | ||||
|     UNIT_SECOND, | ||||
|     UNIT_PERCENT, | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
|     ICON_MOTION_SENSOR, | ||||
|     ICON_TIMELAPSE, | ||||
|     ICON_LIGHTBULB, | ||||
| ) | ||||
| from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns | ||||
|  | ||||
| GateThresholdNumber = ld2410_ns.class_("GateThresholdNumber", number.Number) | ||||
| LightThresholdNumber = ld2410_ns.class_("LightThresholdNumber", number.Number) | ||||
| MaxDistanceTimeoutNumber = ld2410_ns.class_("MaxDistanceTimeoutNumber", number.Number) | ||||
|  | ||||
| CONF_MAX_MOVE_DISTANCE_GATE = "max_move_distance_gate" | ||||
| CONF_MAX_STILL_DISTANCE_GATE = "max_still_distance_gate" | ||||
| CONF_LIGHT_THRESHOLD = "light_threshold" | ||||
| CONF_STILL_THRESHOLD = "still_threshold" | ||||
| CONF_MOVE_THRESHOLD = "move_threshold" | ||||
|  | ||||
| TIMEOUT_GROUP = "timeout" | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||
|         cv.Inclusive(CONF_TIMEOUT, TIMEOUT_GROUP): number.number_schema( | ||||
|             MaxDistanceTimeoutNumber, | ||||
|             unit_of_measurement=UNIT_SECOND, | ||||
|             entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|             icon=ICON_TIMELAPSE, | ||||
|         ), | ||||
|         cv.Inclusive(CONF_MAX_MOVE_DISTANCE_GATE, TIMEOUT_GROUP): number.number_schema( | ||||
|             MaxDistanceTimeoutNumber, | ||||
|             device_class=DEVICE_CLASS_DISTANCE, | ||||
|             entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|             icon=ICON_MOTION_SENSOR, | ||||
|         ), | ||||
|         cv.Inclusive(CONF_MAX_STILL_DISTANCE_GATE, TIMEOUT_GROUP): number.number_schema( | ||||
|             MaxDistanceTimeoutNumber, | ||||
|             device_class=DEVICE_CLASS_DISTANCE, | ||||
|             entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|             icon=ICON_MOTION_SENSOR, | ||||
|         ), | ||||
|         cv.Optional(CONF_LIGHT_THRESHOLD): number.number_schema( | ||||
|             LightThresholdNumber, | ||||
|             device_class=DEVICE_CLASS_ILLUMINANCE, | ||||
|             entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|             icon=ICON_LIGHTBULB, | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = CONFIG_SCHEMA.extend( | ||||
|     { | ||||
|         cv.Optional(f"g{x}"): cv.Schema( | ||||
|             { | ||||
|                 cv.Required(CONF_MOVE_THRESHOLD): number.number_schema( | ||||
|                     GateThresholdNumber, | ||||
|                     device_class=DEVICE_CLASS_SIGNAL_STRENGTH, | ||||
|                     unit_of_measurement=UNIT_PERCENT, | ||||
|                     entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|                     icon=ICON_MOTION_SENSOR, | ||||
|                 ), | ||||
|                 cv.Required(CONF_STILL_THRESHOLD): number.number_schema( | ||||
|                     GateThresholdNumber, | ||||
|                     device_class=DEVICE_CLASS_SIGNAL_STRENGTH, | ||||
|                     unit_of_measurement=UNIT_PERCENT, | ||||
|                     entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|                     icon=ICON_MOTION_SENSOR, | ||||
|                 ), | ||||
|             } | ||||
|         ) | ||||
|         for x in range(9) | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||
|     if timeout_config := config.get(CONF_TIMEOUT): | ||||
|         n = await number.new_number( | ||||
|             timeout_config, min_value=0, max_value=65535, step=1 | ||||
|         ) | ||||
|         await cg.register_parented(n, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_timeout_number(n)) | ||||
|     if max_move_distance_gate_config := config.get(CONF_MAX_MOVE_DISTANCE_GATE): | ||||
|         n = await number.new_number( | ||||
|             max_move_distance_gate_config, min_value=2, max_value=8, step=1 | ||||
|         ) | ||||
|         await cg.register_parented(n, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_max_move_distance_gate_number(n)) | ||||
|     if max_still_distance_gate_config := config.get(CONF_MAX_STILL_DISTANCE_GATE): | ||||
|         n = await number.new_number( | ||||
|             max_still_distance_gate_config, min_value=2, max_value=8, step=1 | ||||
|         ) | ||||
|         await cg.register_parented(n, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_max_still_distance_gate_number(n)) | ||||
|     if light_threshold_config := config.get(CONF_LIGHT_THRESHOLD): | ||||
|         n = await number.new_number( | ||||
|             light_threshold_config, min_value=0, max_value=255, step=1 | ||||
|         ) | ||||
|         await cg.register_parented(n, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_light_threshold_number(n)) | ||||
|     for x in range(9): | ||||
|         if gate_conf := config.get(f"g{x}"): | ||||
|             move_config = gate_conf[CONF_MOVE_THRESHOLD] | ||||
|             n = cg.new_Pvariable(move_config[CONF_ID], x) | ||||
|             await number.register_number( | ||||
|                 n, move_config, min_value=0, max_value=100, step=1 | ||||
|             ) | ||||
|             await cg.register_parented(n, config[CONF_LD2410_ID]) | ||||
|             cg.add(ld2410_component.set_gate_move_threshold_number(x, n)) | ||||
|  | ||||
|             still_config = gate_conf[CONF_STILL_THRESHOLD] | ||||
|             n = cg.new_Pvariable(still_config[CONF_ID], x) | ||||
|             await number.register_number( | ||||
|                 n, still_config, min_value=0, max_value=100, step=1 | ||||
|             ) | ||||
|             await cg.register_parented(n, config[CONF_LD2410_ID]) | ||||
|             cg.add(ld2410_component.set_gate_still_threshold_number(x, n)) | ||||
							
								
								
									
										14
									
								
								esphome/components/ld2410/number/gate_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								esphome/components/ld2410/number/gate_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| #include "gate_threshold_number.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| GateThresholdNumber::GateThresholdNumber(uint8_t gate) : gate_(gate) {} | ||||
|  | ||||
| void GateThresholdNumber::control(float value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_gate_threshold(this->gate_); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										19
									
								
								esphome/components/ld2410/number/gate_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								esphome/components/ld2410/number/gate_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/number/number.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class GateThresholdNumber : public number::Number, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   GateThresholdNumber(uint8_t gate); | ||||
|  | ||||
|  protected: | ||||
|   uint8_t gate_; | ||||
|   void control(float value) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										12
									
								
								esphome/components/ld2410/number/light_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2410/number/light_threshold_number.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #include "light_threshold_number.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void LightThresholdNumber::control(float value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_light_out_control(); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/number/light_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/number/light_threshold_number.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/number/number.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class LightThresholdNumber : public number::Number, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   LightThresholdNumber() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(float value) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,12 @@ | ||||
| #include "max_distance_timeout_number.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void MaxDistanceTimeoutNumber::control(float value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_max_distances_timeout(); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/number/number.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   MaxDistanceTimeoutNumber() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(float value) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										81
									
								
								esphome/components/ld2410/select/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								esphome/components/ld2410/select/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import select | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
|     CONF_BAUD_RATE, | ||||
|     ICON_THERMOMETER, | ||||
|     ICON_SCALE, | ||||
|     ICON_LIGHTBULB, | ||||
|     ICON_RULER, | ||||
| ) | ||||
| from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns | ||||
|  | ||||
| BaudRateSelect = ld2410_ns.class_("BaudRateSelect", select.Select) | ||||
| DistanceResolutionSelect = ld2410_ns.class_("DistanceResolutionSelect", select.Select) | ||||
| LightOutControlSelect = ld2410_ns.class_("LightOutControlSelect", select.Select) | ||||
|  | ||||
| CONF_DISTANCE_RESOLUTION = "distance_resolution" | ||||
| CONF_LIGHT_FUNCTION = "light_function" | ||||
| CONF_OUT_PIN_LEVEL = "out_pin_level" | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = { | ||||
|     cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||
|     cv.Optional(CONF_DISTANCE_RESOLUTION): select.select_schema( | ||||
|         DistanceResolutionSelect, | ||||
|         entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|         icon=ICON_RULER, | ||||
|     ), | ||||
|     cv.Optional(CONF_LIGHT_FUNCTION): select.select_schema( | ||||
|         LightOutControlSelect, | ||||
|         entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|         icon=ICON_LIGHTBULB, | ||||
|     ), | ||||
|     cv.Optional(CONF_OUT_PIN_LEVEL): select.select_schema( | ||||
|         LightOutControlSelect, | ||||
|         entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|         icon=ICON_SCALE, | ||||
|     ), | ||||
|     cv.Optional(CONF_BAUD_RATE): select.select_schema( | ||||
|         BaudRateSelect, | ||||
|         entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|         icon=ICON_THERMOMETER, | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||
|     if distance_resolution_config := config.get(CONF_DISTANCE_RESOLUTION): | ||||
|         s = await select.new_select( | ||||
|             distance_resolution_config, options=["0.2m", "0.75m"] | ||||
|         ) | ||||
|         await cg.register_parented(s, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_distance_resolution_select(s)) | ||||
|     if out_pin_level_config := config.get(CONF_OUT_PIN_LEVEL): | ||||
|         s = await select.new_select(out_pin_level_config, options=["low", "high"]) | ||||
|         await cg.register_parented(s, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_out_pin_level_select(s)) | ||||
|     if light_function_config := config.get(CONF_LIGHT_FUNCTION): | ||||
|         s = await select.new_select( | ||||
|             light_function_config, options=["off", "below", "above"] | ||||
|         ) | ||||
|         await cg.register_parented(s, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_light_function_select(s)) | ||||
|     if baud_rate_config := config.get(CONF_BAUD_RATE): | ||||
|         s = await select.new_select( | ||||
|             baud_rate_config, | ||||
|             options=[ | ||||
|                 "9600", | ||||
|                 "19200", | ||||
|                 "38400", | ||||
|                 "57600", | ||||
|                 "115200", | ||||
|                 "230400", | ||||
|                 "256000", | ||||
|                 "460800", | ||||
|             ], | ||||
|         ) | ||||
|         await cg.register_parented(s, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_baud_rate_select(s)) | ||||
							
								
								
									
										12
									
								
								esphome/components/ld2410/select/baud_rate_select.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2410/select/baud_rate_select.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #include "baud_rate_select.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void BaudRateSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_baud_rate(state); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/select/baud_rate_select.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/select/baud_rate_select.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/select/select.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class BaudRateSelect : public select::Select, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   BaudRateSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,12 @@ | ||||
| #include "distance_resolution_select.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void DistanceResolutionSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_distance_resolution(state); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/select/select.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class DistanceResolutionSelect : public select::Select, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   DistanceResolutionSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -0,0 +1,12 @@ | ||||
| #include "light_out_control_select.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void LightOutControlSelect::control(const std::string &value) { | ||||
|   this->publish_state(value); | ||||
|   this->parent_->set_light_out_control(); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/select/light_out_control_select.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/select/light_out_control_select.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/select/select.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class LightOutControlSelect : public select::Select, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   LightOutControlSelect() = default; | ||||
|  | ||||
|  protected: | ||||
|   void control(const std::string &value) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
| @@ -3,9 +3,15 @@ from esphome.components import sensor | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     DEVICE_CLASS_DISTANCE, | ||||
|     DEVICE_CLASS_ENERGY, | ||||
|     UNIT_CENTIMETER, | ||||
|     UNIT_PERCENT, | ||||
|     CONF_LIGHT, | ||||
|     DEVICE_CLASS_ILLUMINANCE, | ||||
|     ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|     ICON_SIGNAL, | ||||
|     ICON_FLASH, | ||||
|     ICON_MOTION_SENSOR, | ||||
|     ICON_LIGHTBULB, | ||||
| ) | ||||
| from . import CONF_LD2410_ID, LD2410Component | ||||
|  | ||||
| @@ -15,41 +21,88 @@ CONF_STILL_DISTANCE = "still_distance" | ||||
| CONF_MOVING_ENERGY = "moving_energy" | ||||
| CONF_STILL_ENERGY = "still_energy" | ||||
| CONF_DETECTION_DISTANCE = "detection_distance" | ||||
| CONF_MOVE_ENERGY = "move_energy" | ||||
|  | ||||
| CONFIG_SCHEMA = { | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||
|         cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema( | ||||
|         device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER | ||||
|             device_class=DEVICE_CLASS_DISTANCE, | ||||
|             unit_of_measurement=UNIT_CENTIMETER, | ||||
|             icon=ICON_SIGNAL, | ||||
|         ), | ||||
|         cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema( | ||||
|         device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER | ||||
|             device_class=DEVICE_CLASS_DISTANCE, | ||||
|             unit_of_measurement=UNIT_CENTIMETER, | ||||
|             icon=ICON_SIGNAL, | ||||
|         ), | ||||
|         cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema( | ||||
|         device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT | ||||
|             unit_of_measurement=UNIT_PERCENT, | ||||
|             icon=ICON_MOTION_SENSOR, | ||||
|         ), | ||||
|         cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( | ||||
|         device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT | ||||
|             unit_of_measurement=UNIT_PERCENT, | ||||
|             icon=ICON_FLASH, | ||||
|         ), | ||||
|         cv.Optional(CONF_LIGHT): sensor.sensor_schema( | ||||
|             device_class=DEVICE_CLASS_ILLUMINANCE, | ||||
|             entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|             icon=ICON_LIGHTBULB, | ||||
|         ), | ||||
|         cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema( | ||||
|         device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER | ||||
|             device_class=DEVICE_CLASS_DISTANCE, | ||||
|             unit_of_measurement=UNIT_CENTIMETER, | ||||
|             icon=ICON_SIGNAL, | ||||
|         ), | ||||
| } | ||||
|     } | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = CONFIG_SCHEMA.extend( | ||||
|     { | ||||
|         cv.Optional(f"g{x}"): cv.Schema( | ||||
|             { | ||||
|                 cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema( | ||||
|                     unit_of_measurement=UNIT_PERCENT, | ||||
|                     entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|                     icon=ICON_MOTION_SENSOR, | ||||
|                 ), | ||||
|                 cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( | ||||
|                     unit_of_measurement=UNIT_PERCENT, | ||||
|                     entity_category=ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|                     icon=ICON_FLASH, | ||||
|                 ), | ||||
|             } | ||||
|         ) | ||||
|         for x in range(9) | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||
|     if CONF_MOVING_DISTANCE in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_MOVING_DISTANCE]) | ||||
|     if moving_distance_config := config.get(CONF_MOVING_DISTANCE): | ||||
|         sens = await sensor.new_sensor(moving_distance_config) | ||||
|         cg.add(ld2410_component.set_moving_target_distance_sensor(sens)) | ||||
|     if CONF_STILL_DISTANCE in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_STILL_DISTANCE]) | ||||
|     if still_distance_config := config.get(CONF_STILL_DISTANCE): | ||||
|         sens = await sensor.new_sensor(still_distance_config) | ||||
|         cg.add(ld2410_component.set_still_target_distance_sensor(sens)) | ||||
|     if CONF_MOVING_ENERGY in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_MOVING_ENERGY]) | ||||
|     if moving_energy_config := config.get(CONF_MOVING_ENERGY): | ||||
|         sens = await sensor.new_sensor(moving_energy_config) | ||||
|         cg.add(ld2410_component.set_moving_target_energy_sensor(sens)) | ||||
|     if CONF_STILL_ENERGY in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_STILL_ENERGY]) | ||||
|     if still_energy_config := config.get(CONF_STILL_ENERGY): | ||||
|         sens = await sensor.new_sensor(still_energy_config) | ||||
|         cg.add(ld2410_component.set_still_target_energy_sensor(sens)) | ||||
|     if CONF_DETECTION_DISTANCE in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_DETECTION_DISTANCE]) | ||||
|     if light_config := config.get(CONF_LIGHT): | ||||
|         sens = await sensor.new_sensor(light_config) | ||||
|         cg.add(ld2410_component.set_light_sensor(sens)) | ||||
|     if detection_distance_config := config.get(CONF_DETECTION_DISTANCE): | ||||
|         sens = await sensor.new_sensor(detection_distance_config) | ||||
|         cg.add(ld2410_component.set_detection_distance_sensor(sens)) | ||||
|     for x in range(9): | ||||
|         if gate_conf := config.get(f"g{x}"): | ||||
|             if move_config := gate_conf.get(CONF_MOVE_ENERGY): | ||||
|                 sens = await sensor.new_sensor(move_config) | ||||
|                 cg.add(ld2410_component.set_gate_move_sensor(x, sens)) | ||||
|             if still_config := gate_conf.get(CONF_STILL_ENERGY): | ||||
|                 sens = await sensor.new_sensor(still_config) | ||||
|                 cg.add(ld2410_component.set_gate_still_sensor(x, sens)) | ||||
|   | ||||
							
								
								
									
										44
									
								
								esphome/components/ld2410/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/ld2410/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import switch | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     DEVICE_CLASS_SWITCH, | ||||
|     ICON_BLUETOOTH, | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
|     ICON_PULSE, | ||||
| ) | ||||
| from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns | ||||
|  | ||||
| BluetoothSwitch = ld2410_ns.class_("BluetoothSwitch", switch.Switch) | ||||
| EngineeringModeSwitch = ld2410_ns.class_("EngineeringModeSwitch", switch.Switch) | ||||
|  | ||||
| CONF_ENGINEERING_MODE = "engineering_mode" | ||||
| CONF_BLUETOOTH = "bluetooth" | ||||
|  | ||||
| CONFIG_SCHEMA = { | ||||
|     cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||
|     cv.Optional(CONF_ENGINEERING_MODE): switch.switch_schema( | ||||
|         EngineeringModeSwitch, | ||||
|         device_class=DEVICE_CLASS_SWITCH, | ||||
|         entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|         icon=ICON_PULSE, | ||||
|     ), | ||||
|     cv.Optional(CONF_BLUETOOTH): switch.switch_schema( | ||||
|         BluetoothSwitch, | ||||
|         device_class=DEVICE_CLASS_SWITCH, | ||||
|         entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|         icon=ICON_BLUETOOTH, | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||
|     if engineering_mode_config := config.get(CONF_ENGINEERING_MODE): | ||||
|         s = await switch.new_switch(engineering_mode_config) | ||||
|         await cg.register_parented(s, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_engineering_mode_switch(s)) | ||||
|     if bluetooth_config := config.get(CONF_BLUETOOTH): | ||||
|         s = await switch.new_switch(bluetooth_config) | ||||
|         await cg.register_parented(s, config[CONF_LD2410_ID]) | ||||
|         cg.add(ld2410_component.set_bluetooth_switch(s)) | ||||
							
								
								
									
										12
									
								
								esphome/components/ld2410/switch/bluetooth_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2410/switch/bluetooth_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #include "bluetooth_switch.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void BluetoothSwitch::write_state(bool state) { | ||||
|   this->publish_state(state); | ||||
|   this->parent_->set_bluetooth(state); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/switch/bluetooth_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/switch/bluetooth_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class BluetoothSwitch : public switch_::Switch, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   BluetoothSwitch() = default; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										12
									
								
								esphome/components/ld2410/switch/engineering_mode_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								esphome/components/ld2410/switch/engineering_mode_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #include "engineering_mode_switch.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| void EngineeringModeSwitch::write_state(bool state) { | ||||
|   this->publish_state(state); | ||||
|   this->parent_->set_engineering_mode(state); | ||||
| } | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/ld2410/switch/engineering_mode_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/ld2410/switch/engineering_mode_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "../ld2410.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ld2410 { | ||||
|  | ||||
| class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2410Component> { | ||||
|  public: | ||||
|   EngineeringModeSwitch() = default; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace ld2410 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										33
									
								
								esphome/components/ld2410/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								esphome/components/ld2410/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import text_sensor | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     ENTITY_CATEGORY_DIAGNOSTIC, | ||||
|     CONF_VERSION, | ||||
|     CONF_MAC_ADDRESS, | ||||
|     ICON_BLUETOOTH, | ||||
|     ICON_CHIP, | ||||
| ) | ||||
| from . import CONF_LD2410_ID, LD2410Component | ||||
|  | ||||
| DEPENDENCIES = ["ld2410"] | ||||
|  | ||||
| CONFIG_SCHEMA = { | ||||
|     cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||
|     cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema( | ||||
|         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_CHIP | ||||
|     ), | ||||
|     cv.Optional(CONF_MAC_ADDRESS): text_sensor.text_sensor_schema( | ||||
|         entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_BLUETOOTH | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||
|     if version_config := config.get(CONF_VERSION): | ||||
|         sens = await text_sensor.new_text_sensor(version_config) | ||||
|         cg.add(ld2410_component.set_version_text_sensor(sens)) | ||||
|     if mac_address_config := config.get(CONF_MAC_ADDRESS): | ||||
|         sens = await text_sensor.new_text_sensor(mac_address_config) | ||||
|         cg.add(ld2410_component.set_mac_text_sensor(sens)) | ||||
| @@ -28,6 +28,8 @@ from esphome.components.esp32.const import ( | ||||
|     VARIANT_ESP32S2, | ||||
|     VARIANT_ESP32C3, | ||||
|     VARIANT_ESP32S3, | ||||
|     VARIANT_ESP32C2, | ||||
|     VARIANT_ESP32C6, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| @@ -74,6 +76,8 @@ UART_SELECTION_ESP32 = { | ||||
|     VARIANT_ESP32S2: [UART0, UART1, USB_CDC], | ||||
|     VARIANT_ESP32S3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], | ||||
|     VARIANT_ESP32C3: [UART0, UART1, USB_SERIAL_JTAG], | ||||
|     VARIANT_ESP32C2: [UART0, UART1], | ||||
|     VARIANT_ESP32C6: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], | ||||
| } | ||||
|  | ||||
| UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1] | ||||
|   | ||||
| @@ -120,7 +120,7 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { | ||||
|     if ( | ||||
| #if defined(USE_ESP32_VARIANT_ESP32S2) | ||||
|         uart_ == UART_SELECTION_USB_CDC | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32C3) | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) | ||||
|         uart_ == UART_SELECTION_USB_SERIAL_JTAG | ||||
| #elif defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|         uart_ == UART_SELECTION_USB_CDC || uart_ == UART_SELECTION_USB_SERIAL_JTAG | ||||
| @@ -191,8 +191,8 @@ void Logger::pre_setup() { | ||||
|         Serial1.setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE); | ||||
| #endif | ||||
|         break; | ||||
| #if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) && \ | ||||
|     !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
| #if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ | ||||
|     !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|       case UART_SELECTION_UART2: | ||||
|         this->hw_serial_ = &Serial2; | ||||
|         Serial2.begin(this->baud_rate_); | ||||
| @@ -215,7 +215,8 @@ void Logger::pre_setup() { | ||||
|       case UART_SELECTION_UART1: | ||||
|         uart_num_ = UART_NUM_1; | ||||
|         break; | ||||
| #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
| #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ | ||||
|     !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|       case UART_SELECTION_UART2: | ||||
|         uart_num_ = UART_NUM_2; | ||||
|         break; | ||||
| @@ -225,11 +226,11 @@ void Logger::pre_setup() { | ||||
|         uart_num_ = -1; | ||||
|         break; | ||||
| #endif  // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|       case UART_SELECTION_USB_SERIAL_JTAG: | ||||
|         uart_num_ = -1; | ||||
|         break; | ||||
| #endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 | ||||
| #endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 | ||||
|     } | ||||
|     if (uart_num_ >= 0) { | ||||
|       uart_config_t uart_config{}; | ||||
| @@ -278,7 +279,8 @@ const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DE | ||||
| #ifdef USE_ESP32 | ||||
| const char *const UART_SELECTIONS[] = { | ||||
|     "UART0",           "UART1", | ||||
| #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
| #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ | ||||
|     !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|     "UART2", | ||||
| #endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 | ||||
| #if defined(USE_ESP_IDF) | ||||
|   | ||||
| @@ -34,14 +34,15 @@ enum UARTSelection { | ||||
|   UART_SELECTION_UART0 = 0, | ||||
|   UART_SELECTION_UART1, | ||||
| #if defined(USE_ESP32) | ||||
| #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
| #if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ | ||||
|     !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|   UART_SELECTION_UART2, | ||||
| #endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 | ||||
| #ifdef USE_ESP_IDF | ||||
| #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|   UART_SELECTION_USB_CDC, | ||||
| #endif  // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||
|   UART_SELECTION_USB_SERIAL_JTAG, | ||||
| #endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 | ||||
| #endif  // USE_ESP_IDF | ||||
|   | ||||
| @@ -13,7 +13,7 @@ CONF_HOT_JUNCTION = "hot_junction" | ||||
| CONF_COLD_JUNCTION = "cold_junction" | ||||
|  | ||||
| DEPENDENCIES = ["i2c"] | ||||
| CODEOWNERS = ["@MrEditor97"] | ||||
| CODEOWNERS = ["@mreditor97"] | ||||
|  | ||||
| mcp9600_ns = cg.esphome_ns.namespace("mcp9600") | ||||
| MCP9600Component = mcp9600_ns.class_( | ||||
|   | ||||
| @@ -26,7 +26,7 @@ void PCA9554Component::setup() { | ||||
|   this->config_mask_ = 0; | ||||
|   // Invert mask as the part sees a 1 as an input | ||||
|   this->write_register_(CONFIG_REG, ~this->config_mask_); | ||||
|   // All ouputs low | ||||
|   // All outputs low | ||||
|   this->output_mask_ = 0; | ||||
|   this->write_register_(OUTPUT_REG, this->output_mask_); | ||||
|   // Read the inputs | ||||
| @@ -34,6 +34,14 @@ void PCA9554Component::setup() { | ||||
|   ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(), | ||||
|            this->status_has_error()); | ||||
| } | ||||
|  | ||||
| void PCA9554Component::loop() { | ||||
|   // The read_inputs_() method will cache the input values from the chip. | ||||
|   this->read_inputs_(); | ||||
|   // Clear all the previously read flags. | ||||
|   this->was_previously_read_ = 0x00; | ||||
| } | ||||
|  | ||||
| void PCA9554Component::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "PCA9554:"); | ||||
|   LOG_I2C_DEVICE(this) | ||||
| @@ -43,7 +51,16 @@ void PCA9554Component::dump_config() { | ||||
| } | ||||
|  | ||||
| bool PCA9554Component::digital_read(uint8_t pin) { | ||||
|   this->read_inputs_(); | ||||
|   // Note: We want to try and avoid doing any I2C bus read transactions here | ||||
|   // to conserve I2C bus bandwidth. So what we do is check to see if we | ||||
|   // have seen a read during the time esphome is running this loop. If we have, | ||||
|   // we do an I2C bus transaction to get the latest value. If we haven't | ||||
|   // we return a cached value which was read at the time loop() was called. | ||||
|   if (this->was_previously_read_ & (1 << pin)) | ||||
|     this->read_inputs_();  // Force a read of a new value | ||||
|   // Indicate we saw a read request for this pin in case a | ||||
|   // read happens later in the same loop. | ||||
|   this->was_previously_read_ |= (1 << pin); | ||||
|   return this->input_mask_ & (1 << pin); | ||||
| } | ||||
|  | ||||
| @@ -98,6 +115,10 @@ bool PCA9554Component::write_register_(uint8_t reg, uint8_t value) { | ||||
|  | ||||
| float PCA9554Component::get_setup_priority() const { return setup_priority::IO; } | ||||
|  | ||||
| // Run our loop() method very early in the loop, so that we cache read values before | ||||
| // before other components call our digital_read() method. | ||||
| float PCA9554Component::get_loop_priority() const { return 9.0f; }  // Just after WIFI | ||||
|  | ||||
| void PCA9554GPIOPin::setup() { pin_mode(flags_); } | ||||
| void PCA9554GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } | ||||
| bool PCA9554GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } | ||||
|   | ||||
| @@ -13,6 +13,8 @@ class PCA9554Component : public Component, public i2c::I2CDevice { | ||||
|  | ||||
|   /// Check i2c availability and setup masks | ||||
|   void setup() override; | ||||
|   /// Poll for input changes periodically | ||||
|   void loop() override; | ||||
|   /// Helper function to read the value of a pin. | ||||
|   bool digital_read(uint8_t pin); | ||||
|   /// Helper function to write the value of a pin. | ||||
| @@ -22,6 +24,8 @@ class PCA9554Component : public Component, public i2c::I2CDevice { | ||||
|  | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   float get_loop_priority() const override; | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
| @@ -35,6 +39,8 @@ class PCA9554Component : public Component, public i2c::I2CDevice { | ||||
|   uint8_t output_mask_{0x00}; | ||||
|   /// The state of the actual input pin states - 1 means HIGH, 0 means LOW | ||||
|   uint8_t input_mask_{0x00}; | ||||
|   /// Flags to check if read previously during this loop | ||||
|   uint8_t was_previously_read_ = {0x00}; | ||||
|   /// Storage for last I2C error seen | ||||
|   esphome::i2c::ErrorCode last_error_; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										1
									
								
								esphome/components/pmwcs3/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/pmwcs3/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| CODEOWNERS = ["@SeByDocKy"] | ||||
							
								
								
									
										115
									
								
								esphome/components/pmwcs3/pmwcs3.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								esphome/components/pmwcs3/pmwcs3.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| #include "pmwcs3.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pmwcs3 { | ||||
|  | ||||
| static const uint8_t PMWCS3_I2C_ADDRESS = 0x63; | ||||
| static const uint8_t PMWCS3_REG_READ_START = 0x01; | ||||
| static const uint8_t PMWCS3_REG_READ_E25 = 0x02; | ||||
| static const uint8_t PMWCS3_REG_READ_EC = 0x03; | ||||
| static const uint8_t PMWCS3_REG_READ_TEMP = 0x04; | ||||
| static const uint8_t PMWCS3_REG_READ_VWC = 0x05; | ||||
| static const uint8_t PMWCS3_REG_CALIBRATE_AIR = 0x06; | ||||
| static const uint8_t PMWCS3_REG_CALIBRATE_WATER = 0x07; | ||||
| static const uint8_t PMWCS3_SET_I2C_ADDRESS = 0x08; | ||||
| static const uint8_t PMWCS3_REG_GET_DATA = 0x09; | ||||
| static const uint8_t PMWCS3_REG_CALIBRATE_EC = 0x10; | ||||
| static const uint8_t PMWCS3_REG_CAP = 0x0A; | ||||
| static const uint8_t PMWCS3_REG_RES = 0x0B; | ||||
| static const uint8_t PMWCS3_REG_RC = 0x0C; | ||||
| static const uint8_t PMWCS3_REG_RT = 0x0D; | ||||
|  | ||||
| static const char *const TAG = "pmwcs3"; | ||||
|  | ||||
| void PMWCS3Component::new_i2c_address(uint8_t address) { | ||||
|   if (!this->write_byte(PMWCS3_SET_I2C_ADDRESS, address)) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGW(TAG, "couldn't write the new I2C address %d", address); | ||||
|     return; | ||||
|   } | ||||
|   this->set_i2c_address(address);  // Allows device to continue working until new firmware is written with new address. | ||||
|   ESP_LOGVV(TAG, "changed I2C address to %d", address); | ||||
|   this->status_clear_warning(); | ||||
| } | ||||
|  | ||||
| void PMWCS3Component::air_calibration() { | ||||
|   if (!this->write_bytes(PMWCS3_REG_CALIBRATE_AIR, nullptr, 0)) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGW(TAG, "couldn't start air calibration"); | ||||
|     return; | ||||
|   } | ||||
|   ESP_LOGW(TAG, "Start air calibration during the next 300s"); | ||||
| } | ||||
| void PMWCS3Component::water_calibration() { | ||||
|   if (!this->write_bytes(PMWCS3_REG_CALIBRATE_WATER, nullptr, 0)) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGW(TAG, "couldn't start water calibration"); | ||||
|     return; | ||||
|   } | ||||
|   ESP_LOGW(TAG, "Start water calibration during the next 300s"); | ||||
| } | ||||
|  | ||||
| void PMWCS3Component::setup() { ESP_LOGCONFIG(TAG, "Setting up PMWCS3..."); } | ||||
|  | ||||
| void PMWCS3Component::update() { this->read_data_(); } | ||||
|  | ||||
| float PMWCS3Component::get_setup_priority() const { return setup_priority::DATA; } | ||||
|  | ||||
| void PMWCS3Component::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "PMWCS3"); | ||||
|   LOG_I2C_DEVICE(this); | ||||
|   if (this->is_failed()) { | ||||
|     ESP_LOGE(TAG, "Communication with PMWCS3 failed!"); | ||||
|   } | ||||
|   ESP_LOGI(TAG, "%s", this->is_failed() ? "FAILED" : "OK"); | ||||
|  | ||||
|   LOG_UPDATE_INTERVAL(this); | ||||
|   LOG_SENSOR("  ", "e25", this->e25_sensor_); | ||||
|   LOG_SENSOR("  ", "ec", this->ec_sensor_); | ||||
|   LOG_SENSOR("  ", "temperature", this->temperature_sensor_); | ||||
|   LOG_SENSOR("  ", "vwc", this->vwc_sensor_); | ||||
| } | ||||
| void PMWCS3Component::read_data_() { | ||||
|   uint8_t data[8]; | ||||
|   float e25, ec, temperature, vwc; | ||||
|  | ||||
|   /////// Super important !!!! first activate reading PMWCS3_REG_READ_START (if not, return always the same values) //// | ||||
|  | ||||
|   if (!this->write_bytes(PMWCS3_REG_READ_START, nullptr, 0)) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGVV(TAG, "Failed to write into REG_READ_START register !!!"); | ||||
|     return; | ||||
|   } | ||||
|   // NOLINT  delay(100); | ||||
|  | ||||
|   if (!this->read_bytes(PMWCS3_REG_GET_DATA, (uint8_t *) &data, 8)) { | ||||
|     ESP_LOGVV(TAG, "Error reading PMWCS3_REG_GET_DATA registers"); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|   if (this->e25_sensor_ != nullptr) { | ||||
|     e25 = ((data[1] << 8) | data[0]) / 100.0; | ||||
|     this->e25_sensor_->publish_state(e25); | ||||
|     ESP_LOGVV(TAG, "e25: data[0]=%d, data[1]=%d, result=%f", data[0], data[1], e25); | ||||
|   } | ||||
|   if (this->ec_sensor_ != nullptr) { | ||||
|     ec = ((data[3] << 8) | data[2]) / 10.0; | ||||
|     this->ec_sensor_->publish_state(ec); | ||||
|     ESP_LOGVV(TAG, "ec: data[2]=%d, data[3]=%d, result=%f", data[2], data[3], ec); | ||||
|   } | ||||
|   if (this->temperature_sensor_ != nullptr) { | ||||
|     temperature = ((data[5] << 8) | data[4]) / 100.0; | ||||
|     this->temperature_sensor_->publish_state(temperature); | ||||
|     ESP_LOGVV(TAG, "temp: data[4]=%d, data[5]=%d, result=%f", data[4], data[5], temperature); | ||||
|   } | ||||
|   if (this->vwc_sensor_ != nullptr) { | ||||
|     vwc = ((data[7] << 8) | data[6]) / 10.0; | ||||
|     this->vwc_sensor_->publish_state(vwc); | ||||
|     ESP_LOGVV(TAG, "vwc: data[6]=%d, data[7]=%d, result=%f", data[6], data[7], vwc); | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace pmwcs3 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										70
									
								
								esphome/components/pmwcs3/pmwcs3.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								esphome/components/pmwcs3/pmwcs3.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| #pragma once | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
|  | ||||
| // ref: | ||||
| // https://github.com/tinovi/i2cArduino/blob/master/i2cArduino.h | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pmwcs3 { | ||||
|  | ||||
| class PMWCS3Component : public PollingComponent, public i2c::I2CDevice { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void update() override; | ||||
|   void dump_config() override; | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   void set_e25_sensor(sensor::Sensor *e25_sensor) { e25_sensor_ = e25_sensor; } | ||||
|   void set_ec_sensor(sensor::Sensor *ec_sensor) { ec_sensor_ = ec_sensor; } | ||||
|   void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } | ||||
|   void set_vwc_sensor(sensor::Sensor *vwc_sensor) { vwc_sensor_ = vwc_sensor; } | ||||
|  | ||||
|   void new_i2c_address(uint8_t newaddress); | ||||
|   void air_calibration(); | ||||
|   void water_calibration(); | ||||
|  | ||||
|  protected: | ||||
|   void read_data_(); | ||||
|  | ||||
|   sensor::Sensor *e25_sensor_{nullptr}; | ||||
|   sensor::Sensor *ec_sensor_{nullptr}; | ||||
|   sensor::Sensor *temperature_sensor_{nullptr}; | ||||
|   sensor::Sensor *vwc_sensor_{nullptr}; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class PMWCS3AirCalibrationAction : public Action<Ts...> { | ||||
|  public: | ||||
|   PMWCS3AirCalibrationAction(PMWCS3Component *parent) : parent_(parent) {} | ||||
|  | ||||
|   void play(Ts... x) override { this->parent_->air_calibration(); } | ||||
|  | ||||
|  protected: | ||||
|   PMWCS3Component *parent_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class PMWCS3WaterCalibrationAction : public Action<Ts...> { | ||||
|  public: | ||||
|   PMWCS3WaterCalibrationAction(PMWCS3Component *parent) : parent_(parent) {} | ||||
|  | ||||
|   void play(Ts... x) override { this->parent_->water_calibration(); } | ||||
|  | ||||
|  protected: | ||||
|   PMWCS3Component *parent_; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class PMWCS3NewI2cAddressAction : public Action<Ts...> { | ||||
|  public: | ||||
|   PMWCS3NewI2cAddressAction(PMWCS3Component *parent) : parent_(parent) {} | ||||
|   TEMPLATABLE_VALUE(int, new_address) | ||||
|  | ||||
|   void play(Ts... x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); } | ||||
|  | ||||
|  protected: | ||||
|   PMWCS3Component *parent_; | ||||
| }; | ||||
|  | ||||
| }  // namespace pmwcs3 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										140
									
								
								esphome/components/pmwcs3/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								esphome/components/pmwcs3/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| import esphome.codegen as cg | ||||
| from esphome import automation | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, sensor | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_ADDRESS, | ||||
|     CONF_TEMPERATURE, | ||||
|     CONF_EC, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     ICON_THERMOMETER, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@SeByDocKy"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
|  | ||||
| CONF_E25 = "e25" | ||||
| CONF_VWC = "vwc" | ||||
|  | ||||
| ICON_EPSILON = "mdi:epsilon" | ||||
| ICON_SIGMA = "mdi:sigma-lower" | ||||
| ICON_ALPHA = "mdi:alpha-h-circle-outline" | ||||
|  | ||||
| pmwcs3_ns = cg.esphome_ns.namespace("pmwcs3") | ||||
| PMWCS3Component = pmwcs3_ns.class_( | ||||
|     "PMWCS3Component", cg.PollingComponent, i2c.I2CDevice | ||||
| ) | ||||
|  | ||||
| # Actions | ||||
| PMWCS3AirCalibrationAction = pmwcs3_ns.class_( | ||||
|     "PMWCS3AirCalibrationAction", automation.Action | ||||
| ) | ||||
| PMWCS3WaterCalibrationAction = pmwcs3_ns.class_( | ||||
|     "PMWCS3WaterCalibrationAction", automation.Action | ||||
| ) | ||||
| PMWCS3NewI2cAddressAction = pmwcs3_ns.class_( | ||||
|     "PMWCS3NewI2cAddressAction", automation.Action | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(PMWCS3Component), | ||||
|             cv.Optional(CONF_E25): sensor.sensor_schema( | ||||
|                 icon=ICON_EPSILON, | ||||
|                 accuracy_decimals=3, | ||||
|                 unit_of_measurement="dS/m", | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Optional(CONF_EC): sensor.sensor_schema( | ||||
|                 icon=ICON_SIGMA, | ||||
|                 accuracy_decimals=2, | ||||
|                 unit_of_measurement="mS/m", | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||||
|                 icon=ICON_THERMOMETER, | ||||
|                 accuracy_decimals=3, | ||||
|                 unit_of_measurement="°C", | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Optional(CONF_VWC): sensor.sensor_schema( | ||||
|                 icon=ICON_ALPHA, | ||||
|                 accuracy_decimals=3, | ||||
|                 unit_of_measurement="cm3cm−3", | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|         } | ||||
|     ) | ||||
|     .extend(cv.polling_component_schema("60s")) | ||||
|     .extend(i2c.i2c_device_schema(0x63)) | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|     await i2c.register_i2c_device(var, config) | ||||
|  | ||||
|     if CONF_E25 in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_E25]) | ||||
|         cg.add(var.set_e25_sensor(sens)) | ||||
|  | ||||
|     if CONF_EC in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_EC]) | ||||
|         cg.add(var.set_ec_sensor(sens)) | ||||
|  | ||||
|     if CONF_TEMPERATURE in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) | ||||
|         cg.add(var.set_temperature_sensor(sens)) | ||||
|  | ||||
|     if CONF_VWC in config: | ||||
|         sens = await sensor.new_sensor(config[CONF_VWC]) | ||||
|         cg.add(var.set_vwc_sensor(sens)) | ||||
|  | ||||
|  | ||||
| # Actions | ||||
| PMWCS3_CALIBRATION_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(): cv.use_id(PMWCS3Component), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "pmwcs3.air_calibration", | ||||
|     PMWCS3AirCalibrationAction, | ||||
|     PMWCS3_CALIBRATION_SCHEMA, | ||||
| ) | ||||
| @automation.register_action( | ||||
|     "pmwcs3.water_calibration", | ||||
|     PMWCS3WaterCalibrationAction, | ||||
|     PMWCS3_CALIBRATION_SCHEMA, | ||||
| ) | ||||
| async def pmwcs3_calibration_to_code(config, action_id, template_arg, args): | ||||
|     parent = await cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, parent) | ||||
|     return var | ||||
|  | ||||
|  | ||||
| PMWCS3_NEW_I2C_ADDRESS_SCHEMA = cv.maybe_simple_value( | ||||
|     { | ||||
|         cv.GenerateID(): cv.use_id(PMWCS3Component), | ||||
|         cv.Required(CONF_ADDRESS): cv.templatable(cv.i2c_address), | ||||
|     }, | ||||
|     key=CONF_ADDRESS, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
|     "pmwcs3.new_i2c_address", | ||||
|     PMWCS3NewI2cAddressAction, | ||||
|     PMWCS3_NEW_I2C_ADDRESS_SCHEMA, | ||||
| ) | ||||
| async def pmwcs3newi2caddress_to_code(config, action_id, template_arg, args): | ||||
|     parent = await cg.get_variable(config[CONF_ID]) | ||||
|     var = cg.new_Pvariable(action_id, template_arg, parent) | ||||
|     address = await cg.templatable(config[CONF_ADDRESS], args, int) | ||||
|     cg.add(var.set_new_address(address)) | ||||
|     return var | ||||
| @@ -1488,12 +1488,7 @@ MideaData, MideaBinarySensor, MideaTrigger, MideaAction, MideaDumper = declare_p | ||||
| MideaAction = ns.class_("MideaAction", RemoteTransmitterActionBase) | ||||
| MIDEA_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_CODE): cv.templatable( | ||||
|             cv.All( | ||||
|                 [cv.Any(cv.hex_uint8_t, cv.uint8_t)], | ||||
|                 cv.Length(min=5, max=5), | ||||
|             ) | ||||
|         ), | ||||
|         cv.Required(CONF_CODE): cv.All([cv.hex_uint8_t], cv.Length(min=5, max=5)), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| @@ -1513,18 +1508,11 @@ def midea_dumper(var, config): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @register_action( | ||||
|     "midea", | ||||
|     MideaAction, | ||||
|     MIDEA_SCHEMA, | ||||
| ) | ||||
| @register_action("midea", MideaAction, MIDEA_SCHEMA) | ||||
| async def midea_action(var, config, args): | ||||
|     code_ = config[CONF_CODE] | ||||
|     if cg.is_template(code_): | ||||
|         template_ = await cg.templatable(code_, args, cg.std_vector.template(cg.uint8)) | ||||
|         cg.add(var.set_code_template(template_)) | ||||
|     else: | ||||
|         cg.add(var.set_code_static(code_)) | ||||
|     vec_ = cg.std_vector.template(cg.uint8) | ||||
|     template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_) | ||||
|     cg.add(var.set_code(template_)) | ||||
|  | ||||
|  | ||||
| # AEHA | ||||
| @@ -1534,10 +1522,7 @@ AEHAData, AEHABinarySensor, AEHATrigger, AEHAAction, AEHADumper = declare_protoc | ||||
| AEHA_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_ADDRESS): cv.hex_uint16_t, | ||||
|         cv.Required(CONF_DATA): cv.All( | ||||
|             [cv.Any(cv.hex_uint8_t, cv.uint8_t)], | ||||
|             cv.Length(min=2, max=35), | ||||
|         ), | ||||
|         cv.Required(CONF_DATA): cv.All([cv.hex_uint8_t], cv.Length(min=2, max=35)), | ||||
|     } | ||||
| ) | ||||
|  | ||||
| @@ -1569,4 +1554,7 @@ def aeha_dumper(var, config): | ||||
| async def aeha_action(var, config, args): | ||||
|     template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint16) | ||||
|     cg.add(var.set_address(template_)) | ||||
|     cg.add(var.set_data(config[CONF_DATA])) | ||||
|     template_ = await cg.templatable( | ||||
|         config[CONF_DATA], args, cg.std_vector.template(cg.uint8) | ||||
|     ) | ||||
|     cg.add(var.set_data(template_)) | ||||
|   | ||||
| @@ -96,7 +96,7 @@ std::string AEHAProtocol::format_data_(const std::vector<uint8_t> &data) { | ||||
|  | ||||
| void AEHAProtocol::dump(const AEHAData &data) { | ||||
|   auto data_str = format_data_(data.data); | ||||
|   ESP_LOGD(TAG, "Received AEHA: address=0x%04X, data=[%s]", data.address, data_str.c_str()); | ||||
|   ESP_LOGI(TAG, "Received AEHA: address=0x%04X, data=[%s]", data.address, data_str.c_str()); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -96,10 +96,10 @@ optional<CanalSatData> CanalSatBaseProtocol::decode(RemoteReceiveData src) { | ||||
|  | ||||
| void CanalSatBaseProtocol::dump(const CanalSatData &data) { | ||||
|   if (this->tag_ == CANALSATLD_TAG) { | ||||
|     ESP_LOGD(this->tag_, "Received CanalSatLD: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device, | ||||
|     ESP_LOGI(this->tag_, "Received CanalSatLD: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device, | ||||
|              data.address, data.command, data.repeat); | ||||
|   } else { | ||||
|     ESP_LOGD(this->tag_, "Received CanalSat: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device, | ||||
|     ESP_LOGI(this->tag_, "Received CanalSat: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device, | ||||
|              data.address, data.command, data.repeat); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -101,11 +101,11 @@ optional<CoolixData> CoolixProtocol::decode(RemoteReceiveData data) { | ||||
|  | ||||
| void CoolixProtocol::dump(const CoolixData &data) { | ||||
|   if (data.is_strict()) { | ||||
|     ESP_LOGD(TAG, "Received Coolix: 0x%06X", data.first); | ||||
|     ESP_LOGI(TAG, "Received Coolix: 0x%06X", data.first); | ||||
|   } else if (data.has_second()) { | ||||
|     ESP_LOGD(TAG, "Received unstrict Coolix: [0x%06X, 0x%06X]", data.first, data.second); | ||||
|     ESP_LOGI(TAG, "Received unstrict Coolix: [0x%06X, 0x%06X]", data.first, data.second); | ||||
|   } else { | ||||
|     ESP_LOGD(TAG, "Received unstrict Coolix: [0x%06X]", data.first); | ||||
|     ESP_LOGI(TAG, "Received unstrict Coolix: [0x%06X]", data.first); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -87,7 +87,7 @@ optional<DishData> DishProtocol::decode(RemoteReceiveData src) { | ||||
| } | ||||
|  | ||||
| void DishProtocol::dump(const DishData &data) { | ||||
|   ESP_LOGD(TAG, "Received Dish: address=0x%02X, command=0x%02X", data.address, data.command); | ||||
|   ESP_LOGI(TAG, "Received Dish: address=0x%02X, command=0x%02X", data.address, data.command); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -205,7 +205,7 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) { | ||||
|   return out; | ||||
| } | ||||
| void DraytonProtocol::dump(const DraytonData &data) { | ||||
|   ESP_LOGD(TAG, "Received Drayton: address=0x%04X (0x%04x), channel=0x%03x command=0x%03X", data.address, | ||||
|   ESP_LOGI(TAG, "Received Drayton: address=0x%04X (0x%04x), channel=0x%03x command=0x%03X", data.address, | ||||
|            ((data.address << 1) & 0xffff), data.channel, data.command); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -46,7 +46,7 @@ optional<JVCData> JVCProtocol::decode(RemoteReceiveData src) { | ||||
|   } | ||||
|   return out; | ||||
| } | ||||
| void JVCProtocol::dump(const JVCData &data) { ESP_LOGD(TAG, "Received JVC: data=0x%04X", data.data); } | ||||
| void JVCProtocol::dump(const JVCData &data) { ESP_LOGI(TAG, "Received JVC: data=0x%04X", data.data); } | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -51,7 +51,7 @@ optional<LGData> LGProtocol::decode(RemoteReceiveData src) { | ||||
|   return out; | ||||
| } | ||||
| void LGProtocol::dump(const LGData &data) { | ||||
|   ESP_LOGD(TAG, "Received LG: data=0x%08X, nbits=%d", data.data, data.nbits); | ||||
|   ESP_LOGI(TAG, "Received LG: data=0x%08X, nbits=%d", data.data, data.nbits); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -76,7 +76,7 @@ optional<MagiQuestData> MagiQuestProtocol::decode(RemoteReceiveData src) { | ||||
|   return data; | ||||
| } | ||||
| void MagiQuestProtocol::dump(const MagiQuestData &data) { | ||||
|   ESP_LOGD(TAG, "Received MagiQuest: wand_id=0x%08X, magnitude=0x%04X", data.wand_id, data.magnitude); | ||||
|   ESP_LOGI(TAG, "Received MagiQuest: wand_id=0x%08X, magnitude=0x%04X", data.wand_id, data.magnitude); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -70,7 +70,7 @@ optional<MideaData> MideaProtocol::decode(RemoteReceiveData src) { | ||||
|   return {}; | ||||
| } | ||||
|  | ||||
| void MideaProtocol::dump(const MideaData &data) { ESP_LOGD(TAG, "Received Midea: %s", data.to_string().c_str()); } | ||||
| void MideaProtocol::dump(const MideaData &data) { ESP_LOGI(TAG, "Received Midea: %s", data.to_string().c_str()); } | ||||
|  | ||||
| }  // namespace remote_base | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <vector> | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "remote_base.h" | ||||
| #include <array> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace remote_base { | ||||
| @@ -84,23 +84,12 @@ using MideaDumper = RemoteReceiverDumper<MideaProtocol, MideaData>; | ||||
|  | ||||
| template<typename... Ts> class MideaAction : public RemoteTransmitterActionBase<Ts...> { | ||||
|   TEMPLATABLE_VALUE(std::vector<uint8_t>, code) | ||||
|   void set_code_static(std::vector<uint8_t> code) { code_static_ = std::move(code); } | ||||
|   void set_code_template(std::function<std::vector<uint8_t>(Ts...)> func) { this->code_func_ = func; } | ||||
|  | ||||
|   void encode(RemoteTransmitData *dst, Ts... x) override { | ||||
|     MideaData data; | ||||
|     if (!this->code_static_.empty()) { | ||||
|       data = MideaData(this->code_static_); | ||||
|     } else { | ||||
|       data = MideaData(this->code_func_(x...)); | ||||
|     } | ||||
|     MideaData data(this->code_.value(x...)); | ||||
|     data.finalize(); | ||||
|     MideaProtocol().encode(dst, data); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   std::function<std::vector<uint8_t>(Ts...)> code_func_{}; | ||||
|   std::vector<uint8_t> code_static_{}; | ||||
| }; | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -67,7 +67,7 @@ optional<NECData> NECProtocol::decode(RemoteReceiveData src) { | ||||
|   return data; | ||||
| } | ||||
| void NECProtocol::dump(const NECData &data) { | ||||
|   ESP_LOGD(TAG, "Received NEC: address=0x%04X, command=0x%04X", data.address, data.command); | ||||
|   ESP_LOGI(TAG, "Received NEC: address=0x%04X, command=0x%04X", data.address, data.command); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -232,7 +232,7 @@ optional<NexaData> NexaProtocol::decode(RemoteReceiveData src) { | ||||
| } | ||||
|  | ||||
| void NexaProtocol::dump(const NexaData &data) { | ||||
|   ESP_LOGD(TAG, "Received NEXA: device=0x%04X group=%d state=%d channel=%d level=%d", data.device, data.group, | ||||
|   ESP_LOGI(TAG, "Received NEXA: device=0x%04X group=%d state=%d channel=%d level=%d", data.device, data.group, | ||||
|            data.state, data.channel, data.level); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -67,7 +67,7 @@ optional<PanasonicData> PanasonicProtocol::decode(RemoteReceiveData src) { | ||||
|   return out; | ||||
| } | ||||
| void PanasonicProtocol::dump(const PanasonicData &data) { | ||||
|   ESP_LOGD(TAG, "Received Panasonic: address=0x%04X, command=0x%08X", data.address, data.command); | ||||
|   ESP_LOGI(TAG, "Received Panasonic: address=0x%04X, command=0x%08X", data.address, data.command); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -146,9 +146,9 @@ optional<PioneerData> PioneerProtocol::decode(RemoteReceiveData src) { | ||||
| } | ||||
| void PioneerProtocol::dump(const PioneerData &data) { | ||||
|   if (data.rc_code_2 == 0) { | ||||
|     ESP_LOGD(TAG, "Received Pioneer: rc_code_X=0x%04X", data.rc_code_1); | ||||
|     ESP_LOGI(TAG, "Received Pioneer: rc_code_X=0x%04X", data.rc_code_1); | ||||
|   } else { | ||||
|     ESP_LOGD(TAG, "Received Pioneer: rc_code_1=0x%04X, rc_code_2=0x%04X", data.rc_code_1, data.rc_code_2); | ||||
|     ESP_LOGI(TAG, "Received Pioneer: rc_code_1=0x%04X, rc_code_2=0x%04X", data.rc_code_1, data.rc_code_2); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -234,9 +234,9 @@ void ProntoProtocol::dump(const ProntoData &data) { | ||||
|     first = data.data.substr(0, 229); | ||||
|     rest = data.data.substr(230); | ||||
|   } | ||||
|   ESP_LOGD(TAG, "Received Pronto: data=%s", first.c_str()); | ||||
|   ESP_LOGI(TAG, "Received Pronto: data=%s", first.c_str()); | ||||
|   if (!rest.empty()) { | ||||
|     ESP_LOGD(TAG, "%s", rest.c_str()); | ||||
|     ESP_LOGI(TAG, "%s", rest.c_str()); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -25,7 +25,7 @@ bool RawDumper::dump(RemoteReceiveData src) { | ||||
|     if (written < 0 || written >= int(remaining_length)) { | ||||
|       // write failed, flush... | ||||
|       buffer[buffer_offset] = '\0'; | ||||
|       ESP_LOGD(TAG, "%s", buffer); | ||||
|       ESP_LOGI(TAG, "%s", buffer); | ||||
|       buffer_offset = 0; | ||||
|       written = sprintf(buffer, "  "); | ||||
|       if (i + 1 < src.size()) { | ||||
| @@ -38,7 +38,7 @@ bool RawDumper::dump(RemoteReceiveData src) { | ||||
|     buffer_offset += written; | ||||
|   } | ||||
|   if (buffer_offset != 0) { | ||||
|     ESP_LOGD(TAG, "%s", buffer); | ||||
|     ESP_LOGI(TAG, "%s", buffer); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|   | ||||
| @@ -83,7 +83,7 @@ optional<RC5Data> RC5Protocol::decode(RemoteReceiveData src) { | ||||
|   return out; | ||||
| } | ||||
| void RC5Protocol::dump(const RC5Data &data) { | ||||
|   ESP_LOGD(TAG, "Received RC5: address=0x%02X, command=0x%02X", data.address, data.command); | ||||
|   ESP_LOGI(TAG, "Received RC5: address=0x%02X, command=0x%02X", data.address, data.command); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
| @@ -173,7 +173,7 @@ optional<RC6Data> RC6Protocol::decode(RemoteReceiveData src) { | ||||
| } | ||||
|  | ||||
| void RC6Protocol::dump(const RC6Data &data) { | ||||
|   ESP_LOGD(RC6_TAG, "Received RC6: mode=0x%X, address=0x%02X, command=0x%02X, toggle=0x%X", data.mode, data.address, | ||||
|   ESP_LOGI(RC6_TAG, "Received RC6: mode=0x%X, address=0x%02X, command=0x%02X, toggle=0x%X", data.mode, data.address, | ||||
|            data.command, data.toggle); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -258,7 +258,7 @@ bool RCSwitchDumper::dump(RemoteReceiveData src) { | ||||
|         buffer[j] = (out_data & ((uint64_t) 1 << (out_nbits - j - 1))) ? '1' : '0'; | ||||
|  | ||||
|       buffer[out_nbits] = '\0'; | ||||
|       ESP_LOGD(TAG, "Received RCSwitch Raw: protocol=%u data='%s'", i, buffer); | ||||
|       ESP_LOGI(TAG, "Received RCSwitch Raw: protocol=%u data='%s'", i, buffer); | ||||
|  | ||||
|       // only send first decoded protocol | ||||
|       return true; | ||||
|   | ||||
| @@ -96,7 +96,7 @@ optional<Samsung36Data> Samsung36Protocol::decode(RemoteReceiveData src) { | ||||
|   return out; | ||||
| } | ||||
| void Samsung36Protocol::dump(const Samsung36Data &data) { | ||||
|   ESP_LOGD(TAG, "Received Samsung36: address=0x%04X, command=0x%08X", data.address, data.command); | ||||
|   ESP_LOGI(TAG, "Received Samsung36: address=0x%04X, command=0x%08X", data.address, data.command); | ||||
| } | ||||
|  | ||||
| }  // namespace remote_base | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user