mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +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/bp5758d/* @Cossid | ||||||
| esphome/components/button/* @esphome/core | esphome/components/button/* @esphome/core | ||||||
| esphome/components/canbus/* @danielschramm @mvturnho | esphome/components/canbus/* @danielschramm @mvturnho | ||||||
| esphome/components/cap1188/* @MrEditor97 | esphome/components/cap1188/* @mreditor97 | ||||||
| esphome/components/captive_portal/* @OttoWinter | esphome/components/captive_portal/* @OttoWinter | ||||||
| esphome/components/ccs811/* @habbie | esphome/components/ccs811/* @habbie | ||||||
| esphome/components/cd74hc4067/* @asoehlke | esphome/components/cd74hc4067/* @asoehlke | ||||||
| @@ -87,7 +87,7 @@ esphome/components/ens210/* @itn3rd77 | |||||||
| esphome/components/esp32/* @esphome/core | esphome/components/esp32/* @esphome/core | ||||||
| esphome/components/esp32_ble/* @jesserockz | esphome/components/esp32_ble/* @jesserockz | ||||||
| esphome/components/esp32_ble_client/* @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_camera_web_server/* @ayufan | ||||||
| esphome/components/esp32_can/* @Sympatron | esphome/components/esp32_can/* @Sympatron | ||||||
| esphome/components/esp32_improv/* @jesserockz | esphome/components/esp32_improv/* @jesserockz | ||||||
| @@ -133,7 +133,7 @@ esphome/components/i2s_audio/speaker/* @jesserockz | |||||||
| esphome/components/ili9xxx/* @nielsnl68 | esphome/components/ili9xxx/* @nielsnl68 | ||||||
| esphome/components/improv_base/* @esphome/core | esphome/components/improv_base/* @esphome/core | ||||||
| esphome/components/improv_serial/* @esphome/core | esphome/components/improv_serial/* @esphome/core | ||||||
| esphome/components/ina260/* @MrEditor97 | esphome/components/ina260/* @mreditor97 | ||||||
| esphome/components/inkbird_ibsth1_mini/* @fkirill | esphome/components/inkbird_ibsth1_mini/* @fkirill | ||||||
| esphome/components/inkplate6/* @jesserockz | esphome/components/inkplate6/* @jesserockz | ||||||
| esphome/components/integration/* @OttoWinter | esphome/components/integration/* @OttoWinter | ||||||
| @@ -145,7 +145,7 @@ esphome/components/key_collector/* @ssieb | |||||||
| esphome/components/key_provider/* @ssieb | esphome/components/key_provider/* @ssieb | ||||||
| esphome/components/kuntze/* @ssieb | esphome/components/kuntze/* @ssieb | ||||||
| esphome/components/lcd_menu/* @numo68 | esphome/components/lcd_menu/* @numo68 | ||||||
| esphome/components/ld2410/* @sebcaps | esphome/components/ld2410/* @regevbr @sebcaps | ||||||
| esphome/components/ledc/* @OttoWinter | esphome/components/ledc/* @OttoWinter | ||||||
| esphome/components/light/* @esphome/core | esphome/components/light/* @esphome/core | ||||||
| esphome/components/lilygo_t5_47/touchscreen/* @jesserockz | esphome/components/lilygo_t5_47/touchscreen/* @jesserockz | ||||||
| @@ -169,7 +169,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho | |||||||
| esphome/components/mcp3204/* @rsumner | esphome/components/mcp3204/* @rsumner | ||||||
| esphome/components/mcp4728/* @berfenger | esphome/components/mcp4728/* @berfenger | ||||||
| esphome/components/mcp47a1/* @jesserockz | esphome/components/mcp47a1/* @jesserockz | ||||||
| esphome/components/mcp9600/* @MrEditor97 | esphome/components/mcp9600/* @mreditor97 | ||||||
| esphome/components/mcp9808/* @k7hpn | esphome/components/mcp9808/* @k7hpn | ||||||
| esphome/components/md5/* @esphome/core | esphome/components/md5/* @esphome/core | ||||||
| esphome/components/mdns/* @esphome/core | esphome/components/mdns/* @esphome/core | ||||||
| @@ -213,6 +213,7 @@ esphome/components/pid/* @OttoWinter | |||||||
| esphome/components/pipsolar/* @andreashergert1984 | esphome/components/pipsolar/* @andreashergert1984 | ||||||
| esphome/components/pm1006/* @habbie | esphome/components/pm1006/* @habbie | ||||||
| esphome/components/pmsa003i/* @sjtrny | esphome/components/pmsa003i/* @sjtrny | ||||||
|  | esphome/components/pmwcs3/* @SeByDocKy | ||||||
| esphome/components/pn532/* @OttoWinter @jesserockz | esphome/components/pn532/* @OttoWinter @jesserockz | ||||||
| esphome/components/pn532_i2c/* @OttoWinter @jesserockz | esphome/components/pn532_i2c/* @OttoWinter @jesserockz | ||||||
| esphome/components/pn532_spi/* @OttoWinter @jesserockz | esphome/components/pn532_spi/* @OttoWinter @jesserockz | ||||||
|   | |||||||
| @@ -28,14 +28,15 @@ RUN \ | |||||||
|         git=1:2.30.2-1+deb11u2 \ |         git=1:2.30.2-1+deb11u2 \ | ||||||
|         curl=7.74.0-1.3+deb11u7 \ |         curl=7.74.0-1.3+deb11u7 \ | ||||||
|         openssh-client=1:8.4p1-5+deb11u1 \ |         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 \ |     if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ | ||||||
|         apt-get install -y --no-install-recommends \ |         apt-get install -y --no-install-recommends \ | ||||||
|           build-essential=12.9 \ |           build-essential=12.9 \ | ||||||
|           python3-dev=3.9.2-3 \ |           python3-dev=3.9.2-3 \ | ||||||
|           zlib1g-dev=1:1.2.11.dfsg-2+deb11u2 \ |           zlib1g-dev=1:1.2.11.dfsg-2+deb11u2 \ | ||||||
|           libjpeg-dev=1:2.0.6-4 \ |           libjpeg-dev=1:2.0.6-4 \ | ||||||
|           libcairo2=1.16.0-5; \ |           libfreetype-dev=2.10.4+dfsg-1+deb11u1; \ | ||||||
|     fi; \ |     fi; \ | ||||||
|     rm -rf \ |     rm -rf \ | ||||||
|         /tmp/* \ |         /tmp/* \ | ||||||
| @@ -60,7 +61,7 @@ RUN \ | |||||||
|     # Ubuntu python3-pip is missing wheel |     # Ubuntu python3-pip is missing wheel | ||||||
|     pip3 install --no-cache-dir \ |     pip3 install --no-cache-dir \ | ||||||
|         wheel==0.37.1 \ |         wheel==0.37.1 \ | ||||||
|         platformio==6.1.7 \ |         platformio==6.1.10 \ | ||||||
|     # Change some platformio settings |     # Change some platformio settings | ||||||
|     && platformio settings set enable_telemetry No \ |     && platformio settings set enable_telemetry No \ | ||||||
|     && platformio settings set check_platformio_interval 1000000 \ |     && 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 import get_esp32_variant | ||||||
| from esphome.components.esp32.const import ( | from esphome.components.esp32.const import ( | ||||||
|     VARIANT_ESP32, |     VARIANT_ESP32, | ||||||
|  |     VARIANT_ESP32C2, | ||||||
|     VARIANT_ESP32C3, |     VARIANT_ESP32C3, | ||||||
|  |     VARIANT_ESP32C6, | ||||||
|     VARIANT_ESP32H2, |     VARIANT_ESP32H2, | ||||||
|     VARIANT_ESP32S2, |     VARIANT_ESP32S2, | ||||||
|     VARIANT_ESP32S3, |     VARIANT_ESP32S3, | ||||||
| @@ -70,6 +72,22 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = { | |||||||
|         3: adc1_channel_t.ADC1_CHANNEL_3, |         3: adc1_channel_t.ADC1_CHANNEL_3, | ||||||
|         4: adc1_channel_t.ADC1_CHANNEL_4, |         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: { |     VARIANT_ESP32H2: { | ||||||
|         0: adc1_channel_t.ADC1_CHANNEL_0, |         0: adc1_channel_t.ADC1_CHANNEL_0, | ||||||
|         1: adc1_channel_t.ADC1_CHANNEL_1, |         1: adc1_channel_t.ADC1_CHANNEL_1, | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ CONF_ALLOW_MULTIPLE_TOUCHES = "allow_multiple_touches" | |||||||
|  |  | ||||||
| DEPENDENCIES = ["i2c"] | DEPENDENCIES = ["i2c"] | ||||||
| AUTO_LOAD = ["binary_sensor", "output"] | AUTO_LOAD = ["binary_sensor", "output"] | ||||||
| CODEOWNERS = ["@MrEditor97"] | CODEOWNERS = ["@mreditor97"] | ||||||
|  |  | ||||||
| cap1188_ns = cg.esphome_ns.namespace("cap1188") | cap1188_ns = cg.esphome_ns.namespace("cap1188") | ||||||
| CONF_CAP1188_ID = "cap1188_id" | CONF_CAP1188_ID = "cap1188_id" | ||||||
|   | |||||||
| @@ -38,7 +38,7 @@ void CurrentBasedCover::control(const CoverCall &call) { | |||||||
|   } |   } | ||||||
|   if (call.get_position().has_value()) { |   if (call.get_position().has_value()) { | ||||||
|     auto pos = *call.get_position(); |     auto pos = *call.get_position(); | ||||||
|     if (pos == this->position) { |     if (fabsf(this->position - pos) < 0.01) { | ||||||
|       // already at target |       // already at target | ||||||
|     } else { |     } else { | ||||||
|       auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; |       auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; | ||||||
|   | |||||||
| @@ -12,11 +12,15 @@ | |||||||
| #include <esp_heap_caps.h> | #include <esp_heap_caps.h> | ||||||
| #include <esp_system.h> | #include <esp_system.h> | ||||||
|  |  | ||||||
| #if ESP_IDF_VERSION_MAJOR >= 4 |  | ||||||
| #include <esp32/rom/rtc.h> |  | ||||||
| #include <esp_chip_info.h> | #include <esp_chip_info.h> | ||||||
| #else | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
| #include <rom/rtc.h> | #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 | ||||||
|  |  | ||||||
| #endif  // USE_ESP32 | #endif  // USE_ESP32 | ||||||
| @@ -109,13 +113,19 @@ void DebugComponent::dump_config() { | |||||||
|   esp_chip_info_t info; |   esp_chip_info_t info; | ||||||
|   esp_chip_info(&info); |   esp_chip_info(&info); | ||||||
|   const char *model; |   const char *model; | ||||||
|   switch (info.model) { | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|     case CHIP_ESP32: |  | ||||||
|   model = "ESP32"; |   model = "ESP32"; | ||||||
|       break; | #elif defined(USE_ESP32_VARIANT_ESP32C3) | ||||||
|     default: |   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"; |   model = "UNKNOWN"; | ||||||
|   } | #endif | ||||||
|   std::string features; |   std::string features; | ||||||
|   if (info.features & CHIP_FEATURE_EMB_FLASH) { |   if (info.features & CHIP_FEATURE_EMB_FLASH) { | ||||||
|     features += "EMB_FLASH,"; |     features += "EMB_FLASH,"; | ||||||
| @@ -157,18 +167,26 @@ void DebugComponent::dump_config() { | |||||||
|     case POWERON_RESET: |     case POWERON_RESET: | ||||||
|       reset_reason = "Power On Reset"; |       reset_reason = "Power On Reset"; | ||||||
|       break; |       break; | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|     case SW_RESET: |     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"; |       reset_reason = "Software Reset Digital Core"; | ||||||
|       break; |       break; | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|     case OWDT_RESET: |     case OWDT_RESET: | ||||||
|       reset_reason = "Watch Dog Reset Digital Core"; |       reset_reason = "Watch Dog Reset Digital Core"; | ||||||
|       break; |       break; | ||||||
|  | #endif | ||||||
|     case DEEPSLEEP_RESET: |     case DEEPSLEEP_RESET: | ||||||
|       reset_reason = "Deep Sleep Reset Digital Core"; |       reset_reason = "Deep Sleep Reset Digital Core"; | ||||||
|       break; |       break; | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|     case SDIO_RESET: |     case SDIO_RESET: | ||||||
|       reset_reason = "SLC Module Reset Digital Core"; |       reset_reason = "SLC Module Reset Digital Core"; | ||||||
|       break; |       break; | ||||||
|  | #endif | ||||||
|     case TG0WDT_SYS_RESET: |     case TG0WDT_SYS_RESET: | ||||||
|       reset_reason = "Timer Group 0 Watch Dog Reset Digital Core"; |       reset_reason = "Timer Group 0 Watch Dog Reset Digital Core"; | ||||||
|       break; |       break; | ||||||
| @@ -181,24 +199,61 @@ void DebugComponent::dump_config() { | |||||||
|     case INTRUSION_RESET: |     case INTRUSION_RESET: | ||||||
|       reset_reason = "Intrusion Reset CPU"; |       reset_reason = "Intrusion Reset CPU"; | ||||||
|       break; |       break; | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|     case TGWDT_CPU_RESET: |     case TGWDT_CPU_RESET: | ||||||
|       reset_reason = "Timer Group Reset CPU"; |       reset_reason = "Timer Group Reset CPU"; | ||||||
|       break; |       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: |     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"; |       reset_reason = "Software Reset CPU"; | ||||||
|       break; |       break; | ||||||
|     case RTCWDT_CPU_RESET: |     case RTCWDT_CPU_RESET: | ||||||
|       reset_reason = "RTC Watch Dog Reset CPU"; |       reset_reason = "RTC Watch Dog Reset CPU"; | ||||||
|       break; |       break; | ||||||
|  | #if defined(USE_ESP32_VARIANT_ESP32) | ||||||
|     case EXT_CPU_RESET: |     case EXT_CPU_RESET: | ||||||
|       reset_reason = "External CPU Reset"; |       reset_reason = "External CPU Reset"; | ||||||
|       break; |       break; | ||||||
|  | #endif | ||||||
|     case RTCWDT_BROWN_OUT_RESET: |     case RTCWDT_BROWN_OUT_RESET: | ||||||
|       reset_reason = "Voltage Unstable Reset"; |       reset_reason = "Voltage Unstable Reset"; | ||||||
|       break; |       break; | ||||||
|     case RTCWDT_RTC_RESET: |     case RTCWDT_RTC_RESET: | ||||||
|       reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; |       reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; | ||||||
|       break; |       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: |     default: | ||||||
|       reset_reason = "Unknown Reset Reason"; |       reset_reason = "Unknown Reset Reason"; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -22,6 +22,8 @@ from esphome.components.esp32.const import ( | |||||||
|     VARIANT_ESP32C3, |     VARIANT_ESP32C3, | ||||||
|     VARIANT_ESP32S2, |     VARIANT_ESP32S2, | ||||||
|     VARIANT_ESP32S3, |     VARIANT_ESP32S3, | ||||||
|  |     VARIANT_ESP32C2, | ||||||
|  |     VARIANT_ESP32C6, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| WAKEUP_PINS = { | WAKEUP_PINS = { | ||||||
| @@ -94,6 +96,8 @@ WAKEUP_PINS = { | |||||||
|         20, |         20, | ||||||
|         21, |         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_SDKCONFIG_OPTIONS, | ||||||
|     KEY_SUBMODULES, |     KEY_SUBMODULES, | ||||||
|     KEY_VARIANT, |     KEY_VARIANT, | ||||||
|     VARIANT_ESP32C3, |  | ||||||
|     VARIANT_FRIENDLY, |     VARIANT_FRIENDLY, | ||||||
|     VARIANTS, |     VARIANTS, | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -14,13 +14,17 @@ KEY_SUBMODULES = "submodules" | |||||||
| VARIANT_ESP32 = "ESP32" | VARIANT_ESP32 = "ESP32" | ||||||
| VARIANT_ESP32S2 = "ESP32S2" | VARIANT_ESP32S2 = "ESP32S2" | ||||||
| VARIANT_ESP32S3 = "ESP32S3" | VARIANT_ESP32S3 = "ESP32S3" | ||||||
|  | VARIANT_ESP32C2 = "ESP32C2" | ||||||
| VARIANT_ESP32C3 = "ESP32C3" | VARIANT_ESP32C3 = "ESP32C3" | ||||||
|  | VARIANT_ESP32C6 = "ESP32C6" | ||||||
| VARIANT_ESP32H2 = "ESP32H2" | VARIANT_ESP32H2 = "ESP32H2" | ||||||
| VARIANTS = [ | VARIANTS = [ | ||||||
|     VARIANT_ESP32, |     VARIANT_ESP32, | ||||||
|     VARIANT_ESP32S2, |     VARIANT_ESP32S2, | ||||||
|     VARIANT_ESP32S3, |     VARIANT_ESP32S3, | ||||||
|  |     VARIANT_ESP32C2, | ||||||
|     VARIANT_ESP32C3, |     VARIANT_ESP32C3, | ||||||
|  |     VARIANT_ESP32C6, | ||||||
|     VARIANT_ESP32H2, |     VARIANT_ESP32H2, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -28,7 +32,9 @@ VARIANT_FRIENDLY = { | |||||||
|     VARIANT_ESP32: "ESP32", |     VARIANT_ESP32: "ESP32", | ||||||
|     VARIANT_ESP32S2: "ESP32-S2", |     VARIANT_ESP32S2: "ESP32-S2", | ||||||
|     VARIANT_ESP32S3: "ESP32-S3", |     VARIANT_ESP32S3: "ESP32-S3", | ||||||
|  |     VARIANT_ESP32C2: "ESP32-C2", | ||||||
|     VARIANT_ESP32C3: "ESP32-C3", |     VARIANT_ESP32C3: "ESP32-C3", | ||||||
|  |     VARIANT_ESP32C6: "ESP32-C6", | ||||||
|     VARIANT_ESP32H2: "ESP32-H2", |     VARIANT_ESP32H2: "ESP32-H2", | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,9 +10,7 @@ | |||||||
| #include <esp_timer.h> | #include <esp_timer.h> | ||||||
| #include <soc/rtc.h> | #include <soc/rtc.h> | ||||||
|  |  | ||||||
| #if ESP_IDF_VERSION_MAJOR >= 4 |  | ||||||
| #include <hal/cpu_hal.h> | #include <hal/cpu_hal.h> | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifdef USE_ARDUINO | #ifdef USE_ARDUINO | ||||||
| #include <esp32-hal.h> | #include <esp32-hal.h> | ||||||
| @@ -55,15 +53,7 @@ void arch_init() { | |||||||
| void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); } | void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); } | ||||||
|  |  | ||||||
| uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } | uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } | ||||||
| uint32_t arch_get_cpu_cycle_count() { | uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_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_freq_hz() { return rtc_clk_apb_freq_get(); } | uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); } | ||||||
|  |  | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|   | |||||||
| @@ -26,6 +26,8 @@ from .const import ( | |||||||
|     VARIANT_ESP32C3, |     VARIANT_ESP32C3, | ||||||
|     VARIANT_ESP32S2, |     VARIANT_ESP32S2, | ||||||
|     VARIANT_ESP32S3, |     VARIANT_ESP32S3, | ||||||
|  |     VARIANT_ESP32C2, | ||||||
|  |     VARIANT_ESP32C6, | ||||||
|     VARIANT_ESP32H2, |     VARIANT_ESP32H2, | ||||||
|     esp32_ns, |     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_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_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_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 | 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, |         pin_validation=esp32_s3_validate_gpio_pin, | ||||||
|         usage_validation=esp32_s3_validate_supports, |         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( |     VARIANT_ESP32H2: ESP32ValidationFunctions( | ||||||
|         pin_validation=esp32_h2_validate_gpio_pin, |         pin_validation=esp32_h2_validate_gpio_pin, | ||||||
|         usage_validation=esp32_h2_validate_supports, |         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()); |                                  this->advertising_uuids_.end()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void BLEAdvertising::set_manufacturer_data(uint8_t *data, uint16_t size) { | void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) { | ||||||
|   this->advertising_data_.p_manufacturer_data = data; |   delete[] this->advertising_data_.p_manufacturer_data; | ||||||
|   this->advertising_data_.manufacturer_len = size; |   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() { | void BLEAdvertising::start() { | ||||||
| @@ -74,10 +80,14 @@ void BLEAdvertising::start() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (this->scan_response_) { | ||||||
|     memcpy(&this->scan_response_data_, &this->advertising_data_, sizeof(esp_ble_adv_data_t)); |     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_.set_scan_rsp = true; | ||||||
|     this->scan_response_data_.include_name = true; |     this->scan_response_data_.include_name = true; | ||||||
|     this->scan_response_data_.include_txpower = 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_.appearance = 0; | ||||||
|     this->scan_response_data_.flag = 0; |     this->scan_response_data_.flag = 0; | ||||||
|     err = esp_ble_gap_config_adv_data(&this->scan_response_data_); |     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); |       ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %d", err); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   if (this->advertising_data_.service_uuid_len > 0) { |   if (this->advertising_data_.service_uuid_len > 0) { | ||||||
|     delete[] this->advertising_data_.p_service_uuid; |     delete[] this->advertising_data_.p_service_uuid; | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ class BLEAdvertising { | |||||||
|   void remove_service_uuid(ESPBTUUID uuid); |   void remove_service_uuid(ESPBTUUID uuid); | ||||||
|   void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; } |   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_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 start(); | ||||||
|   void stop(); |   void stop(); | ||||||
|   | |||||||
| @@ -6,11 +6,12 @@ from esphome.core import CORE | |||||||
| from esphome.components.esp32 import add_idf_sdkconfig_option | from esphome.components.esp32 import add_idf_sdkconfig_option | ||||||
|  |  | ||||||
| AUTO_LOAD = ["esp32_ble"] | AUTO_LOAD = ["esp32_ble"] | ||||||
| CODEOWNERS = ["@jesserockz"] | CODEOWNERS = ["@jesserockz", "@clydebarrow"] | ||||||
| CONFLICTS_WITH = ["esp32_ble_beacon"] | CONFLICTS_WITH = ["esp32_ble_beacon"] | ||||||
| DEPENDENCIES = ["esp32"] | DEPENDENCIES = ["esp32"] | ||||||
|  |  | ||||||
| CONF_MANUFACTURER = "manufacturer" | CONF_MANUFACTURER = "manufacturer" | ||||||
|  | CONF_MANUFACTURER_DATA = "manufacturer_data" | ||||||
|  |  | ||||||
| esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server") | esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server") | ||||||
| BLEServer = esp32_ble_server_ns.class_( | BLEServer = esp32_ble_server_ns.class_( | ||||||
| @@ -27,6 +28,7 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|         cv.GenerateID(): cv.declare_id(BLEServer), |         cv.GenerateID(): cv.declare_id(BLEServer), | ||||||
|         cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE), |         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, default="ESPHome"): cv.string, | ||||||
|  |         cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.hex_uint8_t]), | ||||||
|         cv.Optional(CONF_MODEL): cv.string, |         cv.Optional(CONF_MODEL): cv.string, | ||||||
|     } |     } | ||||||
| ).extend(cv.COMPONENT_SCHEMA) | ).extend(cv.COMPONENT_SCHEMA) | ||||||
| @@ -42,6 +44,8 @@ async def to_code(config): | |||||||
|     cg.add(var.set_parent(parent)) |     cg.add(var.set_parent(parent)) | ||||||
|  |  | ||||||
|     cg.add(var.set_manufacturer(config[CONF_MANUFACTURER])) |     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: |     if CONF_MODEL in config: | ||||||
|         cg.add(var.set_model(config[CONF_MODEL])) |         cg.add(var.set_model(config[CONF_MODEL])) | ||||||
|     cg.add_define("USE_ESP32_BLE_SERVER") |     cg.add_define("USE_ESP32_BLE_SERVER") | ||||||
|   | |||||||
| @@ -68,6 +68,7 @@ void BLEServer::loop() { | |||||||
|       if (this->device_information_service_->is_running()) { |       if (this->device_information_service_->is_running()) { | ||||||
|         this->state_ = RUNNING; |         this->state_ = RUNNING; | ||||||
|         this->can_proceed_ = true; |         this->can_proceed_ = true; | ||||||
|  |         this->restart_advertising_(); | ||||||
|         ESP_LOGD(TAG, "BLE server setup successfully"); |         ESP_LOGD(TAG, "BLE server setup successfully"); | ||||||
|       } else if (!this->device_information_service_->is_starting()) { |       } else if (!this->device_information_service_->is_starting()) { | ||||||
|         this->device_information_service_->start(); |         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_() { | bool BLEServer::create_device_characteristics_() { | ||||||
|   if (this->model_.has_value()) { |   if (this->model_.has_value()) { | ||||||
|     BLECharacteristic *model = |     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_manufacturer(const std::string &manufacturer) { this->manufacturer_ = manufacturer; } | ||||||
|   void set_model(const std::string &model) { this->model_ = model; } |   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(const uint8_t *uuid, bool advertise = false); | ||||||
|   std::shared_ptr<BLEService> create_service(uint16_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: |  protected: | ||||||
|   bool create_device_characteristics_(); |   bool create_device_characteristics_(); | ||||||
|  |   void restart_advertising_(); | ||||||
|  |  | ||||||
|   void add_client_(uint16_t conn_id, void *client) { |   void add_client_(uint16_t conn_id, void *client) { | ||||||
|     this->clients_.insert(std::pair<uint16_t, void *>(conn_id, 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_; |   std::string manufacturer_; | ||||||
|   optional<std::string> model_; |   optional<std::string> model_; | ||||||
|  |   std::vector<uint8_t> manufacturer_data_; | ||||||
|   esp_gatt_if_t gatts_if_{0}; |   esp_gatt_if_t gatts_if_{0}; | ||||||
|   bool registered_{false}; |   bool registered_{false}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -63,6 +63,7 @@ RMT_CHANNELS = { | |||||||
|     esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], |     esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], | ||||||
|     esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3], |     esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3], | ||||||
|     esp32.const.VARIANT_ESP32C3: [0, 1], |     esp32.const.VARIANT_ESP32C3: [0, 1], | ||||||
|  |     esp32.const.VARIANT_ESP32C6: [0, 1], | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -8,11 +8,7 @@ | |||||||
|  |  | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #if ESP_IDF_VERSION_MAJOR >= 4 |  | ||||||
| #include <driver/touch_sensor.h> | #include <driver/touch_sensor.h> | ||||||
| #else |  | ||||||
| #include <driver/touch_pad.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace esp32_touch { | namespace esp32_touch { | ||||||
|   | |||||||
| @@ -14,7 +14,10 @@ from esphome.const import ( | |||||||
|     CONF_MIN_TEMPERATURE, |     CONF_MIN_TEMPERATURE, | ||||||
|     CONF_PROTOCOL, |     CONF_PROTOCOL, | ||||||
|     CONF_SUPPORTED_MODES, |     CONF_SUPPORTED_MODES, | ||||||
|  |     CONF_SUPPORTED_PRESETS, | ||||||
|     CONF_SUPPORTED_SWING_MODES, |     CONF_SUPPORTED_SWING_MODES, | ||||||
|  |     CONF_TARGET_TEMPERATURE, | ||||||
|  |     CONF_TEMPERATURE_STEP, | ||||||
|     CONF_VISUAL, |     CONF_VISUAL, | ||||||
|     CONF_WIFI, |     CONF_WIFI, | ||||||
|     DEVICE_CLASS_TEMPERATURE, |     DEVICE_CLASS_TEMPERATURE, | ||||||
| @@ -23,25 +26,29 @@ from esphome.const import ( | |||||||
|     UNIT_CELSIUS, |     UNIT_CELSIUS, | ||||||
| ) | ) | ||||||
| from esphome.components.climate import ( | from esphome.components.climate import ( | ||||||
|     ClimateSwingMode, |  | ||||||
|     ClimateMode, |     ClimateMode, | ||||||
|  |     ClimatePreset, | ||||||
|  |     ClimateSwingMode, | ||||||
|  |     CONF_CURRENT_TEMPERATURE, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
|  |  | ||||||
| PROTOCOL_MIN_TEMPERATURE = 16.0 | PROTOCOL_MIN_TEMPERATURE = 16.0 | ||||||
| PROTOCOL_MAX_TEMPERATURE = 30.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"] | CODEOWNERS = ["@paveldn"] | ||||||
| AUTO_LOAD = ["sensor"] | AUTO_LOAD = ["sensor"] | ||||||
| DEPENDENCIES = ["climate", "uart"] | DEPENDENCIES = ["climate", "uart"] | ||||||
| CONF_WIFI_SIGNAL = "wifi_signal" | CONF_WIFI_SIGNAL = "wifi_signal" | ||||||
|  | CONF_ANSWER_TIMEOUT = "answer_timeout" | ||||||
|  | CONF_DISPLAY = "display" | ||||||
| CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" | CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" | ||||||
| CONF_VERTICAL_AIRFLOW = "vertical_airflow" | CONF_VERTICAL_AIRFLOW = "vertical_airflow" | ||||||
| CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow" | CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow" | ||||||
|  |  | ||||||
|  |  | ||||||
| PROTOCOL_HON = "HON" | PROTOCOL_HON = "HON" | ||||||
| PROTOCOL_SMARTAIR2 = "SMARTAIR2" | PROTOCOL_SMARTAIR2 = "SMARTAIR2" | ||||||
| PROTOCOLS_SUPPORTED = [PROTOCOL_HON, PROTOCOL_SMARTAIR2] | PROTOCOLS_SUPPORTED = [PROTOCOL_HON, PROTOCOL_SMARTAIR2] | ||||||
| @@ -82,13 +89,24 @@ SUPPORTED_SWING_MODES_OPTIONS = { | |||||||
|  |  | ||||||
| SUPPORTED_CLIMATE_MODES_OPTIONS = { | SUPPORTED_CLIMATE_MODES_OPTIONS = { | ||||||
|     "OFF": ClimateMode.CLIMATE_MODE_OFF,  # always available |     "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, |     "COOL": ClimateMode.CLIMATE_MODE_COOL, | ||||||
|     "HEAT": ClimateMode.CLIMATE_MODE_HEAT, |     "HEAT": ClimateMode.CLIMATE_MODE_HEAT, | ||||||
|     "DRY": ClimateMode.CLIMATE_MODE_DRY, |     "DRY": ClimateMode.CLIMATE_MODE_DRY, | ||||||
|     "FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY, |     "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): | def validate_visual(config): | ||||||
|     if CONF_VISUAL in config: |     if CONF_VISUAL in config: | ||||||
| @@ -109,10 +127,29 @@ def validate_visual(config): | |||||||
|                 ) |                 ) | ||||||
|         else: |         else: | ||||||
|             config[CONF_VISUAL][CONF_MAX_TEMPERATURE] = PROTOCOL_MAX_TEMPERATURE |             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: |     else: | ||||||
|         config[CONF_VISUAL] = { |         config[CONF_VISUAL] = { | ||||||
|             CONF_MIN_TEMPERATURE: PROTOCOL_MIN_TEMPERATURE, |             CONF_MIN_TEMPERATURE: PROTOCOL_MIN_TEMPERATURE, | ||||||
|             CONF_MAX_TEMPERATURE: PROTOCOL_MAX_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 |     return config | ||||||
|  |  | ||||||
| @@ -132,6 +169,11 @@ BASE_CONFIG_SCHEMA = ( | |||||||
|                     "BOTH", |                     "BOTH", | ||||||
|                 ], |                 ], | ||||||
|             ): cv.ensure_list(cv.enum(SUPPORTED_SWING_MODES_OPTIONS, upper=True)), |             ): 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) |     .extend(uart.UART_DEVICE_SCHEMA) | ||||||
| @@ -144,13 +186,26 @@ CONFIG_SCHEMA = cv.All( | |||||||
|             PROTOCOL_SMARTAIR2: BASE_CONFIG_SCHEMA.extend( |             PROTOCOL_SMARTAIR2: BASE_CONFIG_SCHEMA.extend( | ||||||
|                 { |                 { | ||||||
|                     cv.GenerateID(): cv.declare_id(Smartair2Climate), |                     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( |             PROTOCOL_HON: BASE_CONFIG_SCHEMA.extend( | ||||||
|                 { |                 { | ||||||
|                     cv.GenerateID(): cv.declare_id(HonClimate), |                     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_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( |                     cv.Optional(CONF_OUTDOOR_TEMPERATURE): sensor.sensor_schema( | ||||||
|                         unit_of_measurement=UNIT_CELSIUS, |                         unit_of_measurement=UNIT_CELSIUS, | ||||||
|                         icon=ICON_THERMOMETER, |                         icon=ICON_THERMOMETER, | ||||||
| @@ -354,10 +409,11 @@ async def to_code(config): | |||||||
|     await uart.register_uart_device(var, config) |     await uart.register_uart_device(var, config) | ||||||
|     await climate.register_climate(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])) |     cg.add(var.set_send_wifi(config[CONF_WIFI_SIGNAL])) | ||||||
|     if CONF_BEEPER in config: |     if CONF_BEEPER in config: | ||||||
|         cg.add(var.set_beeper_state(config[CONF_BEEPER])) |         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: |     if CONF_OUTDOOR_TEMPERATURE in config: | ||||||
|         sens = await sensor.new_sensor(config[CONF_OUTDOOR_TEMPERATURE]) |         sens = await sensor.new_sensor(config[CONF_OUTDOOR_TEMPERATURE]) | ||||||
|         cg.add(var.set_outdoor_temperature_sensor(sens)) |         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])) |         cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES])) | ||||||
|     if CONF_SUPPORTED_SWING_MODES in config: |     if CONF_SUPPORTED_SWING_MODES in config: | ||||||
|         cg.add(var.set_supported_swing_modes(config[CONF_SUPPORTED_SWING_MODES])) |         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 |     # 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 <string> | ||||||
| #include "esphome/components/climate/climate.h" | #include "esphome/components/climate/climate.h" | ||||||
| #include "esphome/components/uart/uart.h" | #include "esphome/components/uart/uart.h" | ||||||
|  | #ifdef USE_WIFI | ||||||
|  | #include "esphome/components/wifi/wifi_component.h" | ||||||
|  | #endif | ||||||
| #include "haier_base.h" | #include "haier_base.h" | ||||||
|  |  | ||||||
| using namespace esphome::climate; | 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) { | const char *HaierClimateBase::phase_to_string_(ProtocolPhases phase) { | ||||||
|   static const char *phase_names[] = { |   static const char *phase_names[] = { | ||||||
|       "SENDING_INIT_1", |       "SENDING_INIT_1", | ||||||
|       "WAITING_ANSWER_INIT_1", |       "WAITING_INIT_1_ANSWER", | ||||||
|       "SENDING_INIT_2", |       "SENDING_INIT_2", | ||||||
|       "WAITING_ANSWER_INIT_2", |       "WAITING_INIT_2_ANSWER", | ||||||
|       "SENDING_FIRST_STATUS_REQUEST", |       "SENDING_FIRST_STATUS_REQUEST", | ||||||
|       "WAITING_FIRST_STATUS_ANSWER", |       "WAITING_FIRST_STATUS_ANSWER", | ||||||
|       "SENDING_ALARM_STATUS_REQUEST", |       "SENDING_ALARM_STATUS_REQUEST", | ||||||
|       "WAITING_ALARM_STATUS_ANSWER", |       "WAITING_ALARM_STATUS_ANSWER", | ||||||
|       "IDLE", |       "IDLE", | ||||||
|  |       "UNKNOWN", | ||||||
|       "SENDING_STATUS_REQUEST", |       "SENDING_STATUS_REQUEST", | ||||||
|       "WAITING_STATUS_ANSWER", |       "WAITING_STATUS_ANSWER", | ||||||
|       "SENDING_UPDATE_SIGNAL_REQUEST", |       "SENDING_UPDATE_SIGNAL_REQUEST", | ||||||
| @@ -63,11 +67,12 @@ HaierClimateBase::HaierClimateBase() | |||||||
|       forced_publish_(false), |       forced_publish_(false), | ||||||
|       forced_request_status_(false), |       forced_request_status_(false), | ||||||
|       first_control_attempt_(false), |       first_control_attempt_(false), | ||||||
|       reset_protocol_request_(false) { |       reset_protocol_request_(false), | ||||||
|  |       send_wifi_signal_(true) { | ||||||
|   this->traits_ = climate::ClimateTraits(); |   this->traits_ = climate::ClimateTraits(); | ||||||
|   this->traits_.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT, |   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_FAN_ONLY, climate::CLIMATE_MODE_DRY, | ||||||
|                                      climate::CLIMATE_MODE_AUTO}); |                                      climate::CLIMATE_MODE_HEAT_COOL}); | ||||||
|   this->traits_.set_supported_fan_modes( |   this->traits_.set_supported_fan_modes( | ||||||
|       {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH}); |       {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, |   this->traits_.set_supported_swing_modes({climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, | ||||||
| @@ -77,7 +82,7 @@ HaierClimateBase::HaierClimateBase() | |||||||
|  |  | ||||||
| HaierClimateBase::~HaierClimateBase() {} | HaierClimateBase::~HaierClimateBase() {} | ||||||
|  |  | ||||||
| void HaierClimateBase::set_phase_(ProtocolPhases phase) { | void HaierClimateBase::set_phase(ProtocolPhases phase) { | ||||||
|   if (this->protocol_phase_ != phase) { |   if (this->protocol_phase_ != phase) { | ||||||
| #if (HAIER_LOG_LEVEL > 4) | #if (HAIER_LOG_LEVEL > 4) | ||||||
|     ESP_LOGV(TAG, "Phase transition: %s => %s", phase_to_string_(this->protocol_phase_), phase_to_string_(phase)); |     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); |   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); |   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_; } | bool HaierClimateBase::get_display_state() const { return this->display_status_; } | ||||||
|  |  | ||||||
| void HaierClimateBase::set_display_state(bool state) { | 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::send_power_off_command() { this->action_request_ = ActionRequest::TURN_POWER_OFF; } | ||||||
|  |  | ||||||
| void HaierClimateBase::toggle_power() { this->action_request_ = ActionRequest::TOGGLE_POWER; } | void HaierClimateBase::toggle_power() { this->action_request_ = ActionRequest::TOGGLE_POWER; } | ||||||
|  |  | ||||||
| void HaierClimateBase::set_supported_swing_modes(const std::set<climate::ClimateSwingMode> &modes) { | void HaierClimateBase::set_supported_swing_modes(const std::set<climate::ClimateSwingMode> &modes) { | ||||||
|   this->traits_.set_supported_swing_modes(modes); |   this->traits_.set_supported_swing_modes(modes); | ||||||
|   this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_OFF);       // Always available |   if (!modes.empty()) | ||||||
|   this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);  // Always available |     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) { | void HaierClimateBase::set_supported_modes(const std::set<climate::ClimateMode> &modes) { | ||||||
|   this->traits_.set_supported_modes(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_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, | haier_protocol::HandlerError HaierClimateBase::answer_preprocess_(uint8_t request_message_type, | ||||||
|                                                                   uint8_t expected_request_message_type, |                                                                   uint8_t expected_request_message_type, | ||||||
|                                                                   uint8_t answer_message_type, |                                                                   uint8_t answer_message_type, | ||||||
| @@ -155,9 +190,9 @@ haier_protocol::HandlerError HaierClimateBase::answer_preprocess_(uint8_t reques | |||||||
|                                                                   ProtocolPhases expected_phase) { |                                                                   ProtocolPhases expected_phase) { | ||||||
|   haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK; |   haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK; | ||||||
|   if ((expected_request_message_type != NO_COMMAND) && (request_message_type != expected_request_message_type)) |   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)) |   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_)) |   if ((expected_phase != ProtocolPhases::UNKNOWN) && (expected_phase != this->protocol_phase_)) | ||||||
|     result = haier_protocol::HandlerError::UNEXPECTED_MESSAGE; |     result = haier_protocol::HandlerError::UNEXPECTED_MESSAGE; | ||||||
|   if (is_message_invalid(answer_message_type)) |   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_); |   ESP_LOGW(TAG, "Answer timeout for command %02X, phase %d", request_type, (int) this->protocol_phase_); | ||||||
| #endif | #endif | ||||||
|   if (this->protocol_phase_ > ProtocolPhases::IDLE) { |   if (this->protocol_phase_ > ProtocolPhases::IDLE) { | ||||||
|     this->set_phase_(ProtocolPhases::IDLE); |     this->set_phase(ProtocolPhases::IDLE); | ||||||
|   } else { |   } else { | ||||||
|     this->set_phase_(ProtocolPhases::SENDING_INIT_1); |     this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||||
|   } |   } | ||||||
|   return haier_protocol::HandlerError::HANDLER_OK; |   return haier_protocol::HandlerError::HANDLER_OK; | ||||||
| } | } | ||||||
| @@ -183,8 +218,8 @@ void HaierClimateBase::setup() { | |||||||
|   ESP_LOGI(TAG, "Haier initialization..."); |   ESP_LOGI(TAG, "Haier initialization..."); | ||||||
|   // Set timestamp here to give AC time to boot |   // Set timestamp here to give AC time to boot | ||||||
|   this->last_request_timestamp_ = std::chrono::steady_clock::now(); |   this->last_request_timestamp_ = std::chrono::steady_clock::now(); | ||||||
|   this->set_phase_(ProtocolPhases::SENDING_INIT_1); |   this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||||
|   this->set_answers_handlers(); |   this->set_handlers(); | ||||||
|   this->haier_protocol_.set_default_timeout_handler( |   this->haier_protocol_.set_default_timeout_handler( | ||||||
|       std::bind(&esphome::haier::HaierClimateBase::timeout_default_handler_, this, std::placeholders::_1)); |       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); |       this->set_force_send_control_(false); | ||||||
|       if (this->hvac_settings_.valid) |       if (this->hvac_settings_.valid) | ||||||
|         this->hvac_settings_.reset(); |         this->hvac_settings_.reset(); | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); |       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||||
|       return; |       return; | ||||||
|     } else { |     } else { | ||||||
|       // No need to reset protocol if we didn't pass initialization phase |       // No need to reset protocol if we didn't pass initialization phase | ||||||
| @@ -229,7 +264,7 @@ void HaierClimateBase::loop() { | |||||||
|       this->process_pending_action(); |       this->process_pending_action(); | ||||||
|     } else if (this->hvac_settings_.valid || this->force_send_control_) { |     } else if (this->hvac_settings_.valid || this->force_send_control_) { | ||||||
|       ESP_LOGV(TAG, "Control packet is pending..."); |       ESP_LOGV(TAG, "Control packet is pending..."); | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_CONTROL); |       this->set_phase(ProtocolPhases::SENDING_CONTROL); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   this->process_phase(now); |   this->process_phase(now); | ||||||
| @@ -243,10 +278,10 @@ void HaierClimateBase::process_pending_action() { | |||||||
|   } |   } | ||||||
|   switch (request) { |   switch (request) { | ||||||
|     case ActionRequest::TURN_POWER_ON: |     case ActionRequest::TURN_POWER_ON: | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_POWER_ON_COMMAND); |       this->set_phase(ProtocolPhases::SENDING_POWER_ON_COMMAND); | ||||||
|       break; |       break; | ||||||
|     case ActionRequest::TURN_POWER_OFF: |     case ActionRequest::TURN_POWER_OFF: | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_POWER_OFF_COMMAND); |       this->set_phase(ProtocolPhases::SENDING_POWER_OFF_COMMAND); | ||||||
|       break; |       break; | ||||||
|     case ActionRequest::TOGGLE_POWER: |     case ActionRequest::TOGGLE_POWER: | ||||||
|     case ActionRequest::NO_ACTION: |     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) { | 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->haier_protocol_.send_message(command, use_crc); | ||||||
|  |   } | ||||||
|   this->last_request_timestamp_ = std::chrono::steady_clock::now(); |   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 reset_protocol() { this->reset_protocol_request_ = true; }; | ||||||
|   void set_supported_modes(const std::set<esphome::climate::ClimateMode> &modes); |   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_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 available() noexcept override { return esphome::uart::UARTDevice::available(); }; | ||||||
|   size_t read_array(uint8_t *data, size_t len) noexcept override { |   size_t read_array(uint8_t *data, size_t len) noexcept override { | ||||||
|     return esphome::uart::UARTDevice::read_array(data, len) ? len : 0; |     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); |     esphome::uart::UARTDevice::write_array(data, len); | ||||||
|   }; |   }; | ||||||
|   bool can_send_message() const { return haier_protocol_.get_outgoing_queue_size() == 0; }; |   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: |  protected: | ||||||
|   enum class ProtocolPhases { |   enum class ProtocolPhases { | ||||||
|     UNKNOWN = -1, |     UNKNOWN = -1, | ||||||
|     // INITIALIZATION |     // INITIALIZATION | ||||||
|     SENDING_INIT_1 = 0, |     SENDING_INIT_1 = 0, | ||||||
|     WAITING_ANSWER_INIT_1 = 1, |     WAITING_INIT_1_ANSWER = 1, | ||||||
|     SENDING_INIT_2 = 2, |     SENDING_INIT_2 = 2, | ||||||
|     WAITING_ANSWER_INIT_2 = 3, |     WAITING_INIT_2_ANSWER = 3, | ||||||
|     SENDING_FIRST_STATUS_REQUEST = 4, |     SENDING_FIRST_STATUS_REQUEST = 4, | ||||||
|     WAITING_FIRST_STATUS_ANSWER = 5, |     WAITING_FIRST_STATUS_ANSWER = 5, | ||||||
|     SENDING_ALARM_STATUS_REQUEST = 6, |     SENDING_ALARM_STATUS_REQUEST = 6, | ||||||
|     WAITING_ALARM_STATUS_ANSWER = 7, |     WAITING_ALARM_STATUS_ANSWER = 7, | ||||||
|     // FUNCTIONAL STATE |     // FUNCTIONAL STATE | ||||||
|     IDLE = 8, |     IDLE = 8, | ||||||
|     SENDING_STATUS_REQUEST = 9, |     SENDING_STATUS_REQUEST = 10, | ||||||
|     WAITING_STATUS_ANSWER = 10, |     WAITING_STATUS_ANSWER = 11, | ||||||
|     SENDING_UPDATE_SIGNAL_REQUEST = 11, |     SENDING_UPDATE_SIGNAL_REQUEST = 12, | ||||||
|     WAITING_UPDATE_SIGNAL_ANSWER = 12, |     WAITING_UPDATE_SIGNAL_ANSWER = 13, | ||||||
|     SENDING_SIGNAL_LEVEL = 13, |     SENDING_SIGNAL_LEVEL = 14, | ||||||
|     WAITING_SIGNAL_LEVEL_ANSWER = 14, |     WAITING_SIGNAL_LEVEL_ANSWER = 15, | ||||||
|     SENDING_CONTROL = 15, |     SENDING_CONTROL = 16, | ||||||
|     WAITING_CONTROL_ANSWER = 16, |     WAITING_CONTROL_ANSWER = 17, | ||||||
|     SENDING_POWER_ON_COMMAND = 17, |     SENDING_POWER_ON_COMMAND = 18, | ||||||
|     WAITING_POWER_ON_ANSWER = 18, |     WAITING_POWER_ON_ANSWER = 19, | ||||||
|     SENDING_POWER_OFF_COMMAND = 19, |     SENDING_POWER_OFF_COMMAND = 20, | ||||||
|     WAITING_POWER_OFF_ANSWER = 20, |     WAITING_POWER_OFF_ANSWER = 21, | ||||||
|     NUM_PROTOCOL_PHASES |     NUM_PROTOCOL_PHASES | ||||||
|   }; |   }; | ||||||
| #if (HAIER_LOG_LEVEL > 4) | #if (HAIER_LOG_LEVEL > 4) | ||||||
|   const char *phase_to_string_(ProtocolPhases phase); |   const char *phase_to_string_(ProtocolPhases phase); | ||||||
| #endif | #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 void process_phase(std::chrono::steady_clock::time_point now) = 0; | ||||||
|   virtual haier_protocol::HaierMessage get_control_message() = 0; |   virtual haier_protocol::HaierMessage get_control_message() = 0; | ||||||
|   virtual bool is_message_invalid(uint8_t message_type) = 0; |   virtual bool is_message_invalid(uint8_t message_type) = 0; | ||||||
| @@ -99,14 +102,17 @@ class HaierClimateBase : public esphome::Component, | |||||||
|   // Helper functions |   // Helper functions | ||||||
|   void set_force_send_control_(bool status); |   void set_force_send_control_(bool status); | ||||||
|   void send_message_(const haier_protocol::HaierMessage &command, bool use_crc); |   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, |   bool check_timeout_(std::chrono::steady_clock::time_point now, std::chrono::steady_clock::time_point tpoint, | ||||||
|                       size_t timeout); |                       size_t timeout); | ||||||
|   bool is_message_interval_exceeded_(std::chrono::steady_clock::time_point now); |   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_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_timeout_exceeded_(std::chrono::steady_clock::time_point now); | ||||||
|   bool is_control_message_interval_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 { |   struct HvacSettings { | ||||||
|     esphome::optional<esphome::climate::ClimateMode> mode; |     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_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 last_status_request_;          // To request AC status | ||||||
|   std::chrono::steady_clock::time_point control_request_timestamp_;    // To send control message |   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 | }  // namespace haier | ||||||
|   | |||||||
| @@ -2,9 +2,6 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include "esphome/components/climate/climate.h" | #include "esphome/components/climate/climate.h" | ||||||
| #include "esphome/components/uart/uart.h" | #include "esphome/components/uart/uart.h" | ||||||
| #ifdef USE_WIFI |  | ||||||
| #include "esphome/components/wifi/wifi_component.h" |  | ||||||
| #endif |  | ||||||
| #include "hon_climate.h" | #include "hon_climate.h" | ||||||
| #include "hon_packet.h" | #include "hon_packet.h" | ||||||
|  |  | ||||||
| @@ -58,14 +55,7 @@ HonClimate::HonClimate() | |||||||
|       hvac_functions_{false, false, false, false, false}, |       hvac_functions_{false, false, false, false, false}, | ||||||
|       use_crc_(hvac_functions_[2]), |       use_crc_(hvac_functions_[2]), | ||||||
|       active_alarms_{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |       active_alarms_{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||||||
|       outdoor_sensor_(nullptr), |       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, |  | ||||||
|   }); |  | ||||||
|   this->fan_mode_speed_ = (uint8_t) hon_protocol::FanMode::FAN_MID; |   this->fan_mode_speed_ = (uint8_t) hon_protocol::FanMode::FAN_MID; | ||||||
|   this->other_modes_fan_speed_ = (uint8_t) hon_protocol::FanMode::FAN_AUTO; |   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, | 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) { |                                                                             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_( |   haier_protocol::HandlerError result = this->answer_preprocess_( | ||||||
|       request_type, (uint8_t) hon_protocol::FrameType::GET_DEVICE_VERSION, message_type, |       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 (result == haier_protocol::HandlerError::HANDLER_OK) { | ||||||
|     if (data_size < sizeof(hon_protocol::DeviceVersionAnswer)) { |     if (data_size < sizeof(hon_protocol::DeviceVersionAnswer)) { | ||||||
|       // Wrong structure |       // Wrong structure | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); |       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||||
|       return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; |       return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; | ||||||
|     } |     } | ||||||
|     // All OK |     // 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_[3] = (answr->functions[1] & 0x08) != 0;  // multiple AC support | ||||||
|     this->hvac_functions_[4] = (answr->functions[1] & 0x20) != 0;  // roles support |     this->hvac_functions_[4] = (answr->functions[1] & 0x20) != 0;  // roles support | ||||||
|     this->hvac_hardware_info_available_ = true; |     this->hvac_hardware_info_available_ = true; | ||||||
|     this->set_phase_(ProtocolPhases::SENDING_INIT_2); |     this->set_phase(ProtocolPhases::SENDING_INIT_2); | ||||||
|     return result; |     return result; | ||||||
|   } else { |   } 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); |                                                                     : ProtocolPhases::SENDING_INIT_1); | ||||||
|     return result; |     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) { |                                                                        const uint8_t *data, size_t data_size) { | ||||||
|   haier_protocol::HandlerError result = this->answer_preprocess_( |   haier_protocol::HandlerError result = this->answer_preprocess_( | ||||||
|       request_type, (uint8_t) hon_protocol::FrameType::GET_DEVICE_ID, message_type, |       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) { |   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; |     return result; | ||||||
|   } else { |   } 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); |                                                                     : ProtocolPhases::SENDING_INIT_1); | ||||||
|     return result; |     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); |     result = this->process_status_message_(data, data_size); | ||||||
|     if (result != haier_protocol::HandlerError::HANDLER_OK) { |     if (result != haier_protocol::HandlerError::HANDLER_OK) { | ||||||
|       ESP_LOGW(TAG, "Error %d while parsing Status packet", (int) result); |       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); |                                                                       : ProtocolPhases::SENDING_INIT_1); | ||||||
|     } else { |     } else { | ||||||
|       if (data_size >= sizeof(hon_protocol::HaierPacketControl) + 2) { |       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) { |       if (this->protocol_phase_ == ProtocolPhases::WAITING_FIRST_STATUS_ANSWER) { | ||||||
|         ESP_LOGI(TAG, "First HVAC status received"); |         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) || |       } else if ((this->protocol_phase_ == ProtocolPhases::WAITING_STATUS_ANSWER) || | ||||||
|                  (this->protocol_phase_ == ProtocolPhases::WAITING_POWER_ON_ANSWER) || |                  (this->protocol_phase_ == ProtocolPhases::WAITING_POWER_ON_ANSWER) || | ||||||
|                  (this->protocol_phase_ == ProtocolPhases::WAITING_POWER_OFF_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) { |       } 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); |         this->set_force_send_control_(false); | ||||||
|         if (this->hvac_settings_.valid) |         if (this->hvac_settings_.valid) | ||||||
|           this->hvac_settings_.reset(); |           this->hvac_settings_.reset(); | ||||||
| @@ -210,7 +205,7 @@ haier_protocol::HandlerError HonClimate::status_handler_(uint8_t request_type, u | |||||||
|     } |     } | ||||||
|     return result; |     return result; | ||||||
|   } else { |   } 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); |                                                                     : ProtocolPhases::SENDING_INIT_1); | ||||||
|     return result; |     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, |                                message_type, (uint8_t) hon_protocol::FrameType::GET_MANAGEMENT_INFORMATION_RESPONSE, | ||||||
|                                ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER); |                                ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER); | ||||||
|   if (result == haier_protocol::HandlerError::HANDLER_OK) { |   if (result == haier_protocol::HandlerError::HANDLER_OK) { | ||||||
|     this->set_phase_(ProtocolPhases::SENDING_SIGNAL_LEVEL); |     this->set_phase(ProtocolPhases::SENDING_SIGNAL_LEVEL); | ||||||
|     return result; |     return result; | ||||||
|   } else { |   } else { | ||||||
|     this->set_phase_(ProtocolPhases::IDLE); |     this->set_phase(ProtocolPhases::IDLE); | ||||||
|     return result; |     return result; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -239,7 +234,7 @@ haier_protocol::HandlerError HonClimate::report_network_status_answer_handler_(u | |||||||
|   haier_protocol::HandlerError result = |   haier_protocol::HandlerError result = | ||||||
|       this->answer_preprocess_(request_type, (uint8_t) hon_protocol::FrameType::REPORT_NETWORK_STATUS, message_type, |       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); |                                (uint8_t) hon_protocol::FrameType::CONFIRM, ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER); | ||||||
|   this->set_phase_(ProtocolPhases::IDLE); |   this->set_phase(ProtocolPhases::IDLE); | ||||||
|   return result; |   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 (request_type == (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS) { | ||||||
|     if (message_type != (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS_RESPONSE) { |     if (message_type != (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS_RESPONSE) { | ||||||
|       // Unexpected answer to request |       // Unexpected answer to request | ||||||
|       this->set_phase_(ProtocolPhases::IDLE); |       this->set_phase(ProtocolPhases::IDLE); | ||||||
|       return haier_protocol::HandlerError::UNSUPORTED_MESSAGE; |       return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||||
|     } |     } | ||||||
|     if (this->protocol_phase_ != ProtocolPhases::WAITING_ALARM_STATUS_ANSWER) { |     if (this->protocol_phase_ != ProtocolPhases::WAITING_ALARM_STATUS_ANSWER) { | ||||||
|       // Don't expect this answer now |       // Don't expect this answer now | ||||||
|       this->set_phase_(ProtocolPhases::IDLE); |       this->set_phase(ProtocolPhases::IDLE); | ||||||
|       return haier_protocol::HandlerError::UNEXPECTED_MESSAGE; |       return haier_protocol::HandlerError::UNEXPECTED_MESSAGE; | ||||||
|     } |     } | ||||||
|     memcpy(this->active_alarms_, data + 2, 8); |     memcpy(this->active_alarms_, data + 2, 8); | ||||||
|     this->set_phase_(ProtocolPhases::IDLE); |     this->set_phase(ProtocolPhases::IDLE); | ||||||
|     return haier_protocol::HandlerError::HANDLER_OK; |     return haier_protocol::HandlerError::HANDLER_OK; | ||||||
|   } else { |   } else { | ||||||
|     this->set_phase_(ProtocolPhases::IDLE); |     this->set_phase(ProtocolPhases::IDLE); | ||||||
|     return haier_protocol::HandlerError::UNSUPORTED_MESSAGE; |     return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void HonClimate::set_answers_handlers() { | void HonClimate::set_handlers() { | ||||||
|   // Set handlers |   // Set handlers | ||||||
|   this->haier_protocol_.set_answer_handler( |   this->haier_protocol_.set_answer_handler( | ||||||
|       (uint8_t) (hon_protocol::FrameType::GET_DEVICE_VERSION), |       (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) { | void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | ||||||
|   switch (this->protocol_phase_) { |   switch (this->protocol_phase_) { | ||||||
|     case ProtocolPhases::SENDING_INIT_1: |     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; |         this->hvac_hardware_info_available_ = false; | ||||||
|         // Indicate device capabilities: |         // Indicate device capabilities: | ||||||
|         // bit 0 - if 1 module support interactive mode |         // 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( |         static const haier_protocol::HaierMessage DEVICE_VERSION_REQUEST( | ||||||
|             (uint8_t) hon_protocol::FrameType::GET_DEVICE_VERSION, module_capabilities, sizeof(module_capabilities)); |             (uint8_t) hon_protocol::FrameType::GET_DEVICE_VERSION, module_capabilities, sizeof(module_capabilities)); | ||||||
|         this->send_message_(DEVICE_VERSION_REQUEST, this->use_crc_); |         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; |       break; | ||||||
|     case ProtocolPhases::SENDING_INIT_2: |     case ProtocolPhases::SENDING_INIT_2: | ||||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { |       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); |         static const haier_protocol::HaierMessage DEVICEID_REQUEST((uint8_t) hon_protocol::FrameType::GET_DEVICE_ID); | ||||||
|         this->send_message_(DEVICEID_REQUEST, this->use_crc_); |         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; |       break; | ||||||
|     case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: |     case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: | ||||||
|     case ProtocolPhases::SENDING_STATUS_REQUEST: |     case ProtocolPhases::SENDING_STATUS_REQUEST: | ||||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { |       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||||
|         static const haier_protocol::HaierMessage STATUS_REQUEST( |         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->send_message_(STATUS_REQUEST, this->use_crc_); | ||||||
|         this->last_status_request_ = now; |         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; |       break; | ||||||
| #ifdef USE_WIFI | #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); |             (uint8_t) hon_protocol::FrameType::GET_MANAGEMENT_INFORMATION); | ||||||
|         this->send_message_(UPDATE_SIGNAL_REQUEST, this->use_crc_); |         this->send_message_(UPDATE_SIGNAL_REQUEST, this->use_crc_); | ||||||
|         this->last_signal_request_ = now; |         this->last_signal_request_ = now; | ||||||
|         this->set_phase_(ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER); |         this->set_phase(ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::SENDING_SIGNAL_LEVEL: |     case ProtocolPhases::SENDING_SIGNAL_LEVEL: | ||||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { |       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||||
|         static uint8_t wifi_status_data[4] = {0x00, 0x00, 0x00, 0x00}; |         this->send_message_(this->get_wifi_signal_message_((uint8_t) hon_protocol::FrameType::REPORT_NETWORK_STATUS), | ||||||
|         if (wifi::global_wifi_component->is_connected()) { |                             this->use_crc_); | ||||||
|           wifi_status_data[1] = 0; |         this->set_phase(ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER); | ||||||
|           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); |  | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: |     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::SENDING_SIGNAL_LEVEL: | ||||||
|     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: |     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER: |     case ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER: | ||||||
|       this->set_phase_(ProtocolPhases::IDLE); |       this->set_phase(ProtocolPhases::IDLE); | ||||||
|       break; |       break; | ||||||
| #endif | #endif | ||||||
|     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: |     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( |         static const haier_protocol::HaierMessage ALARM_STATUS_REQUEST( | ||||||
|             (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS); |             (uint8_t) hon_protocol::FrameType::GET_ALARM_STATUS); | ||||||
|         this->send_message_(ALARM_STATUS_REQUEST, this->use_crc_); |         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; |       break; | ||||||
|     case ProtocolPhases::SENDING_CONTROL: |     case ProtocolPhases::SENDING_CONTROL: | ||||||
| @@ -403,12 +386,12 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | |||||||
|           this->hvac_settings_.reset(); |           this->hvac_settings_.reset(); | ||||||
|         this->forced_request_status_ = true; |         this->forced_request_status_ = true; | ||||||
|         this->forced_publish_ = 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)) { |       } else if (this->can_send_message() && this->is_control_message_interval_exceeded_(now)) { | ||||||
|         haier_protocol::HaierMessage control_message = get_control_message(); |         haier_protocol::HaierMessage control_message = get_control_message(); | ||||||
|         this->send_message_(control_message, this->use_crc_); |         this->send_message_(control_message, this->use_crc_); | ||||||
|         ESP_LOGI(TAG, "Control packet sent"); |         ESP_LOGI(TAG, "Control packet sent"); | ||||||
|         this->set_phase_(ProtocolPhases::WAITING_CONTROL_ANSWER); |         this->set_phase(ProtocolPhases::WAITING_CONTROL_ANSWER); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::SENDING_POWER_ON_COMMAND: |     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) |         if (this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND) | ||||||
|           pwr_cmd_buf[1] = 0x01; |           pwr_cmd_buf[1] = 0x01; | ||||||
|         haier_protocol::HaierMessage power_cmd((uint8_t) hon_protocol::FrameType::CONTROL, |         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)); |                                                pwr_cmd_buf, sizeof(pwr_cmd_buf)); | ||||||
|         this->send_message_(power_cmd, this->use_crc_); |         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_ON_ANSWER | ||||||
|                             : ProtocolPhases::WAITING_POWER_OFF_ANSWER); |                             : ProtocolPhases::WAITING_POWER_OFF_ANSWER); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|  |  | ||||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_1: |     case ProtocolPhases::WAITING_INIT_1_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_2: |     case ProtocolPhases::WAITING_INIT_2_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_FIRST_STATUS_ANSWER: |     case ProtocolPhases::WAITING_FIRST_STATUS_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_ALARM_STATUS_ANSWER: |     case ProtocolPhases::WAITING_ALARM_STATUS_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_STATUS_ANSWER: |     case ProtocolPhases::WAITING_STATUS_ANSWER: | ||||||
| @@ -438,14 +421,14 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | |||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::IDLE: { |     case ProtocolPhases::IDLE: { | ||||||
|       if (this->forced_request_status_ || this->is_status_request_interval_exceeded_(now)) { |       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; |         this->forced_request_status_ = false; | ||||||
|       } |       } | ||||||
| #ifdef USE_WIFI | #ifdef USE_WIFI | ||||||
|       else if (this->send_wifi_signal_ && |       else if (this->send_wifi_signal_ && | ||||||
|                (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_signal_request_).count() > |                (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_signal_request_).count() > | ||||||
|                 SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) |                 SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) | ||||||
|         this->set_phase_(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); |         this->set_phase(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); | ||||||
| #endif | #endif | ||||||
|     } break; |     } break; | ||||||
|     default: |     default: | ||||||
| @@ -456,7 +439,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | |||||||
| #else | #else | ||||||
|       ESP_LOGE(TAG, "Wrong protocol handler state: %d, resetting communication", (int) this->protocol_phase_); |       ESP_LOGE(TAG, "Wrong protocol handler state: %d, resetting communication", (int) this->protocol_phase_); | ||||||
| #endif | #endif | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); |       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -475,7 +458,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | |||||||
|         case CLIMATE_MODE_OFF: |         case CLIMATE_MODE_OFF: | ||||||
|           out_data->ac_power = 0; |           out_data->ac_power = 0; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_MODE_AUTO: |         case CLIMATE_MODE_HEAT_COOL: | ||||||
|           out_data->ac_power = 1; |           out_data->ac_power = 1; | ||||||
|           out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::AUTO; |           out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::AUTO; | ||||||
|           out_data->fan_mode = this->other_modes_fan_speed_; |           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()) { |     if (climate_control.target_temperature.has_value()) { | ||||||
|       out_data->set_point = |       float target_temp = climate_control.target_temperature.value(); | ||||||
|           climate_control.target_temperature.value() - 16;  // set the temperature at our offset, subtract 16. |       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 (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->quiet_mode = 0; | ||||||
|       out_data->fast_mode = 0; |       out_data->fast_mode = 0; | ||||||
|       out_data->sleep_mode = 0; |       out_data->sleep_mode = 0; | ||||||
| @@ -631,7 +615,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | |||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|   return haier_protocol::HaierMessage((uint8_t) hon_protocol::FrameType::CONTROL, |   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)); |                                       control_out_buffer, sizeof(hon_protocol::HaierPacketControl)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -669,7 +653,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | |||||||
|   { |   { | ||||||
|     // Target temperature |     // Target temperature | ||||||
|     float old_target_temperature = this->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); |     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_) { |     if (new_cleaning != this->cleaning_status_) { | ||||||
|       ESP_LOGD(TAG, "Cleaning status change: %d => %d", (uint8_t) this->cleaning_status_, (uint8_t) new_cleaning); |       ESP_LOGD(TAG, "Cleaning status change: %d => %d", (uint8_t) this->cleaning_status_, (uint8_t) new_cleaning); | ||||||
|       if (new_cleaning == CleaningState::NO_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->action_request_ = ActionRequest::TURN_POWER_OFF; | ||||||
|       } |       } | ||||||
|       this->cleaning_status_ = new_cleaning; |       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; |           this->mode = CLIMATE_MODE_FAN_ONLY; | ||||||
|           break; |           break; | ||||||
|         case (uint8_t) hon_protocol::ConditioningMode::AUTO: |         case (uint8_t) hon_protocol::ConditioningMode::AUTO: | ||||||
|           this->mode = CLIMATE_MODE_AUTO; |           this->mode = CLIMATE_MODE_HEAT_COOL; | ||||||
|           break; |           break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -837,7 +821,7 @@ void HonClimate::process_pending_action() { | |||||||
|     case ActionRequest::START_SELF_CLEAN: |     case ActionRequest::START_SELF_CLEAN: | ||||||
|     case ActionRequest::START_STERI_CLEAN: |     case ActionRequest::START_STERI_CLEAN: | ||||||
|       // Will reset action with control message sending |       // Will reset action with control message sending | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_CONTROL); |       this->set_phase(ProtocolPhases::SENDING_CONTROL); | ||||||
|       break; |       break; | ||||||
|     default: |     default: | ||||||
|       HaierClimateBase::process_pending_action(); |       HaierClimateBase::process_pending_action(); | ||||||
|   | |||||||
| @@ -48,10 +48,9 @@ class HonClimate : public HaierClimateBase { | |||||||
|   CleaningState get_cleaning_status() const; |   CleaningState get_cleaning_status() const; | ||||||
|   void start_self_cleaning(); |   void start_self_cleaning(); | ||||||
|   void start_steri_cleaning(); |   void start_steri_cleaning(); | ||||||
|   void set_send_wifi(bool send_wifi); |  | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void set_answers_handlers() override; |   void set_handlers() override; | ||||||
|   void process_phase(std::chrono::steady_clock::time_point now) override; |   void process_phase(std::chrono::steady_clock::time_point now) override; | ||||||
|   haier_protocol::HaierMessage get_control_message() override; |   haier_protocol::HaierMessage get_control_message() override; | ||||||
|   bool is_message_invalid(uint8_t message_type) override; |   bool is_message_invalid(uint8_t message_type) override; | ||||||
| @@ -87,8 +86,6 @@ class HonClimate : public HaierClimateBase { | |||||||
|   bool &use_crc_; |   bool &use_crc_; | ||||||
|   uint8_t active_alarms_[8]; |   uint8_t active_alarms_[8]; | ||||||
|   esphome::sensor::Sensor *outdoor_sensor_; |   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 | }  // namespace haier | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ struct HaierPacketControl { | |||||||
|   uint8_t ten_degree : 1;           // 10 degree status |   uint8_t ten_degree : 1;           // 10 degree status | ||||||
|   uint8_t display_status : 1;       // If 0 disables AC's display |   uint8_t display_status : 1;       // If 0 disables AC's display | ||||||
|   uint8_t half_degree : 1;          // Use half degree |   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 pmv_status : 1;           // Comfort/PMV status | ||||||
|   uint8_t use_fahrenheit : 1;       // Use Fahrenheit instead of Celsius |   uint8_t use_fahrenheit : 1;       // Use Fahrenheit instead of Celsius | ||||||
|   uint8_t : 1; |   uint8_t : 1; | ||||||
| @@ -153,7 +153,7 @@ enum class FrameType : uint8_t { | |||||||
|                    // <-> device, required) |                    // <-> device, required) | ||||||
|   REPORT = 0x06,   // Report frame (module <-> device, interactive, required) |   REPORT = 0x06,   // Report frame (module <-> device, interactive, required) | ||||||
|   STOP_FAULT_ALARM = 0x09,             // Stop fault alarm 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) |   DEVICE_UPLINK = 0x12,                // Device uplink frame (module <- device , interactive, optional) | ||||||
|   SYSTEM_QUERY = 0x13,                 // System query frame (module -> device, optional) |   SYSTEM_QUERY = 0x13,                 // System query frame (module -> device, optional) | ||||||
|   SYSTEM_QUERY_RESPONSE = 0x14,        // System query response 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) |   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_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_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) |   GET_BIG_DATA = 0x4DFE,    // Request big data information from device (packet content: None) | ||||||
|   | |||||||
| @@ -11,15 +11,10 @@ namespace esphome { | |||||||
| namespace haier { | namespace haier { | ||||||
|  |  | ||||||
| static const char *const TAG = "haier.climate"; | static const char *const TAG = "haier.climate"; | ||||||
|  | constexpr size_t SIGNAL_LEVEL_UPDATE_INTERVAL_MS = 10000; | ||||||
|  |  | ||||||
| Smartair2Climate::Smartair2Climate() | Smartair2Climate::Smartair2Climate() | ||||||
|     : last_status_message_(new uint8_t[sizeof(smartair2_protocol::HaierPacketControl)]) { |     : last_status_message_(new uint8_t[sizeof(smartair2_protocol::HaierPacketControl)]), timeouts_counter_(0) {} | ||||||
|   this->traits_.set_supported_presets({ |  | ||||||
|       climate::CLIMATE_PRESET_NONE, |  | ||||||
|       climate::CLIMATE_PRESET_BOOST, |  | ||||||
|       climate::CLIMATE_PRESET_COMFORT, |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| haier_protocol::HandlerError Smartair2Climate::status_handler_(uint8_t request_type, uint8_t message_type, | haier_protocol::HandlerError Smartair2Climate::status_handler_(uint8_t request_type, uint8_t message_type, | ||||||
|                                                                const uint8_t *data, size_t data_size) { |                                                                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); |     result = this->process_status_message_(data, data_size); | ||||||
|     if (result != haier_protocol::HandlerError::HANDLER_OK) { |     if (result != haier_protocol::HandlerError::HANDLER_OK) { | ||||||
|       ESP_LOGW(TAG, "Error %d while parsing Status packet", (int) result); |       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); |                                                                       : ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||||
|     } else { |     } else { | ||||||
|       if (data_size >= sizeof(smartair2_protocol::HaierPacketControl) + 2) { |       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) { |       if (this->protocol_phase_ == ProtocolPhases::WAITING_FIRST_STATUS_ANSWER) { | ||||||
|         ESP_LOGI(TAG, "First HVAC status received"); |         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) { |       } 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) { |       } 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); |         this->set_force_send_control_(false); | ||||||
|         if (this->hvac_settings_.valid) |         if (this->hvac_settings_.valid) | ||||||
|           this->hvac_settings_.reset(); |           this->hvac_settings_.reset(); | ||||||
| @@ -53,17 +48,82 @@ haier_protocol::HandlerError Smartair2Climate::status_handler_(uint8_t request_t | |||||||
|     } |     } | ||||||
|     return result; |     return result; | ||||||
|   } else { |   } 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); |                                                                     : ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); | ||||||
|     return result; |     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( |   this->haier_protocol_.set_answer_handler( | ||||||
|       (uint8_t) (smartair2_protocol::FrameType::CONTROL), |       (uint8_t) (smartair2_protocol::FrameType::CONTROL), | ||||||
|       std::bind(&Smartair2Climate::status_handler_, this, std::placeholders::_1, std::placeholders::_2, |       std::bind(&Smartair2Climate::status_handler_, this, std::placeholders::_1, std::placeholders::_2, | ||||||
|                 std::placeholders::_3, std::placeholders::_4)); |                 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() { | void Smartair2Climate::dump_config() { | ||||||
| @@ -74,39 +134,62 @@ void Smartair2Climate::dump_config() { | |||||||
| void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) { | void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) { | ||||||
|   switch (this->protocol_phase_) { |   switch (this->protocol_phase_) { | ||||||
|     case ProtocolPhases::SENDING_INIT_1: |     case ProtocolPhases::SENDING_INIT_1: | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_FIRST_STATUS_REQUEST); |       if (this->can_send_message() && | ||||||
|       break; |           (((this->timeouts_counter_ == 0) && (this->is_protocol_initialisation_interval_exceeded_(now))) || | ||||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_1: |            ((this->timeouts_counter_ > 0) && (this->is_message_interval_exceeded_(now))))) { | ||||||
|     case ProtocolPhases::SENDING_INIT_2: |         // Indicate device capabilities: | ||||||
|     case ProtocolPhases::WAITING_ANSWER_INIT_2: |         // bit 0 - if 1 module support interactive mode | ||||||
|     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: |         // bit 1 - if 1 module support controller-device mode | ||||||
|     case ProtocolPhases::WAITING_ALARM_STATUS_ANSWER: |         // bit 2 - if 1 module support crc | ||||||
|       this->set_phase_(ProtocolPhases::SENDING_INIT_1); |         // bit 3 - if 1 module support multiple devices | ||||||
|       break; |         // bit 4..bit 15 - not used | ||||||
|     case ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST: |         uint8_t module_capabilities[2] = {0b00000000, 0b00000111}; | ||||||
|     case ProtocolPhases::WAITING_UPDATE_SIGNAL_ANSWER: |         static const haier_protocol::HaierMessage DEVICE_VERSION_REQUEST( | ||||||
|     case ProtocolPhases::SENDING_SIGNAL_LEVEL: |             (uint8_t) smartair2_protocol::FrameType::GET_DEVICE_VERSION, module_capabilities, | ||||||
|     case ProtocolPhases::WAITING_SIGNAL_LEVEL_ANSWER: |             sizeof(module_capabilities)); | ||||||
|       this->set_phase_(ProtocolPhases::IDLE); |         this->send_message_(DEVICE_VERSION_REQUEST, false); | ||||||
|       break; |         this->set_phase(ProtocolPhases::WAITING_INIT_1_ANSWER); | ||||||
|     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); |  | ||||||
|       } |       } | ||||||
|       break; |       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: |     case ProtocolPhases::SENDING_STATUS_REQUEST: | ||||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { |       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||||
|         static const haier_protocol::HaierMessage STATUS_REQUEST((uint8_t) smartair2_protocol::FrameType::CONTROL, |         static const haier_protocol::HaierMessage STATUS_REQUEST((uint8_t) smartair2_protocol::FrameType::CONTROL, | ||||||
|                                                                  0x4D01); |                                                                  0x4D01); | ||||||
|         this->send_message_(STATUS_REQUEST, false); |         this->send_message_(STATUS_REQUEST, false); | ||||||
|         this->last_status_request_ = now; |         this->last_status_request_ = now; | ||||||
|         this->set_phase_(ProtocolPhases::WAITING_STATUS_ANSWER); |         this->set_phase((ProtocolPhases) ((uint8_t) this->protocol_phase_ + 1)); | ||||||
|       } |       } | ||||||
|       break; |       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: |     case ProtocolPhases::SENDING_CONTROL: | ||||||
|       if (this->first_control_attempt_) { |       if (this->first_control_attempt_) { | ||||||
|         this->control_request_timestamp_ = now; |         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->hvac_settings_.reset(); | ||||||
|         this->forced_request_status_ = true; |         this->forced_request_status_ = true; | ||||||
|         this->forced_publish_ = 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_( |       } else if (this->can_send_message() && this->is_control_message_interval_exceeded_( | ||||||
|                                                  now))  // Using CONTROL_MESSAGES_INTERVAL_MS to speedup requests |                                                  now))  // Using CONTROL_MESSAGES_INTERVAL_MS to speedup requests | ||||||
|       { |       { | ||||||
|         haier_protocol::HaierMessage control_message = get_control_message(); |         haier_protocol::HaierMessage control_message = get_control_message(); | ||||||
|         this->send_message_(control_message, false); |         this->send_message_(control_message, false); | ||||||
|         ESP_LOGI(TAG, "Control packet sent"); |         ESP_LOGI(TAG, "Control packet sent"); | ||||||
|         this->set_phase_(ProtocolPhases::WAITING_CONTROL_ANSWER); |         this->set_phase(ProtocolPhases::WAITING_CONTROL_ANSWER); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::SENDING_POWER_ON_COMMAND: |     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, |             (uint8_t) smartair2_protocol::FrameType::CONTROL, | ||||||
|             this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND ? 0x4D02 : 0x4D03); |             this->protocol_phase_ == ProtocolPhases::SENDING_POWER_ON_COMMAND ? 0x4D02 : 0x4D03); | ||||||
|         this->send_message_(power_cmd, false); |         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_ON_ANSWER | ||||||
|                             : ProtocolPhases::WAITING_POWER_OFF_ANSWER); |                             : ProtocolPhases::WAITING_POWER_OFF_ANSWER); | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|  |     case ProtocolPhases::WAITING_INIT_1_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_FIRST_STATUS_ANSWER: |     case ProtocolPhases::WAITING_FIRST_STATUS_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_STATUS_ANSWER: |     case ProtocolPhases::WAITING_STATUS_ANSWER: | ||||||
|     case ProtocolPhases::WAITING_CONTROL_ANSWER: |     case ProtocolPhases::WAITING_CONTROL_ANSWER: | ||||||
| @@ -149,14 +233,25 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) | |||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::IDLE: { |     case ProtocolPhases::IDLE: { | ||||||
|       if (this->forced_request_status_ || this->is_status_request_interval_exceeded_(now)) { |       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; |         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; |     } break; | ||||||
|     default: |     default: | ||||||
|       // Shouldn't get here |       // 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_); |       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; |       break; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -175,7 +270,7 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() { | |||||||
|           out_data->ac_power = 0; |           out_data->ac_power = 0; | ||||||
|           break; |           break; | ||||||
|  |  | ||||||
|         case CLIMATE_MODE_AUTO: |         case CLIMATE_MODE_HEAT_COOL: | ||||||
|           out_data->ac_power = 1; |           out_data->ac_power = 1; | ||||||
|           out_data->ac_mode = (uint8_t) smartair2_protocol::ConditioningMode::AUTO; |           out_data->ac_mode = (uint8_t) smartair2_protocol::ConditioningMode::AUTO; | ||||||
|           out_data->fan_mode = this->other_modes_fan_speed_; |           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()) { |     if (climate_control.target_temperature.has_value()) { | ||||||
|       out_data->set_point = |       float target_temp = climate_control.target_temperature.value(); | ||||||
|           climate_control.target_temperature.value() - 16;  // set the temperature at our offset, subtract 16. |       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 (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->turbo_mode = 0; | ||||||
|       out_data->quiet_mode = 0; |       out_data->quiet_mode = 0; | ||||||
|     } else if (climate_control.preset.has_value()) { |     } else if (climate_control.preset.has_value()) { | ||||||
| @@ -312,7 +408,7 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin | |||||||
|   { |   { | ||||||
|     // Target temperature |     // Target temperature | ||||||
|     float old_target_temperature = this->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); |     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) { |     switch (packet.control.fan_mode) { | ||||||
|       case (uint8_t) smartair2_protocol::FanMode::FAN_AUTO: |       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 |         // but never accept this value back | ||||||
|         if (packet.control.ac_mode != (uint8_t) smartair2_protocol::ConditioningMode::FAN) { |         if (packet.control.ac_mode != (uint8_t) smartair2_protocol::ConditioningMode::FAN) { | ||||||
|           this->fan_mode = CLIMATE_FAN_AUTO; |           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; |           this->mode = CLIMATE_MODE_FAN_ONLY; | ||||||
|           break; |           break; | ||||||
|         case (uint8_t) smartair2_protocol::ConditioningMode::AUTO: |         case (uint8_t) smartair2_protocol::ConditioningMode::AUTO: | ||||||
|           this->mode = CLIMATE_MODE_AUTO; |           this->mode = CLIMATE_MODE_HEAT_COOL; | ||||||
|           break; |           break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -453,5 +549,15 @@ bool Smartair2Climate::is_message_invalid(uint8_t message_type) { | |||||||
|   return message_type == (uint8_t) smartair2_protocol::FrameType::INVALID; |   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 haier | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -15,16 +15,25 @@ class Smartair2Climate : public HaierClimateBase { | |||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void set_answers_handlers() override; |   void set_handlers() override; | ||||||
|   void process_phase(std::chrono::steady_clock::time_point now) override; |   void process_phase(std::chrono::steady_clock::time_point now) override; | ||||||
|   haier_protocol::HaierMessage get_control_message() override; |   haier_protocol::HaierMessage get_control_message() override; | ||||||
|   bool is_message_invalid(uint8_t message_type) 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, |   haier_protocol::HandlerError status_handler_(uint8_t request_type, uint8_t message_type, const uint8_t *data, | ||||||
|                                                size_t data_size); |                                                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 |   // Helper functions | ||||||
|   haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size); |   haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size); | ||||||
|   std::unique_ptr<uint8_t[]> last_status_message_; |   std::unique_ptr<uint8_t[]> last_status_message_; | ||||||
|  |   unsigned int timeouts_counter_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace haier | }  // namespace haier | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ struct HaierPacketControl { | |||||||
|   uint8_t : 2; |   uint8_t : 2; | ||||||
|   uint8_t health_mode : 1;  // Health mode on or off |   uint8_t health_mode : 1;  // Health mode on or off | ||||||
|   uint8_t compressor : 1;   // Compressor 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 ten_degree : 1;   // 10 degree status (only work in heat mode) | ||||||
|   uint8_t : 0; |   uint8_t : 0; | ||||||
|   // 28 |   // 28 | ||||||
| @@ -88,6 +88,9 @@ enum class FrameType : uint8_t { | |||||||
|   INVALID = 0x03, |   INVALID = 0x03, | ||||||
|   CONFIRM = 0x05, |   CONFIRM = 0x05, | ||||||
|   GET_DEVICE_VERSION = 0x61, |   GET_DEVICE_VERSION = 0x61, | ||||||
|  |   GET_DEVICE_VERSION_RESPONSE = 0x62, | ||||||
|  |   GET_DEVICE_ID = 0x70, | ||||||
|  |   GET_DEVICE_ID_RESPONSE = 0x71, | ||||||
|   REPORT_NETWORK_STATUS = 0xF7, |   REPORT_NETWORK_STATUS = 0xF7, | ||||||
|   NO_COMMAND = 0xFF, |   NO_COMMAND = 0xFF, | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -33,6 +33,7 @@ PROTOCOLS = { | |||||||
|     "greeya": Protocol.PROTOCOL_GREEYAA, |     "greeya": Protocol.PROTOCOL_GREEYAA, | ||||||
|     "greeyan": Protocol.PROTOCOL_GREEYAN, |     "greeyan": Protocol.PROTOCOL_GREEYAN, | ||||||
|     "greeyac": Protocol.PROTOCOL_GREEYAC, |     "greeyac": Protocol.PROTOCOL_GREEYAC, | ||||||
|  |     "greeyt": Protocol.PROTOCOL_GREEYT, | ||||||
|     "hisense_aud": Protocol.PROTOCOL_HISENSE_AUD, |     "hisense_aud": Protocol.PROTOCOL_HISENSE_AUD, | ||||||
|     "hitachi": Protocol.PROTOCOL_HITACHI, |     "hitachi": Protocol.PROTOCOL_HITACHI, | ||||||
|     "hyundai": Protocol.PROTOCOL_HYUNDAI, |     "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_max_temperature(config[CONF_MAX_TEMPERATURE])) | ||||||
|     cg.add(var.set_min_temperature(config[CONF_MIN_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: |     if CORE.is_esp8266 or CORE.is_esp32: | ||||||
|         cg.add_library("crankyoldgit/IRremoteESP8266", "2.7.12") |         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_GREEYAA, []() { return new GreeYAAHeatpumpIR(); }},                            // NOLINT | ||||||
|     {PROTOCOL_GREEYAN, []() { return new GreeYANHeatpumpIR(); }},                            // NOLINT |     {PROTOCOL_GREEYAN, []() { return new GreeYANHeatpumpIR(); }},                            // NOLINT | ||||||
|     {PROTOCOL_GREEYAC, []() { return new GreeYACHeatpumpIR(); }},                            // NOLINT |     {PROTOCOL_GREEYAC, []() { return new GreeYACHeatpumpIR(); }},                            // NOLINT | ||||||
|  |     {PROTOCOL_GREEYT, []() { return new GreeYTHeatpumpIR(); }},                              // NOLINT | ||||||
|     {PROTOCOL_HISENSE_AUD, []() { return new HisenseHeatpumpIR(); }},                        // NOLINT |     {PROTOCOL_HISENSE_AUD, []() { return new HisenseHeatpumpIR(); }},                        // NOLINT | ||||||
|     {PROTOCOL_HITACHI, []() { return new HitachiHeatpumpIR(); }},                            // NOLINT |     {PROTOCOL_HITACHI, []() { return new HitachiHeatpumpIR(); }},                            // NOLINT | ||||||
|     {PROTOCOL_HYUNDAI, []() { return new HyundaiHeatpumpIR(); }},                            // NOLINT |     {PROTOCOL_HYUNDAI, []() { return new HyundaiHeatpumpIR(); }},                            // NOLINT | ||||||
|   | |||||||
| @@ -27,6 +27,7 @@ enum Protocol { | |||||||
|   PROTOCOL_GREEYAA, |   PROTOCOL_GREEYAA, | ||||||
|   PROTOCOL_GREEYAN, |   PROTOCOL_GREEYAN, | ||||||
|   PROTOCOL_GREEYAC, |   PROTOCOL_GREEYAC, | ||||||
|  |   PROTOCOL_GREEYT, | ||||||
|   PROTOCOL_HISENSE_AUD, |   PROTOCOL_HISENSE_AUD, | ||||||
|   PROTOCOL_HITACHI, |   PROTOCOL_HITACHI, | ||||||
|   PROTOCOL_HYUNDAI, |   PROTOCOL_HYUNDAI, | ||||||
|   | |||||||
| @@ -23,13 +23,13 @@ CONFIG_SCHEMA = ( | |||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
|             cv.GenerateID(): cv.declare_id(HTU21DComponent), |             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, |                 unit_of_measurement=UNIT_CELSIUS, | ||||||
|                 accuracy_decimals=1, |                 accuracy_decimals=1, | ||||||
|                 device_class=DEVICE_CLASS_TEMPERATURE, |                 device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|                 state_class=STATE_CLASS_MEASUREMENT, |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|             ), |             ), | ||||||
|             cv.Required(CONF_HUMIDITY): sensor.sensor_schema( |             cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( | ||||||
|                 unit_of_measurement=UNIT_PERCENT, |                 unit_of_measurement=UNIT_PERCENT, | ||||||
|                 accuracy_decimals=1, |                 accuracy_decimals=1, | ||||||
|                 device_class=DEVICE_CLASS_HUMIDITY, |                 device_class=DEVICE_CLASS_HUMIDITY, | ||||||
|   | |||||||
| @@ -202,12 +202,14 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b | |||||||
|       return ERROR_UNKNOWN; |       return ERROR_UNKNOWN; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |   if (stop) { | ||||||
|     err = i2c_master_stop(cmd); |     err = i2c_master_stop(cmd); | ||||||
|     if (err != ESP_OK) { |     if (err != ESP_OK) { | ||||||
|       ESP_LOGVV(TAG, "TX to %02X master stop failed: %s", address, esp_err_to_name(err)); |       ESP_LOGVV(TAG, "TX to %02X master stop failed: %s", address, esp_err_to_name(err)); | ||||||
|       i2c_cmd_link_delete(cmd); |       i2c_cmd_link_delete(cmd); | ||||||
|       return ERROR_UNKNOWN; |       return ERROR_UNKNOWN; | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|   err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); |   err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); | ||||||
|   i2c_cmd_link_delete(cmd); |   i2c_cmd_link_delete(cmd); | ||||||
|   if (err == ESP_FAIL) { |   if (err == ESP_FAIL) { | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud | |||||||
| #endif | #endif | ||||||
|   void set_external_dac_channels(uint8_t channels) { this->external_dac_channels_ = channels; } |   void set_external_dac_channels(uint8_t channels) { this->external_dac_channels_ = channels; } | ||||||
|  |  | ||||||
|   void start(); |   void start() override; | ||||||
|   void stop() override; |   void stop() override; | ||||||
|  |  | ||||||
|   size_t play(const uint8_t *data, size_t length) override; |   size_t play(const uint8_t *data, size_t length) override; | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ from esphome.const import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| DEPENDENCIES = ["i2c"] | DEPENDENCIES = ["i2c"] | ||||||
| CODEOWNERS = ["@MrEditor97"] | CODEOWNERS = ["@mreditor97"] | ||||||
|  |  | ||||||
| ina260_ns = cg.esphome_ns.namespace("ina260") | ina260_ns = cg.esphome_ns.namespace("ina260") | ||||||
| INA260Component = ina260_ns.class_( | INA260Component = ina260_ns.class_( | ||||||
|   | |||||||
| @@ -1,113 +1,64 @@ | |||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.components import uart | 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 import automation | ||||||
| from esphome.automation import maybe_simple_id | from esphome.automation import maybe_simple_id | ||||||
|  |  | ||||||
| DEPENDENCIES = ["uart"] | DEPENDENCIES = ["uart"] | ||||||
| CODEOWNERS = ["@sebcaps"] | CODEOWNERS = ["@sebcaps", "@regevbr"] | ||||||
| MULTI_CONF = True | MULTI_CONF = True | ||||||
|  |  | ||||||
| ld2410_ns = cg.esphome_ns.namespace("ld2410") | ld2410_ns = cg.esphome_ns.namespace("ld2410") | ||||||
| LD2410Component = ld2410_ns.class_("LD2410Component", cg.Component, uart.UARTDevice) | LD2410Component = ld2410_ns.class_("LD2410Component", cg.Component, uart.UARTDevice) | ||||||
| LD2410Restart = ld2410_ns.class_("LD2410Restart", automation.Action) |  | ||||||
| CONF_LD2410_ID = "ld2410_id" | CONF_LD2410_ID = "ld2410_id" | ||||||
|  |  | ||||||
| CONF_MAX_MOVE_DISTANCE = "max_move_distance" | CONF_MAX_MOVE_DISTANCE = "max_move_distance" | ||||||
| CONF_MAX_STILL_DISTANCE = "max_still_distance" | CONF_MAX_STILL_DISTANCE = "max_still_distance" | ||||||
| CONF_G0_MOVE_THRESHOLD = "g0_move_threshold" | CONF_STILL_THRESHOLDS = [f"g{x}_still_threshold" for x in range(9)] | ||||||
| CONF_G0_STILL_THRESHOLD = "g0_still_threshold" | CONF_MOVE_THRESHOLDS = [f"g{x}_move_threshold" for x in range(9)] | ||||||
| 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" |  | ||||||
|  |  | ||||||
| DISTANCES = [0.75, 1.5, 2.25, 3, 3.75, 4.5, 5.25, 6] | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.All( |  | ||||||
|     cv.Schema( |  | ||||||
|     { |     { | ||||||
|         cv.GenerateID(): cv.declare_id(LD2410Component), |         cv.GenerateID(): cv.declare_id(LD2410Component), | ||||||
|             cv.Optional(CONF_MAX_MOVE_DISTANCE, default="4.5m"): cv.All( |         cv.Optional(CONF_THROTTLE, default="1000ms"): cv.All( | ||||||
|                 cv.distance, cv.one_of(*DISTANCES, float=True) |             cv.positive_time_period_milliseconds, | ||||||
|  |             cv.Range(min=cv.TimePeriod(milliseconds=1)), | ||||||
|         ), |         ), | ||||||
|             cv.Optional(CONF_MAX_STILL_DISTANCE, default="4.5m"): cv.All( |         cv.Optional(CONF_MAX_MOVE_DISTANCE): cv.invalid( | ||||||
|                 cv.distance, cv.one_of(*DISTANCES, float=True) |             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.Optional(CONF_MAX_STILL_DISTANCE): cv.invalid( | ||||||
|                 cv.positive_time_period_seconds, |             f"The '{CONF_MAX_STILL_DISTANCE}' option has been moved to the '{CONF_MAX_STILL_DISTANCE}'" | ||||||
|                 cv.Range(max=cv.TimePeriod(seconds=32767)), |             f" number component" | ||||||
|         ), |         ), | ||||||
|             cv.Optional(CONF_G0_MOVE_THRESHOLD, default=50): cv.int_range( |         cv.Optional(CONF_TIMEOUT): cv.invalid( | ||||||
|                 min=0, max=100 |             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( |                 cv.Optional(CONF_STILL_THRESHOLDS[i]): cv.invalid( | ||||||
|                 min=0, max=100 |                     f"The '{CONF_STILL_THRESHOLDS[i]}' option has been moved to the '{CONF_STILL_THRESHOLDS[i]}'" | ||||||
|             ), |                     f" number component" | ||||||
|             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 |  | ||||||
|                 ), |                 ), | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|     .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( | FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( | ||||||
| @@ -123,31 +74,7 @@ async def to_code(config): | |||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await cg.register_component(var, config) |     await cg.register_component(var, config) | ||||||
|     await uart.register_uart_device(var, config) |     await uart.register_uart_device(var, config) | ||||||
|     cg.add(var.set_timeout(config[CONF_TIMEOUT])) |     cg.add(var.set_throttle(config[CONF_THROTTLE])) | ||||||
|     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], |  | ||||||
|         ) |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| CALIBRATION_ACTION_SCHEMA = maybe_simple_id( | CALIBRATION_ACTION_SCHEMA = maybe_simple_id( | ||||||
| @@ -155,3 +82,28 @@ CALIBRATION_ACTION_SCHEMA = maybe_simple_id( | |||||||
|         cv.Required(CONF_ID): cv.use_id(LD2410Component), |         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 | import esphome.codegen as cg | ||||||
| from esphome.components import binary_sensor | from esphome.components import binary_sensor | ||||||
| import esphome.config_validation as cv | 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 | from . import CONF_LD2410_ID, LD2410Component | ||||||
|  |  | ||||||
| DEPENDENCIES = ["ld2410"] | DEPENDENCIES = ["ld2410"] | ||||||
| CONF_HAS_TARGET = "has_target" | CONF_HAS_TARGET = "has_target" | ||||||
| CONF_HAS_MOVING_TARGET = "has_moving_target" | CONF_HAS_MOVING_TARGET = "has_moving_target" | ||||||
| CONF_HAS_STILL_TARGET = "has_still_target" | CONF_HAS_STILL_TARGET = "has_still_target" | ||||||
|  | CONF_OUT_PIN_PRESENCE_STATUS = "out_pin_presence_status" | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = { | CONFIG_SCHEMA = { | ||||||
|     cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), |     cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||||
|     cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema( |     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( |     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( |     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): | async def to_code(config): | ||||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) |     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||||
|     if CONF_HAS_TARGET in config: |     if has_target_config := config.get(CONF_HAS_TARGET): | ||||||
|         sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_TARGET]) |         sens = await binary_sensor.new_binary_sensor(has_target_config) | ||||||
|         cg.add(ld2410_component.set_target_sensor(sens)) |         cg.add(ld2410_component.set_target_binary_sensor(sens)) | ||||||
|     if CONF_HAS_MOVING_TARGET in config: |     if has_moving_target_config := config.get(CONF_HAS_MOVING_TARGET): | ||||||
|         sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_MOVING_TARGET]) |         sens = await binary_sensor.new_binary_sensor(has_moving_target_config) | ||||||
|         cg.add(ld2410_component.set_moving_target_sensor(sens)) |         cg.add(ld2410_component.set_moving_target_binary_sensor(sens)) | ||||||
|     if CONF_HAS_STILL_TARGET in config: |     if has_still_target_config := config.get(CONF_HAS_STILL_TARGET): | ||||||
|         sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_STILL_TARGET]) |         sens = await binary_sensor.new_binary_sensor(has_still_target_config) | ||||||
|         cg.add(ld2410_component.set_still_target_sensor(sens)) |         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 "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 highbyte(val) (uint8_t)((val) >> 8) | ||||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | #define lowbyte(val) (uint8_t)((val) &0xff) | ||||||
|  |  | ||||||
| @@ -8,48 +16,97 @@ namespace ld2410 { | |||||||
|  |  | ||||||
| static const char *const TAG = "ld2410"; | static const char *const TAG = "ld2410"; | ||||||
|  |  | ||||||
|  | LD2410Component::LD2410Component() {} | ||||||
|  |  | ||||||
| void LD2410Component::dump_config() { | void LD2410Component::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "LD2410:"); |   ESP_LOGCONFIG(TAG, "LD2410:"); | ||||||
| #ifdef USE_BINARY_SENSOR | #ifdef USE_BINARY_SENSOR | ||||||
|   LOG_BINARY_SENSOR("  ", "HasTargetSensor", this->target_binary_sensor_); |   LOG_BINARY_SENSOR("  ", "TargetBinarySensor", this->target_binary_sensor_); | ||||||
|   LOG_BINARY_SENSOR("  ", "MovingSensor", this->moving_binary_sensor_); |   LOG_BINARY_SENSOR("  ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_); | ||||||
|   LOG_BINARY_SENSOR("  ", "StillSensor", this->still_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 | #endif | ||||||
| #ifdef USE_SENSOR | #ifdef USE_SENSOR | ||||||
|   LOG_SENSOR("  ", "Moving Distance", this->moving_target_distance_sensor_); |   LOG_SENSOR("  ", "LightSensor", this->light_sensor_); | ||||||
|   LOG_SENSOR("  ", "Still Distance", this->still_target_distance_sensor_); |   LOG_SENSOR("  ", "MovingTargetDistanceSensor", this->moving_target_distance_sensor_); | ||||||
|   LOG_SENSOR("  ", "Moving Energy", this->moving_target_energy_sensor_); |   LOG_SENSOR("  ", "StillTargetDistanceSensor", this->still_target_distance_sensor_); | ||||||
|   LOG_SENSOR("  ", "Still Energy", this->still_target_energy_sensor_); |   LOG_SENSOR("  ", "MovingTargetEnergySensor", this->moving_target_energy_sensor_); | ||||||
|   LOG_SENSOR("  ", "Detection Distance", this->detection_distance_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 | #endif | ||||||
|   this->set_config_mode_(true); | #ifdef USE_TEXT_SENSOR | ||||||
|   this->get_version_(); |   LOG_TEXT_SENSOR("  ", "VersionTextSensor", this->version_text_sensor_); | ||||||
|   this->set_config_mode_(false); |   LOG_TEXT_SENSOR("  ", "MacTextSensor", this->mac_text_sensor_); | ||||||
|   ESP_LOGCONFIG(TAG, "  Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2], | #endif | ||||||
|                 this->version_[3], this->version_[4], this->version_[5]); | #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() { | void LD2410Component::setup() { | ||||||
|   ESP_LOGCONFIG(TAG, "Setting up LD2410..."); |   ESP_LOGCONFIG(TAG, "Setting up LD2410..."); | ||||||
|   this->set_config_mode_(true); |   this->read_all_info(); | ||||||
|   this->set_max_distances_timeout_(this->max_move_distance_, this->max_still_distance_, this->timeout_); |   ESP_LOGCONFIG(TAG, "Mac Address : %s", const_cast<char *>(this->mac_.c_str())); | ||||||
|   // Configure Gates sensitivity |   ESP_LOGCONFIG(TAG, "Firmware Version : %s", const_cast<char *>(this->version_.c_str())); | ||||||
|   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]); |  | ||||||
|   ESP_LOGCONFIG(TAG, "LD2410 setup complete."); |   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() { | void LD2410Component::loop() { | ||||||
|   const int max_line_length = 80; |   const int max_line_length = 80; | ||||||
|   static uint8_t buffer[max_line_length]; |   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) { | void LD2410Component::send_command_(uint8_t command, const uint8_t *command_value, int command_value_len) { | ||||||
|   // lastCommandSuccess->publish_state(false); |   ESP_LOGV(TAG, "Sending COMMAND %02X", command); | ||||||
|  |  | ||||||
|   // frame start bytes |   // frame start bytes | ||||||
|   this->write_array(CMD_FRAME_HEADER, 4); |   this->write_array(CMD_FRAME_HEADER, 4); | ||||||
|   // length bytes |   // 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 |     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 |     0x01: Engineering mode | ||||||
|     0x02: Normal 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 |     Target states: 9th | ||||||
|     0x00 = No target |     0x00 = No target | ||||||
| @@ -108,27 +179,15 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) { | |||||||
|     0x02 = Still targets |     0x02 = Still targets | ||||||
|     0x03 = Moving+Still targets |     0x03 = Moving+Still targets | ||||||
|   */ |   */ | ||||||
| #ifdef USE_BINARY_SENSOR |  | ||||||
|   char target_state = buffer[TARGET_STATES]; |   char target_state = buffer[TARGET_STATES]; | ||||||
|   if (this->target_binary_sensor_ != nullptr) { |   if (this->target_binary_sensor_ != nullptr) { | ||||||
|     this->target_binary_sensor_->publish_state(target_state != 0x00); |     this->target_binary_sensor_->publish_state(target_state != 0x00); | ||||||
|   } |   } | ||||||
| #endif |   if (this->moving_target_binary_sensor_ != nullptr) { | ||||||
|  |     this->moving_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 0)); | ||||||
|   /* |  | ||||||
|     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->still_binary_sensor_ != nullptr) { |   if (this->still_target_binary_sensor_ != nullptr) { | ||||||
|     this->still_binary_sensor_->publish_state(CHECK_BIT(target_state, 1)); |     this->still_target_binary_sensor_->publish_state(CHECK_BIT(target_state, 1)); | ||||||
|   } |   } | ||||||
| #endif | #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) |     if (this->detection_distance_sensor_->get_state() != new_detect_distance) | ||||||
|       this->detection_distance_sensor_->publish_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 | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | const char VERSION_FMT[] = "%u.%02X.%02X%02X%02X%02X"; | ||||||
|   ESP_LOGV(TAG, "Handling ACK DATA for COMMAND"); |  | ||||||
|  | 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) { |   if (len < 10) { | ||||||
|     ESP_LOGE(TAG, "Error with last command : incorrect length"); |     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 |   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"); |     ESP_LOGE(TAG, "Error with last command : incorrect Header"); | ||||||
|     return; |     return true; | ||||||
|   } |   } | ||||||
|   if (buffer[COMMAND_STATUS] != 0x01) { |   if (buffer[COMMAND_STATUS] != 0x01) { | ||||||
|     ESP_LOGE(TAG, "Error with last 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) { |   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]); |     ESP_LOGE(TAG, "Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]); | ||||||
|     return; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   switch (buffer[COMMAND]) { |   switch (buffer[COMMAND]) { | ||||||
| @@ -193,49 +352,127 @@ void LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | |||||||
|     case lowbyte(CMD_DISABLE_CONF): |     case lowbyte(CMD_DISABLE_CONF): | ||||||
|       ESP_LOGV(TAG, "Handled Disabled conf command"); |       ESP_LOGV(TAG, "Handled Disabled conf command"); | ||||||
|       break; |       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): |     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], |       this->version_ = format_version(buffer); | ||||||
|                buffer[14]); |       ESP_LOGV(TAG, "FW Version is: %s", const_cast<char *>(this->version_.c_str())); | ||||||
|       this->version_[0] = buffer[13]; | #ifdef USE_TEXT_SENSOR | ||||||
|       this->version_[1] = buffer[12]; |       if (this->version_text_sensor_ != nullptr) { | ||||||
|       this->version_[2] = buffer[17]; |         this->version_text_sensor_->publish_state(this->version_); | ||||||
|       this->version_[3] = buffer[16]; |       } | ||||||
|       this->version_[4] = buffer[15]; | #endif | ||||||
|       this->version_[5] = buffer[14]; |       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; |       break; | ||||||
|     case lowbyte(CMD_GATE_SENS): |     case lowbyte(CMD_GATE_SENS): | ||||||
|       ESP_LOGV(TAG, "Handled sensitivity command"); |       ESP_LOGV(TAG, "Handled sensitivity command"); | ||||||
|       break; |       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 |     case lowbyte(CMD_QUERY):  // Query parameters response | ||||||
|     { |     { | ||||||
|       if (buffer[10] != 0xAA) |       if (buffer[10] != 0xAA) | ||||||
|         return;  // value head=0xAA |         return true;  // value head=0xAA | ||||||
|  | #ifdef USE_NUMBER | ||||||
|       /* |       /* | ||||||
|         Moving distance range: 13th byte |         Moving distance range: 13th byte | ||||||
|         Still distance range: 14th byte |         Still distance range: 14th byte | ||||||
|       */ |       */ | ||||||
|       // TODO |       std::vector<std::function<void(void)>> updates; | ||||||
|       // maxMovingDistanceRange->publish_state(buffer[12]); |       updates.push_back(set_number_value(this->max_move_distance_gate_number_, buffer[12])); | ||||||
|       // maxStillDistanceRange->publish_state(buffer[13]); |       updates.push_back(set_number_value(this->max_still_distance_gate_number_, buffer[13])); | ||||||
|       /* |       /* | ||||||
|         Moving Sensitivities: 15~23th bytes |         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 |         Still Sensitivities: 24~32th bytes | ||||||
|       */ |       */ | ||||||
|       for (int i = 0; i < 9; i++) { |       for (std::vector<number::Number *>::size_type i = 0; i != this->gate_still_threshold_numbers_.size(); i++) { | ||||||
|         moving_sensitivities[i] = buffer[14 + i]; |         updates.push_back(set_number_value(this->gate_still_threshold_numbers_[i], buffer[23 + i])); | ||||||
|       } |  | ||||||
|       for (int i = 0; i < 9; i++) { |  | ||||||
|         still_sensitivities[i] = buffer[23 + i]; |  | ||||||
|       } |       } | ||||||
|       /* |       /* | ||||||
|         None Duration: 33~34th bytes |         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; |     } break; | ||||||
|     default: |     default: | ||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| void LD2410Component::readline_(int readch, uint8_t *buffer, int len) { | 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 && |       } else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 && | ||||||
|                  buffer[pos - 1] == 0x01) { |                  buffer[pos - 1] == 0x01) { | ||||||
|         ESP_LOGV(TAG, "Will handle ACK Data"); |         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 |           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); |   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::query_parameters_() { this->send_command_(CMD_QUERY, nullptr, 0); } | ||||||
| void LD2410Component::get_version_() { this->send_command_(CMD_VERSION, 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, | void LD2410Component::get_light_control_() { this->send_command_(CMD_QUERY_LIGHT_CONTROL, nullptr, 0); } | ||||||
|                                                  uint16_t timeout) { |  | ||||||
|  | #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, |   uint8_t value[18] = {0x00, | ||||||
|                        0x00, |                        0x00, | ||||||
|                        lowbyte(max_moving_distance_range), |                        lowbyte(max_moving_distance_gate_range), | ||||||
|                        highbyte(max_moving_distance_range), |                        highbyte(max_moving_distance_gate_range), | ||||||
|                        0x00, |                        0x00, | ||||||
|                        0x00, |                        0x00, | ||||||
|                        0x01, |                        0x01, | ||||||
|                        0x00, |                        0x00, | ||||||
|                        lowbyte(max_still_distance_range), |                        lowbyte(max_still_distance_gate_range), | ||||||
|                        highbyte(max_still_distance_range), |                        highbyte(max_still_distance_gate_range), | ||||||
|                        0x00, |                        0x00, | ||||||
|                        0x00, |                        0x00, | ||||||
|                        0x02, |                        0x02, | ||||||
| @@ -292,10 +596,25 @@ void LD2410Component::set_max_distances_timeout_(uint8_t max_moving_distance_ran | |||||||
|                        highbyte(timeout), |                        highbyte(timeout), | ||||||
|                        0x00, |                        0x00, | ||||||
|                        0x00}; |                        0x00}; | ||||||
|  |   this->set_config_mode_(true); | ||||||
|   this->send_command_(CMD_MAXDIST_DURATION, value, 18); |   this->send_command_(CMD_MAXDIST_DURATION, value, 18); | ||||||
|  |   delay(50);  // NOLINT | ||||||
|   this->query_parameters_(); |   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 |   // reference | ||||||
|   // https://drive.google.com/drive/folders/1p4dhbEJA3YubyIjIIC7wwVsSo8x29Fq-?spm=a2g0o.detail.1000023.17.93465697yFwVxH |   // 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 |   //   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) |   // 02 00 (still sensitivtiy) | ||||||
|   // 28 00 00 00 (value) |   // 28 00 00 00 (value) | ||||||
|   uint8_t value[18] = {0x00, 0x00, lowbyte(gate),   highbyte(gate),   0x00, 0x00, |   uint8_t value[18] = {0x00, 0x00, lowbyte(gate),   highbyte(gate),   0x00, 0x00, | ||||||
|                        0x01, 0x00, lowbyte(motionsens), highbyte(motionsens), 0x00, 0x00, |                        0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00, | ||||||
|                        0x02, 0x00, lowbyte(stillsens),  highbyte(stillsens),  0x00, 0x00}; |                        0x02, 0x00, lowbyte(still),  highbyte(still),  0x00, 0x00}; | ||||||
|   this->send_command_(CMD_GATE_SENS, value, 18); |   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 ld2410 | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -7,10 +7,27 @@ | |||||||
| #ifdef USE_SENSOR | #ifdef USE_SENSOR | ||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
| #endif | #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/components/uart/uart.h" | ||||||
| #include "esphome/core/automation.h" | #include "esphome/core/automation.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
|  | #include <map> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace ld2410 { | namespace ld2410 { | ||||||
|  |  | ||||||
| @@ -19,10 +36,63 @@ namespace ld2410 { | |||||||
| // Commands | // Commands | ||||||
| static const uint8_t CMD_ENABLE_CONF = 0x00FF; | static const uint8_t CMD_ENABLE_CONF = 0x00FF; | ||||||
| static const uint8_t CMD_DISABLE_CONF = 0x00FE; | 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_MAXDIST_DURATION = 0x0060; | ||||||
| static const uint8_t CMD_QUERY = 0x0061; | static const uint8_t CMD_QUERY = 0x0061; | ||||||
| static const uint8_t CMD_GATE_SENS = 0x0064; | static const uint8_t CMD_GATE_SENS = 0x0064; | ||||||
| static const uint8_t CMD_VERSION = 0x00A0; | 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 | // Commands values | ||||||
| static const uint8_t CMD_MAX_MOVE_VALUE = 0x0000; | static const uint8_t CMD_MAX_MOVE_VALUE = 0x0000; | ||||||
| @@ -44,7 +114,7 @@ Target states: 9th byte | |||||||
|     Detect distance: 16~17th bytes |     Detect distance: 16~17th bytes | ||||||
| */ | */ | ||||||
| enum PeriodicDataStructure : uint8_t { | enum PeriodicDataStructure : uint8_t { | ||||||
|   DATA_TYPES = 5, |   DATA_TYPES = 6, | ||||||
|   TARGET_STATES = 8, |   TARGET_STATES = 8, | ||||||
|   MOVING_TARGET_LOW = 9, |   MOVING_TARGET_LOW = 9, | ||||||
|   MOVING_TARGET_HIGH = 10, |   MOVING_TARGET_HIGH = 10, | ||||||
| @@ -54,6 +124,10 @@ enum PeriodicDataStructure : uint8_t { | |||||||
|   STILL_ENERGY = 14, |   STILL_ENERGY = 14, | ||||||
|   DETECT_DISTANCE_LOW = 15, |   DETECT_DISTANCE_LOW = 15, | ||||||
|   DETECT_DISTANCE_HIGH = 16, |   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 }; | 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(still_target_distance) | ||||||
|   SUB_SENSOR(moving_target_energy) |   SUB_SENSOR(moving_target_energy) | ||||||
|   SUB_SENSOR(still_target_energy) |   SUB_SENSOR(still_target_energy) | ||||||
|  |   SUB_SENSOR(light) | ||||||
|   SUB_SENSOR(detection_distance) |   SUB_SENSOR(detection_distance) | ||||||
| #endif | #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: |  public: | ||||||
|  |   LD2410Component(); | ||||||
|   void setup() override; |   void setup() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|   void loop() override; |   void loop() override; | ||||||
|  |   void set_light_out_control(); | ||||||
| #ifdef USE_BINARY_SENSOR | #ifdef USE_NUMBER | ||||||
|   void set_target_sensor(binary_sensor::BinarySensor *sens) { this->target_binary_sensor_ = sens; }; |   void set_gate_still_threshold_number(int gate, number::Number *n); | ||||||
|   void set_moving_target_sensor(binary_sensor::BinarySensor *sens) { this->moving_binary_sensor_ = sens; }; |   void set_gate_move_threshold_number(int gate, number::Number *n); | ||||||
|   void set_still_target_sensor(binary_sensor::BinarySensor *sens) { this->still_binary_sensor_ = sens; }; |   void set_max_distances_timeout(); | ||||||
|  |   void set_gate_threshold(uint8_t gate); | ||||||
| #endif | #endif | ||||||
|  | #ifdef USE_SENSOR | ||||||
|   void set_timeout(uint16_t value) { this->timeout_ = value; }; |   void set_gate_move_sensor(int gate, sensor::Sensor *s); | ||||||
|   void set_max_move_distance(uint8_t value) { this->max_move_distance_ = value; }; |   void set_gate_still_sensor(int gate, sensor::Sensor *s); | ||||||
|   void set_max_still_distance(uint8_t value) { this->max_still_distance_ = value; }; | #endif | ||||||
|   void set_range_config(int rg0_move, int rg0_still, int rg1_move, int rg1_still, int rg2_move, int rg2_still, |   void set_throttle(uint16_t value) { this->throttle_ = value; }; | ||||||
|                         int rg3_move, int rg3_still, int rg4_move, int rg4_still, int rg5_move, int rg5_still, |   void set_bluetooth_password(const std::string &password); | ||||||
|                         int rg6_move, int rg6_still, int rg7_move, int rg7_still, int rg8_move, int rg8_still) { |   void set_engineering_mode(bool enable); | ||||||
|     this->rg0_move_threshold_ = rg0_move; |   void read_all_info(); | ||||||
|     this->rg0_still_threshold_ = rg0_still; |   void restart_and_read_all_info(); | ||||||
|     this->rg1_move_threshold_ = rg1_move; |   void set_bluetooth(bool enable); | ||||||
|     this->rg1_still_threshold_ = rg1_still; |   void set_distance_resolution(const std::string &state); | ||||||
|     this->rg2_move_threshold_ = rg2_move; |   void set_baud_rate(const std::string &state); | ||||||
|     this->rg2_still_threshold_ = rg2_still; |   void factory_reset(); | ||||||
|     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(); |  | ||||||
|  |  | ||||||
|  protected: |  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; } |   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 send_command_(uint8_t command_str, const 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 set_config_mode_(bool enable); |   void set_config_mode_(bool enable); | ||||||
|   void handle_periodic_data_(uint8_t *buffer, int len); |   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 readline_(int readch, uint8_t *buffer, int len); | ||||||
|   void query_parameters_(); |   void query_parameters_(); | ||||||
|   void get_version_(); |   void get_version_(); | ||||||
|  |   void get_mac_(); | ||||||
|  |   void get_distance_resolution_(); | ||||||
|  |   void get_light_control_(); | ||||||
|  |   void restart_(); | ||||||
|  |  | ||||||
|   uint16_t timeout_; |   int32_t last_periodic_millis_ = millis(); | ||||||
|   uint8_t max_move_distance_; |   int32_t last_engineering_mode_change_millis_ = millis(); | ||||||
|   uint8_t max_still_distance_; |   uint16_t throttle_; | ||||||
|  |   std::string version_; | ||||||
|   uint8_t version_[6]; |   std::string mac_; | ||||||
|   uint8_t rg0_move_threshold_, rg0_still_threshold_, rg1_move_threshold_, rg1_still_threshold_, rg2_move_threshold_, |   std::string out_pin_level_; | ||||||
|       rg2_still_threshold_, rg3_move_threshold_, rg3_still_threshold_, rg4_move_threshold_, rg4_still_threshold_, |   std::string light_function_; | ||||||
|       rg5_move_threshold_, rg5_still_threshold_, rg6_move_threshold_, rg6_still_threshold_, rg7_move_threshold_, |   float light_threshold_ = -1; | ||||||
|       rg7_still_threshold_, rg8_move_threshold_, rg8_still_threshold_; | #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 | }  // 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 | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|     DEVICE_CLASS_DISTANCE, |     DEVICE_CLASS_DISTANCE, | ||||||
|     DEVICE_CLASS_ENERGY, |  | ||||||
|     UNIT_CENTIMETER, |     UNIT_CENTIMETER, | ||||||
|     UNIT_PERCENT, |     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 | from . import CONF_LD2410_ID, LD2410Component | ||||||
|  |  | ||||||
| @@ -15,41 +21,88 @@ CONF_STILL_DISTANCE = "still_distance" | |||||||
| CONF_MOVING_ENERGY = "moving_energy" | CONF_MOVING_ENERGY = "moving_energy" | ||||||
| CONF_STILL_ENERGY = "still_energy" | CONF_STILL_ENERGY = "still_energy" | ||||||
| CONF_DETECTION_DISTANCE = "detection_distance" | 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.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), | ||||||
|         cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema( |         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( |         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( |         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( |         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( |         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): | async def to_code(config): | ||||||
|     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) |     ld2410_component = await cg.get_variable(config[CONF_LD2410_ID]) | ||||||
|     if CONF_MOVING_DISTANCE in config: |     if moving_distance_config := config.get(CONF_MOVING_DISTANCE): | ||||||
|         sens = await sensor.new_sensor(config[CONF_MOVING_DISTANCE]) |         sens = await sensor.new_sensor(moving_distance_config) | ||||||
|         cg.add(ld2410_component.set_moving_target_distance_sensor(sens)) |         cg.add(ld2410_component.set_moving_target_distance_sensor(sens)) | ||||||
|     if CONF_STILL_DISTANCE in config: |     if still_distance_config := config.get(CONF_STILL_DISTANCE): | ||||||
|         sens = await sensor.new_sensor(config[CONF_STILL_DISTANCE]) |         sens = await sensor.new_sensor(still_distance_config) | ||||||
|         cg.add(ld2410_component.set_still_target_distance_sensor(sens)) |         cg.add(ld2410_component.set_still_target_distance_sensor(sens)) | ||||||
|     if CONF_MOVING_ENERGY in config: |     if moving_energy_config := config.get(CONF_MOVING_ENERGY): | ||||||
|         sens = await sensor.new_sensor(config[CONF_MOVING_ENERGY]) |         sens = await sensor.new_sensor(moving_energy_config) | ||||||
|         cg.add(ld2410_component.set_moving_target_energy_sensor(sens)) |         cg.add(ld2410_component.set_moving_target_energy_sensor(sens)) | ||||||
|     if CONF_STILL_ENERGY in config: |     if still_energy_config := config.get(CONF_STILL_ENERGY): | ||||||
|         sens = await sensor.new_sensor(config[CONF_STILL_ENERGY]) |         sens = await sensor.new_sensor(still_energy_config) | ||||||
|         cg.add(ld2410_component.set_still_target_energy_sensor(sens)) |         cg.add(ld2410_component.set_still_target_energy_sensor(sens)) | ||||||
|     if CONF_DETECTION_DISTANCE in config: |     if light_config := config.get(CONF_LIGHT): | ||||||
|         sens = await sensor.new_sensor(config[CONF_DETECTION_DISTANCE]) |         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)) |         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_ESP32S2, | ||||||
|     VARIANT_ESP32C3, |     VARIANT_ESP32C3, | ||||||
|     VARIANT_ESP32S3, |     VARIANT_ESP32S3, | ||||||
|  |     VARIANT_ESP32C2, | ||||||
|  |     VARIANT_ESP32C6, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CODEOWNERS = ["@esphome/core"] | CODEOWNERS = ["@esphome/core"] | ||||||
| @@ -74,6 +76,8 @@ UART_SELECTION_ESP32 = { | |||||||
|     VARIANT_ESP32S2: [UART0, UART1, USB_CDC], |     VARIANT_ESP32S2: [UART0, UART1, USB_CDC], | ||||||
|     VARIANT_ESP32S3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], |     VARIANT_ESP32S3: [UART0, UART1, USB_CDC, USB_SERIAL_JTAG], | ||||||
|     VARIANT_ESP32C3: [UART0, UART1, 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] | 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 ( | ||||||
| #if defined(USE_ESP32_VARIANT_ESP32S2) | #if defined(USE_ESP32_VARIANT_ESP32S2) | ||||||
|         uart_ == UART_SELECTION_USB_CDC |         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 |         uart_ == UART_SELECTION_USB_SERIAL_JTAG | ||||||
| #elif defined(USE_ESP32_VARIANT_ESP32S3) | #elif defined(USE_ESP32_VARIANT_ESP32S3) | ||||||
|         uart_ == UART_SELECTION_USB_CDC || uart_ == UART_SELECTION_USB_SERIAL_JTAG |         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); |         Serial1.setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE); | ||||||
| #endif | #endif | ||||||
|         break; |         break; | ||||||
| #if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32S2) && \ | #if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \ | ||||||
|     !defined(USE_ESP32_VARIANT_ESP32S3) |     !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3) | ||||||
|       case UART_SELECTION_UART2: |       case UART_SELECTION_UART2: | ||||||
|         this->hw_serial_ = &Serial2; |         this->hw_serial_ = &Serial2; | ||||||
|         Serial2.begin(this->baud_rate_); |         Serial2.begin(this->baud_rate_); | ||||||
| @@ -215,7 +215,8 @@ void Logger::pre_setup() { | |||||||
|       case UART_SELECTION_UART1: |       case UART_SELECTION_UART1: | ||||||
|         uart_num_ = UART_NUM_1; |         uart_num_ = UART_NUM_1; | ||||||
|         break; |         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: |       case UART_SELECTION_UART2: | ||||||
|         uart_num_ = UART_NUM_2; |         uart_num_ = UART_NUM_2; | ||||||
|         break; |         break; | ||||||
| @@ -225,11 +226,11 @@ void Logger::pre_setup() { | |||||||
|         uart_num_ = -1; |         uart_num_ = -1; | ||||||
|         break; |         break; | ||||||
| #endif  // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 | #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: |       case UART_SELECTION_USB_SERIAL_JTAG: | ||||||
|         uart_num_ = -1; |         uart_num_ = -1; | ||||||
|         break; |         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) { |     if (uart_num_ >= 0) { | ||||||
|       uart_config_t uart_config{}; |       uart_config_t uart_config{}; | ||||||
| @@ -278,7 +279,8 @@ const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DE | |||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
| const char *const UART_SELECTIONS[] = { | const char *const UART_SELECTIONS[] = { | ||||||
|     "UART0",           "UART1", |     "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", |     "UART2", | ||||||
| #endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 | #endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 | ||||||
| #if defined(USE_ESP_IDF) | #if defined(USE_ESP_IDF) | ||||||
|   | |||||||
| @@ -34,14 +34,15 @@ enum UARTSelection { | |||||||
|   UART_SELECTION_UART0 = 0, |   UART_SELECTION_UART0 = 0, | ||||||
|   UART_SELECTION_UART1, |   UART_SELECTION_UART1, | ||||||
| #if defined(USE_ESP32) | #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, |   UART_SELECTION_UART2, | ||||||
| #endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 | #endif  // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
| #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) | #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) | ||||||
|   UART_SELECTION_USB_CDC, |   UART_SELECTION_USB_CDC, | ||||||
| #endif  // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 | #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, |   UART_SELECTION_USB_SERIAL_JTAG, | ||||||
| #endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 | #endif  // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32S3 | ||||||
| #endif  // USE_ESP_IDF | #endif  // USE_ESP_IDF | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ CONF_HOT_JUNCTION = "hot_junction" | |||||||
| CONF_COLD_JUNCTION = "cold_junction" | CONF_COLD_JUNCTION = "cold_junction" | ||||||
|  |  | ||||||
| DEPENDENCIES = ["i2c"] | DEPENDENCIES = ["i2c"] | ||||||
| CODEOWNERS = ["@MrEditor97"] | CODEOWNERS = ["@mreditor97"] | ||||||
|  |  | ||||||
| mcp9600_ns = cg.esphome_ns.namespace("mcp9600") | mcp9600_ns = cg.esphome_ns.namespace("mcp9600") | ||||||
| MCP9600Component = mcp9600_ns.class_( | MCP9600Component = mcp9600_ns.class_( | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ void PCA9554Component::setup() { | |||||||
|   this->config_mask_ = 0; |   this->config_mask_ = 0; | ||||||
|   // Invert mask as the part sees a 1 as an input |   // Invert mask as the part sees a 1 as an input | ||||||
|   this->write_register_(CONFIG_REG, ~this->config_mask_); |   this->write_register_(CONFIG_REG, ~this->config_mask_); | ||||||
|   // All ouputs low |   // All outputs low | ||||||
|   this->output_mask_ = 0; |   this->output_mask_ = 0; | ||||||
|   this->write_register_(OUTPUT_REG, this->output_mask_); |   this->write_register_(OUTPUT_REG, this->output_mask_); | ||||||
|   // Read the inputs |   // Read the inputs | ||||||
| @@ -34,6 +34,14 @@ void PCA9554Component::setup() { | |||||||
|   ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(), |   ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(), | ||||||
|            this->status_has_error()); |            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() { | void PCA9554Component::dump_config() { | ||||||
|   ESP_LOGCONFIG(TAG, "PCA9554:"); |   ESP_LOGCONFIG(TAG, "PCA9554:"); | ||||||
|   LOG_I2C_DEVICE(this) |   LOG_I2C_DEVICE(this) | ||||||
| @@ -43,7 +51,16 @@ void PCA9554Component::dump_config() { | |||||||
| } | } | ||||||
|  |  | ||||||
| bool PCA9554Component::digital_read(uint8_t pin) { | 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); |   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; } | 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::setup() { pin_mode(flags_); } | ||||||
| void PCA9554GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, 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_; } | 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 |   /// Check i2c availability and setup masks | ||||||
|   void setup() override; |   void setup() override; | ||||||
|  |   /// Poll for input changes periodically | ||||||
|  |   void loop() override; | ||||||
|   /// Helper function to read the value of a pin. |   /// Helper function to read the value of a pin. | ||||||
|   bool digital_read(uint8_t pin); |   bool digital_read(uint8_t pin); | ||||||
|   /// Helper function to write the value of a 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_setup_priority() const override; | ||||||
|  |  | ||||||
|  |   float get_loop_priority() const override; | ||||||
|  |  | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
| @@ -35,6 +39,8 @@ class PCA9554Component : public Component, public i2c::I2CDevice { | |||||||
|   uint8_t output_mask_{0x00}; |   uint8_t output_mask_{0x00}; | ||||||
|   /// The state of the actual input pin states - 1 means HIGH, 0 means LOW |   /// The state of the actual input pin states - 1 means HIGH, 0 means LOW | ||||||
|   uint8_t input_mask_{0x00}; |   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 |   /// Storage for last I2C error seen | ||||||
|   esphome::i2c::ErrorCode last_error_; |   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) | MideaAction = ns.class_("MideaAction", RemoteTransmitterActionBase) | ||||||
| MIDEA_SCHEMA = cv.Schema( | MIDEA_SCHEMA = cv.Schema( | ||||||
|     { |     { | ||||||
|         cv.Required(CONF_CODE): cv.templatable( |         cv.Required(CONF_CODE): cv.All([cv.hex_uint8_t], cv.Length(min=5, max=5)), | ||||||
|             cv.All( |  | ||||||
|                 [cv.Any(cv.hex_uint8_t, cv.uint8_t)], |  | ||||||
|                 cv.Length(min=5, max=5), |  | ||||||
|             ) |  | ||||||
|         ), |  | ||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -1513,18 +1508,11 @@ def midea_dumper(var, config): | |||||||
|     pass |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
| @register_action( | @register_action("midea", MideaAction, MIDEA_SCHEMA) | ||||||
|     "midea", |  | ||||||
|     MideaAction, |  | ||||||
|     MIDEA_SCHEMA, |  | ||||||
| ) |  | ||||||
| async def midea_action(var, config, args): | async def midea_action(var, config, args): | ||||||
|     code_ = config[CONF_CODE] |     vec_ = cg.std_vector.template(cg.uint8) | ||||||
|     if cg.is_template(code_): |     template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_) | ||||||
|         template_ = await cg.templatable(code_, args, cg.std_vector.template(cg.uint8)) |     cg.add(var.set_code(template_)) | ||||||
|         cg.add(var.set_code_template(template_)) |  | ||||||
|     else: |  | ||||||
|         cg.add(var.set_code_static(code_)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # AEHA | # AEHA | ||||||
| @@ -1534,10 +1522,7 @@ AEHAData, AEHABinarySensor, AEHATrigger, AEHAAction, AEHADumper = declare_protoc | |||||||
| AEHA_SCHEMA = cv.Schema( | AEHA_SCHEMA = cv.Schema( | ||||||
|     { |     { | ||||||
|         cv.Required(CONF_ADDRESS): cv.hex_uint16_t, |         cv.Required(CONF_ADDRESS): cv.hex_uint16_t, | ||||||
|         cv.Required(CONF_DATA): cv.All( |         cv.Required(CONF_DATA): cv.All([cv.hex_uint8_t], cv.Length(min=2, max=35)), | ||||||
|             [cv.Any(cv.hex_uint8_t, cv.uint8_t)], |  | ||||||
|             cv.Length(min=2, max=35), |  | ||||||
|         ), |  | ||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -1569,4 +1554,7 @@ def aeha_dumper(var, config): | |||||||
| async def aeha_action(var, config, args): | async def aeha_action(var, config, args): | ||||||
|     template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint16) |     template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint16) | ||||||
|     cg.add(var.set_address(template_)) |     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) { | void AEHAProtocol::dump(const AEHAData &data) { | ||||||
|   auto data_str = format_data_(data.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 | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -96,10 +96,10 @@ optional<CanalSatData> CanalSatBaseProtocol::decode(RemoteReceiveData src) { | |||||||
|  |  | ||||||
| void CanalSatBaseProtocol::dump(const CanalSatData &data) { | void CanalSatBaseProtocol::dump(const CanalSatData &data) { | ||||||
|   if (this->tag_ == CANALSATLD_TAG) { |   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); |              data.address, data.command, data.repeat); | ||||||
|   } else { |   } 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); |              data.address, data.command, data.repeat); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -101,11 +101,11 @@ optional<CoolixData> CoolixProtocol::decode(RemoteReceiveData data) { | |||||||
|  |  | ||||||
| void CoolixProtocol::dump(const CoolixData &data) { | void CoolixProtocol::dump(const CoolixData &data) { | ||||||
|   if (data.is_strict()) { |   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()) { |   } 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 { |   } 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) { | 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 | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -205,7 +205,7 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) { | |||||||
|   return out; |   return out; | ||||||
| } | } | ||||||
| void DraytonProtocol::dump(const DraytonData &data) { | 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); |            ((data.address << 1) & 0xffff), data.channel, data.command); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -46,7 +46,7 @@ optional<JVCData> JVCProtocol::decode(RemoteReceiveData src) { | |||||||
|   } |   } | ||||||
|   return out; |   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 remote_base | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ optional<LGData> LGProtocol::decode(RemoteReceiveData src) { | |||||||
|   return out; |   return out; | ||||||
| } | } | ||||||
| void LGProtocol::dump(const LGData &data) { | 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 | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ optional<MagiQuestData> MagiQuestProtocol::decode(RemoteReceiveData src) { | |||||||
|   return data; |   return data; | ||||||
| } | } | ||||||
| void MagiQuestProtocol::dump(const MagiQuestData &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 | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -70,7 +70,7 @@ optional<MideaData> MideaProtocol::decode(RemoteReceiveData src) { | |||||||
|   return {}; |   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 remote_base | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "remote_base.h" | #include "remote_base.h" | ||||||
| #include <array> |  | ||||||
| #include <utility> |  | ||||||
| #include <vector> |  | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace remote_base { | namespace remote_base { | ||||||
| @@ -84,23 +84,12 @@ using MideaDumper = RemoteReceiverDumper<MideaProtocol, MideaData>; | |||||||
|  |  | ||||||
| template<typename... Ts> class MideaAction : public RemoteTransmitterActionBase<Ts...> { | template<typename... Ts> class MideaAction : public RemoteTransmitterActionBase<Ts...> { | ||||||
|   TEMPLATABLE_VALUE(std::vector<uint8_t>, code) |   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 { |   void encode(RemoteTransmitData *dst, Ts... x) override { | ||||||
|     MideaData data; |     MideaData data(this->code_.value(x...)); | ||||||
|     if (!this->code_static_.empty()) { |  | ||||||
|       data = MideaData(this->code_static_); |  | ||||||
|     } else { |  | ||||||
|       data = MideaData(this->code_func_(x...)); |  | ||||||
|     } |  | ||||||
|     data.finalize(); |     data.finalize(); | ||||||
|     MideaProtocol().encode(dst, data); |     MideaProtocol().encode(dst, data); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  protected: |  | ||||||
|   std::function<std::vector<uint8_t>(Ts...)> code_func_{}; |  | ||||||
|   std::vector<uint8_t> code_static_{}; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace remote_base | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ optional<NECData> NECProtocol::decode(RemoteReceiveData src) { | |||||||
|   return data; |   return data; | ||||||
| } | } | ||||||
| void NECProtocol::dump(const NECData &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 | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -232,7 +232,7 @@ optional<NexaData> NexaProtocol::decode(RemoteReceiveData src) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void NexaProtocol::dump(const NexaData &data) { | 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); |            data.state, data.channel, data.level); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ optional<PanasonicData> PanasonicProtocol::decode(RemoteReceiveData src) { | |||||||
|   return out; |   return out; | ||||||
| } | } | ||||||
| void PanasonicProtocol::dump(const PanasonicData &data) { | 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 | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -146,9 +146,9 @@ optional<PioneerData> PioneerProtocol::decode(RemoteReceiveData src) { | |||||||
| } | } | ||||||
| void PioneerProtocol::dump(const PioneerData &data) { | void PioneerProtocol::dump(const PioneerData &data) { | ||||||
|   if (data.rc_code_2 == 0) { |   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 { |   } 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); |     first = data.data.substr(0, 229); | ||||||
|     rest = data.data.substr(230); |     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()) { |   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)) { |     if (written < 0 || written >= int(remaining_length)) { | ||||||
|       // write failed, flush... |       // write failed, flush... | ||||||
|       buffer[buffer_offset] = '\0'; |       buffer[buffer_offset] = '\0'; | ||||||
|       ESP_LOGD(TAG, "%s", buffer); |       ESP_LOGI(TAG, "%s", buffer); | ||||||
|       buffer_offset = 0; |       buffer_offset = 0; | ||||||
|       written = sprintf(buffer, "  "); |       written = sprintf(buffer, "  "); | ||||||
|       if (i + 1 < src.size()) { |       if (i + 1 < src.size()) { | ||||||
| @@ -38,7 +38,7 @@ bool RawDumper::dump(RemoteReceiveData src) { | |||||||
|     buffer_offset += written; |     buffer_offset += written; | ||||||
|   } |   } | ||||||
|   if (buffer_offset != 0) { |   if (buffer_offset != 0) { | ||||||
|     ESP_LOGD(TAG, "%s", buffer); |     ESP_LOGI(TAG, "%s", buffer); | ||||||
|   } |   } | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -83,7 +83,7 @@ optional<RC5Data> RC5Protocol::decode(RemoteReceiveData src) { | |||||||
|   return out; |   return out; | ||||||
| } | } | ||||||
| void RC5Protocol::dump(const RC5Data &data) { | 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 | }  // namespace remote_base | ||||||
|   | |||||||
| @@ -173,7 +173,7 @@ optional<RC6Data> RC6Protocol::decode(RemoteReceiveData src) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void RC6Protocol::dump(const RC6Data &data) { | 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); |            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[j] = (out_data & ((uint64_t) 1 << (out_nbits - j - 1))) ? '1' : '0'; | ||||||
|  |  | ||||||
|       buffer[out_nbits] = '\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 |       // only send first decoded protocol | ||||||
|       return true; |       return true; | ||||||
|   | |||||||
| @@ -96,7 +96,7 @@ optional<Samsung36Data> Samsung36Protocol::decode(RemoteReceiveData src) { | |||||||
|   return out; |   return out; | ||||||
| } | } | ||||||
| void Samsung36Protocol::dump(const Samsung36Data &data) { | 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 | }  // 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