mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			56 Commits
		
	
	
		
			jesserockz
			...
			2023.2.0b4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4899dfe642 | ||
| 
						 | 
					d6b6e94059 | ||
| 
						 | 
					310355a00b | ||
| 
						 | 
					8cf26d6f3c | ||
| 
						 | 
					b15a10f905 | ||
| 
						 | 
					5dcf1debd7 | ||
| 
						 | 
					9b57e1ac1d | ||
| 
						 | 
					68683e3a50 | ||
| 
						 | 
					d83324c4dc | ||
| 
						 | 
					ecde4c1d2d | ||
| 
						 | 
					bd8e470726 | ||
| 
						 | 
					d2913fe627 | ||
| 
						 | 
					43acc7dc2c | ||
| 
						 | 
					e2a16d758b | ||
| 
						 | 
					17ea0efb08 | ||
| 
						 | 
					2fbd33267e | ||
| 
						 | 
					cf3977f088 | ||
| 
						 | 
					d20d4947ac | ||
| 
						 | 
					7810ad40d7 | ||
| 
						 | 
					7e1e799b3a | ||
| 
						 | 
					dfafc41ce6 | ||
| 
						 | 
					e460792c43 | ||
| 
						 | 
					a9dc491a54 | ||
| 
						 | 
					ac6693f177 | ||
| 
						 | 
					c6742117d3 | ||
| 
						 | 
					b5c47b9669 | ||
| 
						 | 
					40df3aa55e | ||
| 
						 | 
					393ca64d70 | ||
| 
						 | 
					d3627f0972 | ||
| 
						 | 
					124ab31f22 | ||
| 
						 | 
					1b66fa5004 | ||
| 
						 | 
					9494c27ad8 | ||
| 
						 | 
					3facfa5c21 | ||
| 
						 | 
					93ddce2e79 | ||
| 
						 | 
					0bf6e21e1a | ||
| 
						 | 
					6b7b076875 | ||
| 
						 | 
					8d6ffb9169 | ||
| 
						 | 
					cb520c00a5 | ||
| 
						 | 
					2f24138345 | ||
| 
						 | 
					96512b80cc | ||
| 
						 | 
					fcb9b51978 | ||
| 
						 | 
					f408f1a368 | ||
| 
						 | 
					7d8d563c62 | ||
| 
						 | 
					0a1f705fda | ||
| 
						 | 
					1952c1880b | ||
| 
						 | 
					b03967dac1 | ||
| 
						 | 
					bcae2596a6 | ||
| 
						 | 
					fc0347c86c | ||
| 
						 | 
					d9563d4de1 | ||
| 
						 | 
					cc7e2bf8db | ||
| 
						 | 
					5d98e2923b | ||
| 
						 | 
					07197d12f6 | ||
| 
						 | 
					7b0a298497 | ||
| 
						 | 
					21679cf2ba | ||
| 
						 | 
					4be7cd12a1 | ||
| 
						 | 
					dee4d0ccb7 | 
							
								
								
									
										26
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -138,14 +138,18 @@ jobs:
 | 
				
			|||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    needs: [deploy-docker]
 | 
					    needs: [deploy-docker]
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - env:
 | 
					      - name: Trigger Workflow
 | 
				
			||||||
          TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
					        uses: actions/github-script@v6
 | 
				
			||||||
        # yamllint disable rule:line-length
 | 
					        with:
 | 
				
			||||||
        run: |
 | 
					          github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
				
			||||||
          curl \
 | 
					          script: |
 | 
				
			||||||
            -u ":$TOKEN" \
 | 
					            github.rest.actions.createWorkflowDispatch({
 | 
				
			||||||
            -X POST \
 | 
					              owner: "esphome",
 | 
				
			||||||
            -H "Accept: application/vnd.github.v3+json" \
 | 
					              repo: "home-assistant-addon",
 | 
				
			||||||
            https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
 | 
					              workflow_id: "bump-version.yml",
 | 
				
			||||||
            -d '{"ref":"main","inputs":{"version":"${{ github.event.release.tag_name }}","content":${{ toJSON(github.event.release.body) }}}}'
 | 
					              ref: "main",
 | 
				
			||||||
        # yamllint enable rule:line-length
 | 
					              inputs: {
 | 
				
			||||||
 | 
					                version: "${{ github.event.release.tag_name }}",
 | 
				
			||||||
 | 
					                content: ${{ toJSON(github.event.release.body) }}
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -128,3 +128,5 @@ tests/.esphome/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
sdkconfig.*
 | 
					sdkconfig.*
 | 
				
			||||||
!sdkconfig.defaults
 | 
					!sdkconfig.defaults
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.tests/
 | 
				
			||||||
@@ -119,6 +119,7 @@ esphome/components/kalman_combinator/* @Cat-Ion
 | 
				
			|||||||
esphome/components/key_collector/* @ssieb
 | 
					esphome/components/key_collector/* @ssieb
 | 
				
			||||||
esphome/components/key_provider/* @ssieb
 | 
					esphome/components/key_provider/* @ssieb
 | 
				
			||||||
esphome/components/lcd_menu/* @numo68
 | 
					esphome/components/lcd_menu/* @numo68
 | 
				
			||||||
 | 
					esphome/components/ld2410/* @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
 | 
				
			||||||
@@ -274,6 +275,7 @@ esphome/components/uart/* @esphome/core
 | 
				
			|||||||
esphome/components/ufire_ec/* @pvizeli
 | 
					esphome/components/ufire_ec/* @pvizeli
 | 
				
			||||||
esphome/components/ufire_ise/* @pvizeli
 | 
					esphome/components/ufire_ise/* @pvizeli
 | 
				
			||||||
esphome/components/ultrasonic/* @OttoWinter
 | 
					esphome/components/ultrasonic/* @OttoWinter
 | 
				
			||||||
 | 
					esphome/components/vbus/* @ssieb
 | 
				
			||||||
esphome/components/version/* @esphome/core
 | 
					esphome/components/version/* @esphome/core
 | 
				
			||||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
					esphome/components/wake_on_lan/* @willwill2will54
 | 
				
			||||||
esphome/components/web_server_base/* @OttoWinter
 | 
					esphome/components/web_server_base/* @OttoWinter
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,7 +26,7 @@ RUN \
 | 
				
			|||||||
        python3-cryptography=3.3.2-1 \
 | 
					        python3-cryptography=3.3.2-1 \
 | 
				
			||||||
        iputils-ping=3:20210202-1 \
 | 
					        iputils-ping=3:20210202-1 \
 | 
				
			||||||
        git=1:2.30.2-1 \
 | 
					        git=1:2.30.2-1 \
 | 
				
			||||||
        curl=7.74.0-1.3+deb11u3 \
 | 
					        curl=7.74.0-1.3+deb11u5 \
 | 
				
			||||||
        openssh-client=1:8.4p1-5+deb11u1 \
 | 
					        openssh-client=1:8.4p1-5+deb11u1 \
 | 
				
			||||||
    && rm -rf \
 | 
					    && rm -rf \
 | 
				
			||||||
        /tmp/* \
 | 
					        /tmp/* \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -787,6 +787,7 @@ enum ClimateFanMode {
 | 
				
			|||||||
  CLIMATE_FAN_MIDDLE = 6;
 | 
					  CLIMATE_FAN_MIDDLE = 6;
 | 
				
			||||||
  CLIMATE_FAN_FOCUS = 7;
 | 
					  CLIMATE_FAN_FOCUS = 7;
 | 
				
			||||||
  CLIMATE_FAN_DIFFUSE = 8;
 | 
					  CLIMATE_FAN_DIFFUSE = 8;
 | 
				
			||||||
 | 
					  CLIMATE_FAN_QUIET = 9;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
enum ClimateSwingMode {
 | 
					enum ClimateSwingMode {
 | 
				
			||||||
  CLIMATE_SWING_OFF = 0;
 | 
					  CLIMATE_SWING_OFF = 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -235,6 +235,8 @@ template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::Climat
 | 
				
			|||||||
      return "CLIMATE_FAN_FOCUS";
 | 
					      return "CLIMATE_FAN_FOCUS";
 | 
				
			||||||
    case enums::CLIMATE_FAN_DIFFUSE:
 | 
					    case enums::CLIMATE_FAN_DIFFUSE:
 | 
				
			||||||
      return "CLIMATE_FAN_DIFFUSE";
 | 
					      return "CLIMATE_FAN_DIFFUSE";
 | 
				
			||||||
 | 
					    case enums::CLIMATE_FAN_QUIET:
 | 
				
			||||||
 | 
					      return "CLIMATE_FAN_QUIET";
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return "UNKNOWN";
 | 
					      return "UNKNOWN";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,6 +99,7 @@ enum ClimateFanMode : uint32_t {
 | 
				
			|||||||
  CLIMATE_FAN_MIDDLE = 6,
 | 
					  CLIMATE_FAN_MIDDLE = 6,
 | 
				
			||||||
  CLIMATE_FAN_FOCUS = 7,
 | 
					  CLIMATE_FAN_FOCUS = 7,
 | 
				
			||||||
  CLIMATE_FAN_DIFFUSE = 8,
 | 
					  CLIMATE_FAN_DIFFUSE = 8,
 | 
				
			||||||
 | 
					  CLIMATE_FAN_QUIET = 9,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
enum ClimateSwingMode : uint32_t {
 | 
					enum ClimateSwingMode : uint32_t {
 | 
				
			||||||
  CLIMATE_SWING_OFF = 0,
 | 
					  CLIMATE_SWING_OFF = 0,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,8 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_MODE_STATE_TOPIC,
 | 
					    CONF_MODE_STATE_TOPIC,
 | 
				
			||||||
    CONF_ON_STATE,
 | 
					    CONF_ON_STATE,
 | 
				
			||||||
    CONF_PRESET,
 | 
					    CONF_PRESET,
 | 
				
			||||||
 | 
					    CONF_PRESET_COMMAND_TOPIC,
 | 
				
			||||||
 | 
					    CONF_PRESET_STATE_TOPIC,
 | 
				
			||||||
    CONF_SWING_MODE,
 | 
					    CONF_SWING_MODE,
 | 
				
			||||||
    CONF_SWING_MODE_COMMAND_TOPIC,
 | 
					    CONF_SWING_MODE_COMMAND_TOPIC,
 | 
				
			||||||
    CONF_SWING_MODE_STATE_TOPIC,
 | 
					    CONF_SWING_MODE_STATE_TOPIC,
 | 
				
			||||||
@@ -73,6 +75,7 @@ CLIMATE_FAN_MODES = {
 | 
				
			|||||||
    "MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE,
 | 
					    "MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE,
 | 
				
			||||||
    "FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS,
 | 
					    "FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS,
 | 
				
			||||||
    "DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE,
 | 
					    "DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE,
 | 
				
			||||||
 | 
					    "QUIET": ClimateFanMode.CLIMATE_FAN_QUIET,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
 | 
					validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
 | 
				
			||||||
@@ -142,6 +145,12 @@ CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).
 | 
				
			|||||||
        cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
					        cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					            cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_PRESET_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
 | 
					            cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        cv.Optional(CONF_PRESET_STATE_TOPIC): cv.All(
 | 
				
			||||||
 | 
					            cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
        cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
 | 
					        cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
 | 
				
			||||||
            cv.requires_component("mqtt"), cv.publish_topic
 | 
					            cv.requires_component("mqtt"), cv.publish_topic
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
@@ -216,7 +225,12 @@ async def setup_climate_core_(var, config):
 | 
				
			|||||||
            cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC]))
 | 
					            cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC]))
 | 
				
			||||||
        if CONF_MODE_STATE_TOPIC in config:
 | 
					        if CONF_MODE_STATE_TOPIC in config:
 | 
				
			||||||
            cg.add(mqtt_.set_custom_mode_state_topic(config[CONF_MODE_STATE_TOPIC]))
 | 
					            cg.add(mqtt_.set_custom_mode_state_topic(config[CONF_MODE_STATE_TOPIC]))
 | 
				
			||||||
 | 
					        if CONF_PRESET_COMMAND_TOPIC in config:
 | 
				
			||||||
 | 
					            cg.add(
 | 
				
			||||||
 | 
					                mqtt_.set_custom_preset_command_topic(config[CONF_PRESET_COMMAND_TOPIC])
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        if CONF_PRESET_STATE_TOPIC in config:
 | 
				
			||||||
 | 
					            cg.add(mqtt_.set_custom_preset_state_topic(config[CONF_PRESET_STATE_TOPIC]))
 | 
				
			||||||
        if CONF_SWING_MODE_COMMAND_TOPIC in config:
 | 
					        if CONF_SWING_MODE_COMMAND_TOPIC in config:
 | 
				
			||||||
            cg.add(
 | 
					            cg.add(
 | 
				
			||||||
                mqtt_.set_custom_swing_mode_command_topic(
 | 
					                mqtt_.set_custom_swing_mode_command_topic(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -174,6 +174,8 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
 | 
				
			|||||||
    this->set_fan_mode(CLIMATE_FAN_FOCUS);
 | 
					    this->set_fan_mode(CLIMATE_FAN_FOCUS);
 | 
				
			||||||
  } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
 | 
					  } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
 | 
				
			||||||
    this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
 | 
					    this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
 | 
				
			||||||
 | 
					  } else if (str_equals_case_insensitive(fan_mode, "QUIET")) {
 | 
				
			||||||
 | 
					    this->set_fan_mode(CLIMATE_FAN_QUIET);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) {
 | 
					    if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) {
 | 
				
			||||||
      this->custom_fan_mode_ = fan_mode;
 | 
					      this->custom_fan_mode_ = fan_mode;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,6 +62,8 @@ const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
 | 
				
			|||||||
      return LOG_STR("FOCUS");
 | 
					      return LOG_STR("FOCUS");
 | 
				
			||||||
    case climate::CLIMATE_FAN_DIFFUSE:
 | 
					    case climate::CLIMATE_FAN_DIFFUSE:
 | 
				
			||||||
      return LOG_STR("DIFFUSE");
 | 
					      return LOG_STR("DIFFUSE");
 | 
				
			||||||
 | 
					    case climate::CLIMATE_FAN_QUIET:
 | 
				
			||||||
 | 
					      return LOG_STR("QUIET");
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return LOG_STR("UNKNOWN");
 | 
					      return LOG_STR("UNKNOWN");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,6 +62,8 @@ enum ClimateFanMode : uint8_t {
 | 
				
			|||||||
  CLIMATE_FAN_FOCUS = 7,
 | 
					  CLIMATE_FAN_FOCUS = 7,
 | 
				
			||||||
  /// The fan mode is set to Diffuse
 | 
					  /// The fan mode is set to Diffuse
 | 
				
			||||||
  CLIMATE_FAN_DIFFUSE = 8,
 | 
					  CLIMATE_FAN_DIFFUSE = 8,
 | 
				
			||||||
 | 
					  /// The fan mode is set to Quiet
 | 
				
			||||||
 | 
					  CLIMATE_FAN_QUIET = 9,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Enum for all modes a climate swing can be in
 | 
					/// Enum for all modes a climate swing can be in
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ namespace climate {
 | 
				
			|||||||
 *  - supports action - if the climate device supports reporting the active
 | 
					 *  - supports action - if the climate device supports reporting the active
 | 
				
			||||||
 *    current action of the device with the action property.
 | 
					 *    current action of the device with the action property.
 | 
				
			||||||
 *  - supports fan modes - optionally, if it has a fan which can be configured in different ways:
 | 
					 *  - supports fan modes - optionally, if it has a fan which can be configured in different ways:
 | 
				
			||||||
 *    - on, off, auto, high, medium, low, middle, focus, diffuse
 | 
					 *    - on, off, auto, high, medium, low, middle, focus, diffuse, quiet
 | 
				
			||||||
 *  - supports swing modes - optionally, if it has a swing which can be configured in different ways:
 | 
					 *  - supports swing modes - optionally, if it has a swing which can be configured in different ways:
 | 
				
			||||||
 *    - off, both, vertical, horizontal
 | 
					 *    - off, both, vertical, horizontal
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					import base64
 | 
				
			||||||
 | 
					import secrets
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from typing import Optional
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,6 +75,7 @@ def import_config(
 | 
				
			|||||||
    project_name: str,
 | 
					    project_name: str,
 | 
				
			||||||
    import_url: str,
 | 
					    import_url: str,
 | 
				
			||||||
    network: str = CONF_WIFI,
 | 
					    network: str = CONF_WIFI,
 | 
				
			||||||
 | 
					    encryption: bool = False,
 | 
				
			||||||
) -> None:
 | 
					) -> None:
 | 
				
			||||||
    p = Path(path)
 | 
					    p = Path(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -80,15 +83,21 @@ def import_config(
 | 
				
			|||||||
        raise FileExistsError
 | 
					        raise FileExistsError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if project_name == "esphome.web":
 | 
					    if project_name == "esphome.web":
 | 
				
			||||||
 | 
					        kwargs = {
 | 
				
			||||||
 | 
					            "name": name,
 | 
				
			||||||
 | 
					            "friendly_name": friendly_name,
 | 
				
			||||||
 | 
					            "platform": "ESP32" if "esp32" in import_url else "ESP8266",
 | 
				
			||||||
 | 
					            "board": "esp32dev" if "esp32" in import_url else "esp01_1m",
 | 
				
			||||||
 | 
					            "ssid": "!secret wifi_ssid",
 | 
				
			||||||
 | 
					            "psk": "!secret wifi_password",
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if encryption:
 | 
				
			||||||
 | 
					            noise_psk = secrets.token_bytes(32)
 | 
				
			||||||
 | 
					            key = base64.b64encode(noise_psk).decode()
 | 
				
			||||||
 | 
					            kwargs["api_encryption_key"] = key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        p.write_text(
 | 
					        p.write_text(
 | 
				
			||||||
            wizard_file(
 | 
					            wizard_file(**kwargs),
 | 
				
			||||||
                name=name,
 | 
					 | 
				
			||||||
                friendly_name=friendly_name,
 | 
					 | 
				
			||||||
                platform="ESP32" if "esp32" in import_url else "ESP8266",
 | 
					 | 
				
			||||||
                board="esp32dev" if "esp32" in import_url else "esp01_1m",
 | 
					 | 
				
			||||||
                ssid="!secret wifi_ssid",
 | 
					 | 
				
			||||||
                psk="!secret wifi_password",
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
            encoding="utf8",
 | 
					            encoding="utf8",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
@@ -115,6 +124,11 @@ def import_config(
 | 
				
			|||||||
                "packages": {project_name: import_url},
 | 
					                "packages": {project_name: import_url},
 | 
				
			||||||
                "esphome": esphome_core,
 | 
					                "esphome": esphome_core,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if encryption:
 | 
				
			||||||
 | 
					                noise_psk = secrets.token_bytes(32)
 | 
				
			||||||
 | 
					                key = base64.b64encode(noise_psk).decode()
 | 
				
			||||||
 | 
					                config["api"] = {"encryption": {"key": key}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            output = dump(config)
 | 
					            output = dump(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if network == CONF_WIFI:
 | 
					            if network == CONF_WIFI:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,6 +111,7 @@ class DemoClimate : public climate::Climate, public Component {
 | 
				
			|||||||
            climate::CLIMATE_FAN_MIDDLE,
 | 
					            climate::CLIMATE_FAN_MIDDLE,
 | 
				
			||||||
            climate::CLIMATE_FAN_FOCUS,
 | 
					            climate::CLIMATE_FAN_FOCUS,
 | 
				
			||||||
            climate::CLIMATE_FAN_DIFFUSE,
 | 
					            climate::CLIMATE_FAN_DIFFUSE,
 | 
				
			||||||
 | 
					            climate::CLIMATE_FAN_QUIET,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        traits.set_supported_custom_fan_modes({"Auto Low", "Auto High"});
 | 
					        traits.set_supported_custom_fan_modes({"Auto Low", "Auto High"});
 | 
				
			||||||
        traits.set_supported_swing_modes({
 | 
					        traits.set_supported_swing_modes({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,84 @@ static const char *const TAG = "display";
 | 
				
			|||||||
const Color COLOR_OFF(0, 0, 0, 0);
 | 
					const Color COLOR_OFF(0, 0, 0, 0);
 | 
				
			||||||
const Color COLOR_ON(255, 255, 255, 255);
 | 
					const Color COLOR_ON(255, 255, 255, 255);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Rect::expand(int16_t horizontal, int16_t vertical) {
 | 
				
			||||||
 | 
					  if (this->is_set() && (this->w >= (-2 * horizontal)) && (this->h >= (-2 * vertical))) {
 | 
				
			||||||
 | 
					    this->x = this->x - horizontal;
 | 
				
			||||||
 | 
					    this->y = this->y - vertical;
 | 
				
			||||||
 | 
					    this->w = this->w + (2 * horizontal);
 | 
				
			||||||
 | 
					    this->h = this->h + (2 * vertical);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Rect::extend(Rect rect) {
 | 
				
			||||||
 | 
					  if (!this->is_set()) {
 | 
				
			||||||
 | 
					    this->x = rect.x;
 | 
				
			||||||
 | 
					    this->y = rect.y;
 | 
				
			||||||
 | 
					    this->w = rect.w;
 | 
				
			||||||
 | 
					    this->h = rect.h;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    if (this->x > rect.x) {
 | 
				
			||||||
 | 
					      this->x = rect.x;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->y > rect.y) {
 | 
				
			||||||
 | 
					      this->y = rect.y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->x2() < rect.x2()) {
 | 
				
			||||||
 | 
					      this->w = rect.x2() - this->x;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->y2() < rect.y2()) {
 | 
				
			||||||
 | 
					      this->h = rect.y2() - this->y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void Rect::shrink(Rect rect) {
 | 
				
			||||||
 | 
					  if (!this->inside(rect)) {
 | 
				
			||||||
 | 
					    (*this) = Rect();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    if (this->x < rect.x) {
 | 
				
			||||||
 | 
					      this->x = rect.x;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->y < rect.y) {
 | 
				
			||||||
 | 
					      this->y = rect.y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->x2() > rect.x2()) {
 | 
				
			||||||
 | 
					      this->w = rect.x2() - this->x;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->y2() > rect.y2()) {
 | 
				
			||||||
 | 
					      this->h = rect.y2() - this->y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Rect::inside(int16_t x, int16_t y, bool absolute) {  // NOLINT
 | 
				
			||||||
 | 
					  if (!this->is_set()) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (absolute) {
 | 
				
			||||||
 | 
					    return ((x >= 0) && (x <= this->w) && (y >= 0) && (y <= this->h));
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return ((x >= this->x) && (x <= this->x2()) && (y >= this->y) && (y <= this->y2()));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Rect::inside(Rect rect, bool absolute) {
 | 
				
			||||||
 | 
					  if (!this->is_set() || !rect.is_set()) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (absolute) {
 | 
				
			||||||
 | 
					    return ((rect.x <= this->w) && (rect.w >= 0) && (rect.y <= this->h) && (rect.h >= 0));
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return ((rect.x <= this->x2()) && (rect.x2() >= this->x) && (rect.y <= this->y2()) && (rect.y2() >= this->y));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Rect::info(const std::string &prefix) {
 | 
				
			||||||
 | 
					  if (this->is_set()) {
 | 
				
			||||||
 | 
					    ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d]", prefix.c_str(), this->x, this->y, this->w, this->h);
 | 
				
			||||||
 | 
					  } else
 | 
				
			||||||
 | 
					    ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
 | 
					void DisplayBuffer::init_internal_(uint32_t buffer_length) {
 | 
				
			||||||
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
					  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
				
			||||||
  this->buffer_ = allocator.allocate(buffer_length);
 | 
					  this->buffer_ = allocator.allocate(buffer_length);
 | 
				
			||||||
@@ -24,6 +102,7 @@ void DisplayBuffer::init_internal_(uint32_t buffer_length) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  this->clear();
 | 
					  this->clear();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void DisplayBuffer::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
 | 
					void DisplayBuffer::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
 | 
				
			||||||
void DisplayBuffer::clear() { this->fill(COLOR_OFF); }
 | 
					void DisplayBuffer::clear() { this->fill(COLOR_OFF); }
 | 
				
			||||||
int DisplayBuffer::get_width() {
 | 
					int DisplayBuffer::get_width() {
 | 
				
			||||||
@@ -50,6 +129,9 @@ int DisplayBuffer::get_height() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
void DisplayBuffer::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
 | 
					void DisplayBuffer::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
 | 
				
			||||||
void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
 | 
					void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
 | 
				
			||||||
 | 
					  if (!this->get_clipping().inside(x, y))
 | 
				
			||||||
 | 
					    return;  // NOLINT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  switch (this->rotation_) {
 | 
					  switch (this->rotation_) {
 | 
				
			||||||
    case DISPLAY_ROTATION_0_DEGREES:
 | 
					    case DISPLAY_ROTATION_0_DEGREES:
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
@@ -368,6 +450,10 @@ void DisplayBuffer::do_update_() {
 | 
				
			|||||||
  } else if (this->writer_.has_value()) {
 | 
					  } else if (this->writer_.has_value()) {
 | 
				
			||||||
    (*this->writer_)(*this);
 | 
					    (*this->writer_)(*this);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  // remove all not ended clipping regions
 | 
				
			||||||
 | 
					  while (is_clipping()) {
 | 
				
			||||||
 | 
					    end_clipping();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
 | 
					void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
 | 
				
			||||||
  if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
 | 
					  if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
 | 
				
			||||||
@@ -392,6 +478,41 @@ void DisplayBuffer::strftime(int x, int y, Font *font, const char *format, time:
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DisplayBuffer::start_clipping(Rect rect) {
 | 
				
			||||||
 | 
					  if (!this->clipping_rectangle_.empty()) {
 | 
				
			||||||
 | 
					    Rect r = this->clipping_rectangle_.back();
 | 
				
			||||||
 | 
					    rect.shrink(r);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this->clipping_rectangle_.push_back(rect);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void DisplayBuffer::end_clipping() {
 | 
				
			||||||
 | 
					  if (this->clipping_rectangle_.empty()) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "clear: Clipping is not set.");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->clipping_rectangle_.pop_back();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void DisplayBuffer::extend_clipping(Rect add_rect) {
 | 
				
			||||||
 | 
					  if (this->clipping_rectangle_.empty()) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "add: Clipping is not set.");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->clipping_rectangle_.back().extend(add_rect);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void DisplayBuffer::shrink_clipping(Rect add_rect) {
 | 
				
			||||||
 | 
					  if (this->clipping_rectangle_.empty()) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "add: Clipping is not set.");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    this->clipping_rectangle_.back().shrink(add_rect);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					Rect DisplayBuffer::get_clipping() {
 | 
				
			||||||
 | 
					  if (this->clipping_rectangle_.empty()) {
 | 
				
			||||||
 | 
					    return Rect();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return this->clipping_rectangle_.back();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
bool Glyph::get_pixel(int x, int y) const {
 | 
					bool Glyph::get_pixel(int x, int y) const {
 | 
				
			||||||
  const int x_data = x - this->glyph_data_->offset_x;
 | 
					  const int x_data = x - this->glyph_data_->offset_x;
 | 
				
			||||||
  const int y_data = y - this->glyph_data_->offset_y;
 | 
					  const int y_data = y - this->glyph_data_->offset_y;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,6 @@
 | 
				
			|||||||
#include "esphome/core/defines.h"
 | 
					#include "esphome/core/defines.h"
 | 
				
			||||||
#include "esphome/core/automation.h"
 | 
					#include "esphome/core/automation.h"
 | 
				
			||||||
#include "display_color_utils.h"
 | 
					#include "display_color_utils.h"
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cstdarg>
 | 
					#include <cstdarg>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,6 +99,32 @@ enum DisplayRotation {
 | 
				
			|||||||
  DISPLAY_ROTATION_270_DEGREES = 270,
 | 
					  DISPLAY_ROTATION_270_DEGREES = 270,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const int16_t VALUE_NO_SET = 32766;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Rect {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  int16_t x;  ///< X coordinate of corner
 | 
				
			||||||
 | 
					  int16_t y;  ///< Y coordinate of corner
 | 
				
			||||||
 | 
					  int16_t w;  ///< Width of region
 | 
				
			||||||
 | 
					  int16_t h;  ///< Height of region
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Rect() : x(VALUE_NO_SET), y(VALUE_NO_SET), w(VALUE_NO_SET), h(VALUE_NO_SET) {}  // NOLINT
 | 
				
			||||||
 | 
					  inline Rect(int16_t x, int16_t y, int16_t w, int16_t h) ALWAYS_INLINE : x(x), y(y), w(w), h(h) {}
 | 
				
			||||||
 | 
					  inline int16_t x2() { return this->x + this->w; };  ///< X coordinate of corner
 | 
				
			||||||
 | 
					  inline int16_t y2() { return this->y + this->h; };  ///< Y coordinate of corner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  inline bool is_set() ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void expand(int16_t horizontal, int16_t vertical);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void extend(Rect rect);
 | 
				
			||||||
 | 
					  void shrink(Rect rect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool inside(Rect rect, bool absolute = false);
 | 
				
			||||||
 | 
					  bool inside(int16_t x, int16_t y, bool absolute = false);
 | 
				
			||||||
 | 
					  void info(const std::string &prefix = "rect info:");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Font;
 | 
					class Font;
 | 
				
			||||||
class Image;
 | 
					class Image;
 | 
				
			||||||
class DisplayBuffer;
 | 
					class DisplayBuffer;
 | 
				
			||||||
@@ -126,6 +151,7 @@ class DisplayBuffer {
 | 
				
			|||||||
  int get_width();
 | 
					  int get_width();
 | 
				
			||||||
  /// Get the height of the image in pixels with rotation applied.
 | 
					  /// Get the height of the image in pixels with rotation applied.
 | 
				
			||||||
  int get_height();
 | 
					  int get_height();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Set a single pixel at the specified coordinates to the given color.
 | 
					  /// Set a single pixel at the specified coordinates to the given color.
 | 
				
			||||||
  void draw_pixel_at(int x, int y, Color color = COLOR_ON);
 | 
					  void draw_pixel_at(int x, int y, Color color = COLOR_ON);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -374,6 +400,49 @@ class DisplayBuffer {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  virtual DisplayType get_display_type() = 0;
 | 
					  virtual DisplayType get_display_type() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Set the clipping rectangle for further drawing
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param[in]  rect:       Pointer to Rect for clipping (or NULL for entire screen)
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * return true if success, false if error
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  void start_clipping(Rect rect);
 | 
				
			||||||
 | 
					  void start_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
 | 
				
			||||||
 | 
					    start_clipping(Rect(left, top, right - left, bottom - top));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Add a rectangular region to the invalidation region
 | 
				
			||||||
 | 
					   * - This is usually called when an element has been modified
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param[in]  rect: Rectangle to add to the invalidation region
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  void extend_clipping(Rect rect);
 | 
				
			||||||
 | 
					  void extend_clipping(int16_t left, int16_t top, int16_t right, int16_t bottom) {
 | 
				
			||||||
 | 
					    this->extend_clipping(Rect(left, top, right - left, bottom - top));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** substract a rectangular region to the invalidation region
 | 
				
			||||||
 | 
					   *  - This is usually called when an element has been modified
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param[in]  rect: Rectangle to add to the invalidation region
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  void shrink_clipping(Rect rect);
 | 
				
			||||||
 | 
					  void shrink_clipping(uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {
 | 
				
			||||||
 | 
					    this->shrink_clipping(Rect(left, top, right - left, bottom - top));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Reset the invalidation region
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  void end_clipping();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Get the current the clipping rectangle
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * return rect for active clipping region
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  Rect get_clipping();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);
 | 
					  void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -390,6 +459,7 @@ class DisplayBuffer {
 | 
				
			|||||||
  DisplayPage *previous_page_{nullptr};
 | 
					  DisplayPage *previous_page_{nullptr};
 | 
				
			||||||
  std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
 | 
					  std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
 | 
				
			||||||
  bool auto_clear_enabled_{true};
 | 
					  bool auto_clear_enabled_{true};
 | 
				
			||||||
 | 
					  std::vector<Rect> clipping_rectangle_;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DisplayPage {
 | 
					class DisplayPage {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_TRIGGER_ID,
 | 
					    CONF_TRIGGER_ID,
 | 
				
			||||||
    CONF_ON_VALUE,
 | 
					    CONF_ON_VALUE,
 | 
				
			||||||
    CONF_COMMAND,
 | 
					    CONF_COMMAND,
 | 
				
			||||||
 | 
					    CONF_CUSTOM,
 | 
				
			||||||
    CONF_NUMBER,
 | 
					    CONF_NUMBER,
 | 
				
			||||||
    CONF_FORMAT,
 | 
					    CONF_FORMAT,
 | 
				
			||||||
    CONF_MODE,
 | 
					    CONF_MODE,
 | 
				
			||||||
@@ -32,7 +33,6 @@ CONF_BACK = "back"
 | 
				
			|||||||
CONF_TEXT = "text"
 | 
					CONF_TEXT = "text"
 | 
				
			||||||
CONF_SELECT = "select"
 | 
					CONF_SELECT = "select"
 | 
				
			||||||
CONF_SWITCH = "switch"
 | 
					CONF_SWITCH = "switch"
 | 
				
			||||||
CONF_CUSTOM = "custom"
 | 
					 | 
				
			||||||
CONF_ITEMS = "items"
 | 
					CONF_ITEMS = "items"
 | 
				
			||||||
CONF_ON_TEXT = "on_text"
 | 
					CONF_ON_TEXT = "on_text"
 | 
				
			||||||
CONF_OFF_TEXT = "off_text"
 | 
					CONF_OFF_TEXT = "off_text"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,11 +123,8 @@ def validate_gpio_pin(value):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def validate_supports(value):
 | 
					def validate_supports(value):
 | 
				
			||||||
    mode = value[CONF_MODE]
 | 
					    mode = value[CONF_MODE]
 | 
				
			||||||
    is_input = mode[CONF_INPUT]
 | 
					 | 
				
			||||||
    is_output = mode[CONF_OUTPUT]
 | 
					    is_output = mode[CONF_OUTPUT]
 | 
				
			||||||
    is_open_drain = mode[CONF_OPEN_DRAIN]
 | 
					    is_open_drain = mode[CONF_OPEN_DRAIN]
 | 
				
			||||||
    is_pullup = mode[CONF_PULLUP]
 | 
					 | 
				
			||||||
    is_pulldown = mode[CONF_PULLDOWN]
 | 
					 | 
				
			||||||
    variant = CORE.data[KEY_ESP32][KEY_VARIANT]
 | 
					    variant = CORE.data[KEY_ESP32][KEY_VARIANT]
 | 
				
			||||||
    if variant not in _esp32_validations:
 | 
					    if variant not in _esp32_validations:
 | 
				
			||||||
        raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
 | 
					        raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
 | 
				
			||||||
@@ -138,26 +135,6 @@ def validate_supports(value):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    value = _esp32_validations[variant].usage_validation(value)
 | 
					    value = _esp32_validations[variant].usage_validation(value)
 | 
				
			||||||
    if CORE.using_arduino:
 | 
					 | 
				
			||||||
        # (input, output, open_drain, pullup, pulldown)
 | 
					 | 
				
			||||||
        supported_modes = {
 | 
					 | 
				
			||||||
            # INPUT
 | 
					 | 
				
			||||||
            (True, False, False, False, False),
 | 
					 | 
				
			||||||
            # OUTPUT
 | 
					 | 
				
			||||||
            (False, True, False, False, False),
 | 
					 | 
				
			||||||
            # INPUT_PULLUP
 | 
					 | 
				
			||||||
            (True, False, False, True, False),
 | 
					 | 
				
			||||||
            # INPUT_PULLDOWN
 | 
					 | 
				
			||||||
            (True, False, False, False, True),
 | 
					 | 
				
			||||||
            # OUTPUT_OPEN_DRAIN
 | 
					 | 
				
			||||||
            (False, True, True, False, False),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown)
 | 
					 | 
				
			||||||
        if key not in supported_modes:
 | 
					 | 
				
			||||||
            raise cv.Invalid(
 | 
					 | 
				
			||||||
                "This pin mode is not supported on ESP32 for arduino frameworks",
 | 
					 | 
				
			||||||
                [CONF_MODE],
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
    return value
 | 
					    return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,7 +195,7 @@ void ESP32ImprovComponent::send_response_(std::vector<uint8_t> &response) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ESP32ImprovComponent::start() {
 | 
					void ESP32ImprovComponent::start() {
 | 
				
			||||||
  if (this->state_ != improv::STATE_STOPPED)
 | 
					  if (this->should_start_ || this->state_ != improv::STATE_STOPPED)
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ESP_LOGD(TAG, "Setting Improv to start");
 | 
					  ESP_LOGD(TAG, "Setting Improv to start");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ ETHERNET_TYPES = {
 | 
				
			|||||||
    "RTL8201": EthernetType.ETHERNET_TYPE_RTL8201,
 | 
					    "RTL8201": EthernetType.ETHERNET_TYPE_RTL8201,
 | 
				
			||||||
    "DP83848": EthernetType.ETHERNET_TYPE_DP83848,
 | 
					    "DP83848": EthernetType.ETHERNET_TYPE_DP83848,
 | 
				
			||||||
    "IP101": EthernetType.ETHERNET_TYPE_IP101,
 | 
					    "IP101": EthernetType.ETHERNET_TYPE_IP101,
 | 
				
			||||||
 | 
					    "JL1101": EthernetType.ETHERNET_TYPE_JL1101,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t")
 | 
					emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t")
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										339
									
								
								esphome/components/ethernet/esp_eth_phy_jl1101.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								esphome/components/ethernet/esp_eth_phy_jl1101.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,339 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_ESP32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <sys/cdefs.h>
 | 
				
			||||||
 | 
					#include "esp_log.h"
 | 
				
			||||||
 | 
					#include "esp_eth.h"
 | 
				
			||||||
 | 
					#include "eth_phy_regs_struct.h"
 | 
				
			||||||
 | 
					#include "freertos/FreeRTOS.h"
 | 
				
			||||||
 | 
					#include "freertos/task.h"
 | 
				
			||||||
 | 
					#include "driver/gpio.h"
 | 
				
			||||||
 | 
					#include "esp_rom_gpio.h"
 | 
				
			||||||
 | 
					#include "esp_rom_sys.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *TAG = "jl1101";
 | 
				
			||||||
 | 
					#define PHY_CHECK(a, str, goto_tag, ...) \
 | 
				
			||||||
 | 
					  do { \
 | 
				
			||||||
 | 
					    if (!(a)) { \
 | 
				
			||||||
 | 
					      ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
 | 
				
			||||||
 | 
					      goto goto_tag; \
 | 
				
			||||||
 | 
					    } \
 | 
				
			||||||
 | 
					  } while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***************Vendor Specific Register***************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief PSR(Page Select Register)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef union {
 | 
				
			||||||
 | 
					  struct {
 | 
				
			||||||
 | 
					    uint16_t page_select : 8; /* Select register page, default is 0 */
 | 
				
			||||||
 | 
					    uint16_t reserved : 8;    /* Reserved */
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  uint16_t val;
 | 
				
			||||||
 | 
					} psr_reg_t;
 | 
				
			||||||
 | 
					#define ETH_PHY_PSR_REG_ADDR (0x1F)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					  esp_eth_phy_t parent;
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth;
 | 
				
			||||||
 | 
					  int addr;
 | 
				
			||||||
 | 
					  uint32_t reset_timeout_ms;
 | 
				
			||||||
 | 
					  uint32_t autonego_timeout_ms;
 | 
				
			||||||
 | 
					  eth_link_t link_status;
 | 
				
			||||||
 | 
					  int reset_gpio_num;
 | 
				
			||||||
 | 
					} phy_jl1101_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_page_select(phy_jl1101_t *jl1101, uint32_t page) {
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth = jl1101->eth;
 | 
				
			||||||
 | 
					  psr_reg_t psr = {.page_select = page};
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_write(eth, jl1101->addr, ETH_PHY_PSR_REG_ADDR, psr.val) == ESP_OK, "write PSR failed", err);
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_update_link_duplex_speed(phy_jl1101_t *jl1101) {
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth = jl1101->eth;
 | 
				
			||||||
 | 
					  eth_speed_t speed = ETH_SPEED_10M;
 | 
				
			||||||
 | 
					  eth_duplex_t duplex = ETH_DUPLEX_HALF;
 | 
				
			||||||
 | 
					  bmcr_reg_t bmcr;
 | 
				
			||||||
 | 
					  bmsr_reg_t bmsr;
 | 
				
			||||||
 | 
					  uint32_t peer_pause_ability = false;
 | 
				
			||||||
 | 
					  anlpar_reg_t anlpar;
 | 
				
			||||||
 | 
					  PHY_CHECK(jl1101_page_select(jl1101, 0) == ESP_OK, "select page 0 failed", err);
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed",
 | 
				
			||||||
 | 
					            err);
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)) == ESP_OK,
 | 
				
			||||||
 | 
					            "read ANLPAR failed", err);
 | 
				
			||||||
 | 
					  eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN;
 | 
				
			||||||
 | 
					  /* check if link status changed */
 | 
				
			||||||
 | 
					  if (jl1101->link_status != link) {
 | 
				
			||||||
 | 
					    /* when link up, read negotiation result */
 | 
				
			||||||
 | 
					    if (link == ETH_LINK_UP) {
 | 
				
			||||||
 | 
					      PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed",
 | 
				
			||||||
 | 
					                err);
 | 
				
			||||||
 | 
					      if (bmcr.speed_select) {
 | 
				
			||||||
 | 
					        speed = ETH_SPEED_100M;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        speed = ETH_SPEED_10M;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (bmcr.duplex_mode) {
 | 
				
			||||||
 | 
					        duplex = ETH_DUPLEX_FULL;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        duplex = ETH_DUPLEX_HALF;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *) speed) == ESP_OK, "change speed failed", err);
 | 
				
			||||||
 | 
					      PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *) duplex) == ESP_OK, "change duplex failed", err);
 | 
				
			||||||
 | 
					      /* if we're in duplex mode, and peer has the flow control ability */
 | 
				
			||||||
 | 
					      if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) {
 | 
				
			||||||
 | 
					        peer_pause_ability = 1;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        peer_pause_ability = 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *) peer_pause_ability) == ESP_OK,
 | 
				
			||||||
 | 
					                "change pause ability failed", err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *) link) == ESP_OK, "change link failed", err);
 | 
				
			||||||
 | 
					    jl1101->link_status = link;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth) {
 | 
				
			||||||
 | 
					  PHY_CHECK(eth, "can't set mediator to null", err);
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  jl1101->eth = eth;
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_ERR_INVALID_ARG;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_get_link(esp_eth_phy_t *phy) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  /* Updata information about link, speed, duplex */
 | 
				
			||||||
 | 
					  PHY_CHECK(jl1101_update_link_duplex_speed(jl1101) == ESP_OK, "update link duplex speed failed", err);
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_reset(esp_eth_phy_t *phy) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  jl1101->link_status = ETH_LINK_DOWN;
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth = jl1101->eth;
 | 
				
			||||||
 | 
					  bmcr_reg_t bmcr = {.reset = 1};
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_write(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
 | 
				
			||||||
 | 
					  /* Wait for reset complete */
 | 
				
			||||||
 | 
					  uint32_t to = 0;
 | 
				
			||||||
 | 
					  for (to = 0; to < jl1101->reset_timeout_ms / 50; to++) {
 | 
				
			||||||
 | 
					    vTaskDelay(pdMS_TO_TICKS(50));
 | 
				
			||||||
 | 
					    PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed",
 | 
				
			||||||
 | 
					              err);
 | 
				
			||||||
 | 
					    if (!bmcr.reset) {
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  PHY_CHECK(to < jl1101->reset_timeout_ms / 50, "reset timeout", err);
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_reset_hw(esp_eth_phy_t *phy) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  if (jl1101->reset_gpio_num >= 0) {
 | 
				
			||||||
 | 
					    esp_rom_gpio_pad_select_gpio(jl1101->reset_gpio_num);
 | 
				
			||||||
 | 
					    gpio_set_direction(jl1101->reset_gpio_num, GPIO_MODE_OUTPUT);
 | 
				
			||||||
 | 
					    gpio_set_level(jl1101->reset_gpio_num, 0);
 | 
				
			||||||
 | 
					    esp_rom_delay_us(100);  // insert min input assert time
 | 
				
			||||||
 | 
					    gpio_set_level(jl1101->reset_gpio_num, 1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_negotiate(esp_eth_phy_t *phy) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth = jl1101->eth;
 | 
				
			||||||
 | 
					  /* in case any link status has changed, let's assume we're in link down status */
 | 
				
			||||||
 | 
					  jl1101->link_status = ETH_LINK_DOWN;
 | 
				
			||||||
 | 
					  /* Restart auto negotiation */
 | 
				
			||||||
 | 
					  bmcr_reg_t bmcr = {
 | 
				
			||||||
 | 
					      .speed_select = 1,     /* 100Mbps */
 | 
				
			||||||
 | 
					      .duplex_mode = 1,      /* Full Duplex */
 | 
				
			||||||
 | 
					      .en_auto_nego = 1,     /* Auto Negotiation */
 | 
				
			||||||
 | 
					      .restart_auto_nego = 1 /* Restart Auto Negotiation */
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_write(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
 | 
				
			||||||
 | 
					  /* Wait for auto negotiation complete */
 | 
				
			||||||
 | 
					  bmsr_reg_t bmsr;
 | 
				
			||||||
 | 
					  uint32_t to = 0;
 | 
				
			||||||
 | 
					  for (to = 0; to < jl1101->autonego_timeout_ms / 100; to++) {
 | 
				
			||||||
 | 
					    vTaskDelay(pdMS_TO_TICKS(100));
 | 
				
			||||||
 | 
					    PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, "read BMSR failed",
 | 
				
			||||||
 | 
					              err);
 | 
				
			||||||
 | 
					    if (bmsr.auto_nego_complete) {
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /* Auto negotiation failed, maybe no network cable plugged in, so output a warning */
 | 
				
			||||||
 | 
					  if (to >= jl1101->autonego_timeout_ms / 100) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "auto negotiation timeout");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_pwrctl(esp_eth_phy_t *phy, bool enable) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth = jl1101->eth;
 | 
				
			||||||
 | 
					  bmcr_reg_t bmcr;
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed",
 | 
				
			||||||
 | 
					            err);
 | 
				
			||||||
 | 
					  if (!enable) {
 | 
				
			||||||
 | 
					    /* Enable IEEE Power Down Mode */
 | 
				
			||||||
 | 
					    bmcr.power_down = 1;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    /* Disable IEEE Power Down Mode */
 | 
				
			||||||
 | 
					    bmcr.power_down = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_write(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, bmcr.val) == ESP_OK, "write BMCR failed", err);
 | 
				
			||||||
 | 
					  if (!enable) {
 | 
				
			||||||
 | 
					    PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed",
 | 
				
			||||||
 | 
					              err);
 | 
				
			||||||
 | 
					    PHY_CHECK(bmcr.power_down == 1, "power down failed", err);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    /* wait for power up complete */
 | 
				
			||||||
 | 
					    uint32_t to = 0;
 | 
				
			||||||
 | 
					    for (to = 0; to < jl1101->reset_timeout_ms / 10; to++) {
 | 
				
			||||||
 | 
					      vTaskDelay(pdMS_TO_TICKS(10));
 | 
				
			||||||
 | 
					      PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)) == ESP_OK, "read BMCR failed",
 | 
				
			||||||
 | 
					                err);
 | 
				
			||||||
 | 
					      if (bmcr.power_down == 0) {
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    PHY_CHECK(to < jl1101->reset_timeout_ms / 10, "power up timeout", err);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_set_addr(esp_eth_phy_t *phy, uint32_t addr) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  jl1101->addr = addr;
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_get_addr(esp_eth_phy_t *phy, uint32_t *addr) {
 | 
				
			||||||
 | 
					  PHY_CHECK(addr, "addr can't be null", err);
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  *addr = jl1101->addr;
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_ERR_INVALID_ARG;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_del(esp_eth_phy_t *phy) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  free(jl1101);
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth = jl1101->eth;
 | 
				
			||||||
 | 
					  /* Set PAUSE function ability */
 | 
				
			||||||
 | 
					  anar_reg_t anar;
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_ANAR_REG_ADDR, &(anar.val)) == ESP_OK, "read ANAR failed",
 | 
				
			||||||
 | 
					            err);
 | 
				
			||||||
 | 
					  if (ability) {
 | 
				
			||||||
 | 
					    anar.asymmetric_pause = 1;
 | 
				
			||||||
 | 
					    anar.symmetric_pause = 1;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    anar.asymmetric_pause = 0;
 | 
				
			||||||
 | 
					    anar.symmetric_pause = 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_write(eth, jl1101->addr, ETH_PHY_ANAR_REG_ADDR, anar.val) == ESP_OK, "write ANAR failed", err);
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_init(esp_eth_phy_t *phy) {
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent);
 | 
				
			||||||
 | 
					  esp_eth_mediator_t *eth = jl1101->eth;
 | 
				
			||||||
 | 
					  // Detect PHY address
 | 
				
			||||||
 | 
					  if (jl1101->addr == ESP_ETH_PHY_ADDR_AUTO) {
 | 
				
			||||||
 | 
					    PHY_CHECK(esp_eth_detect_phy_addr(eth, &jl1101->addr) == ESP_OK, "Detect PHY address failed", err);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  /* Power on Ethernet PHY */
 | 
				
			||||||
 | 
					  PHY_CHECK(jl1101_pwrctl(phy, true) == ESP_OK, "power control failed", err);
 | 
				
			||||||
 | 
					  /* Reset Ethernet PHY */
 | 
				
			||||||
 | 
					  PHY_CHECK(jl1101_reset(phy) == ESP_OK, "reset failed", err);
 | 
				
			||||||
 | 
					  /* Check PHY ID */
 | 
				
			||||||
 | 
					  phyidr1_reg_t id1;
 | 
				
			||||||
 | 
					  phyidr2_reg_t id2;
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_IDR1_REG_ADDR, &(id1.val)) == ESP_OK, "read ID1 failed", err);
 | 
				
			||||||
 | 
					  PHY_CHECK(eth->phy_reg_read(eth, jl1101->addr, ETH_PHY_IDR2_REG_ADDR, &(id2.val)) == ESP_OK, "read ID2 failed", err);
 | 
				
			||||||
 | 
					  PHY_CHECK(id1.oui_msb == 0x937C && id2.oui_lsb == 0x10 && id2.vendor_model == 0x2, "wrong chip ID", err);
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static esp_err_t jl1101_deinit(esp_eth_phy_t *phy) {
 | 
				
			||||||
 | 
					  /* Power off Ethernet PHY */
 | 
				
			||||||
 | 
					  PHY_CHECK(jl1101_pwrctl(phy, false) == ESP_OK, "power control failed", err);
 | 
				
			||||||
 | 
					  return ESP_OK;
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return ESP_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config) {
 | 
				
			||||||
 | 
					  PHY_CHECK(config, "can't set phy config to null", err);
 | 
				
			||||||
 | 
					  phy_jl1101_t *jl1101 = calloc(1, sizeof(phy_jl1101_t));
 | 
				
			||||||
 | 
					  PHY_CHECK(jl1101, "calloc jl1101 failed", err);
 | 
				
			||||||
 | 
					  jl1101->addr = config->phy_addr;
 | 
				
			||||||
 | 
					  jl1101->reset_gpio_num = config->reset_gpio_num;
 | 
				
			||||||
 | 
					  jl1101->reset_timeout_ms = config->reset_timeout_ms;
 | 
				
			||||||
 | 
					  jl1101->link_status = ETH_LINK_DOWN;
 | 
				
			||||||
 | 
					  jl1101->autonego_timeout_ms = config->autonego_timeout_ms;
 | 
				
			||||||
 | 
					  jl1101->parent.reset = jl1101_reset;
 | 
				
			||||||
 | 
					  jl1101->parent.reset_hw = jl1101_reset_hw;
 | 
				
			||||||
 | 
					  jl1101->parent.init = jl1101_init;
 | 
				
			||||||
 | 
					  jl1101->parent.deinit = jl1101_deinit;
 | 
				
			||||||
 | 
					  jl1101->parent.set_mediator = jl1101_set_mediator;
 | 
				
			||||||
 | 
					  jl1101->parent.negotiate = jl1101_negotiate;
 | 
				
			||||||
 | 
					  jl1101->parent.get_link = jl1101_get_link;
 | 
				
			||||||
 | 
					  jl1101->parent.pwrctl = jl1101_pwrctl;
 | 
				
			||||||
 | 
					  jl1101->parent.get_addr = jl1101_get_addr;
 | 
				
			||||||
 | 
					  jl1101->parent.set_addr = jl1101_set_addr;
 | 
				
			||||||
 | 
					  jl1101->parent.advertise_pause_ability = jl1101_advertise_pause_ability;
 | 
				
			||||||
 | 
					  jl1101->parent.del = jl1101_del;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return &(jl1101->parent);
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
					  return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif /* USE_ESP32 */
 | 
				
			||||||
@@ -71,6 +71,10 @@ void EthernetComponent::setup() {
 | 
				
			|||||||
      phy = esp_eth_phy_new_ip101(&phy_config);
 | 
					      phy = esp_eth_phy_new_ip101(&phy_config);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case ETHERNET_TYPE_JL1101: {
 | 
				
			||||||
 | 
					      phy = esp_eth_phy_new_jl1101(&phy_config);
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    default: {
 | 
					    default: {
 | 
				
			||||||
      this->mark_failed();
 | 
					      this->mark_failed();
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ enum EthernetType {
 | 
				
			|||||||
  ETHERNET_TYPE_RTL8201,
 | 
					  ETHERNET_TYPE_RTL8201,
 | 
				
			||||||
  ETHERNET_TYPE_DP83848,
 | 
					  ETHERNET_TYPE_DP83848,
 | 
				
			||||||
  ETHERNET_TYPE_IP101,
 | 
					  ETHERNET_TYPE_IP101,
 | 
				
			||||||
 | 
					  ETHERNET_TYPE_JL1101,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ManualIP {
 | 
					struct ManualIP {
 | 
				
			||||||
@@ -82,6 +83,7 @@ class EthernetComponent : public Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
					// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
				
			||||||
extern EthernetComponent *global_eth_component;
 | 
					extern EthernetComponent *global_eth_component;
 | 
				
			||||||
 | 
					extern "C" esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace ethernet
 | 
					}  // namespace ethernet
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										158
									
								
								esphome/components/ld2410/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								esphome/components/ld2410/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,158 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import uart
 | 
				
			||||||
 | 
					from esphome.const import CONF_ID, CONF_TIMEOUT
 | 
				
			||||||
 | 
					from esphome import automation
 | 
				
			||||||
 | 
					from esphome.automation import maybe_simple_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["uart"]
 | 
				
			||||||
 | 
					CODEOWNERS = ["@sebcaps"]
 | 
				
			||||||
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ld2410_ns = cg.esphome_ns.namespace("ld2410")
 | 
				
			||||||
 | 
					LD2410Component = ld2410_ns.class_("LD2410Component", cg.Component, uart.UARTDevice)
 | 
				
			||||||
 | 
					LD2410Restart = ld2410_ns.class_("LD2410Restart", automation.Action)
 | 
				
			||||||
 | 
					CONF_LD2410_ID = "ld2410_id"
 | 
				
			||||||
 | 
					CONF_MAX_MOVE_DISTANCE = "max_move_distance"
 | 
				
			||||||
 | 
					CONF_MAX_STILL_DISTANCE = "max_still_distance"
 | 
				
			||||||
 | 
					CONF_G0_MOVE_THRESHOLD = "g0_move_threshold"
 | 
				
			||||||
 | 
					CONF_G0_STILL_THRESHOLD = "g0_still_threshold"
 | 
				
			||||||
 | 
					CONF_G1_MOVE_THRESHOLD = "g1_move_threshold"
 | 
				
			||||||
 | 
					CONF_G1_STILL_THRESHOLD = "g1_still_threshold"
 | 
				
			||||||
 | 
					CONF_G2_MOVE_THRESHOLD = "g2_move_threshold"
 | 
				
			||||||
 | 
					CONF_G2_STILL_THRESHOLD = "g2_still_threshold"
 | 
				
			||||||
 | 
					CONF_G3_MOVE_THRESHOLD = "g3_move_threshold"
 | 
				
			||||||
 | 
					CONF_G3_STILL_THRESHOLD = "g3_still_threshold"
 | 
				
			||||||
 | 
					CONF_G4_MOVE_THRESHOLD = "g4_move_threshold"
 | 
				
			||||||
 | 
					CONF_G4_STILL_THRESHOLD = "g4_still_threshold"
 | 
				
			||||||
 | 
					CONF_G5_MOVE_THRESHOLD = "g5_move_threshold"
 | 
				
			||||||
 | 
					CONF_G5_STILL_THRESHOLD = "g5_still_threshold"
 | 
				
			||||||
 | 
					CONF_G6_MOVE_THRESHOLD = "g6_move_threshold"
 | 
				
			||||||
 | 
					CONF_G6_STILL_THRESHOLD = "g6_still_threshold"
 | 
				
			||||||
 | 
					CONF_G7_MOVE_THRESHOLD = "g7_move_threshold"
 | 
				
			||||||
 | 
					CONF_G7_STILL_THRESHOLD = "g7_still_threshold"
 | 
				
			||||||
 | 
					CONF_G8_MOVE_THRESHOLD = "g8_move_threshold"
 | 
				
			||||||
 | 
					CONF_G8_STILL_THRESHOLD = "g8_still_threshold"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DISTANCES = [0.75, 1.5, 2.25, 3, 3.75, 4.5, 5.25, 6]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.All(
 | 
				
			||||||
 | 
					    cv.Schema(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.declare_id(LD2410Component),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_MAX_MOVE_DISTANCE, default="4.5m"): cv.All(
 | 
				
			||||||
 | 
					                cv.distance, cv.one_of(*DISTANCES, float=True)
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_MAX_STILL_DISTANCE, default="4.5m"): cv.All(
 | 
				
			||||||
 | 
					                cv.distance, cv.one_of(*DISTANCES, float=True)
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_TIMEOUT, default="5s"): cv.All(
 | 
				
			||||||
 | 
					                cv.positive_time_period_seconds,
 | 
				
			||||||
 | 
					                cv.Range(max=cv.TimePeriod(seconds=32767)),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G0_MOVE_THRESHOLD, default=50): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G0_STILL_THRESHOLD, default=0): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G1_MOVE_THRESHOLD, default=50): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G1_STILL_THRESHOLD, default=0): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G2_MOVE_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G2_STILL_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G3_MOVE_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G3_STILL_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G4_MOVE_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G4_STILL_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G5_MOVE_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G5_STILL_THRESHOLD, default=40): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G6_MOVE_THRESHOLD, default=30): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G6_STILL_THRESHOLD, default=15): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G7_MOVE_THRESHOLD, default=30): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G7_STILL_THRESHOLD, default=15): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G8_MOVE_THRESHOLD, default=30): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_G8_STILL_THRESHOLD, default=15): cv.int_range(
 | 
				
			||||||
 | 
					                min=0, max=100
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    .extend(uart.UART_DEVICE_SCHEMA)
 | 
				
			||||||
 | 
					    .extend(cv.COMPONENT_SCHEMA)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
 | 
				
			||||||
 | 
					    "ld2410",
 | 
				
			||||||
 | 
					    baud_rate=256000,
 | 
				
			||||||
 | 
					    require_tx=True,
 | 
				
			||||||
 | 
					    require_rx=True,
 | 
				
			||||||
 | 
					    parity="NONE",
 | 
				
			||||||
 | 
					    stop_bits=1,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await uart.register_uart_device(var, config)
 | 
				
			||||||
 | 
					    cg.add(var.set_timeout(config[CONF_TIMEOUT]))
 | 
				
			||||||
 | 
					    cg.add(var.set_max_move_distance(int(config[CONF_MAX_MOVE_DISTANCE] / 0.75)))
 | 
				
			||||||
 | 
					    cg.add(var.set_max_still_distance(int(config[CONF_MAX_STILL_DISTANCE] / 0.75)))
 | 
				
			||||||
 | 
					    cg.add(
 | 
				
			||||||
 | 
					        var.set_range_config(
 | 
				
			||||||
 | 
					            config[CONF_G0_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G0_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G1_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G1_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G2_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G2_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G3_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G3_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G4_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G4_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G5_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G5_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G6_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G6_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G7_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G7_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G8_MOVE_THRESHOLD],
 | 
				
			||||||
 | 
					            config[CONF_G8_STILL_THRESHOLD],
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_ID): cv.use_id(LD2410Component),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										36
									
								
								esphome/components/ld2410/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								esphome/components/ld2410/binary_sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import binary_sensor
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.const import DEVICE_CLASS_MOTION, DEVICE_CLASS_OCCUPANCY
 | 
				
			||||||
 | 
					from . import CONF_LD2410_ID, LD2410Component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["ld2410"]
 | 
				
			||||||
 | 
					CONF_HAS_TARGET = "has_target"
 | 
				
			||||||
 | 
					CONF_HAS_MOVING_TARGET = "has_moving_target"
 | 
				
			||||||
 | 
					CONF_HAS_STILL_TARGET = "has_still_target"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = {
 | 
				
			||||||
 | 
					    cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_OCCUPANCY
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_MOTION
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_OCCUPANCY
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    ld2410_component = await cg.get_variable(config[CONF_LD2410_ID])
 | 
				
			||||||
 | 
					    if CONF_HAS_TARGET in config:
 | 
				
			||||||
 | 
					        sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_TARGET])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_target_sensor(sens))
 | 
				
			||||||
 | 
					    if CONF_HAS_MOVING_TARGET in config:
 | 
				
			||||||
 | 
					        sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_MOVING_TARGET])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_moving_target_sensor(sens))
 | 
				
			||||||
 | 
					    if CONF_HAS_STILL_TARGET in config:
 | 
				
			||||||
 | 
					        sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_STILL_TARGET])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_still_target_sensor(sens))
 | 
				
			||||||
							
								
								
									
										315
									
								
								esphome/components/ld2410/ld2410.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								esphome/components/ld2410/ld2410.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,315 @@
 | 
				
			|||||||
 | 
					#include "ld2410.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define highbyte(val) (uint8_t)((val) >> 8)
 | 
				
			||||||
 | 
					#define lowbyte(val) (uint8_t)((val) &0xff)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace ld2410 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "ld2410";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "LD2410:");
 | 
				
			||||||
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "HasTargetSensor", this->target_binary_sensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "MovingSensor", this->moving_binary_sensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "StillSensor", this->still_binary_sensor_);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Moving Distance", this->moving_target_distance_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Still Distance", this->still_target_distance_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Moving Energy", this->moving_target_energy_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Still Energy", this->still_target_energy_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Detection Distance", this->detection_distance_sensor_);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  this->set_config_mode_(true);
 | 
				
			||||||
 | 
					  this->get_version_();
 | 
				
			||||||
 | 
					  this->set_config_mode_(false);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2],
 | 
				
			||||||
 | 
					                this->version_[3], this->version_[4], this->version_[5]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::setup() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Setting up LD2410...");
 | 
				
			||||||
 | 
					  this->set_config_mode_(true);
 | 
				
			||||||
 | 
					  this->set_max_distances_timeout_(this->max_move_distance_, this->max_still_distance_, this->timeout_);
 | 
				
			||||||
 | 
					  // Configure Gates sensitivity
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(0, this->rg0_move_threshold_, this->rg0_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(1, this->rg1_move_threshold_, this->rg1_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(2, this->rg2_move_threshold_, this->rg2_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(3, this->rg3_move_threshold_, this->rg3_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(4, this->rg4_move_threshold_, this->rg4_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(5, this->rg5_move_threshold_, this->rg5_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(6, this->rg6_move_threshold_, this->rg6_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(7, this->rg7_move_threshold_, this->rg7_still_threshold_);
 | 
				
			||||||
 | 
					  this->set_gate_threshold_(8, this->rg8_move_threshold_, this->rg8_still_threshold_);
 | 
				
			||||||
 | 
					  this->get_version_();
 | 
				
			||||||
 | 
					  this->set_config_mode_(false);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2],
 | 
				
			||||||
 | 
					                this->version_[3], this->version_[4], this->version_[5]);
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "LD2410 setup complete.");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::loop() {
 | 
				
			||||||
 | 
					  const int max_line_length = 80;
 | 
				
			||||||
 | 
					  static uint8_t buffer[max_line_length];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (available()) {
 | 
				
			||||||
 | 
					    this->readline_(read(), buffer, max_line_length);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::send_command_(uint8_t command, uint8_t *command_value, int command_value_len) {
 | 
				
			||||||
 | 
					  // lastCommandSuccess->publish_state(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // frame start bytes
 | 
				
			||||||
 | 
					  this->write_array(CMD_FRAME_HEADER, 4);
 | 
				
			||||||
 | 
					  // length bytes
 | 
				
			||||||
 | 
					  int len = 2;
 | 
				
			||||||
 | 
					  if (command_value != nullptr)
 | 
				
			||||||
 | 
					    len += command_value_len;
 | 
				
			||||||
 | 
					  this->write_byte(lowbyte(len));
 | 
				
			||||||
 | 
					  this->write_byte(highbyte(len));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // command
 | 
				
			||||||
 | 
					  this->write_byte(lowbyte(command));
 | 
				
			||||||
 | 
					  this->write_byte(highbyte(command));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // command value bytes
 | 
				
			||||||
 | 
					  if (command_value != nullptr) {
 | 
				
			||||||
 | 
					    for (int i = 0; i < command_value_len; i++) {
 | 
				
			||||||
 | 
					      this->write_byte(command_value[i]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // frame end bytes
 | 
				
			||||||
 | 
					  this->write_array(CMD_FRAME_END, 4);
 | 
				
			||||||
 | 
					  // FIXME to remove
 | 
				
			||||||
 | 
					  delay(50);  // NOLINT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
 | 
				
			||||||
 | 
					  if (len < 12)
 | 
				
			||||||
 | 
					    return;  // 4 frame start bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame end bytes
 | 
				
			||||||
 | 
					  if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1)  // check 4 frame start bytes
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  if (buffer[7] != HEAD || buffer[len - 6] != END || buffer[len - 5] != CHECK)  // Check constant values
 | 
				
			||||||
 | 
					    return;  // data head=0xAA, data end=0x55, crc=0x00
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					      Data Type: 6th
 | 
				
			||||||
 | 
					      0x01: Engineering mode
 | 
				
			||||||
 | 
					      0x02: Normal mode
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    // char data_type = buffer[DATA_TYPES];
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					      Target states: 9th
 | 
				
			||||||
 | 
					      0x00 = No target
 | 
				
			||||||
 | 
					      0x01 = Moving targets
 | 
				
			||||||
 | 
					      0x02 = Still targets
 | 
				
			||||||
 | 
					      0x03 = Moving+Still targets
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
 | 
					  char target_state = buffer[TARGET_STATES];
 | 
				
			||||||
 | 
					  if (this->target_binary_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    this->target_binary_sensor_->publish_state(target_state != 0x00);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    Reduce data update rate to prevent home assistant database size grow fast
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  int32_t current_millis = millis();
 | 
				
			||||||
 | 
					  if (current_millis - last_periodic_millis < 1000)
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  last_periodic_millis = current_millis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
 | 
					  if (this->moving_binary_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    this->moving_binary_sensor_->publish_state(CHECK_BIT(target_state, 0));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->still_binary_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    this->still_binary_sensor_->publish_state(CHECK_BIT(target_state, 1));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    Moving target distance: 10~11th bytes
 | 
				
			||||||
 | 
					    Moving target energy: 12th byte
 | 
				
			||||||
 | 
					    Still target distance: 13~14th bytes
 | 
				
			||||||
 | 
					    Still target energy: 15th byte
 | 
				
			||||||
 | 
					    Detect distance: 16~17th bytes
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  if (this->moving_target_distance_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    int new_moving_target_distance = this->two_byte_to_int_(buffer[MOVING_TARGET_LOW], buffer[MOVING_TARGET_HIGH]);
 | 
				
			||||||
 | 
					    if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance)
 | 
				
			||||||
 | 
					      this->moving_target_distance_sensor_->publish_state(new_moving_target_distance);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->moving_target_energy_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    int new_moving_target_energy = buffer[MOVING_ENERGY];
 | 
				
			||||||
 | 
					    if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy)
 | 
				
			||||||
 | 
					      this->moving_target_energy_sensor_->publish_state(new_moving_target_energy);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->still_target_distance_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    int new_still_target_distance = this->two_byte_to_int_(buffer[STILL_TARGET_LOW], buffer[STILL_TARGET_HIGH]);
 | 
				
			||||||
 | 
					    if (this->still_target_distance_sensor_->get_state() != new_still_target_distance)
 | 
				
			||||||
 | 
					      this->still_target_distance_sensor_->publish_state(new_still_target_distance);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->still_target_energy_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    int new_still_target_energy = buffer[STILL_ENERGY];
 | 
				
			||||||
 | 
					    if (this->still_target_energy_sensor_->get_state() != new_still_target_energy)
 | 
				
			||||||
 | 
					      this->still_target_energy_sensor_->publish_state(new_still_target_energy);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->detection_distance_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    int new_detect_distance = this->two_byte_to_int_(buffer[DETECT_DISTANCE_LOW], buffer[DETECT_DISTANCE_HIGH]);
 | 
				
			||||||
 | 
					    if (this->detection_distance_sensor_->get_state() != new_detect_distance)
 | 
				
			||||||
 | 
					      this->detection_distance_sensor_->publish_state(new_detect_distance);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
 | 
				
			||||||
 | 
					  ESP_LOGV(TAG, "Handling ACK DATA for COMMAND");
 | 
				
			||||||
 | 
					  if (len < 10) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Error with last command : incorrect length");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) {  // check 4 frame start bytes
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Error with last command : incorrect Header");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (buffer[COMMAND_STATUS] != 0x01) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Error with last command : status != 0x01");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (buffer[COMMAND]) {
 | 
				
			||||||
 | 
					    case lowbyte(CMD_ENABLE_CONF):
 | 
				
			||||||
 | 
					      ESP_LOGV(TAG, "Handled Enable conf command");
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case lowbyte(CMD_DISABLE_CONF):
 | 
				
			||||||
 | 
					      ESP_LOGV(TAG, "Handled Disabled conf command");
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case lowbyte(CMD_VERSION):
 | 
				
			||||||
 | 
					      ESP_LOGV(TAG, "FW Version is: %u.%u.%u%u%u%u", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15],
 | 
				
			||||||
 | 
					               buffer[14]);
 | 
				
			||||||
 | 
					      this->version_[0] = buffer[13];
 | 
				
			||||||
 | 
					      this->version_[1] = buffer[12];
 | 
				
			||||||
 | 
					      this->version_[2] = buffer[17];
 | 
				
			||||||
 | 
					      this->version_[3] = buffer[16];
 | 
				
			||||||
 | 
					      this->version_[4] = buffer[15];
 | 
				
			||||||
 | 
					      this->version_[5] = buffer[14];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case lowbyte(CMD_GATE_SENS):
 | 
				
			||||||
 | 
					      ESP_LOGV(TAG, "Handled sensitivity command");
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    case lowbyte(CMD_QUERY):  // Query parameters response
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      if (buffer[10] != 0xAA)
 | 
				
			||||||
 | 
					        return;  // value head=0xAA
 | 
				
			||||||
 | 
					      /*
 | 
				
			||||||
 | 
					        Moving distance range: 13th byte
 | 
				
			||||||
 | 
					        Still distance range: 14th byte
 | 
				
			||||||
 | 
					      */
 | 
				
			||||||
 | 
					      // TODO
 | 
				
			||||||
 | 
					      // maxMovingDistanceRange->publish_state(buffer[12]);
 | 
				
			||||||
 | 
					      // maxStillDistanceRange->publish_state(buffer[13]);
 | 
				
			||||||
 | 
					      /*
 | 
				
			||||||
 | 
					        Moving Sensitivities: 15~23th bytes
 | 
				
			||||||
 | 
					        Still Sensitivities: 24~32th bytes
 | 
				
			||||||
 | 
					      */
 | 
				
			||||||
 | 
					      for (int i = 0; i < 9; i++) {
 | 
				
			||||||
 | 
					        moving_sensitivities[i] = buffer[14 + i];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      for (int i = 0; i < 9; i++) {
 | 
				
			||||||
 | 
					        still_sensitivities[i] = buffer[23 + i];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      /*
 | 
				
			||||||
 | 
					        None Duration: 33~34th bytes
 | 
				
			||||||
 | 
					      */
 | 
				
			||||||
 | 
					      // noneDuration->publish_state(this->two_byte_to_int_(buffer[32], buffer[33]));
 | 
				
			||||||
 | 
					    } break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::readline_(int readch, uint8_t *buffer, int len) {
 | 
				
			||||||
 | 
					  static int pos = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (readch >= 0) {
 | 
				
			||||||
 | 
					    if (pos < len - 1) {
 | 
				
			||||||
 | 
					      buffer[pos++] = readch;
 | 
				
			||||||
 | 
					      buffer[pos] = 0;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      pos = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (pos >= 4) {
 | 
				
			||||||
 | 
					      if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5) {
 | 
				
			||||||
 | 
					        ESP_LOGV(TAG, "Will handle Periodic Data");
 | 
				
			||||||
 | 
					        this->handle_periodic_data_(buffer, pos);
 | 
				
			||||||
 | 
					        pos = 0;  // Reset position index ready for next time
 | 
				
			||||||
 | 
					      } else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 &&
 | 
				
			||||||
 | 
					                 buffer[pos - 1] == 0x01) {
 | 
				
			||||||
 | 
					        ESP_LOGV(TAG, "Will handle ACK Data");
 | 
				
			||||||
 | 
					        this->handle_ack_data_(buffer, pos);
 | 
				
			||||||
 | 
					        pos = 0;  // Reset position index ready for next time
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::set_config_mode_(bool enable) {
 | 
				
			||||||
 | 
					  uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
 | 
				
			||||||
 | 
					  uint8_t cmd_value[2] = {0x01, 0x00};
 | 
				
			||||||
 | 
					  this->send_command_(cmd, enable ? cmd_value : nullptr, 2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::query_parameters_() { this->send_command_(CMD_QUERY, nullptr, 0); }
 | 
				
			||||||
 | 
					void LD2410Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LD2410Component::set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range,
 | 
				
			||||||
 | 
					                                                 uint16_t timeout) {
 | 
				
			||||||
 | 
					  uint8_t value[18] = {0x00,
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       lowbyte(max_moving_distance_range),
 | 
				
			||||||
 | 
					                       highbyte(max_moving_distance_range),
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       0x01,
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       lowbyte(max_still_distance_range),
 | 
				
			||||||
 | 
					                       highbyte(max_still_distance_range),
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       0x02,
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       lowbyte(timeout),
 | 
				
			||||||
 | 
					                       highbyte(timeout),
 | 
				
			||||||
 | 
					                       0x00,
 | 
				
			||||||
 | 
					                       0x00};
 | 
				
			||||||
 | 
					  this->send_command_(CMD_MAXDIST_DURATION, value, 18);
 | 
				
			||||||
 | 
					  this->query_parameters_();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void LD2410Component::set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens) {
 | 
				
			||||||
 | 
					  // reference
 | 
				
			||||||
 | 
					  // https://drive.google.com/drive/folders/1p4dhbEJA3YubyIjIIC7wwVsSo8x29Fq-?spm=a2g0o.detail.1000023.17.93465697yFwVxH
 | 
				
			||||||
 | 
					  //   Send data: configure the motion sensitivity of distance gate 3 to 40, and the static sensitivity of 40
 | 
				
			||||||
 | 
					  // 00 00 (gate)
 | 
				
			||||||
 | 
					  // 03 00 00 00 (gate number)
 | 
				
			||||||
 | 
					  // 01 00 (motion sensitivity)
 | 
				
			||||||
 | 
					  // 28 00 00 00 (value)
 | 
				
			||||||
 | 
					  // 02 00 (still sensitivtiy)
 | 
				
			||||||
 | 
					  // 28 00 00 00 (value)
 | 
				
			||||||
 | 
					  uint8_t value[18] = {0x00, 0x00, lowbyte(gate),       highbyte(gate),       0x00, 0x00,
 | 
				
			||||||
 | 
					                       0x01, 0x00, lowbyte(motionsens), highbyte(motionsens), 0x00, 0x00,
 | 
				
			||||||
 | 
					                       0x02, 0x00, lowbyte(stillsens),  highbyte(stillsens),  0x00, 0x00};
 | 
				
			||||||
 | 
					  this->send_command_(CMD_GATE_SENS, value, 18);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace ld2410
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										146
									
								
								esphome/components/ld2410/ld2410.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								esphome/components/ld2410/ld2410.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include "esphome/core/defines.h"
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
 | 
					#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include "esphome/components/uart/uart.h"
 | 
				
			||||||
 | 
					#include "esphome/core/automation.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace ld2410 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Commands
 | 
				
			||||||
 | 
					static const uint8_t CMD_ENABLE_CONF = 0x00FF;
 | 
				
			||||||
 | 
					static const uint8_t CMD_DISABLE_CONF = 0x00FE;
 | 
				
			||||||
 | 
					static const uint8_t CMD_MAXDIST_DURATION = 0x0060;
 | 
				
			||||||
 | 
					static const uint8_t CMD_QUERY = 0x0061;
 | 
				
			||||||
 | 
					static const uint8_t CMD_GATE_SENS = 0x0064;
 | 
				
			||||||
 | 
					static const uint8_t CMD_VERSION = 0x00A0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Commands values
 | 
				
			||||||
 | 
					static const uint8_t CMD_MAX_MOVE_VALUE = 0x0000;
 | 
				
			||||||
 | 
					static const uint8_t CMD_MAX_STILL_VALUE = 0x0001;
 | 
				
			||||||
 | 
					static const uint8_t CMD_DURATION_VALUE = 0x0002;
 | 
				
			||||||
 | 
					// Command Header & Footer
 | 
				
			||||||
 | 
					static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA};
 | 
				
			||||||
 | 
					static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01};
 | 
				
			||||||
 | 
					// Data Header & Footer
 | 
				
			||||||
 | 
					static const uint8_t DATA_FRAME_HEADER[4] = {0xF4, 0xF3, 0xF2, 0xF1};
 | 
				
			||||||
 | 
					static const uint8_t DATA_FRAME_END[4] = {0xF8, 0xF7, 0xF6, 0xF5};
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Data Type: 6th byte
 | 
				
			||||||
 | 
					Target states: 9th byte
 | 
				
			||||||
 | 
					    Moving target distance: 10~11th bytes
 | 
				
			||||||
 | 
					    Moving target energy: 12th byte
 | 
				
			||||||
 | 
					    Still target distance: 13~14th bytes
 | 
				
			||||||
 | 
					    Still target energy: 15th byte
 | 
				
			||||||
 | 
					    Detect distance: 16~17th bytes
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					enum PeriodicDataStructure : uint8_t {
 | 
				
			||||||
 | 
					  DATA_TYPES = 5,
 | 
				
			||||||
 | 
					  TARGET_STATES = 8,
 | 
				
			||||||
 | 
					  MOVING_TARGET_LOW = 9,
 | 
				
			||||||
 | 
					  MOVING_TARGET_HIGH = 10,
 | 
				
			||||||
 | 
					  MOVING_ENERGY = 11,
 | 
				
			||||||
 | 
					  STILL_TARGET_LOW = 12,
 | 
				
			||||||
 | 
					  STILL_TARGET_HIGH = 13,
 | 
				
			||||||
 | 
					  STILL_ENERGY = 14,
 | 
				
			||||||
 | 
					  DETECT_DISTANCE_LOW = 15,
 | 
				
			||||||
 | 
					  DETECT_DISTANCE_HIGH = 16,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  char cmd[2] = {enable ? 0xFF : 0xFE, 0x00};
 | 
				
			||||||
 | 
					class LD2410Component : public Component, public uart::UARTDevice {
 | 
				
			||||||
 | 
					#ifdef USE_SENSOR
 | 
				
			||||||
 | 
					  SUB_SENSOR(moving_target_distance)
 | 
				
			||||||
 | 
					  SUB_SENSOR(still_target_distance)
 | 
				
			||||||
 | 
					  SUB_SENSOR(moving_target_energy)
 | 
				
			||||||
 | 
					  SUB_SENSOR(still_target_energy)
 | 
				
			||||||
 | 
					  SUB_SENSOR(detection_distance)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void setup() override;
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void loop() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
 | 
					  void set_target_sensor(binary_sensor::BinarySensor *sens) { this->target_binary_sensor_ = sens; };
 | 
				
			||||||
 | 
					  void set_moving_target_sensor(binary_sensor::BinarySensor *sens) { this->moving_binary_sensor_ = sens; };
 | 
				
			||||||
 | 
					  void set_still_target_sensor(binary_sensor::BinarySensor *sens) { this->still_binary_sensor_ = sens; };
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_timeout(uint16_t value) { this->timeout_ = value; };
 | 
				
			||||||
 | 
					  void set_max_move_distance(uint8_t value) { this->max_move_distance_ = value; };
 | 
				
			||||||
 | 
					  void set_max_still_distance(uint8_t value) { this->max_still_distance_ = value; };
 | 
				
			||||||
 | 
					  void set_range_config(int rg0_move, int rg0_still, int rg1_move, int rg1_still, int rg2_move, int rg2_still,
 | 
				
			||||||
 | 
					                        int rg3_move, int rg3_still, int rg4_move, int rg4_still, int rg5_move, int rg5_still,
 | 
				
			||||||
 | 
					                        int rg6_move, int rg6_still, int rg7_move, int rg7_still, int rg8_move, int rg8_still) {
 | 
				
			||||||
 | 
					    this->rg0_move_threshold_ = rg0_move;
 | 
				
			||||||
 | 
					    this->rg0_still_threshold_ = rg0_still;
 | 
				
			||||||
 | 
					    this->rg1_move_threshold_ = rg1_move;
 | 
				
			||||||
 | 
					    this->rg1_still_threshold_ = rg1_still;
 | 
				
			||||||
 | 
					    this->rg2_move_threshold_ = rg2_move;
 | 
				
			||||||
 | 
					    this->rg2_still_threshold_ = rg2_still;
 | 
				
			||||||
 | 
					    this->rg3_move_threshold_ = rg3_move;
 | 
				
			||||||
 | 
					    this->rg3_still_threshold_ = rg3_still;
 | 
				
			||||||
 | 
					    this->rg4_move_threshold_ = rg4_move;
 | 
				
			||||||
 | 
					    this->rg4_still_threshold_ = rg4_still;
 | 
				
			||||||
 | 
					    this->rg5_move_threshold_ = rg5_move;
 | 
				
			||||||
 | 
					    this->rg5_still_threshold_ = rg5_still;
 | 
				
			||||||
 | 
					    this->rg6_move_threshold_ = rg6_move;
 | 
				
			||||||
 | 
					    this->rg6_still_threshold_ = rg6_still;
 | 
				
			||||||
 | 
					    this->rg7_move_threshold_ = rg7_move;
 | 
				
			||||||
 | 
					    this->rg7_still_threshold_ = rg7_still;
 | 
				
			||||||
 | 
					    this->rg8_move_threshold_ = rg8_move;
 | 
				
			||||||
 | 
					    this->rg8_still_threshold_ = rg8_still;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  int moving_sensitivities[9] = {0};
 | 
				
			||||||
 | 
					  int still_sensitivities[9] = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int32_t last_periodic_millis = millis();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					#ifdef USE_BINARY_SENSOR
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *target_binary_sensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *moving_binary_sensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *still_binary_sensor_{nullptr};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::vector<uint8_t> rx_buffer_;
 | 
				
			||||||
 | 
					  int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t)(secondbyte << 8) + firstbyte; }
 | 
				
			||||||
 | 
					  void send_command_(uint8_t command_str, uint8_t *command_value, int command_value_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range,
 | 
				
			||||||
 | 
					                                  uint16_t timeout);
 | 
				
			||||||
 | 
					  void set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens);
 | 
				
			||||||
 | 
					  void set_config_mode_(bool enable);
 | 
				
			||||||
 | 
					  void handle_periodic_data_(uint8_t *buffer, int len);
 | 
				
			||||||
 | 
					  void handle_ack_data_(uint8_t *buffer, int len);
 | 
				
			||||||
 | 
					  void readline_(int readch, uint8_t *buffer, int len);
 | 
				
			||||||
 | 
					  void query_parameters_();
 | 
				
			||||||
 | 
					  void get_version_();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint16_t timeout_;
 | 
				
			||||||
 | 
					  uint8_t max_move_distance_;
 | 
				
			||||||
 | 
					  uint8_t max_still_distance_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t version_[6];
 | 
				
			||||||
 | 
					  uint8_t rg0_move_threshold_, rg0_still_threshold_, rg1_move_threshold_, rg1_still_threshold_, rg2_move_threshold_,
 | 
				
			||||||
 | 
					      rg2_still_threshold_, rg3_move_threshold_, rg3_still_threshold_, rg4_move_threshold_, rg4_still_threshold_,
 | 
				
			||||||
 | 
					      rg5_move_threshold_, rg5_still_threshold_, rg6_move_threshold_, rg6_still_threshold_, rg7_move_threshold_,
 | 
				
			||||||
 | 
					      rg7_still_threshold_, rg8_move_threshold_, rg8_still_threshold_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace ld2410
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										55
									
								
								esphome/components/ld2410/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								esphome/components/ld2410/sensor.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.components import sensor
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    DEVICE_CLASS_DISTANCE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_ENERGY,
 | 
				
			||||||
 | 
					    UNIT_CENTIMETER,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from . import CONF_LD2410_ID, LD2410Component
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["ld2410"]
 | 
				
			||||||
 | 
					CONF_MOVING_DISTANCE = "moving_distance"
 | 
				
			||||||
 | 
					CONF_STILL_DISTANCE = "still_distance"
 | 
				
			||||||
 | 
					CONF_MOVING_ENERGY = "moving_energy"
 | 
				
			||||||
 | 
					CONF_STILL_ENERGY = "still_energy"
 | 
				
			||||||
 | 
					CONF_DETECTION_DISTANCE = "detection_distance"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = {
 | 
				
			||||||
 | 
					    cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					        device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    ld2410_component = await cg.get_variable(config[CONF_LD2410_ID])
 | 
				
			||||||
 | 
					    if CONF_MOVING_DISTANCE in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_MOVING_DISTANCE])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_moving_target_distance_sensor(sens))
 | 
				
			||||||
 | 
					    if CONF_STILL_DISTANCE in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_STILL_DISTANCE])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_still_target_distance_sensor(sens))
 | 
				
			||||||
 | 
					    if CONF_MOVING_ENERGY in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_MOVING_ENERGY])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_moving_target_energy_sensor(sens))
 | 
				
			||||||
 | 
					    if CONF_STILL_ENERGY in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_STILL_ENERGY])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_still_target_energy_sensor(sens))
 | 
				
			||||||
 | 
					    if CONF_DETECTION_DISTANCE in config:
 | 
				
			||||||
 | 
					        sens = await sensor.new_sensor(config[CONF_DETECTION_DISTANCE])
 | 
				
			||||||
 | 
					        cg.add(ld2410_component.set_detection_distance_sensor(sens))
 | 
				
			||||||
@@ -35,6 +35,8 @@ class MDNSComponent : public Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  void add_extra_service(MDNSService service) { services_extra_.push_back(std::move(service)); }
 | 
					  void add_extra_service(MDNSService service) { services_extra_.push_back(std::move(service)); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void on_shutdown() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  std::vector<MDNSService> services_extra_{};
 | 
					  std::vector<MDNSService> services_extra_{};
 | 
				
			||||||
  std::vector<MDNSService> services_{};
 | 
					  std::vector<MDNSService> services_{};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
#ifdef USE_ESP_IDF
 | 
					#ifdef USE_ESP32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "mdns_component.h"
 | 
					 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					 | 
				
			||||||
#include <mdns.h>
 | 
					#include <mdns.h>
 | 
				
			||||||
#include <cstring>
 | 
					#include <cstring>
 | 
				
			||||||
 | 
					#include "esphome/core/hal.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					#include "mdns_component.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace mdns {
 | 
					namespace mdns {
 | 
				
			||||||
@@ -47,7 +48,12 @@ void MDNSComponent::setup() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MDNSComponent::on_shutdown() {
 | 
				
			||||||
 | 
					  mdns_free();
 | 
				
			||||||
 | 
					  delay(40);  // Allow the mdns packets announcing service removal to be sent
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace mdns
 | 
					}  // namespace mdns
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif  // USE_ESP32
 | 
				
			||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "mdns_component.h"
 | 
					 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					 | 
				
			||||||
#include <ESPmDNS.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace esphome {
 | 
					 | 
				
			||||||
namespace mdns {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void MDNSComponent::setup() {
 | 
					 | 
				
			||||||
  this->compile_records_();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  MDNS.begin(this->hostname_.c_str());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (const auto &service : this->services_) {
 | 
					 | 
				
			||||||
    MDNS.addService(service.service_type.c_str(), service.proto.c_str(), service.port);
 | 
					 | 
				
			||||||
    for (const auto &record : service.txt_records) {
 | 
					 | 
				
			||||||
      MDNS.addServiceTxt(service.service_type.c_str(), service.proto.c_str(), record.key.c_str(), record.value.c_str());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}  // namespace mdns
 | 
					 | 
				
			||||||
}  // namespace esphome
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif  // USE_ESP32_FRAMEWORK_ARDUINO
 | 
					 | 
				
			||||||
@@ -1,10 +1,11 @@
 | 
				
			|||||||
#if defined(USE_ESP8266) && defined(USE_ARDUINO)
 | 
					#if defined(USE_ESP8266) && defined(USE_ARDUINO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "mdns_component.h"
 | 
					#include <ESP8266mDNS.h>
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					 | 
				
			||||||
#include "esphome/components/network/ip_address.h"
 | 
					#include "esphome/components/network/ip_address.h"
 | 
				
			||||||
#include "esphome/components/network/util.h"
 | 
					#include "esphome/components/network/util.h"
 | 
				
			||||||
#include <ESP8266mDNS.h>
 | 
					#include "esphome/core/hal.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					#include "mdns_component.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace mdns {
 | 
					namespace mdns {
 | 
				
			||||||
@@ -37,6 +38,11 @@ void MDNSComponent::setup() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void MDNSComponent::loop() { MDNS.update(); }
 | 
					void MDNSComponent::loop() { MDNS.update(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MDNSComponent::on_shutdown() {
 | 
				
			||||||
 | 
					  MDNS.close();
 | 
				
			||||||
 | 
					  delay(10);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace mdns
 | 
					}  // namespace mdns
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,11 @@ void MDNSComponent::setup() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void MDNSComponent::loop() { MDNS.update(); }
 | 
					void MDNSComponent::loop() { MDNS.update(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MDNSComponent::on_shutdown() {
 | 
				
			||||||
 | 
					  MDNS.close();
 | 
				
			||||||
 | 
					  delay(40);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace mdns
 | 
					}  // namespace mdns
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ void MICS4514Component::setup() {
 | 
				
			|||||||
  uint8_t power_mode;
 | 
					  uint8_t power_mode;
 | 
				
			||||||
  this->read_register(POWER_MODE_REGISTER, &power_mode, 1);
 | 
					  this->read_register(POWER_MODE_REGISTER, &power_mode, 1);
 | 
				
			||||||
  if (power_mode == 0x00) {
 | 
					  if (power_mode == 0x00) {
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "Waking up MICS 4514");
 | 
					    ESP_LOGCONFIG(TAG, "Waking up MICS 4514, sensors will have data after 3 minutes...");
 | 
				
			||||||
    power_mode = 0x01;
 | 
					    power_mode = 0x01;
 | 
				
			||||||
    this->write_register(POWER_MODE_REGISTER, &power_mode, 1);
 | 
					    this->write_register(POWER_MODE_REGISTER, &power_mode, 1);
 | 
				
			||||||
    delay(100);  // NOLINT
 | 
					    delay(100);  // NOLINT
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -53,6 +53,7 @@ bool MopekaProCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
 | 
				
			|||||||
  // Now parse the data - See Datasheet for definition
 | 
					  // Now parse the data - See Datasheet for definition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (static_cast<SensorType>(manu_data.data[0]) != STANDARD_BOTTOM_UP &&
 | 
					  if (static_cast<SensorType>(manu_data.data[0]) != STANDARD_BOTTOM_UP &&
 | 
				
			||||||
 | 
					      static_cast<SensorType>(manu_data.data[0]) != LIPPERT_BOTTOM_UP &&
 | 
				
			||||||
      static_cast<SensorType>(manu_data.data[0]) != PLUS_BOTTOM_UP) {
 | 
					      static_cast<SensorType>(manu_data.data[0]) != PLUS_BOTTOM_UP) {
 | 
				
			||||||
    ESP_LOGE(TAG, "Unsupported Sensor Type (0x%X)", manu_data.data[0]);
 | 
					    ESP_LOGE(TAG, "Unsupported Sensor Type (0x%X)", manu_data.data[0]);
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@ enum SensorType {
 | 
				
			|||||||
  STANDARD_BOTTOM_UP = 0x03,
 | 
					  STANDARD_BOTTOM_UP = 0x03,
 | 
				
			||||||
  TOP_DOWN_AIR_ABOVE = 0x04,
 | 
					  TOP_DOWN_AIR_ABOVE = 0x04,
 | 
				
			||||||
  BOTTOM_UP_WATER = 0x05,
 | 
					  BOTTOM_UP_WATER = 0x05,
 | 
				
			||||||
 | 
					  LIPPERT_BOTTOM_UP = 0x06,
 | 
				
			||||||
  PLUS_BOTTOM_UP = 0x08
 | 
					  PLUS_BOTTOM_UP = 0x08
 | 
				
			||||||
  // all other values are reserved
 | 
					  // all other values are reserved
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,18 +66,42 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
 | 
				
			|||||||
  // temperature units are always coerced to Celsius internally
 | 
					  // temperature units are always coerced to Celsius internally
 | 
				
			||||||
  root[MQTT_TEMPERATURE_UNIT] = "C";
 | 
					  root[MQTT_TEMPERATURE_UNIT] = "C";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (traits.supports_preset(CLIMATE_PRESET_AWAY)) {
 | 
					  if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
 | 
				
			||||||
    // away_mode_command_topic
 | 
					    // preset_mode_command_topic
 | 
				
			||||||
    root[MQTT_AWAY_MODE_COMMAND_TOPIC] = this->get_away_command_topic();
 | 
					    root[MQTT_PRESET_MODE_COMMAND_TOPIC] = this->get_preset_command_topic();
 | 
				
			||||||
    // away_mode_state_topic
 | 
					    // preset_mode_state_topic
 | 
				
			||||||
    root[MQTT_AWAY_MODE_STATE_TOPIC] = this->get_away_state_topic();
 | 
					    root[MQTT_PRESET_MODE_STATE_TOPIC] = this->get_preset_state_topic();
 | 
				
			||||||
 | 
					    // presets
 | 
				
			||||||
 | 
					    JsonArray presets = root.createNestedArray("presets");
 | 
				
			||||||
 | 
					    if (traits.supports_preset(CLIMATE_PRESET_HOME))
 | 
				
			||||||
 | 
					      presets.add("home");
 | 
				
			||||||
 | 
					    if (traits.supports_preset(CLIMATE_PRESET_AWAY)) {
 | 
				
			||||||
 | 
					      // away_mode_command_topic
 | 
				
			||||||
 | 
					      root[MQTT_AWAY_MODE_COMMAND_TOPIC] = this->get_away_command_topic();
 | 
				
			||||||
 | 
					      // away_mode_state_topic
 | 
				
			||||||
 | 
					      root[MQTT_AWAY_MODE_STATE_TOPIC] = this->get_away_state_topic();
 | 
				
			||||||
 | 
					      presets.add("away");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (traits.supports_preset(CLIMATE_PRESET_BOOST))
 | 
				
			||||||
 | 
					      presets.add("boost");
 | 
				
			||||||
 | 
					    if (traits.supports_preset(CLIMATE_PRESET_COMFORT))
 | 
				
			||||||
 | 
					      presets.add("comfort");
 | 
				
			||||||
 | 
					    if (traits.supports_preset(CLIMATE_PRESET_ECO))
 | 
				
			||||||
 | 
					      presets.add("eco");
 | 
				
			||||||
 | 
					    if (traits.supports_preset(CLIMATE_PRESET_SLEEP))
 | 
				
			||||||
 | 
					      presets.add("sleep");
 | 
				
			||||||
 | 
					    if (traits.supports_preset(CLIMATE_PRESET_ACTIVITY))
 | 
				
			||||||
 | 
					      presets.add("activity");
 | 
				
			||||||
 | 
					    for (const auto &preset : traits.get_supported_custom_presets())
 | 
				
			||||||
 | 
					      presets.add(preset);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (traits.get_supports_action()) {
 | 
					  if (traits.get_supports_action()) {
 | 
				
			||||||
    // action_topic
 | 
					    // action_topic
 | 
				
			||||||
    root[MQTT_ACTION_TOPIC] = this->get_action_state_topic();
 | 
					    root[MQTT_ACTION_TOPIC] = this->get_action_state_topic();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
 | 
					  if (traits.get_supports_fan_modes()) {
 | 
				
			||||||
    // fan_mode_command_topic
 | 
					    // fan_mode_command_topic
 | 
				
			||||||
    root[MQTT_FAN_MODE_COMMAND_TOPIC] = this->get_fan_mode_command_topic();
 | 
					    root[MQTT_FAN_MODE_COMMAND_TOPIC] = this->get_fan_mode_command_topic();
 | 
				
			||||||
    // fan_mode_state_topic
 | 
					    // fan_mode_state_topic
 | 
				
			||||||
@@ -102,6 +126,8 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
 | 
				
			|||||||
      fan_modes.add("focus");
 | 
					      fan_modes.add("focus");
 | 
				
			||||||
    if (traits.supports_fan_mode(CLIMATE_FAN_DIFFUSE))
 | 
					    if (traits.supports_fan_mode(CLIMATE_FAN_DIFFUSE))
 | 
				
			||||||
      fan_modes.add("diffuse");
 | 
					      fan_modes.add("diffuse");
 | 
				
			||||||
 | 
					    if (traits.supports_fan_mode(CLIMATE_FAN_QUIET))
 | 
				
			||||||
 | 
					      fan_modes.add("quiet");
 | 
				
			||||||
    for (const auto &fan_mode : traits.get_supported_custom_fan_modes())
 | 
					    for (const auto &fan_mode : traits.get_supported_custom_fan_modes())
 | 
				
			||||||
      fan_modes.add(fan_mode);
 | 
					      fan_modes.add(fan_mode);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -194,6 +220,14 @@ void MQTTClimateComponent::setup() {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
 | 
				
			||||||
 | 
					    this->subscribe(this->get_preset_command_topic(), [this](const std::string &topic, const std::string &payload) {
 | 
				
			||||||
 | 
					      auto call = this->device_->make_call();
 | 
				
			||||||
 | 
					      call.set_preset(payload);
 | 
				
			||||||
 | 
					      call.perform();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (traits.get_supports_fan_modes()) {
 | 
					  if (traits.get_supports_fan_modes()) {
 | 
				
			||||||
    this->subscribe(this->get_fan_mode_command_topic(), [this](const std::string &topic, const std::string &payload) {
 | 
					    this->subscribe(this->get_fan_mode_command_topic(), [this](const std::string &topic, const std::string &payload) {
 | 
				
			||||||
      auto call = this->device_->make_call();
 | 
					      auto call = this->device_->make_call();
 | 
				
			||||||
@@ -271,6 +305,42 @@ bool MQTTClimateComponent::publish_state_() {
 | 
				
			|||||||
    if (!this->publish(this->get_away_state_topic(), payload))
 | 
					    if (!this->publish(this->get_away_state_topic(), payload))
 | 
				
			||||||
      success = false;
 | 
					      success = false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
 | 
				
			||||||
 | 
					    std::string payload;
 | 
				
			||||||
 | 
					    if (this->device_->preset.has_value()) {
 | 
				
			||||||
 | 
					      switch (this->device_->preset.value()) {
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_NONE:
 | 
				
			||||||
 | 
					          payload = "none";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_HOME:
 | 
				
			||||||
 | 
					          payload = "home";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_AWAY:
 | 
				
			||||||
 | 
					          payload = "away";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_BOOST:
 | 
				
			||||||
 | 
					          payload = "boost";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_COMFORT:
 | 
				
			||||||
 | 
					          payload = "comfort";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_ECO:
 | 
				
			||||||
 | 
					          payload = "eco";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_SLEEP:
 | 
				
			||||||
 | 
					          payload = "sleep";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_PRESET_ACTIVITY:
 | 
				
			||||||
 | 
					          payload = "activity";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->device_->custom_preset.has_value())
 | 
				
			||||||
 | 
					      payload = this->device_->custom_preset.value();
 | 
				
			||||||
 | 
					    if (!this->publish(this->get_preset_state_topic(), payload))
 | 
				
			||||||
 | 
					      success = false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (traits.get_supports_action()) {
 | 
					  if (traits.get_supports_action()) {
 | 
				
			||||||
    const char *payload = "unknown";
 | 
					    const char *payload = "unknown";
 | 
				
			||||||
    switch (this->device_->action) {
 | 
					    switch (this->device_->action) {
 | 
				
			||||||
@@ -328,6 +398,9 @@ bool MQTTClimateComponent::publish_state_() {
 | 
				
			|||||||
        case CLIMATE_FAN_DIFFUSE:
 | 
					        case CLIMATE_FAN_DIFFUSE:
 | 
				
			||||||
          payload = "diffuse";
 | 
					          payload = "diffuse";
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
 | 
					        case CLIMATE_FAN_QUIET:
 | 
				
			||||||
 | 
					          payload = "quiet";
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this->device_->custom_fan_mode.has_value())
 | 
					    if (this->device_->custom_fan_mode.has_value())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,8 @@ class MQTTClimateComponent : public mqtt::MQTTComponent {
 | 
				
			|||||||
  MQTT_COMPONENT_CUSTOM_TOPIC(fan_mode, command)
 | 
					  MQTT_COMPONENT_CUSTOM_TOPIC(fan_mode, command)
 | 
				
			||||||
  MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, state)
 | 
					  MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, state)
 | 
				
			||||||
  MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, command)
 | 
					  MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, command)
 | 
				
			||||||
 | 
					  MQTT_COMPONENT_CUSTOM_TOPIC(preset, state)
 | 
				
			||||||
 | 
					  MQTT_COMPONENT_CUSTOM_TOPIC(preset, command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 protected:
 | 
					 protected:
 | 
				
			||||||
  const EntityBase *get_entity() const override;
 | 
					  const EntityBase *get_entity() const override;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -165,7 +165,7 @@ def do_packages_pass(config: dict):
 | 
				
			|||||||
                f"Packages must be a key to value mapping, got {type(packages)} instead"
 | 
					                f"Packages must be a key to value mapping, got {type(packages)} instead"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for package_name, package_config in packages.items():
 | 
					        for package_name, package_config in reversed(packages.items()):
 | 
				
			||||||
            with cv.prepend_path(package_name):
 | 
					            with cv.prepend_path(package_name):
 | 
				
			||||||
                recursive_package = package_config
 | 
					                recursive_package = package_config
 | 
				
			||||||
                if CONF_URL in package_config:
 | 
					                if CONF_URL in package_config:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,10 @@
 | 
				
			|||||||
#include "pid_autotuner.h"
 | 
					#include "pid_autotuner.h"
 | 
				
			||||||
#include "esphome/core/log.h"
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef M_PI
 | 
				
			||||||
 | 
					#define M_PI 3.1415926535897932384626433
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace esphome {
 | 
					namespace esphome {
 | 
				
			||||||
namespace pid {
 | 
					namespace pid {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,7 +77,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!std::isnan(this->setpoint_) && this->setpoint_ != setpoint) {
 | 
					  if (!std::isnan(this->setpoint_) && this->setpoint_ != setpoint) {
 | 
				
			||||||
    ESP_LOGW(TAG, "Setpoint changed during autotune! The result will not be accurate!");
 | 
					    ESP_LOGW(TAG, "%s: Setpoint changed during autotune! The result will not be accurate!", this->id_.c_str());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  this->setpoint_ = setpoint;
 | 
					  this->setpoint_ = setpoint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -87,7 +91,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (!this->frequency_detector_.has_enough_data() || !this->amplitude_detector_.has_enough_data()) {
 | 
					  if (!this->frequency_detector_.has_enough_data() || !this->amplitude_detector_.has_enough_data()) {
 | 
				
			||||||
    // not enough data for calculation yet
 | 
					    // not enough data for calculation yet
 | 
				
			||||||
    ESP_LOGV(TAG, "  Not enough data yet for aututuner");
 | 
					    ESP_LOGV(TAG, "%s:   Not enough data yet for autotuner", this->id_.c_str());
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -97,12 +101,13 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
				
			|||||||
    // The frequency/amplitude is not fully accurate yet, try to wait
 | 
					    // The frequency/amplitude is not fully accurate yet, try to wait
 | 
				
			||||||
    // until the fault clears, or terminate after a while anyway
 | 
					    // until the fault clears, or terminate after a while anyway
 | 
				
			||||||
    if (zc_symmetrical) {
 | 
					    if (zc_symmetrical) {
 | 
				
			||||||
      ESP_LOGVV(TAG, "  ZC is not symmetrical");
 | 
					      ESP_LOGVV(TAG, "%s:   ZC is not symmetrical", this->id_.c_str());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (amplitude_convergent) {
 | 
					    if (amplitude_convergent) {
 | 
				
			||||||
      ESP_LOGVV(TAG, "  Amplitude is not convergent");
 | 
					      ESP_LOGVV(TAG, "%s:   Amplitude is not convergent", this->id_.c_str());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    uint32_t phase = this->relay_function_.phase_count;
 | 
					    uint32_t phase = this->relay_function_.phase_count;
 | 
				
			||||||
 | 
					    ESP_LOGVV(TAG, "%s: >", this->id_.c_str());
 | 
				
			||||||
    ESP_LOGVV(TAG, "  Phase %u, enough=%u", phase, enough_data_phase_);
 | 
					    ESP_LOGVV(TAG, "  Phase %u, enough=%u", phase, enough_data_phase_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this->enough_data_phase_ == 0) {
 | 
					    if (this->enough_data_phase_ == 0) {
 | 
				
			||||||
@@ -116,7 +121,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ESP_LOGI(TAG, "PID Autotune finished!");
 | 
					  ESP_LOGI(TAG, "%s: PID Autotune finished!", this->id_.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  float osc_ampl = this->amplitude_detector_.get_mean_oscillation_amplitude();
 | 
					  float osc_ampl = this->amplitude_detector_.get_mean_oscillation_amplitude();
 | 
				
			||||||
  float d = (this->relay_function_.output_positive - this->relay_function_.output_negative) / 2.0f;
 | 
					  float d = (this->relay_function_.output_positive - this->relay_function_.output_negative) / 2.0f;
 | 
				
			||||||
@@ -131,12 +136,12 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
				
			|||||||
  return res;
 | 
					  return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
void PIDAutotuner::dump_config() {
 | 
					void PIDAutotuner::dump_config() {
 | 
				
			||||||
  ESP_LOGI(TAG, "PID Autotune:");
 | 
					 | 
				
			||||||
  if (this->state_ == AUTOTUNE_SUCCEEDED) {
 | 
					  if (this->state_ == AUTOTUNE_SUCCEEDED) {
 | 
				
			||||||
 | 
					    ESP_LOGI(TAG, "%s: PID Autotune:", this->id_.c_str());
 | 
				
			||||||
    ESP_LOGI(TAG, "  State: Succeeded!");
 | 
					    ESP_LOGI(TAG, "  State: Succeeded!");
 | 
				
			||||||
    bool has_issue = false;
 | 
					    bool has_issue = false;
 | 
				
			||||||
    if (!this->amplitude_detector_.is_amplitude_convergent()) {
 | 
					    if (!this->amplitude_detector_.is_amplitude_convergent()) {
 | 
				
			||||||
      ESP_LOGW(TAG, "  Could not reliable determine oscillation amplitude, PID parameters may be inaccurate!");
 | 
					      ESP_LOGW(TAG, "  Could not reliably determine oscillation amplitude, PID parameters may be inaccurate!");
 | 
				
			||||||
      ESP_LOGW(TAG, "    Please make sure you eliminate all outside influences on the measured temperature.");
 | 
					      ESP_LOGW(TAG, "    Please make sure you eliminate all outside influences on the measured temperature.");
 | 
				
			||||||
      has_issue = true;
 | 
					      has_issue = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -173,10 +178,12 @@ void PIDAutotuner::dump_config() {
 | 
				
			|||||||
    print_rule_("Pessen Integral PID", 0.7f, 1.75f, 0.105f);
 | 
					    print_rule_("Pessen Integral PID", 0.7f, 1.75f, 0.105f);
 | 
				
			||||||
    print_rule_("Some Overshoot PID", 0.333f, 0.667f, 0.111f);
 | 
					    print_rule_("Some Overshoot PID", 0.333f, 0.667f, 0.111f);
 | 
				
			||||||
    print_rule_("No Overshoot PID", 0.2f, 0.4f, 0.0625f);
 | 
					    print_rule_("No Overshoot PID", 0.2f, 0.4f, 0.0625f);
 | 
				
			||||||
 | 
					    ESP_LOGI(TAG, "%s: Autotune completed", this->id_.c_str());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (this->state_ == AUTOTUNE_RUNNING) {
 | 
					  if (this->state_ == AUTOTUNE_RUNNING) {
 | 
				
			||||||
    ESP_LOGI(TAG, "  Autotune is still running!");
 | 
					    ESP_LOGD(TAG, "%s: PID Autotune:", this->id_.c_str());
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "  Autotune is still running!");
 | 
				
			||||||
    ESP_LOGD(TAG, "  Status: Trying to reach %.2f °C", setpoint_ - relay_function_.current_target_error());
 | 
					    ESP_LOGD(TAG, "  Status: Trying to reach %.2f °C", setpoint_ - relay_function_.current_target_error());
 | 
				
			||||||
    ESP_LOGD(TAG, "  Stats so far:");
 | 
					    ESP_LOGD(TAG, "  Stats so far:");
 | 
				
			||||||
    ESP_LOGD(TAG, "    Phases: %u", relay_function_.phase_count);
 | 
					    ESP_LOGD(TAG, "    Phases: %u", relay_function_.phase_count);
 | 
				
			||||||
@@ -221,7 +228,6 @@ float PIDAutotuner::RelayFunction::update(float error) {
 | 
				
			|||||||
  float output = state == RELAY_FUNCTION_POSITIVE ? output_positive : output_negative;
 | 
					  float output = state == RELAY_FUNCTION_POSITIVE ? output_positive : output_negative;
 | 
				
			||||||
  if (change) {
 | 
					  if (change) {
 | 
				
			||||||
    this->phase_count++;
 | 
					    this->phase_count++;
 | 
				
			||||||
    ESP_LOGV(TAG, "Autotune: Turning output to %.1f%%", output * 100);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return output;
 | 
					  return output;
 | 
				
			||||||
@@ -245,10 +251,8 @@ void PIDAutotuner::OscillationFrequencyDetector::update(uint32_t now, float erro
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (had_crossing) {
 | 
					  if (had_crossing) {
 | 
				
			||||||
    // Had crossing above hysteresis threshold, record
 | 
					    // Had crossing above hysteresis threshold, record
 | 
				
			||||||
    ESP_LOGV(TAG, "Autotune: Detected Zero-Cross at %u", now);
 | 
					 | 
				
			||||||
    if (this->last_zerocross != 0) {
 | 
					    if (this->last_zerocross != 0) {
 | 
				
			||||||
      uint32_t dt = now - this->last_zerocross;
 | 
					      uint32_t dt = now - this->last_zerocross;
 | 
				
			||||||
      ESP_LOGV(TAG, "  dt: %u", dt);
 | 
					 | 
				
			||||||
      this->zerocrossing_intervals.push_back(dt);
 | 
					      this->zerocrossing_intervals.push_back(dt);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this->last_zerocross = now;
 | 
					    this->last_zerocross = now;
 | 
				
			||||||
@@ -297,13 +301,11 @@ void PIDAutotuner::OscillationAmplitudeDetector::update(float error,
 | 
				
			|||||||
      // The positive error peak must have been in previous segment (180° shifted)
 | 
					      // The positive error peak must have been in previous segment (180° shifted)
 | 
				
			||||||
      // record phase_max
 | 
					      // record phase_max
 | 
				
			||||||
      this->phase_maxs.push_back(phase_max);
 | 
					      this->phase_maxs.push_back(phase_max);
 | 
				
			||||||
      ESP_LOGV(TAG, "Autotune: Phase Max: %f", phase_max);
 | 
					 | 
				
			||||||
    } else if (last_relay_state == RelayFunction::RELAY_FUNCTION_NEGATIVE) {
 | 
					    } else if (last_relay_state == RelayFunction::RELAY_FUNCTION_NEGATIVE) {
 | 
				
			||||||
      // Transitioned from negative error to positive error.
 | 
					      // Transitioned from negative error to positive error.
 | 
				
			||||||
      // The negative error peak must have been in previous segment (180° shifted)
 | 
					      // The negative error peak must have been in previous segment (180° shifted)
 | 
				
			||||||
      // record phase_min
 | 
					      // record phase_min
 | 
				
			||||||
      this->phase_mins.push_back(phase_min);
 | 
					      this->phase_mins.push_back(phase_min);
 | 
				
			||||||
      ESP_LOGV(TAG, "Autotune: Phase Min: %f", phase_min);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // reset phase values for next phase
 | 
					    // reset phase values for next phase
 | 
				
			||||||
    this->phase_min = error;
 | 
					    this->phase_min = error;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,8 @@ class PIDAutotuner {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  void dump_config();
 | 
					  void dump_config();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void set_autotuner_id(std::string id) { this->id_ = std::move(id); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void set_noiseband(float noiseband) {
 | 
					  void set_noiseband(float noiseband) {
 | 
				
			||||||
    relay_function_.noiseband = noiseband;
 | 
					    relay_function_.noiseband = noiseband;
 | 
				
			||||||
    // ZC detector uses 1/4 the noiseband of relay function (noise suppression)
 | 
					    // ZC detector uses 1/4 the noiseband of relay function (noise suppression)
 | 
				
			||||||
@@ -106,6 +108,7 @@ class PIDAutotuner {
 | 
				
			|||||||
  } state_ = AUTOTUNE_RUNNING;
 | 
					  } state_ = AUTOTUNE_RUNNING;
 | 
				
			||||||
  float ku_;
 | 
					  float ku_;
 | 
				
			||||||
  float pu_;
 | 
					  float pu_;
 | 
				
			||||||
 | 
					  std::string id_;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace pid
 | 
					}  // namespace pid
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -130,9 +130,6 @@ void PIDClimate::update_pid_() {
 | 
				
			|||||||
        // keep autotuner instance so that subsequent dump_configs will print the long result message.
 | 
					        // keep autotuner instance so that subsequent dump_configs will print the long result message.
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        value = res.output;
 | 
					        value = res.output;
 | 
				
			||||||
        if (mode != climate::CLIMATE_MODE_HEAT_COOL) {
 | 
					 | 
				
			||||||
          ESP_LOGW(TAG, "For PID autotuner you need to set AUTO (also called heat/cool) mode!");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -151,10 +148,24 @@ void PIDClimate::start_autotune(std::unique_ptr<PIDAutotuner> &&autotune) {
 | 
				
			|||||||
  float min_value = this->supports_cool_() ? -1.0f : 0.0f;
 | 
					  float min_value = this->supports_cool_() ? -1.0f : 0.0f;
 | 
				
			||||||
  float max_value = this->supports_heat_() ? 1.0f : 0.0f;
 | 
					  float max_value = this->supports_heat_() ? 1.0f : 0.0f;
 | 
				
			||||||
  this->autotuner_->config(min_value, max_value);
 | 
					  this->autotuner_->config(min_value, max_value);
 | 
				
			||||||
 | 
					  this->autotuner_->set_autotuner_id(this->get_object_id());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ESP_LOGI(TAG,
 | 
				
			||||||
 | 
					           "%s: Autotune has started. This can take a long time depending on the "
 | 
				
			||||||
 | 
					           "responsiveness of your system. Your system "
 | 
				
			||||||
 | 
					           "output will be altered to deliberately oscillate above and below the setpoint multiple times. "
 | 
				
			||||||
 | 
					           "Until your sensor provides a reading, the autotuner may display \'nan\'",
 | 
				
			||||||
 | 
					           this->get_object_id().c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this->set_interval("autotune-progress", 10000, [this]() {
 | 
					  this->set_interval("autotune-progress", 10000, [this]() {
 | 
				
			||||||
    if (this->autotuner_ != nullptr && !this->autotuner_->is_finished())
 | 
					    if (this->autotuner_ != nullptr && !this->autotuner_->is_finished())
 | 
				
			||||||
      this->autotuner_->dump_config();
 | 
					      this->autotuner_->dump_config();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (mode != climate::CLIMATE_MODE_HEAT_COOL) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "%s: !!! For PID autotuner you need to set AUTO (also called heat/cool) mode!",
 | 
				
			||||||
 | 
					             this->get_object_id().c_str());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void PIDClimate::reset_integral_term() { this->controller_.reset_accumulated_integral(); }
 | 
					void PIDClimate::reset_integral_term() { this->controller_.reset_accumulated_integral(); }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -237,6 +237,107 @@ async def build_dumpers(config):
 | 
				
			|||||||
    return dumpers
 | 
					    return dumpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# CanalSat
 | 
				
			||||||
 | 
					(
 | 
				
			||||||
 | 
					    CanalSatData,
 | 
				
			||||||
 | 
					    CanalSatBinarySensor,
 | 
				
			||||||
 | 
					    CanalSatTrigger,
 | 
				
			||||||
 | 
					    CanalSatAction,
 | 
				
			||||||
 | 
					    CanalSatDumper,
 | 
				
			||||||
 | 
					) = declare_protocol("CanalSat")
 | 
				
			||||||
 | 
					CANALSAT_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_DEVICE): cv.hex_uint8_t,
 | 
				
			||||||
 | 
					        cv.Optional(CONF_ADDRESS, default=0): cv.hex_uint8_t,
 | 
				
			||||||
 | 
					        cv.Required(CONF_COMMAND): cv.hex_uint8_t,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_binary_sensor("canalsat", CanalSatBinarySensor, CANALSAT_SCHEMA)
 | 
				
			||||||
 | 
					def canalsat_binary_sensor(var, config):
 | 
				
			||||||
 | 
					    cg.add(
 | 
				
			||||||
 | 
					        var.set_data(
 | 
				
			||||||
 | 
					            cg.StructInitializer(
 | 
				
			||||||
 | 
					                CanalSatData,
 | 
				
			||||||
 | 
					                ("device", config[CONF_DEVICE]),
 | 
				
			||||||
 | 
					                ("address", config[CONF_ADDRESS]),
 | 
				
			||||||
 | 
					                ("command", config[CONF_COMMAND]),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_trigger("canalsat", CanalSatTrigger, CanalSatData)
 | 
				
			||||||
 | 
					def canalsat_trigger(var, config):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_dumper("canalsat", CanalSatDumper)
 | 
				
			||||||
 | 
					def canalsat_dumper(var, config):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_action("canalsat", CanalSatAction, CANALSAT_SCHEMA)
 | 
				
			||||||
 | 
					async def canalsat_action(var, config, args):
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_DEVICE], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_device(template_))
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_address(template_))
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_command(template_))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(
 | 
				
			||||||
 | 
					    CanalSatLDData,
 | 
				
			||||||
 | 
					    CanalSatLDBinarySensor,
 | 
				
			||||||
 | 
					    CanalSatLDTrigger,
 | 
				
			||||||
 | 
					    CanalSatLDAction,
 | 
				
			||||||
 | 
					    CanalSatLDDumper,
 | 
				
			||||||
 | 
					) = declare_protocol("CanalSatLD")
 | 
				
			||||||
 | 
					CANALSATLD_SCHEMA = cv.Schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.Required(CONF_DEVICE): cv.hex_uint8_t,
 | 
				
			||||||
 | 
					        cv.Optional(CONF_ADDRESS, default=0): cv.hex_uint8_t,
 | 
				
			||||||
 | 
					        cv.Required(CONF_COMMAND): cv.hex_uint8_t,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_binary_sensor("canalsatld", CanalSatLDBinarySensor, CANALSAT_SCHEMA)
 | 
				
			||||||
 | 
					def canalsatld_binary_sensor(var, config):
 | 
				
			||||||
 | 
					    cg.add(
 | 
				
			||||||
 | 
					        var.set_data(
 | 
				
			||||||
 | 
					            cg.StructInitializer(
 | 
				
			||||||
 | 
					                CanalSatLDData,
 | 
				
			||||||
 | 
					                ("device", config[CONF_DEVICE]),
 | 
				
			||||||
 | 
					                ("address", config[CONF_ADDRESS]),
 | 
				
			||||||
 | 
					                ("command", config[CONF_COMMAND]),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_trigger("canalsatld", CanalSatLDTrigger, CanalSatLDData)
 | 
				
			||||||
 | 
					def canalsatld_trigger(var, config):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_dumper("canalsatld", CanalSatLDDumper)
 | 
				
			||||||
 | 
					def canalsatld_dumper(var, config):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register_action("canalsatld", CanalSatLDAction, CANALSATLD_SCHEMA)
 | 
				
			||||||
 | 
					async def canalsatld_action(var, config, args):
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_DEVICE], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_device(template_))
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_address(template_))
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8)
 | 
				
			||||||
 | 
					    cg.add(var.set_command(template_))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Coolix
 | 
					# Coolix
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
    CoolixData,
 | 
					    CoolixData,
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										108
									
								
								esphome/components/remote_base/canalsat_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								esphome/components/remote_base/canalsat_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					#include "canalsat_protocol.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace remote_base {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const CANALSAT_TAG = "remote.canalsat";
 | 
				
			||||||
 | 
					static const char *const CANALSATLD_TAG = "remote.canalsatld";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint16_t CANALSAT_FREQ = 55500;
 | 
				
			||||||
 | 
					static const uint16_t CANALSATLD_FREQ = 56000;
 | 
				
			||||||
 | 
					static const uint16_t CANALSAT_UNIT = 250;
 | 
				
			||||||
 | 
					static const uint16_t CANALSATLD_UNIT = 320;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CanalSatProtocol::CanalSatProtocol() {
 | 
				
			||||||
 | 
					  this->frequency_ = CANALSAT_FREQ;
 | 
				
			||||||
 | 
					  this->unit_ = CANALSAT_UNIT;
 | 
				
			||||||
 | 
					  this->tag_ = CANALSAT_TAG;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CanalSatLDProtocol::CanalSatLDProtocol() {
 | 
				
			||||||
 | 
					  this->frequency_ = CANALSATLD_FREQ;
 | 
				
			||||||
 | 
					  this->unit_ = CANALSATLD_UNIT;
 | 
				
			||||||
 | 
					  this->tag_ = CANALSATLD_TAG;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CanalSatBaseProtocol::encode(RemoteTransmitData *dst, const CanalSatData &data) {
 | 
				
			||||||
 | 
					  dst->reserve(48);
 | 
				
			||||||
 | 
					  dst->set_carrier_frequency(this->frequency_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t raw{
 | 
				
			||||||
 | 
					      static_cast<uint32_t>((1 << 23) | (data.device << 16) | (data.address << 10) | (0 << 9) | (data.command << 1))};
 | 
				
			||||||
 | 
					  bool was_high{true};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (uint32_t mask = 0x800000; mask; mask >>= 1) {
 | 
				
			||||||
 | 
					    if (raw & mask) {
 | 
				
			||||||
 | 
					      if (was_high) {
 | 
				
			||||||
 | 
					        dst->mark(this->unit_);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      was_high = true;
 | 
				
			||||||
 | 
					      if (raw & mask >> 1) {
 | 
				
			||||||
 | 
					        dst->space(this->unit_);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        dst->space(this->unit_ * 2);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (!was_high) {
 | 
				
			||||||
 | 
					        dst->space(this->unit_);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      was_high = false;
 | 
				
			||||||
 | 
					      if (raw & mask >> 1) {
 | 
				
			||||||
 | 
					        dst->mark(this->unit_ * 2);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        dst->mark(this->unit_);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					optional<CanalSatData> CanalSatBaseProtocol::decode(RemoteReceiveData src) {
 | 
				
			||||||
 | 
					  CanalSatData data{
 | 
				
			||||||
 | 
					      .device = 0,
 | 
				
			||||||
 | 
					      .address = 0,
 | 
				
			||||||
 | 
					      .repeat = 0,
 | 
				
			||||||
 | 
					      .command = 0,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check if initial mark and spaces match
 | 
				
			||||||
 | 
					  if (!src.peek_mark(this->unit_) || !(src.peek_space(this->unit_, 1) || src.peek_space(this->unit_ * 2, 1))) {
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint8_t bit{1};
 | 
				
			||||||
 | 
					  uint8_t offset{1};
 | 
				
			||||||
 | 
					  uint32_t buffer{0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (offset < 24) {
 | 
				
			||||||
 | 
					    buffer = buffer | (bit << (24 - offset++));
 | 
				
			||||||
 | 
					    src.advance();
 | 
				
			||||||
 | 
					    if (src.peek_mark(this->unit_) || src.peek_space(this->unit_)) {
 | 
				
			||||||
 | 
					      src.advance();
 | 
				
			||||||
 | 
					    } else if (src.peek_mark(this->unit_ * 2) || src.peek_space(this->unit_ * 2)) {
 | 
				
			||||||
 | 
					      bit = !bit;
 | 
				
			||||||
 | 
					    } else if (offset != 24 && bit != 1) {  // If last bit is high, final space is indistinguishable
 | 
				
			||||||
 | 
					      return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  data.device = (0xFF0000 & buffer) >> 16;
 | 
				
			||||||
 | 
					  data.address = (0x00FF00 & buffer) >> 10;
 | 
				
			||||||
 | 
					  data.repeat = (0x00FF00 & buffer) >> 9;
 | 
				
			||||||
 | 
					  data.command = (0x0000FF & buffer) >> 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CanalSatBaseProtocol::dump(const CanalSatData &data) {
 | 
				
			||||||
 | 
					  if (this->tag_ == CANALSATLD_TAG) {
 | 
				
			||||||
 | 
					    ESP_LOGD(this->tag_, "Received CanalSatLD: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device,
 | 
				
			||||||
 | 
					             data.address, data.command, data.repeat);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGD(this->tag_, "Received CanalSat: device=0x%02X, address=0x%02X, command=0x%02X, repeat=0x%X", data.device,
 | 
				
			||||||
 | 
					             data.address, data.command, data.repeat);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace remote_base
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										78
									
								
								esphome/components/remote_base/canalsat_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								esphome/components/remote_base/canalsat_protocol.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "remote_base.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace remote_base {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct CanalSatData {
 | 
				
			||||||
 | 
					  uint8_t device : 7;
 | 
				
			||||||
 | 
					  uint8_t address : 6;
 | 
				
			||||||
 | 
					  uint8_t repeat : 1;
 | 
				
			||||||
 | 
					  uint8_t command : 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool operator==(const CanalSatData &rhs) const {
 | 
				
			||||||
 | 
					    return device == rhs.device && address == rhs.address && command == rhs.command;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct CanalSatLDData : public CanalSatData {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CanalSatBaseProtocol : public RemoteProtocol<CanalSatData> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void encode(RemoteTransmitData *dst, const CanalSatData &data) override;
 | 
				
			||||||
 | 
					  optional<CanalSatData> decode(RemoteReceiveData src) override;
 | 
				
			||||||
 | 
					  void dump(const CanalSatData &data) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  uint16_t frequency_;
 | 
				
			||||||
 | 
					  uint16_t unit_;
 | 
				
			||||||
 | 
					  const char *tag_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CanalSatProtocol : public CanalSatBaseProtocol {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  CanalSatProtocol();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CanalSatLDProtocol : public CanalSatBaseProtocol {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  CanalSatLDProtocol();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DECLARE_REMOTE_PROTOCOL(CanalSat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class CanalSatAction : public RemoteTransmitterActionBase<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, device)
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, address)
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void encode(RemoteTransmitData *dst, Ts... x) {
 | 
				
			||||||
 | 
					    CanalSatData data{};
 | 
				
			||||||
 | 
					    data.device = this->device_.value(x...);
 | 
				
			||||||
 | 
					    data.address = this->address_.value(x...);
 | 
				
			||||||
 | 
					    data.command = this->command_.value(x...);
 | 
				
			||||||
 | 
					    CanalSatProtocol().encode(dst, data);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DECLARE_REMOTE_PROTOCOL(CanalSatLD)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class CanalSatLDAction : public RemoteTransmitterActionBase<Ts...> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, device)
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, address)
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint8_t, command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void encode(RemoteTransmitData *dst, Ts... x) {
 | 
				
			||||||
 | 
					    CanalSatData data{};
 | 
				
			||||||
 | 
					    data.device = this->device_.value(x...);
 | 
				
			||||||
 | 
					    data.address = this->address_.value(x...);
 | 
				
			||||||
 | 
					    data.command = this->command_.value(x...);
 | 
				
			||||||
 | 
					    CanalSatLDProtocol().encode(dst, data);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace remote_base
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										23
									
								
								esphome/components/scd30/automation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								esphome/components/scd30/automation.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/core/automation.h"
 | 
				
			||||||
 | 
					#include "scd30.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace scd30 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename... Ts> class ForceRecalibrationWithReference : public Action<Ts...>, public Parented<SCD30Component> {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void play(Ts... x) override {
 | 
				
			||||||
 | 
					    if (this->value_.has_value()) {
 | 
				
			||||||
 | 
					      this->parent_->force_recalibration_with_reference(this->value_.value(x...));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  TEMPLATABLE_VALUE(uint16_t, value)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace scd30
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
@@ -202,5 +202,27 @@ bool SCD30Component::is_data_ready_() {
 | 
				
			|||||||
  return is_data_ready == 1;
 | 
					  return is_data_ready == 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SCD30Component::force_recalibration_with_reference(uint16_t co2_reference) {
 | 
				
			||||||
 | 
					  ESP_LOGD(TAG, "Performing CO2 force recalibration with reference %dppm.", co2_reference);
 | 
				
			||||||
 | 
					  if (this->write_command(SCD30_CMD_FORCED_CALIBRATION, co2_reference)) {
 | 
				
			||||||
 | 
					    ESP_LOGD(TAG, "Force recalibration complete.");
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Failed to force recalibration with reference.");
 | 
				
			||||||
 | 
					    this->error_code_ = FORCE_RECALIBRATION_FAILED;
 | 
				
			||||||
 | 
					    this->status_set_warning();
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint16_t SCD30Component::get_forced_calibration_reference() {
 | 
				
			||||||
 | 
					  uint16_t forced_calibration_reference;
 | 
				
			||||||
 | 
					  // Get current CO2 calibration
 | 
				
			||||||
 | 
					  if (!this->get_register(SCD30_CMD_FORCED_CALIBRATION, forced_calibration_reference)) {
 | 
				
			||||||
 | 
					    ESP_LOGE(TAG, "Unable to read forced calibration reference.");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return forced_calibration_reference;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace scd30
 | 
					}  // namespace scd30
 | 
				
			||||||
}  // namespace esphome
 | 
					}  // namespace esphome
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,8 @@ class SCD30Component : public Component, public sensirion_common::SensirionI2CDe
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  void set_temperature_offset(float offset) { temperature_offset_ = offset; }
 | 
					  void set_temperature_offset(float offset) { temperature_offset_ = offset; }
 | 
				
			||||||
  void set_update_interval(uint16_t interval) { update_interval_ = interval; }
 | 
					  void set_update_interval(uint16_t interval) { update_interval_ = interval; }
 | 
				
			||||||
 | 
					  bool force_recalibration_with_reference(uint16_t co2_reference);
 | 
				
			||||||
 | 
					  uint16_t get_forced_calibration_reference();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void setup() override;
 | 
					  void setup() override;
 | 
				
			||||||
  void update();
 | 
					  void update();
 | 
				
			||||||
@@ -33,6 +35,7 @@ class SCD30Component : public Component, public sensirion_common::SensirionI2CDe
 | 
				
			|||||||
    COMMUNICATION_FAILED,
 | 
					    COMMUNICATION_FAILED,
 | 
				
			||||||
    FIRMWARE_IDENTIFICATION_FAILED,
 | 
					    FIRMWARE_IDENTIFICATION_FAILED,
 | 
				
			||||||
    MEASUREMENT_INIT_FAILED,
 | 
					    MEASUREMENT_INIT_FAILED,
 | 
				
			||||||
 | 
					    FORCE_RECALIBRATION_FAILED,
 | 
				
			||||||
    UNKNOWN
 | 
					    UNKNOWN
 | 
				
			||||||
  } error_code_{UNKNOWN};
 | 
					  } error_code_{UNKNOWN};
 | 
				
			||||||
  bool enable_asc_{true};
 | 
					  bool enable_asc_{true};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
from esphome import core
 | 
					from esphome import automation, core
 | 
				
			||||||
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 i2c, sensor
 | 
					from esphome.components import i2c, sensor
 | 
				
			||||||
@@ -9,6 +9,7 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_TEMPERATURE,
 | 
					    CONF_TEMPERATURE,
 | 
				
			||||||
    CONF_CO2,
 | 
					    CONF_CO2,
 | 
				
			||||||
    CONF_UPDATE_INTERVAL,
 | 
					    CONF_UPDATE_INTERVAL,
 | 
				
			||||||
 | 
					    CONF_VALUE,
 | 
				
			||||||
    DEVICE_CLASS_HUMIDITY,
 | 
					    DEVICE_CLASS_HUMIDITY,
 | 
				
			||||||
    DEVICE_CLASS_TEMPERATURE,
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
    STATE_CLASS_MEASUREMENT,
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
@@ -26,6 +27,11 @@ SCD30Component = scd30_ns.class_(
 | 
				
			|||||||
    "SCD30Component", cg.Component, sensirion_common.SensirionI2CDevice
 | 
					    "SCD30Component", cg.Component, sensirion_common.SensirionI2CDevice
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Actions
 | 
				
			||||||
 | 
					ForceRecalibrationWithReference = scd30_ns.class_(
 | 
				
			||||||
 | 
					    "ForceRecalibrationWithReference", automation.Action
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration"
 | 
					CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration"
 | 
				
			||||||
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
 | 
					CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
 | 
				
			||||||
CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation"
 | 
					CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation"
 | 
				
			||||||
@@ -106,3 +112,26 @@ async def to_code(config):
 | 
				
			|||||||
    if CONF_TEMPERATURE in config:
 | 
					    if CONF_TEMPERATURE in config:
 | 
				
			||||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
					        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
				
			||||||
        cg.add(var.set_temperature_sensor(sens))
 | 
					        cg.add(var.set_temperature_sensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@automation.register_action(
 | 
				
			||||||
 | 
					    "scd30.force_recalibration_with_reference",
 | 
				
			||||||
 | 
					    ForceRecalibrationWithReference,
 | 
				
			||||||
 | 
					    cv.maybe_simple_value(
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            cv.GenerateID(): cv.use_id(SCD30Component),
 | 
				
			||||||
 | 
					            cv.Required(CONF_VALUE): cv.templatable(
 | 
				
			||||||
 | 
					                cv.int_range(min=400, max=2000, max_included=True)
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        key=CONF_VALUE,
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def scd30_force_recalibration_with_reference_to_code(
 | 
				
			||||||
 | 
					    config, action_id, template_arg, args
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(action_id, template_arg)
 | 
				
			||||||
 | 
					    await cg.register_parented(var, config[CONF_ID])
 | 
				
			||||||
 | 
					    template_ = await cg.templatable(config[CONF_VALUE], args, cg.uint16)
 | 
				
			||||||
 | 
					    cg.add(var.set_value(template_))
 | 
				
			||||||
 | 
					    return var
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,8 +6,7 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_STORE_BASELINE,
 | 
					    CONF_STORE_BASELINE,
 | 
				
			||||||
    CONF_TEMPERATURE_SOURCE,
 | 
					    CONF_TEMPERATURE_SOURCE,
 | 
				
			||||||
    ICON_RADIATOR,
 | 
					    ICON_RADIATOR,
 | 
				
			||||||
    DEVICE_CLASS_NITROUS_OXIDE,
 | 
					    DEVICE_CLASS_AQI,
 | 
				
			||||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
					 | 
				
			||||||
    STATE_CLASS_MEASUREMENT,
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,13 +66,13 @@ CONFIG_SCHEMA = cv.All(
 | 
				
			|||||||
            cv.Optional(CONF_VOC): sensor.sensor_schema(
 | 
					            cv.Optional(CONF_VOC): sensor.sensor_schema(
 | 
				
			||||||
                icon=ICON_RADIATOR,
 | 
					                icon=ICON_RADIATOR,
 | 
				
			||||||
                accuracy_decimals=0,
 | 
					                accuracy_decimals=0,
 | 
				
			||||||
                device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
					                device_class=DEVICE_CLASS_AQI,
 | 
				
			||||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
					                state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
            ).extend(GAS_SENSOR),
 | 
					            ).extend(GAS_SENSOR),
 | 
				
			||||||
            cv.Optional(CONF_NOX): sensor.sensor_schema(
 | 
					            cv.Optional(CONF_NOX): sensor.sensor_schema(
 | 
				
			||||||
                icon=ICON_RADIATOR,
 | 
					                icon=ICON_RADIATOR,
 | 
				
			||||||
                accuracy_decimals=0,
 | 
					                accuracy_decimals=0,
 | 
				
			||||||
                device_class=DEVICE_CLASS_NITROUS_OXIDE,
 | 
					                device_class=DEVICE_CLASS_AQI,
 | 
				
			||||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
					                state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
            ).extend(GAS_SENSOR),
 | 
					            ).extend(GAS_SENSOR),
 | 
				
			||||||
            cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
 | 
					            cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ from esphome.const import (
 | 
				
			|||||||
    UNIT_WATT,
 | 
					    UNIT_WATT,
 | 
				
			||||||
    DEVICE_CLASS_POWER,
 | 
					    DEVICE_CLASS_POWER,
 | 
				
			||||||
    DEVICE_CLASS_VOLTAGE,
 | 
					    DEVICE_CLASS_VOLTAGE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_CURRENT,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from esphome.core import HexInt, CORE
 | 
					from esphome.core import HexInt, CORE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -169,7 +170,7 @@ CONFIG_SCHEMA = (
 | 
				
			|||||||
            ),
 | 
					            ),
 | 
				
			||||||
            cv.Optional(CONF_CURRENT): sensor.sensor_schema(
 | 
					            cv.Optional(CONF_CURRENT): sensor.sensor_schema(
 | 
				
			||||||
                unit_of_measurement=UNIT_AMPERE,
 | 
					                unit_of_measurement=UNIT_AMPERE,
 | 
				
			||||||
                device_class=DEVICE_CLASS_POWER,
 | 
					                device_class=DEVICE_CLASS_CURRENT,
 | 
				
			||||||
                accuracy_decimals=2,
 | 
					                accuracy_decimals=2,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            # Change the default gamma_correct setting.
 | 
					            # Change the default gamma_correct setting.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -156,6 +156,7 @@ void Sim800LComponent::parse_cmd_(std::string message) {
 | 
				
			|||||||
    case STATE_SEND_USSD1:
 | 
					    case STATE_SEND_USSD1:
 | 
				
			||||||
      this->send_cmd_("AT+CUSD=1, \"" + this->ussd_ + "\"");
 | 
					      this->send_cmd_("AT+CUSD=1, \"" + this->ussd_ + "\"");
 | 
				
			||||||
      this->state_ = STATE_SEND_USSD2;
 | 
					      this->state_ = STATE_SEND_USSD2;
 | 
				
			||||||
 | 
					      this->expect_ack_ = true;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    case STATE_SEND_USSD2:
 | 
					    case STATE_SEND_USSD2:
 | 
				
			||||||
      ESP_LOGD(TAG, "SendUssd2: '%s'", message.c_str());
 | 
					      ESP_LOGD(TAG, "SendUssd2: '%s'", message.c_str());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ from esphome.const import (
 | 
				
			|||||||
    CONF_FAN_MODE_MIDDLE_ACTION,
 | 
					    CONF_FAN_MODE_MIDDLE_ACTION,
 | 
				
			||||||
    CONF_FAN_MODE_FOCUS_ACTION,
 | 
					    CONF_FAN_MODE_FOCUS_ACTION,
 | 
				
			||||||
    CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
					    CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
				
			||||||
 | 
					    CONF_FAN_MODE_QUIET_ACTION,
 | 
				
			||||||
    CONF_FAN_ONLY_ACTION,
 | 
					    CONF_FAN_ONLY_ACTION,
 | 
				
			||||||
    CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER,
 | 
					    CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER,
 | 
				
			||||||
    CONF_FAN_ONLY_COOLING,
 | 
					    CONF_FAN_ONLY_COOLING,
 | 
				
			||||||
@@ -273,6 +274,7 @@ def validate_thermostat(config):
 | 
				
			|||||||
            CONF_FAN_MODE_MIDDLE_ACTION,
 | 
					            CONF_FAN_MODE_MIDDLE_ACTION,
 | 
				
			||||||
            CONF_FAN_MODE_FOCUS_ACTION,
 | 
					            CONF_FAN_MODE_FOCUS_ACTION,
 | 
				
			||||||
            CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
					            CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
				
			||||||
 | 
					            CONF_FAN_MODE_QUIET_ACTION,
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    for req_config_item, config_triggers in requirements.items():
 | 
					    for req_config_item, config_triggers in requirements.items():
 | 
				
			||||||
@@ -413,6 +415,7 @@ def validate_thermostat(config):
 | 
				
			|||||||
            "MIDDLE": [CONF_FAN_MODE_MIDDLE_ACTION],
 | 
					            "MIDDLE": [CONF_FAN_MODE_MIDDLE_ACTION],
 | 
				
			||||||
            "FOCUS": [CONF_FAN_MODE_FOCUS_ACTION],
 | 
					            "FOCUS": [CONF_FAN_MODE_FOCUS_ACTION],
 | 
				
			||||||
            "DIFFUSE": [CONF_FAN_MODE_DIFFUSE_ACTION],
 | 
					            "DIFFUSE": [CONF_FAN_MODE_DIFFUSE_ACTION],
 | 
				
			||||||
 | 
					            "QUIET": [CONF_FAN_MODE_QUIET_ACTION],
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for preset_config in config[CONF_PRESET]:
 | 
					        for preset_config in config[CONF_PRESET]:
 | 
				
			||||||
@@ -500,12 +503,13 @@ def validate_thermostat(config):
 | 
				
			|||||||
            CONF_FAN_MODE_MIDDLE_ACTION,
 | 
					            CONF_FAN_MODE_MIDDLE_ACTION,
 | 
				
			||||||
            CONF_FAN_MODE_FOCUS_ACTION,
 | 
					            CONF_FAN_MODE_FOCUS_ACTION,
 | 
				
			||||||
            CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
					            CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
				
			||||||
 | 
					            CONF_FAN_MODE_QUIET_ACTION,
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        for config_req_action in requirements:
 | 
					        for config_req_action in requirements:
 | 
				
			||||||
            if config_req_action in config:
 | 
					            if config_req_action in config:
 | 
				
			||||||
                return config
 | 
					                return config
 | 
				
			||||||
        raise cv.Invalid(
 | 
					        raise cv.Invalid(
 | 
				
			||||||
            f"At least one of {CONF_FAN_MODE_ON_ACTION}, {CONF_FAN_MODE_OFF_ACTION}, {CONF_FAN_MODE_AUTO_ACTION}, {CONF_FAN_MODE_LOW_ACTION}, {CONF_FAN_MODE_MEDIUM_ACTION}, {CONF_FAN_MODE_HIGH_ACTION}, {CONF_FAN_MODE_MIDDLE_ACTION}, {CONF_FAN_MODE_FOCUS_ACTION}, {CONF_FAN_MODE_DIFFUSE_ACTION} must be defined to use {CONF_MIN_FAN_MODE_SWITCHING_TIME}"
 | 
					            f"At least one of {CONF_FAN_MODE_ON_ACTION}, {CONF_FAN_MODE_OFF_ACTION}, {CONF_FAN_MODE_AUTO_ACTION}, {CONF_FAN_MODE_LOW_ACTION}, {CONF_FAN_MODE_MEDIUM_ACTION}, {CONF_FAN_MODE_HIGH_ACTION}, {CONF_FAN_MODE_MIDDLE_ACTION}, {CONF_FAN_MODE_FOCUS_ACTION}, {CONF_FAN_MODE_DIFFUSE_ACTION}, {CONF_FAN_MODE_QUIET_ACTION} must be defined to use {CONF_MIN_FAN_MODE_SWITCHING_TIME}"
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    return config
 | 
					    return config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -563,6 +567,9 @@ CONFIG_SCHEMA = cv.All(
 | 
				
			|||||||
            cv.Optional(CONF_FAN_MODE_DIFFUSE_ACTION): automation.validate_automation(
 | 
					            cv.Optional(CONF_FAN_MODE_DIFFUSE_ACTION): automation.validate_automation(
 | 
				
			||||||
                single=True
 | 
					                single=True
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
					            cv.Optional(CONF_FAN_MODE_QUIET_ACTION): automation.validate_automation(
 | 
				
			||||||
 | 
					                single=True
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
            cv.Optional(CONF_SWING_BOTH_ACTION): automation.validate_automation(
 | 
					            cv.Optional(CONF_SWING_BOTH_ACTION): automation.validate_automation(
 | 
				
			||||||
                single=True
 | 
					                single=True
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
@@ -836,6 +843,11 @@ async def to_code(config):
 | 
				
			|||||||
            var.get_fan_mode_diffuse_trigger(), [], config[CONF_FAN_MODE_DIFFUSE_ACTION]
 | 
					            var.get_fan_mode_diffuse_trigger(), [], config[CONF_FAN_MODE_DIFFUSE_ACTION]
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        cg.add(var.set_supports_fan_mode_diffuse(True))
 | 
					        cg.add(var.set_supports_fan_mode_diffuse(True))
 | 
				
			||||||
 | 
					    if CONF_FAN_MODE_QUIET_ACTION in config:
 | 
				
			||||||
 | 
					        await automation.build_automation(
 | 
				
			||||||
 | 
					            var.get_fan_mode_quiet_trigger(), [], config[CONF_FAN_MODE_QUIET_ACTION]
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        cg.add(var.set_supports_fan_mode_quiet(True))
 | 
				
			||||||
    if CONF_SWING_BOTH_ACTION in config:
 | 
					    if CONF_SWING_BOTH_ACTION in config:
 | 
				
			||||||
        await automation.build_automation(
 | 
					        await automation.build_automation(
 | 
				
			||||||
            var.get_swing_mode_both_trigger(), [], config[CONF_SWING_BOTH_ACTION]
 | 
					            var.get_swing_mode_both_trigger(), [], config[CONF_SWING_BOTH_ACTION]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -247,6 +247,8 @@ climate::ClimateTraits ThermostatClimate::traits() {
 | 
				
			|||||||
    traits.add_supported_fan_mode(climate::CLIMATE_FAN_FOCUS);
 | 
					    traits.add_supported_fan_mode(climate::CLIMATE_FAN_FOCUS);
 | 
				
			||||||
  if (supports_fan_mode_diffuse_)
 | 
					  if (supports_fan_mode_diffuse_)
 | 
				
			||||||
    traits.add_supported_fan_mode(climate::CLIMATE_FAN_DIFFUSE);
 | 
					    traits.add_supported_fan_mode(climate::CLIMATE_FAN_DIFFUSE);
 | 
				
			||||||
 | 
					  if (supports_fan_mode_quiet_)
 | 
				
			||||||
 | 
					    traits.add_supported_fan_mode(climate::CLIMATE_FAN_QUIET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (supports_swing_mode_both_)
 | 
					  if (supports_swing_mode_both_)
 | 
				
			||||||
    traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
 | 
					    traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
 | 
				
			||||||
@@ -594,6 +596,10 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bo
 | 
				
			|||||||
        trig = this->fan_mode_diffuse_trigger_;
 | 
					        trig = this->fan_mode_diffuse_trigger_;
 | 
				
			||||||
        ESP_LOGVV(TAG, "Switching to FAN_DIFFUSE mode");
 | 
					        ESP_LOGVV(TAG, "Switching to FAN_DIFFUSE mode");
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					      case climate::CLIMATE_FAN_QUIET:
 | 
				
			||||||
 | 
					        trig = this->fan_mode_quiet_trigger_;
 | 
				
			||||||
 | 
					        ESP_LOGVV(TAG, "Switching to FAN_QUIET mode");
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        // we cannot report an invalid mode back to HA (even if it asked for one)
 | 
					        // we cannot report an invalid mode back to HA (even if it asked for one)
 | 
				
			||||||
        //  and must assume some valid value
 | 
					        //  and must assume some valid value
 | 
				
			||||||
@@ -1093,6 +1099,7 @@ ThermostatClimate::ThermostatClimate()
 | 
				
			|||||||
      fan_mode_middle_trigger_(new Trigger<>()),
 | 
					      fan_mode_middle_trigger_(new Trigger<>()),
 | 
				
			||||||
      fan_mode_focus_trigger_(new Trigger<>()),
 | 
					      fan_mode_focus_trigger_(new Trigger<>()),
 | 
				
			||||||
      fan_mode_diffuse_trigger_(new Trigger<>()),
 | 
					      fan_mode_diffuse_trigger_(new Trigger<>()),
 | 
				
			||||||
 | 
					      fan_mode_quiet_trigger_(new Trigger<>()),
 | 
				
			||||||
      swing_mode_both_trigger_(new Trigger<>()),
 | 
					      swing_mode_both_trigger_(new Trigger<>()),
 | 
				
			||||||
      swing_mode_off_trigger_(new Trigger<>()),
 | 
					      swing_mode_off_trigger_(new Trigger<>()),
 | 
				
			||||||
      swing_mode_horizontal_trigger_(new Trigger<>()),
 | 
					      swing_mode_horizontal_trigger_(new Trigger<>()),
 | 
				
			||||||
@@ -1208,6 +1215,9 @@ void ThermostatClimate::set_supports_fan_mode_focus(bool supports_fan_mode_focus
 | 
				
			|||||||
void ThermostatClimate::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) {
 | 
					void ThermostatClimate::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) {
 | 
				
			||||||
  this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse;
 | 
					  this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					void ThermostatClimate::set_supports_fan_mode_quiet(bool supports_fan_mode_quiet) {
 | 
				
			||||||
 | 
					  this->supports_fan_mode_quiet_ = supports_fan_mode_quiet;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
void ThermostatClimate::set_supports_swing_mode_both(bool supports_swing_mode_both) {
 | 
					void ThermostatClimate::set_supports_swing_mode_both(bool supports_swing_mode_both) {
 | 
				
			||||||
  this->supports_swing_mode_both_ = supports_swing_mode_both;
 | 
					  this->supports_swing_mode_both_ = supports_swing_mode_both;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1250,6 +1260,7 @@ Trigger<> *ThermostatClimate::get_fan_mode_high_trigger() const { return this->f
 | 
				
			|||||||
Trigger<> *ThermostatClimate::get_fan_mode_middle_trigger() const { return this->fan_mode_middle_trigger_; }
 | 
					Trigger<> *ThermostatClimate::get_fan_mode_middle_trigger() const { return this->fan_mode_middle_trigger_; }
 | 
				
			||||||
Trigger<> *ThermostatClimate::get_fan_mode_focus_trigger() const { return this->fan_mode_focus_trigger_; }
 | 
					Trigger<> *ThermostatClimate::get_fan_mode_focus_trigger() const { return this->fan_mode_focus_trigger_; }
 | 
				
			||||||
Trigger<> *ThermostatClimate::get_fan_mode_diffuse_trigger() const { return this->fan_mode_diffuse_trigger_; }
 | 
					Trigger<> *ThermostatClimate::get_fan_mode_diffuse_trigger() const { return this->fan_mode_diffuse_trigger_; }
 | 
				
			||||||
 | 
					Trigger<> *ThermostatClimate::get_fan_mode_quiet_trigger() const { return this->fan_mode_quiet_trigger_; }
 | 
				
			||||||
Trigger<> *ThermostatClimate::get_swing_mode_both_trigger() const { return this->swing_mode_both_trigger_; }
 | 
					Trigger<> *ThermostatClimate::get_swing_mode_both_trigger() const { return this->swing_mode_both_trigger_; }
 | 
				
			||||||
Trigger<> *ThermostatClimate::get_swing_mode_off_trigger() const { return this->swing_mode_off_trigger_; }
 | 
					Trigger<> *ThermostatClimate::get_swing_mode_off_trigger() const { return this->swing_mode_off_trigger_; }
 | 
				
			||||||
Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; }
 | 
					Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; }
 | 
				
			||||||
@@ -1294,7 +1305,8 @@ void ThermostatClimate::dump_config() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  if (this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ ||
 | 
					  if (this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ ||
 | 
				
			||||||
      this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
 | 
					      this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
 | 
				
			||||||
      this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_) {
 | 
					      this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_ ||
 | 
				
			||||||
 | 
					      this->supports_fan_mode_quiet_) {
 | 
				
			||||||
    ESP_LOGCONFIG(TAG, "  Minimum Fan Mode Switching Time: %us",
 | 
					    ESP_LOGCONFIG(TAG, "  Minimum Fan Mode Switching Time: %us",
 | 
				
			||||||
                  this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000);
 | 
					                  this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -1323,6 +1335,7 @@ void ThermostatClimate::dump_config() {
 | 
				
			|||||||
  ESP_LOGCONFIG(TAG, "  Supports FAN MODE MIDDLE: %s", YESNO(this->supports_fan_mode_middle_));
 | 
					  ESP_LOGCONFIG(TAG, "  Supports FAN MODE MIDDLE: %s", YESNO(this->supports_fan_mode_middle_));
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "  Supports FAN MODE FOCUS: %s", YESNO(this->supports_fan_mode_focus_));
 | 
					  ESP_LOGCONFIG(TAG, "  Supports FAN MODE FOCUS: %s", YESNO(this->supports_fan_mode_focus_));
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "  Supports FAN MODE DIFFUSE: %s", YESNO(this->supports_fan_mode_diffuse_));
 | 
					  ESP_LOGCONFIG(TAG, "  Supports FAN MODE DIFFUSE: %s", YESNO(this->supports_fan_mode_diffuse_));
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Supports FAN MODE QUIET: %s", YESNO(this->supports_fan_mode_quiet_));
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "  Supports SWING MODE BOTH: %s", YESNO(this->supports_swing_mode_both_));
 | 
					  ESP_LOGCONFIG(TAG, "  Supports SWING MODE BOTH: %s", YESNO(this->supports_swing_mode_both_));
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "  Supports SWING MODE OFF: %s", YESNO(this->supports_swing_mode_off_));
 | 
					  ESP_LOGCONFIG(TAG, "  Supports SWING MODE OFF: %s", YESNO(this->supports_swing_mode_off_));
 | 
				
			||||||
  ESP_LOGCONFIG(TAG, "  Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_));
 | 
					  ESP_LOGCONFIG(TAG, "  Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,7 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
				
			|||||||
  void set_supports_fan_mode_middle(bool supports_fan_mode_middle);
 | 
					  void set_supports_fan_mode_middle(bool supports_fan_mode_middle);
 | 
				
			||||||
  void set_supports_fan_mode_focus(bool supports_fan_mode_focus);
 | 
					  void set_supports_fan_mode_focus(bool supports_fan_mode_focus);
 | 
				
			||||||
  void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
 | 
					  void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
 | 
				
			||||||
 | 
					  void set_supports_fan_mode_quiet(bool supports_fan_mode_quiet);
 | 
				
			||||||
  void set_supports_swing_mode_both(bool supports_swing_mode_both);
 | 
					  void set_supports_swing_mode_both(bool supports_swing_mode_both);
 | 
				
			||||||
  void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal);
 | 
					  void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal);
 | 
				
			||||||
  void set_supports_swing_mode_off(bool supports_swing_mode_off);
 | 
					  void set_supports_swing_mode_off(bool supports_swing_mode_off);
 | 
				
			||||||
@@ -132,6 +133,7 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
				
			|||||||
  Trigger<> *get_fan_mode_middle_trigger() const;
 | 
					  Trigger<> *get_fan_mode_middle_trigger() const;
 | 
				
			||||||
  Trigger<> *get_fan_mode_focus_trigger() const;
 | 
					  Trigger<> *get_fan_mode_focus_trigger() const;
 | 
				
			||||||
  Trigger<> *get_fan_mode_diffuse_trigger() const;
 | 
					  Trigger<> *get_fan_mode_diffuse_trigger() const;
 | 
				
			||||||
 | 
					  Trigger<> *get_fan_mode_quiet_trigger() const;
 | 
				
			||||||
  Trigger<> *get_swing_mode_both_trigger() const;
 | 
					  Trigger<> *get_swing_mode_both_trigger() const;
 | 
				
			||||||
  Trigger<> *get_swing_mode_horizontal_trigger() const;
 | 
					  Trigger<> *get_swing_mode_horizontal_trigger() const;
 | 
				
			||||||
  Trigger<> *get_swing_mode_off_trigger() const;
 | 
					  Trigger<> *get_swing_mode_off_trigger() const;
 | 
				
			||||||
@@ -277,6 +279,7 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
				
			|||||||
  bool supports_fan_mode_middle_{false};
 | 
					  bool supports_fan_mode_middle_{false};
 | 
				
			||||||
  bool supports_fan_mode_focus_{false};
 | 
					  bool supports_fan_mode_focus_{false};
 | 
				
			||||||
  bool supports_fan_mode_diffuse_{false};
 | 
					  bool supports_fan_mode_diffuse_{false};
 | 
				
			||||||
 | 
					  bool supports_fan_mode_quiet_{false};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Whether the controller supports various swing modes.
 | 
					  /// Whether the controller supports various swing modes.
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
@@ -372,6 +375,9 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
				
			|||||||
  /// The trigger to call when the controller should switch the fan to "diffuse" position.
 | 
					  /// The trigger to call when the controller should switch the fan to "diffuse" position.
 | 
				
			||||||
  Trigger<> *fan_mode_diffuse_trigger_{nullptr};
 | 
					  Trigger<> *fan_mode_diffuse_trigger_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The trigger to call when the controller should switch the fan to "quiet" position.
 | 
				
			||||||
 | 
					  Trigger<> *fan_mode_quiet_trigger_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// The trigger to call when the controller should switch the swing mode to "both".
 | 
					  /// The trigger to call when the controller should switch the swing mode to "both".
 | 
				
			||||||
  Trigger<> *swing_mode_both_trigger_{nullptr};
 | 
					  Trigger<> *swing_mode_both_trigger_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								esphome/components/vbus/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								esphome/components/vbus/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import uart
 | 
				
			||||||
 | 
					from esphome.const import CONF_ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CODEOWNERS = ["@ssieb"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEPENDENCIES = ["uart"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MULTI_CONF = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vbus_ns = cg.esphome_ns.namespace("vbus")
 | 
				
			||||||
 | 
					VBus = vbus_ns.class_("VBus", uart.UARTDevice, cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_VBUS_ID = "vbus_id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_DELTASOL_BS_PLUS = "deltasol_bs_plus"
 | 
				
			||||||
 | 
					CONF_DELTASOL_C = "deltasol_c"
 | 
				
			||||||
 | 
					CONF_DELTASOL_CS2 = "deltasol_cs2"
 | 
				
			||||||
 | 
					CONF_DELTASOL_CS_PLUS = "deltasol_cs_plus"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = uart.UART_DEVICE_SCHEMA.extend(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        cv.GenerateID(): cv.declare_id(VBus),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					    await uart.register_uart_device(var, config)
 | 
				
			||||||
							
								
								
									
										296
									
								
								esphome/components/vbus/binary_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								esphome/components/vbus/binary_sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,296 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import binary_sensor
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_BINARY_SENSORS,
 | 
				
			||||||
 | 
					    CONF_COMMAND,
 | 
				
			||||||
 | 
					    CONF_CUSTOM,
 | 
				
			||||||
 | 
					    CONF_DEST,
 | 
				
			||||||
 | 
					    CONF_LAMBDA,
 | 
				
			||||||
 | 
					    CONF_MODEL,
 | 
				
			||||||
 | 
					    CONF_SOURCE,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					    ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from .. import (
 | 
				
			||||||
 | 
					    vbus_ns,
 | 
				
			||||||
 | 
					    VBus,
 | 
				
			||||||
 | 
					    CONF_VBUS_ID,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_BS_PLUS,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_C,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_CS2,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_CS_PLUS,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusBSensor", cg.Component)
 | 
				
			||||||
 | 
					DeltaSol_C = vbus_ns.class_("DeltaSolCBSensor", cg.Component)
 | 
				
			||||||
 | 
					DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2BSensor", cg.Component)
 | 
				
			||||||
 | 
					DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusBSensor", cg.Component)
 | 
				
			||||||
 | 
					VBusCustom = vbus_ns.class_("VBusCustomBSensor", cg.Component)
 | 
				
			||||||
 | 
					VBusCustomSub = vbus_ns.class_("VBusCustomSubBSensor", cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_RELAY1 = "relay1"
 | 
				
			||||||
 | 
					CONF_RELAY2 = "relay2"
 | 
				
			||||||
 | 
					CONF_SENSOR1_ERROR = "sensor1_error"
 | 
				
			||||||
 | 
					CONF_SENSOR2_ERROR = "sensor2_error"
 | 
				
			||||||
 | 
					CONF_SENSOR3_ERROR = "sensor3_error"
 | 
				
			||||||
 | 
					CONF_SENSOR4_ERROR = "sensor4_error"
 | 
				
			||||||
 | 
					CONF_COLLECTOR_MAX = "collector_max"
 | 
				
			||||||
 | 
					CONF_COLLECTOR_MIN = "collector_min"
 | 
				
			||||||
 | 
					CONF_COLLECTOR_FROST = "collector_frost"
 | 
				
			||||||
 | 
					CONF_TUBE_COLLECTOR = "tube_collector"
 | 
				
			||||||
 | 
					CONF_RECOOLING = "recooling"
 | 
				
			||||||
 | 
					CONF_HQM = "hqm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.typed_schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        CONF_DELTASOL_BS_PLUS: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_BS_Plus),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_RELAY1): binary_sensor.binary_sensor_schema(),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_RELAY2): binary_sensor.binary_sensor_schema(),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_COLLECTOR_MAX): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_COLLECTOR_MIN): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_COLLECTOR_FROST): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TUBE_COLLECTOR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_RECOOLING): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_HQM): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_C),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_DELTASOL_CS2: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_CS2),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_DELTASOL_CS_PLUS: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_CS_Plus),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR1_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR2_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR3_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSOR4_ERROR): binary_sensor.binary_sensor_schema(
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_PROBLEM,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_CUSTOM: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(VBusCustom),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_COMMAND): cv.uint16_t,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SOURCE): cv.uint16_t,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_DEST): cv.uint16_t,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list(
 | 
				
			||||||
 | 
					                    binary_sensor.binary_sensor_schema().extend(
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            cv.GenerateID(): cv.declare_id(VBusCustomSub),
 | 
				
			||||||
 | 
					                            cv.Required(CONF_LAMBDA): cv.lambda_,
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    key=CONF_MODEL,
 | 
				
			||||||
 | 
					    lower=True,
 | 
				
			||||||
 | 
					    space="_",
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config[CONF_MODEL] == CONF_DELTASOL_BS_PLUS:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x4221))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_RELAY1 in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_RELAY1])
 | 
				
			||||||
 | 
					            cg.add(var.set_relay1_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_RELAY2 in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_RELAY2])
 | 
				
			||||||
 | 
					            cg.add(var.set_relay2_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR1_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s1_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR2_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s2_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR3_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s3_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR4_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s4_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_COLLECTOR_MAX in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_COLLECTOR_MAX])
 | 
				
			||||||
 | 
					            cg.add(var.set_collector_max_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_COLLECTOR_MIN in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_COLLECTOR_MIN])
 | 
				
			||||||
 | 
					            cg.add(var.set_collector_min_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_COLLECTOR_FROST in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_COLLECTOR_FROST])
 | 
				
			||||||
 | 
					            cg.add(var.set_collector_frost_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TUBE_COLLECTOR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_TUBE_COLLECTOR])
 | 
				
			||||||
 | 
					            cg.add(var.set_tube_collector_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_RECOOLING in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_RECOOLING])
 | 
				
			||||||
 | 
					            cg.add(var.set_recooling_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_HQM in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_HQM])
 | 
				
			||||||
 | 
					            cg.add(var.set_hqm_bsensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_MODEL] == CONF_DELTASOL_C:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x4212))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_SENSOR1_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s1_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR2_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s2_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR3_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s3_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR4_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s4_error_bsensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_MODEL] == CONF_DELTASOL_CS2:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x1121))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_SENSOR1_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s1_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR2_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s2_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR3_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s3_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR4_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s4_error_bsensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_MODEL] == CONF_DELTASOL_CS_PLUS:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x2211))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_SENSOR1_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR1_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s1_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR2_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR2_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s2_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR3_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR3_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s3_error_bsensor(sens))
 | 
				
			||||||
 | 
					        if CONF_SENSOR4_ERROR in config:
 | 
				
			||||||
 | 
					            sens = await binary_sensor.new_binary_sensor(config[CONF_SENSOR4_ERROR])
 | 
				
			||||||
 | 
					            cg.add(var.set_s4_error_bsensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_MODEL] == CONF_CUSTOM:
 | 
				
			||||||
 | 
					        if CONF_COMMAND in config:
 | 
				
			||||||
 | 
					            cg.add(var.set_command(config[CONF_COMMAND]))
 | 
				
			||||||
 | 
					        if CONF_SOURCE in config:
 | 
				
			||||||
 | 
					            cg.add(var.set_source(config[CONF_SOURCE]))
 | 
				
			||||||
 | 
					        if CONF_DEST in config:
 | 
				
			||||||
 | 
					            cg.add(var.set_dest(config[CONF_DEST]))
 | 
				
			||||||
 | 
					        bsensors = []
 | 
				
			||||||
 | 
					        for conf in config[CONF_BINARY_SENSORS]:
 | 
				
			||||||
 | 
					            bsens = await binary_sensor.new_binary_sensor(conf)
 | 
				
			||||||
 | 
					            lambda_ = await cg.process_lambda(
 | 
				
			||||||
 | 
					                conf[CONF_LAMBDA],
 | 
				
			||||||
 | 
					                [(cg.std_vector.template(cg.uint8), "x")],
 | 
				
			||||||
 | 
					                return_type=cg.bool_,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            cg.add(bsens.set_message_parser(lambda_))
 | 
				
			||||||
 | 
					            bsensors.append(bsens)
 | 
				
			||||||
 | 
					        cg.add(var.set_bsensors(bsensors))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vbus = await cg.get_variable(config[CONF_VBUS_ID])
 | 
				
			||||||
 | 
					    cg.add(vbus.register_listener(var))
 | 
				
			||||||
							
								
								
									
										142
									
								
								esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								esphome/components/vbus/binary_sensor/vbus_binary_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					#include "vbus_binary_sensor.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace vbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "vbus.binary_sensor";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolBSPlusBSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol BS Plus:");
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Relay 1 On", this->relay1_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Relay 2 On", this->relay2_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 1 Error", this->s1_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 2 Error", this->s2_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 3 Error", this->s3_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 4 Error", this->s4_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Option Collector Max", this->collector_max_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Option Collector Min", this->collector_min_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Option Collector Frost", this->collector_frost_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Option Tube Collector", this->tube_collector_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Option Recooling", this->recooling_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Option Heat Quantity Measurement", this->hqm_bsensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolBSPlusBSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->relay1_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->relay1_bsensor_->publish_state(message[10] & 1);
 | 
				
			||||||
 | 
					  if (this->relay2_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->relay2_bsensor_->publish_state(message[10] & 2);
 | 
				
			||||||
 | 
					  if (this->s1_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s1_error_bsensor_->publish_state(message[11] & 1);
 | 
				
			||||||
 | 
					  if (this->s2_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s2_error_bsensor_->publish_state(message[11] & 2);
 | 
				
			||||||
 | 
					  if (this->s3_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s3_error_bsensor_->publish_state(message[11] & 4);
 | 
				
			||||||
 | 
					  if (this->s4_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s4_error_bsensor_->publish_state(message[11] & 8);
 | 
				
			||||||
 | 
					  if (this->collector_max_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->collector_max_bsensor_->publish_state(message[15] & 1);
 | 
				
			||||||
 | 
					  if (this->collector_min_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->collector_min_bsensor_->publish_state(message[15] & 2);
 | 
				
			||||||
 | 
					  if (this->collector_frost_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->collector_frost_bsensor_->publish_state(message[15] & 4);
 | 
				
			||||||
 | 
					  if (this->tube_collector_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->tube_collector_bsensor_->publish_state(message[15] & 8);
 | 
				
			||||||
 | 
					  if (this->recooling_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->recooling_bsensor_->publish_state(message[15] & 0x10);
 | 
				
			||||||
 | 
					  if (this->hqm_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->hqm_bsensor_->publish_state(message[15] & 0x20);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCBSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol C:");
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 1 Error", this->s1_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 2 Error", this->s2_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 3 Error", this->s3_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 4 Error", this->s4_error_bsensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCBSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->s1_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s1_error_bsensor_->publish_state(message[10] & 1);
 | 
				
			||||||
 | 
					  if (this->s2_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s2_error_bsensor_->publish_state(message[10] & 2);
 | 
				
			||||||
 | 
					  if (this->s3_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s3_error_bsensor_->publish_state(message[10] & 4);
 | 
				
			||||||
 | 
					  if (this->s4_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s4_error_bsensor_->publish_state(message[10] & 8);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCS2BSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol CS2:");
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 1 Error", this->s1_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 2 Error", this->s2_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 3 Error", this->s3_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 4 Error", this->s4_error_bsensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCS2BSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->s1_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s1_error_bsensor_->publish_state(message[18] & 1);
 | 
				
			||||||
 | 
					  if (this->s2_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s2_error_bsensor_->publish_state(message[18] & 2);
 | 
				
			||||||
 | 
					  if (this->s3_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s3_error_bsensor_->publish_state(message[18] & 4);
 | 
				
			||||||
 | 
					  if (this->s4_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s4_error_bsensor_->publish_state(message[18] & 8);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCSPlusBSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol CS Plus:");
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 1 Error", this->s1_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 2 Error", this->s2_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 3 Error", this->s3_error_bsensor_);
 | 
				
			||||||
 | 
					  LOG_BINARY_SENSOR("  ", "Sensor 4 Error", this->s4_error_bsensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCSPlusBSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->s1_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s1_error_bsensor_->publish_state(message[20] & 1);
 | 
				
			||||||
 | 
					  if (this->s2_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s2_error_bsensor_->publish_state(message[20] & 2);
 | 
				
			||||||
 | 
					  if (this->s3_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s3_error_bsensor_->publish_state(message[20] & 4);
 | 
				
			||||||
 | 
					  if (this->s4_error_bsensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->s4_error_bsensor_->publish_state(message[20] & 8);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBusCustomBSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "VBus Custom Binary Sensor:");
 | 
				
			||||||
 | 
					  if (this->source_ == 0xffff) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Source address: ANY");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Source address: 0x%04x", this->source_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->dest_ == 0xffff) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Dest address: ANY");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Dest address: 0x%04x", this->dest_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->command_ == 0xffff) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Command: ANY");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Command: 0x%04x", this->command_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Binary Sensors:");
 | 
				
			||||||
 | 
					  for (VBusCustomSubBSensor *bsensor : this->bsensors_)
 | 
				
			||||||
 | 
					    LOG_BINARY_SENSOR("  ", "-", bsensor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBusCustomBSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  for (VBusCustomSubBSensor *bsensor : this->bsensors_)
 | 
				
			||||||
 | 
					    bsensor->parse_message(message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBusCustomSubBSensor::parse_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  this->publish_state(this->message_parser_(message));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace vbus
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										115
									
								
								esphome/components/vbus/binary_sensor/vbus_binary_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								esphome/components/vbus/binary_sensor/vbus_binary_sensor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../vbus.h"
 | 
				
			||||||
 | 
					#include "esphome/components/binary_sensor/binary_sensor.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace vbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolBSPlusBSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_relay1_bsensor(binary_sensor::BinarySensor *bsensor) { this->relay1_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_relay2_bsensor(binary_sensor::BinarySensor *bsensor) { this->relay2_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_collector_max_bsensor(binary_sensor::BinarySensor *bsensor) { this->collector_max_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_collector_min_bsensor(binary_sensor::BinarySensor *bsensor) { this->collector_min_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_collector_frost_bsensor(binary_sensor::BinarySensor *bsensor) { this->collector_frost_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_tube_collector_bsensor(binary_sensor::BinarySensor *bsensor) { this->tube_collector_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_recooling_bsensor(binary_sensor::BinarySensor *bsensor) { this->recooling_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_hqm_bsensor(binary_sensor::BinarySensor *bsensor) { this->hqm_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *relay1_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *relay2_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s1_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s2_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s3_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s4_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *collector_max_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *collector_min_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *collector_frost_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *tube_collector_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *recooling_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *hqm_bsensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolCBSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s1_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s2_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s3_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s4_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolCS2BSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s1_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s2_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s3_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s4_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolCSPlusBSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_s1_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s1_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s2_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s2_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s3_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s3_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					  void set_s4_error_bsensor(binary_sensor::BinarySensor *bsensor) { this->s4_error_bsensor_ = bsensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s1_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s2_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s3_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					  binary_sensor::BinarySensor *s4_error_bsensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBusCustomSubBSensor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBusCustomBSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_bsensors(std::vector<VBusCustomSubBSensor *> bsensors) { this->bsensors_ = std::move(bsensors); };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  std::vector<VBusCustomSubBSensor *> bsensors_;
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBusCustomSubBSensor : public binary_sensor::BinarySensor, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_message_parser(message_parser_t parser) { this->message_parser_ = std::move(parser); };
 | 
				
			||||||
 | 
					  void parse_message(std::vector<uint8_t> &message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  message_parser_t message_parser_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace vbus
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										568
									
								
								esphome/components/vbus/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										568
									
								
								esphome/components/vbus/sensor/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,568 @@
 | 
				
			|||||||
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					from esphome.components import sensor
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_COMMAND,
 | 
				
			||||||
 | 
					    CONF_CUSTOM,
 | 
				
			||||||
 | 
					    CONF_DEST,
 | 
				
			||||||
 | 
					    CONF_LAMBDA,
 | 
				
			||||||
 | 
					    CONF_MODEL,
 | 
				
			||||||
 | 
					    CONF_SENSORS,
 | 
				
			||||||
 | 
					    CONF_SOURCE,
 | 
				
			||||||
 | 
					    CONF_TIME,
 | 
				
			||||||
 | 
					    CONF_VERSION,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_ENERGY,
 | 
				
			||||||
 | 
					    DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					    ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					    ICON_PERCENT,
 | 
				
			||||||
 | 
					    ICON_RADIATOR,
 | 
				
			||||||
 | 
					    ICON_THERMOMETER,
 | 
				
			||||||
 | 
					    ICON_TIMER,
 | 
				
			||||||
 | 
					    STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					    UNIT_CELSIUS,
 | 
				
			||||||
 | 
					    UNIT_HOUR,
 | 
				
			||||||
 | 
					    UNIT_MINUTE,
 | 
				
			||||||
 | 
					    UNIT_PERCENT,
 | 
				
			||||||
 | 
					    UNIT_WATT_HOURS,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from .. import (
 | 
				
			||||||
 | 
					    vbus_ns,
 | 
				
			||||||
 | 
					    VBus,
 | 
				
			||||||
 | 
					    CONF_VBUS_ID,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_BS_PLUS,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_C,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_CS2,
 | 
				
			||||||
 | 
					    CONF_DELTASOL_CS_PLUS,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DeltaSol_BS_Plus = vbus_ns.class_("DeltaSolBSPlusSensor", cg.Component)
 | 
				
			||||||
 | 
					DeltaSol_C = vbus_ns.class_("DeltaSolCSensor", cg.Component)
 | 
				
			||||||
 | 
					DeltaSol_CS2 = vbus_ns.class_("DeltaSolCS2Sensor", cg.Component)
 | 
				
			||||||
 | 
					DeltaSol_CS_Plus = vbus_ns.class_("DeltaSolCSPlusSensor", cg.Component)
 | 
				
			||||||
 | 
					VBusCustom = vbus_ns.class_("VBusCustomSensor", cg.Component)
 | 
				
			||||||
 | 
					VBusCustomSub = vbus_ns.class_("VBusCustomSubSensor", cg.Component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONF_FLOW_RATE = "flow_rate"
 | 
				
			||||||
 | 
					CONF_HEAT_QUANTITY = "heat_quantity"
 | 
				
			||||||
 | 
					CONF_OPERATING_HOURS = "operating_hours"
 | 
				
			||||||
 | 
					CONF_OPERATING_HOURS_1 = "operating_hours_1"
 | 
				
			||||||
 | 
					CONF_OPERATING_HOURS_2 = "operating_hours_2"
 | 
				
			||||||
 | 
					CONF_PUMP_SPEED = "pump_speed"
 | 
				
			||||||
 | 
					CONF_PUMP_SPEED_1 = "pump_speed_1"
 | 
				
			||||||
 | 
					CONF_PUMP_SPEED_2 = "pump_speed_2"
 | 
				
			||||||
 | 
					CONF_TEMPERATURE_1 = "temperature_1"
 | 
				
			||||||
 | 
					CONF_TEMPERATURE_2 = "temperature_2"
 | 
				
			||||||
 | 
					CONF_TEMPERATURE_3 = "temperature_3"
 | 
				
			||||||
 | 
					CONF_TEMPERATURE_4 = "temperature_4"
 | 
				
			||||||
 | 
					CONF_TEMPERATURE_5 = "temperature_5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_SCHEMA = cv.typed_schema(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        CONF_DELTASOL_BS_PLUS: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_BS_Plus),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                    icon=ICON_PERCENT,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                    icon=ICON_PERCENT,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_HOUR,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_HOUR,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_WATT_HOURS,
 | 
				
			||||||
 | 
					                    icon=ICON_RADIATOR,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_ENERGY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TIME): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_MINUTE,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_VERSION): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    accuracy_decimals=2,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_DELTASOL_C: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_C),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                    icon=ICON_PERCENT,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                    icon=ICON_PERCENT,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_HOUR,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_HOUR,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_WATT_HOURS,
 | 
				
			||||||
 | 
					                    icon=ICON_RADIATOR,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_ENERGY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TIME): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_MINUTE,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_DELTASOL_CS2: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_CS2),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PUMP_SPEED): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                    icon=ICON_PERCENT,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OPERATING_HOURS): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_HOUR,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_WATT_HOURS,
 | 
				
			||||||
 | 
					                    icon=ICON_RADIATOR,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_ENERGY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_VERSION): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    accuracy_decimals=2,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_DELTASOL_CS_PLUS: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(DeltaSol_CS_Plus),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_3): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_4): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TEMPERATURE_5): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_CELSIUS,
 | 
				
			||||||
 | 
					                    icon=ICON_THERMOMETER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=1,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_TEMPERATURE,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PUMP_SPEED_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                    icon=ICON_PERCENT,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_PUMP_SPEED_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_PERCENT,
 | 
				
			||||||
 | 
					                    icon=ICON_PERCENT,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OPERATING_HOURS_1): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_HOUR,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_OPERATING_HOURS_2): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_HOUR,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_HEAT_QUANTITY): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_WATT_HOURS,
 | 
				
			||||||
 | 
					                    icon=ICON_RADIATOR,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_ENERGY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_TIME): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    unit_of_measurement=UNIT_MINUTE,
 | 
				
			||||||
 | 
					                    icon=ICON_TIMER,
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_DURATION,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_VERSION): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    accuracy_decimals=2,
 | 
				
			||||||
 | 
					                    entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_FLOW_RATE): sensor.sensor_schema(
 | 
				
			||||||
 | 
					                    accuracy_decimals=0,
 | 
				
			||||||
 | 
					                    device_class=DEVICE_CLASS_EMPTY,
 | 
				
			||||||
 | 
					                    state_class=STATE_CLASS_MEASUREMENT,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        CONF_CUSTOM: cv.COMPONENT_SCHEMA.extend(
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                cv.GenerateID(): cv.declare_id(VBusCustom),
 | 
				
			||||||
 | 
					                cv.GenerateID(CONF_VBUS_ID): cv.use_id(VBus),
 | 
				
			||||||
 | 
					                cv.Optional(CONF_COMMAND): cv.uint16_t,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SOURCE): cv.uint16_t,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_DEST): cv.uint16_t,
 | 
				
			||||||
 | 
					                cv.Optional(CONF_SENSORS): cv.ensure_list(
 | 
				
			||||||
 | 
					                    sensor.sensor_schema().extend(
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            cv.GenerateID(): cv.declare_id(VBusCustomSub),
 | 
				
			||||||
 | 
					                            cv.Required(CONF_LAMBDA): cv.lambda_,
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    key=CONF_MODEL,
 | 
				
			||||||
 | 
					    lower=True,
 | 
				
			||||||
 | 
					    space="_",
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def to_code(config):
 | 
				
			||||||
 | 
					    var = cg.new_Pvariable(config[CONF_ID])
 | 
				
			||||||
 | 
					    await cg.register_component(var, config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config[CONF_MODEL] == CONF_DELTASOL_BS_PLUS:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x4221))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_3 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature3_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_4 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature4_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_PUMP_SPEED_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_pump_speed1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_PUMP_SPEED_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_pump_speed2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_OPERATING_HOURS_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_operating_hours1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_OPERATING_HOURS_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_operating_hours2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_HEAT_QUANTITY in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY])
 | 
				
			||||||
 | 
					            cg.add(var.set_heat_quantity_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TIME in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TIME])
 | 
				
			||||||
 | 
					            cg.add(var.set_time_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_VERSION in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_VERSION])
 | 
				
			||||||
 | 
					            cg.add(var.set_version_sensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_MODEL] == CONF_DELTASOL_C:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x4212))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_3 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature3_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_4 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature4_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_PUMP_SPEED_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_pump_speed1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_PUMP_SPEED_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_pump_speed2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_OPERATING_HOURS_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_operating_hours1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_OPERATING_HOURS_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_operating_hours2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_HEAT_QUANTITY in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY])
 | 
				
			||||||
 | 
					            cg.add(var.set_heat_quantity_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TIME in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TIME])
 | 
				
			||||||
 | 
					            cg.add(var.set_time_sensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_MODEL] == CONF_DELTASOL_CS2:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x1121))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_3 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature3_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_4 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature4_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_PUMP_SPEED in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_PUMP_SPEED])
 | 
				
			||||||
 | 
					            cg.add(var.set_pump_speed_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_OPERATING_HOURS in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS])
 | 
				
			||||||
 | 
					            cg.add(var.set_operating_hours_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_HEAT_QUANTITY in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY])
 | 
				
			||||||
 | 
					            cg.add(var.set_heat_quantity_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_VERSION in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_VERSION])
 | 
				
			||||||
 | 
					            cg.add(var.set_version_sensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if config[CONF_MODEL] == CONF_DELTASOL_CS_PLUS:
 | 
				
			||||||
 | 
					        cg.add(var.set_command(0x0100))
 | 
				
			||||||
 | 
					        cg.add(var.set_source(0x2211))
 | 
				
			||||||
 | 
					        cg.add(var.set_dest(0x0010))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_3 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_3])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature3_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_4 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_4])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature4_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TEMPERATURE_5 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TEMPERATURE_5])
 | 
				
			||||||
 | 
					            cg.add(var.set_temperature5_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_PUMP_SPEED_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_pump_speed1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_PUMP_SPEED_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_PUMP_SPEED_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_pump_speed2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_OPERATING_HOURS_1 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_1])
 | 
				
			||||||
 | 
					            cg.add(var.set_operating_hours1_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_OPERATING_HOURS_2 in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_OPERATING_HOURS_2])
 | 
				
			||||||
 | 
					            cg.add(var.set_operating_hours2_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_HEAT_QUANTITY in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_HEAT_QUANTITY])
 | 
				
			||||||
 | 
					            cg.add(var.set_heat_quantity_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_TIME in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_TIME])
 | 
				
			||||||
 | 
					            cg.add(var.set_time_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_VERSION in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_VERSION])
 | 
				
			||||||
 | 
					            cg.add(var.set_version_sensor(sens))
 | 
				
			||||||
 | 
					        if CONF_FLOW_RATE in config:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(config[CONF_FLOW_RATE])
 | 
				
			||||||
 | 
					            cg.add(var.set_flow_rate_sensor(sens))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif config[CONF_MODEL] == CONF_CUSTOM:
 | 
				
			||||||
 | 
					        if CONF_COMMAND in config:
 | 
				
			||||||
 | 
					            cg.add(var.set_command(config[CONF_COMMAND]))
 | 
				
			||||||
 | 
					        if CONF_SOURCE in config:
 | 
				
			||||||
 | 
					            cg.add(var.set_source(config[CONF_SOURCE]))
 | 
				
			||||||
 | 
					        if CONF_DEST in config:
 | 
				
			||||||
 | 
					            cg.add(var.set_dest(config[CONF_DEST]))
 | 
				
			||||||
 | 
					        sensors = []
 | 
				
			||||||
 | 
					        for conf in config[CONF_SENSORS]:
 | 
				
			||||||
 | 
					            sens = await sensor.new_sensor(conf)
 | 
				
			||||||
 | 
					            lambda_ = await cg.process_lambda(
 | 
				
			||||||
 | 
					                conf[CONF_LAMBDA],
 | 
				
			||||||
 | 
					                [(cg.std_vector.template(cg.uint8), "x")],
 | 
				
			||||||
 | 
					                return_type=cg.float_,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            cg.add(sens.set_message_parser(lambda_))
 | 
				
			||||||
 | 
					            sensors.append(sens)
 | 
				
			||||||
 | 
					        cg.add(var.set_sensors(sensors))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vbus = await cg.get_variable(config[CONF_VBUS_ID])
 | 
				
			||||||
 | 
					    cg.add(vbus.register_listener(var))
 | 
				
			||||||
							
								
								
									
										208
									
								
								esphome/components/vbus/sensor/vbus_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								esphome/components/vbus/sensor/vbus_sensor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,208 @@
 | 
				
			|||||||
 | 
					#include "vbus_sensor.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace vbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "vbus.sensor";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline uint16_t get_u16(std::vector<uint8_t> &message, int start) {
 | 
				
			||||||
 | 
					  return (message[start + 1] << 8) + message[start];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int16_t get_i16(std::vector<uint8_t> &message, int start) {
 | 
				
			||||||
 | 
					  return (int16_t)((message[start + 1] << 8) + message[start]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolBSPlusSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol BS Plus:");
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 1", this->temperature1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 2", this->temperature2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 3", this->temperature3_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 4", this->temperature4_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pump Speed 1", this->pump_speed1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pump Speed 2", this->pump_speed2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Operating Hours 1", this->operating_hours1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Operating Hours 2", this->operating_hours2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Heat Quantity", this->heat_quantity_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "System Time", this->time_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "FW Version", this->version_sensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolBSPlusSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->temperature1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature3_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature4_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->pump_speed1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->pump_speed1_sensor_->publish_state(message[8]);
 | 
				
			||||||
 | 
					  if (this->pump_speed2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->pump_speed2_sensor_->publish_state(message[9]);
 | 
				
			||||||
 | 
					  if (this->operating_hours1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->operating_hours1_sensor_->publish_state(get_u16(message, 16));
 | 
				
			||||||
 | 
					  if (this->operating_hours2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->operating_hours2_sensor_->publish_state(get_u16(message, 18));
 | 
				
			||||||
 | 
					  if (this->heat_quantity_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    this->heat_quantity_sensor_->publish_state(get_u16(message, 20) + get_u16(message, 22) * 1000 +
 | 
				
			||||||
 | 
					                                               get_u16(message, 24) * 1000000);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->time_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->time_sensor_->publish_state(get_u16(message, 12));
 | 
				
			||||||
 | 
					  if (this->version_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol C:");
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 1", this->temperature1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 2", this->temperature2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 3", this->temperature3_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 4", this->temperature4_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pump Speed 1", this->pump_speed1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pump Speed 2", this->pump_speed2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Operating Hours 1", this->operating_hours1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Operating Hours 2", this->operating_hours2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Heat Quantity", this->heat_quantity_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "System Time", this->time_sensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->temperature1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature3_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature4_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->pump_speed1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->pump_speed1_sensor_->publish_state(message[8]);
 | 
				
			||||||
 | 
					  if (this->pump_speed2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->pump_speed2_sensor_->publish_state(message[9]);
 | 
				
			||||||
 | 
					  if (this->operating_hours1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->operating_hours1_sensor_->publish_state(get_u16(message, 12));
 | 
				
			||||||
 | 
					  if (this->operating_hours2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->operating_hours2_sensor_->publish_state(get_u16(message, 14));
 | 
				
			||||||
 | 
					  if (this->heat_quantity_sensor_ != nullptr) {
 | 
				
			||||||
 | 
					    this->heat_quantity_sensor_->publish_state(get_u16(message, 16) + get_u16(message, 18) * 1000 +
 | 
				
			||||||
 | 
					                                               get_u16(message, 20) * 1000000);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->time_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->time_sensor_->publish_state(get_u16(message, 22));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCS2Sensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol CS2:");
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 1", this->temperature1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 2", this->temperature2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 3", this->temperature3_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 4", this->temperature4_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pump Speed", this->pump_speed_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Operating Hours", this->operating_hours_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Heat Quantity", this->heat_quantity_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "FW Version", this->version_sensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCS2Sensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->temperature1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature3_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature4_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->pump_speed_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->pump_speed_sensor_->publish_state(message[12]);
 | 
				
			||||||
 | 
					  if (this->operating_hours_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->operating_hours_sensor_->publish_state(get_u16(message, 14));
 | 
				
			||||||
 | 
					  if (this->heat_quantity_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->heat_quantity_sensor_->publish_state((get_u16(message, 26) << 16) + get_u16(message, 24));
 | 
				
			||||||
 | 
					  if (this->version_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->version_sensor_->publish_state(get_u16(message, 28) * 0.01f);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCSPlusSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "Deltasol CS Plus:");
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 1", this->temperature1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 2", this->temperature2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 3", this->temperature3_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 4", this->temperature4_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Temperature 5", this->temperature5_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pump Speed 1", this->pump_speed1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Pump Speed 2", this->pump_speed2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Operating Hours 1", this->operating_hours1_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Operating Hours 2", this->operating_hours2_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Heat Quantity", this->heat_quantity_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "System Time", this->time_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "FW Version", this->version_sensor_);
 | 
				
			||||||
 | 
					  LOG_SENSOR("  ", "Flow Rate", this->flow_rate_sensor_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DeltaSolCSPlusSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if (this->temperature1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature1_sensor_->publish_state(get_i16(message, 0) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature2_sensor_->publish_state(get_i16(message, 2) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature3_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature3_sensor_->publish_state(get_i16(message, 4) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature4_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature4_sensor_->publish_state(get_i16(message, 6) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->temperature5_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->temperature5_sensor_->publish_state(get_i16(message, 36) * 0.1f);
 | 
				
			||||||
 | 
					  if (this->pump_speed1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->pump_speed1_sensor_->publish_state(message[8]);
 | 
				
			||||||
 | 
					  if (this->pump_speed2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->pump_speed2_sensor_->publish_state(message[12]);
 | 
				
			||||||
 | 
					  if (this->operating_hours1_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->operating_hours1_sensor_->publish_state(get_u16(message, 10));
 | 
				
			||||||
 | 
					  if (this->operating_hours2_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->operating_hours2_sensor_->publish_state(get_u16(message, 14));
 | 
				
			||||||
 | 
					  if (this->heat_quantity_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->heat_quantity_sensor_->publish_state((get_u16(message, 30) << 16) + get_u16(message, 28));
 | 
				
			||||||
 | 
					  if (this->time_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->time_sensor_->publish_state(get_u16(message, 12));
 | 
				
			||||||
 | 
					  if (this->version_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->version_sensor_->publish_state(get_u16(message, 26) * 0.01f);
 | 
				
			||||||
 | 
					  if (this->flow_rate_sensor_ != nullptr)
 | 
				
			||||||
 | 
					    this->flow_rate_sensor_->publish_state(get_u16(message, 38));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBusCustomSensor::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "VBus Custom Sensor:");
 | 
				
			||||||
 | 
					  if (this->source_ == 0xffff) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Source address: ANY");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Source address: 0x%04x", this->source_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->dest_ == 0xffff) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Dest address: ANY");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Dest address: 0x%04x", this->dest_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this->command_ == 0xffff) {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Command: ANY");
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    ESP_LOGCONFIG(TAG, "  Command: 0x%04x", this->command_);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "  Sensors:");
 | 
				
			||||||
 | 
					  for (VBusCustomSubSensor *sensor : this->sensors_)
 | 
				
			||||||
 | 
					    LOG_SENSOR("  ", "-", sensor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBusCustomSensor::handle_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  for (VBusCustomSubSensor *sensor : this->sensors_)
 | 
				
			||||||
 | 
					    sensor->parse_message(message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBusCustomSubSensor::parse_message(std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  this->publish_state(this->message_parser_(message));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace vbus
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										151
									
								
								esphome/components/vbus/sensor/vbus_sensor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								esphome/components/vbus/sensor/vbus_sensor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../vbus.h"
 | 
				
			||||||
 | 
					#include "esphome/components/sensor/sensor.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace vbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolBSPlusSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature3_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature4_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pump_speed1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pump_speed2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *operating_hours1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *operating_hours2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *heat_quantity_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *time_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *version_sensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolCSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature3_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature4_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pump_speed1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pump_speed2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *operating_hours1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *operating_hours2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *heat_quantity_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *time_sensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolCS2Sensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pump_speed_sensor(sensor::Sensor *sensor) { this->pump_speed_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_operating_hours_sensor(sensor::Sensor *sensor) { this->operating_hours_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature3_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature4_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pump_speed_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *operating_hours_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *heat_quantity_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *version_sensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeltaSolCSPlusSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_temperature1_sensor(sensor::Sensor *sensor) { this->temperature1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature2_sensor(sensor::Sensor *sensor) { this->temperature2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature3_sensor(sensor::Sensor *sensor) { this->temperature3_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature4_sensor(sensor::Sensor *sensor) { this->temperature4_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_temperature5_sensor(sensor::Sensor *sensor) { this->temperature5_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pump_speed1_sensor(sensor::Sensor *sensor) { this->pump_speed1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_pump_speed2_sensor(sensor::Sensor *sensor) { this->pump_speed2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_operating_hours1_sensor(sensor::Sensor *sensor) { this->operating_hours1_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_operating_hours2_sensor(sensor::Sensor *sensor) { this->operating_hours2_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_heat_quantity_sensor(sensor::Sensor *sensor) { this->heat_quantity_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_time_sensor(sensor::Sensor *sensor) { this->time_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_version_sensor(sensor::Sensor *sensor) { this->version_sensor_ = sensor; }
 | 
				
			||||||
 | 
					  void set_flow_rate_sensor(sensor::Sensor *sensor) { this->flow_rate_sensor_ = sensor; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature3_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature4_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *temperature5_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pump_speed1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *pump_speed2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *operating_hours1_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *operating_hours2_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *heat_quantity_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *time_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *version_sensor_{nullptr};
 | 
				
			||||||
 | 
					  sensor::Sensor *flow_rate_sensor_{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBusCustomSubSensor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBusCustomSensor : public VBusListener, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void set_sensors(std::vector<VBusCustomSubSensor *> sensors) { this->sensors_ = std::move(sensors); };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  std::vector<VBusCustomSubSensor *> sensors_;
 | 
				
			||||||
 | 
					  void handle_message(std::vector<uint8_t> &message) override;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBusCustomSubSensor : public sensor::Sensor, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_message_parser(message_parser_t parser) { this->message_parser_ = std::move(parser); };
 | 
				
			||||||
 | 
					  void parse_message(std::vector<uint8_t> &message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  message_parser_t message_parser_;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace vbus
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										124
									
								
								esphome/components/vbus/vbus.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								esphome/components/vbus/vbus.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					#include "vbus.h"
 | 
				
			||||||
 | 
					#include "esphome/core/helpers.h"
 | 
				
			||||||
 | 
					#include "esphome/core/log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace vbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *const TAG = "vbus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBus::dump_config() {
 | 
				
			||||||
 | 
					  ESP_LOGCONFIG(TAG, "VBus:");
 | 
				
			||||||
 | 
					  check_uart_settings(9600);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void septet_spread(uint8_t *data, int start, int count, uint8_t septet) {
 | 
				
			||||||
 | 
					  for (int i = 0; i < count; i++, septet >>= 1) {
 | 
				
			||||||
 | 
					    if (septet & 1)
 | 
				
			||||||
 | 
					      data[start + i] |= 0x80;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool checksum(const uint8_t *data, int start, int count) {
 | 
				
			||||||
 | 
					  uint8_t csum = 0x7f;
 | 
				
			||||||
 | 
					  for (int i = 0; i < count; i++)
 | 
				
			||||||
 | 
					    csum = (csum - data[start + i]) & 0x7f;
 | 
				
			||||||
 | 
					  return csum == 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBus::loop() {
 | 
				
			||||||
 | 
					  if (!available())
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (available()) {
 | 
				
			||||||
 | 
					    uint8_t c;
 | 
				
			||||||
 | 
					    read_byte(&c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (c == 0xaa) {
 | 
				
			||||||
 | 
					      this->state_ = 1;
 | 
				
			||||||
 | 
					      this->buffer_.clear();
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (c & 0x80) {
 | 
				
			||||||
 | 
					      this->state_ = 0;
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this->state_ == 0)
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this->state_ == 1) {
 | 
				
			||||||
 | 
					      this->buffer_.push_back(c);
 | 
				
			||||||
 | 
					      if (this->buffer_.size() == 7) {
 | 
				
			||||||
 | 
					        this->protocol_ = this->buffer_[4];
 | 
				
			||||||
 | 
					        this->source_ = (this->buffer_[3] << 8) + this->buffer_[2];
 | 
				
			||||||
 | 
					        this->dest_ = (this->buffer_[1] << 8) + this->buffer_[0];
 | 
				
			||||||
 | 
					        this->command_ = (this->buffer_[6] << 8) + this->buffer_[5];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if ((this->protocol_ == 0x20) && (this->buffer_.size() == 15)) {
 | 
				
			||||||
 | 
					        this->state_ = 0;
 | 
				
			||||||
 | 
					        if (!checksum(this->buffer_.data(), 0, 15)) {
 | 
				
			||||||
 | 
					          ESP_LOGE(TAG, "P2 checksum failed");
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        septet_spread(this->buffer_.data(), 7, 6, this->buffer_[13]);
 | 
				
			||||||
 | 
					        uint16_t id = (this->buffer_[8] << 8) + this->buffer_[7];
 | 
				
			||||||
 | 
					        uint32_t value =
 | 
				
			||||||
 | 
					            (this->buffer_[12] << 24) + (this->buffer_[11] << 16) + (this->buffer_[10] << 8) + this->buffer_[9];
 | 
				
			||||||
 | 
					        ESP_LOGV(TAG, "P1 C%04x %04x->%04x: %04x %04x (%d)", this->command_, this->source_, this->dest_, id, value,
 | 
				
			||||||
 | 
					                 value);
 | 
				
			||||||
 | 
					      } else if ((this->protocol_ == 0x10) && (this->buffer_.size() == 9)) {
 | 
				
			||||||
 | 
					        if (!checksum(this->buffer_.data(), 0, 9)) {
 | 
				
			||||||
 | 
					          ESP_LOGE(TAG, "P1 checksum failed");
 | 
				
			||||||
 | 
					          this->state_ = 0;
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this->frames_ = this->buffer_[7];
 | 
				
			||||||
 | 
					        if (this->frames_) {
 | 
				
			||||||
 | 
					          this->state_ = 2;
 | 
				
			||||||
 | 
					          this->cframe_ = 0;
 | 
				
			||||||
 | 
					          this->fbcount_ = 0;
 | 
				
			||||||
 | 
					          this->buffer_.clear();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this->state_ = 0;
 | 
				
			||||||
 | 
					          ESP_LOGD(TAG, "P1 empty message");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this->state_ == 2) {
 | 
				
			||||||
 | 
					      this->fbytes_[this->fbcount_++] = c;
 | 
				
			||||||
 | 
					      if (this->fbcount_ < 6)
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      this->fbcount_ = 0;
 | 
				
			||||||
 | 
					      if (!checksum(this->fbytes_, 0, 6)) {
 | 
				
			||||||
 | 
					        ESP_LOGE(TAG, "frame checksum failed");
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      septet_spread(this->fbytes_, 0, 4, this->fbytes_[4]);
 | 
				
			||||||
 | 
					      for (int i = 0; i < 4; i++)
 | 
				
			||||||
 | 
					        this->buffer_.push_back(this->fbytes_[i]);
 | 
				
			||||||
 | 
					      if (++this->cframe_ < this->frames_)
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      ESP_LOGV(TAG, "P2 C%04x %04x->%04x: %s", this->command_, this->source_, this->dest_,
 | 
				
			||||||
 | 
					               format_hex(this->buffer_).c_str());
 | 
				
			||||||
 | 
					      for (auto &listener : this->listeners_)
 | 
				
			||||||
 | 
					        listener->on_message(this->command_, this->source_, this->dest_, this->buffer_);
 | 
				
			||||||
 | 
					      this->state_ = 0;
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VBusListener::on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector<uint8_t> &message) {
 | 
				
			||||||
 | 
					  if ((this->command_ != 0xffff) && (this->command_ != command))
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  if ((this->source_ != 0xffff) && (this->source_ != source))
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  if ((this->dest_ != 0xffff) && (this->dest_ != dest))
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  this->handle_message(message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace vbus
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
							
								
								
									
										52
									
								
								esphome/components/vbus/vbus.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								esphome/components/vbus/vbus.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "esphome/core/component.h"
 | 
				
			||||||
 | 
					#include "esphome/components/uart/uart.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace esphome {
 | 
				
			||||||
 | 
					namespace vbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using message_parser_t = std::function<float(std::vector<uint8_t> &)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBusListener {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void set_command(uint16_t command) { this->command_ = command; }
 | 
				
			||||||
 | 
					  void set_source(uint16_t source) { this->source_ = source; }
 | 
				
			||||||
 | 
					  void set_dest(uint16_t dest) { this->dest_ = dest; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void on_message(uint16_t command, uint16_t source, uint16_t dest, std::vector<uint8_t> &message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  uint16_t command_{0xffff};
 | 
				
			||||||
 | 
					  uint16_t source_{0xffff};
 | 
				
			||||||
 | 
					  uint16_t dest_{0xffff};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  virtual void handle_message(std::vector<uint8_t> &message) = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VBus : public uart::UARTDevice, public Component {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  void dump_config() override;
 | 
				
			||||||
 | 
					  void loop() override;
 | 
				
			||||||
 | 
					  float get_setup_priority() const override { return setup_priority::DATA; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void register_listener(VBusListener *listener) { this->listeners_.push_back(listener); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 protected:
 | 
				
			||||||
 | 
					  int state_{0};
 | 
				
			||||||
 | 
					  std::vector<uint8_t> buffer_;
 | 
				
			||||||
 | 
					  uint8_t protocol_;
 | 
				
			||||||
 | 
					  uint16_t source_;
 | 
				
			||||||
 | 
					  uint16_t dest_;
 | 
				
			||||||
 | 
					  uint16_t command_;
 | 
				
			||||||
 | 
					  uint8_t frames_;
 | 
				
			||||||
 | 
					  uint8_t cframe_;
 | 
				
			||||||
 | 
					  uint8_t fbytes_[6];
 | 
				
			||||||
 | 
					  int fbcount_;
 | 
				
			||||||
 | 
					  std::vector<VBusListener *> listeners_{};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace vbus
 | 
				
			||||||
 | 
					}  // namespace esphome
 | 
				
			||||||
@@ -14,6 +14,7 @@ from esphome import core, yaml_util, loader
 | 
				
			|||||||
import esphome.core.config as core_config
 | 
					import esphome.core.config as core_config
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    CONF_ESPHOME,
 | 
					    CONF_ESPHOME,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
    CONF_PLATFORM,
 | 
					    CONF_PLATFORM,
 | 
				
			||||||
    CONF_PACKAGES,
 | 
					    CONF_PACKAGES,
 | 
				
			||||||
    CONF_SUBSTITUTIONS,
 | 
					    CONF_SUBSTITUTIONS,
 | 
				
			||||||
@@ -24,6 +25,7 @@ from esphome.core import CORE, EsphomeError
 | 
				
			|||||||
from esphome.helpers import indent
 | 
					from esphome.helpers import indent
 | 
				
			||||||
from esphome.util import safe_print, OrderedDict
 | 
					from esphome.util import safe_print, OrderedDict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from esphome.config_helpers import Extend
 | 
				
			||||||
from esphome.loader import get_component, get_platform, ComponentManifest
 | 
					from esphome.loader import get_component, get_platform, ComponentManifest
 | 
				
			||||||
from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue
 | 
					from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue
 | 
				
			||||||
from esphome.voluptuous_schema import ExtraKeysInvalid
 | 
					from esphome.voluptuous_schema import ExtraKeysInvalid
 | 
				
			||||||
@@ -334,6 +336,13 @@ class LoadValidationStep(ConfigValidationStep):
 | 
				
			|||||||
                continue
 | 
					                continue
 | 
				
			||||||
            p_name = p_config.get("platform")
 | 
					            p_name = p_config.get("platform")
 | 
				
			||||||
            if p_name is None:
 | 
					            if p_name is None:
 | 
				
			||||||
 | 
					                p_id = p_config.get(CONF_ID)
 | 
				
			||||||
 | 
					                if isinstance(p_id, Extend):
 | 
				
			||||||
 | 
					                    result.add_str_error(
 | 
				
			||||||
 | 
					                        f"Source for extension of ID '{p_id.value}' was not found.",
 | 
				
			||||||
 | 
					                        path + [CONF_ID],
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
                result.add_str_error("No platform specified! See 'platform' key.", path)
 | 
					                result.add_str_error("No platform specified! See 'platform' key.", path)
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            # Remove temp output path and construct new one
 | 
					            # Remove temp output path and construct new one
 | 
				
			||||||
@@ -654,7 +663,7 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
				
			|||||||
    loader.clear_component_meta_finders()
 | 
					    loader.clear_component_meta_finders()
 | 
				
			||||||
    loader.install_custom_components_meta_finder()
 | 
					    loader.install_custom_components_meta_finder()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Load packages
 | 
					    # 0. Load packages
 | 
				
			||||||
    if CONF_PACKAGES in config:
 | 
					    if CONF_PACKAGES in config:
 | 
				
			||||||
        from esphome.components.packages import do_packages_pass
 | 
					        from esphome.components.packages import do_packages_pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -668,7 +677,7 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    CORE.raw_config = config
 | 
					    CORE.raw_config = config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Load substitutions
 | 
					    # 1. Load substitutions
 | 
				
			||||||
    if CONF_SUBSTITUTIONS in config:
 | 
					    if CONF_SUBSTITUTIONS in config:
 | 
				
			||||||
        from esphome.components import substitutions
 | 
					        from esphome.components import substitutions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -686,7 +695,13 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    CORE.raw_config = config
 | 
					    CORE.raw_config = config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Load external_components
 | 
					    # 1.1. Check for REPLACEME special value
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        recursive_check_replaceme(config)
 | 
				
			||||||
 | 
					    except vol.Invalid as err:
 | 
				
			||||||
 | 
					        result.add_error(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 1.2. Load external_components
 | 
				
			||||||
    if CONF_EXTERNAL_COMPONENTS in config:
 | 
					    if CONF_EXTERNAL_COMPONENTS in config:
 | 
				
			||||||
        from esphome.components.external_components import do_external_components_pass
 | 
					        from esphome.components.external_components import do_external_components_pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -713,7 +728,7 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        return result
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Load partial core config
 | 
					    # 2. Load partial core config
 | 
				
			||||||
    result[CONF_ESPHOME] = config[CONF_ESPHOME]
 | 
					    result[CONF_ESPHOME] = config[CONF_ESPHOME]
 | 
				
			||||||
    result.add_output_path([CONF_ESPHOME], CONF_ESPHOME)
 | 
					    result.add_output_path([CONF_ESPHOME], CONF_ESPHOME)
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
@@ -734,12 +749,6 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
				
			|||||||
        # do not try to validate further as we don't know what the target is
 | 
					        # do not try to validate further as we don't know what the target is
 | 
				
			||||||
        return result
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Check for REPLACEME special value
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        recursive_check_replaceme(config)
 | 
					 | 
				
			||||||
    except vol.Invalid as err:
 | 
					 | 
				
			||||||
        result.add_error(err)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for domain, conf in config.items():
 | 
					    for domain, conf in config.items():
 | 
				
			||||||
        result.add_validation_step(LoadValidationStep(domain, conf))
 | 
					        result.add_validation_step(LoadValidationStep(domain, conf))
 | 
				
			||||||
    result.add_validation_step(IDPassValidationStep())
 | 
					    result.add_validation_step(IDPassValidationStep())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,27 @@
 | 
				
			|||||||
import json
 | 
					import json
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from esphome.const import CONF_ID
 | 
				
			||||||
from esphome.core import CORE
 | 
					from esphome.core import CORE
 | 
				
			||||||
from esphome.helpers import read_file
 | 
					from esphome.helpers import read_file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Extend:
 | 
				
			||||||
 | 
					    def __init__(self, value):
 | 
				
			||||||
 | 
					        self.value = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return f"!extend {self.value}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __eq__(self, b):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Check if two Extend objects contain the same ID.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Only used in unit tests.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return isinstance(b, Extend) and self.value == b.value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def read_config_file(path: str) -> str:
 | 
					def read_config_file(path: str) -> str:
 | 
				
			||||||
    if CORE.vscode and (
 | 
					    if CORE.vscode and (
 | 
				
			||||||
        not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)
 | 
					        not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)
 | 
				
			||||||
@@ -36,7 +53,25 @@ def merge_config(full_old, full_new):
 | 
				
			|||||||
        if isinstance(new, list):
 | 
					        if isinstance(new, list):
 | 
				
			||||||
            if not isinstance(old, list):
 | 
					            if not isinstance(old, list):
 | 
				
			||||||
                return new
 | 
					                return new
 | 
				
			||||||
            return old + new
 | 
					            res = old.copy()
 | 
				
			||||||
 | 
					            ids = {
 | 
				
			||||||
 | 
					                v[CONF_ID]: i
 | 
				
			||||||
 | 
					                for i, v in enumerate(res)
 | 
				
			||||||
 | 
					                if CONF_ID in v and isinstance(v[CONF_ID], str)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for v in new:
 | 
				
			||||||
 | 
					                if CONF_ID in v:
 | 
				
			||||||
 | 
					                    new_id = v[CONF_ID]
 | 
				
			||||||
 | 
					                    if isinstance(new_id, Extend):
 | 
				
			||||||
 | 
					                        new_id = new_id.value
 | 
				
			||||||
 | 
					                        if new_id in ids:
 | 
				
			||||||
 | 
					                            v[CONF_ID] = new_id
 | 
				
			||||||
 | 
					                            res[ids[new_id]] = merge(res[ids[new_id]], v)
 | 
				
			||||||
 | 
					                            continue
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        ids[new_id] = len(res)
 | 
				
			||||||
 | 
					                res.append(v)
 | 
				
			||||||
 | 
					            return res
 | 
				
			||||||
        if new is None:
 | 
					        if new is None:
 | 
				
			||||||
            return old
 | 
					            return old
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import voluptuous as vol
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from esphome import core
 | 
					from esphome import core
 | 
				
			||||||
import esphome.codegen as cg
 | 
					import esphome.codegen as cg
 | 
				
			||||||
 | 
					from esphome.config_helpers import Extend
 | 
				
			||||||
from esphome.const import (
 | 
					from esphome.const import (
 | 
				
			||||||
    ALLOWED_NAME_CHARS,
 | 
					    ALLOWED_NAME_CHARS,
 | 
				
			||||||
    CONF_AVAILABILITY,
 | 
					    CONF_AVAILABILITY,
 | 
				
			||||||
@@ -490,6 +491,8 @@ def declare_id(type):
 | 
				
			|||||||
        if value is None:
 | 
					        if value is None:
 | 
				
			||||||
            return core.ID(None, is_declaration=True, type=type)
 | 
					            return core.ID(None, is_declaration=True, type=type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(value, Extend):
 | 
				
			||||||
 | 
					            raise Invalid(f"Source for extension of ID '{value.value}' was not found.")
 | 
				
			||||||
        return core.ID(validate_id_name(value), is_declaration=True, type=type)
 | 
					        return core.ID(validate_id_name(value), is_declaration=True, type=type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return validator
 | 
					    return validator
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
"""Constants used by esphome."""
 | 
					"""Constants used by esphome."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__version__ = "2023.2.0-dev"
 | 
					__version__ = "2023.2.0b4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
 | 
					ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -12,7 +12,7 @@ TARGET_PLATFORMS = [PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
 | 
					SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
 | 
				
			||||||
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
 | 
					HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
 | 
				
			||||||
SECRETS_FILES = {"secrets.yaml", "secrets.yml"}
 | 
					SECRETS_FILES = ("secrets.yaml", "secrets.yml")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONF_ABOVE = "above"
 | 
					CONF_ABOVE = "above"
 | 
				
			||||||
@@ -141,6 +141,7 @@ CONF_CURRENT = "current"
 | 
				
			|||||||
CONF_CURRENT_OPERATION = "current_operation"
 | 
					CONF_CURRENT_OPERATION = "current_operation"
 | 
				
			||||||
CONF_CURRENT_RESISTOR = "current_resistor"
 | 
					CONF_CURRENT_RESISTOR = "current_resistor"
 | 
				
			||||||
CONF_CURRENT_TEMPERATURE_STATE_TOPIC = "current_temperature_state_topic"
 | 
					CONF_CURRENT_TEMPERATURE_STATE_TOPIC = "current_temperature_state_topic"
 | 
				
			||||||
 | 
					CONF_CUSTOM = "custom"
 | 
				
			||||||
CONF_CUSTOM_FAN_MODE = "custom_fan_mode"
 | 
					CONF_CUSTOM_FAN_MODE = "custom_fan_mode"
 | 
				
			||||||
CONF_CUSTOM_FAN_MODES = "custom_fan_modes"
 | 
					CONF_CUSTOM_FAN_MODES = "custom_fan_modes"
 | 
				
			||||||
CONF_CUSTOM_PRESET = "custom_preset"
 | 
					CONF_CUSTOM_PRESET = "custom_preset"
 | 
				
			||||||
@@ -167,6 +168,7 @@ CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length"
 | 
				
			|||||||
CONF_DELAY = "delay"
 | 
					CONF_DELAY = "delay"
 | 
				
			||||||
CONF_DELIMITER = "delimiter"
 | 
					CONF_DELIMITER = "delimiter"
 | 
				
			||||||
CONF_DELTA = "delta"
 | 
					CONF_DELTA = "delta"
 | 
				
			||||||
 | 
					CONF_DEST = "dest"
 | 
				
			||||||
CONF_DEVICE = "device"
 | 
					CONF_DEVICE = "device"
 | 
				
			||||||
CONF_DEVICE_CLASS = "device_class"
 | 
					CONF_DEVICE_CLASS = "device_class"
 | 
				
			||||||
CONF_DEVICE_FACTOR = "device_factor"
 | 
					CONF_DEVICE_FACTOR = "device_factor"
 | 
				
			||||||
@@ -235,6 +237,7 @@ CONF_FAN_MODE_MEDIUM_ACTION = "fan_mode_medium_action"
 | 
				
			|||||||
CONF_FAN_MODE_MIDDLE_ACTION = "fan_mode_middle_action"
 | 
					CONF_FAN_MODE_MIDDLE_ACTION = "fan_mode_middle_action"
 | 
				
			||||||
CONF_FAN_MODE_OFF_ACTION = "fan_mode_off_action"
 | 
					CONF_FAN_MODE_OFF_ACTION = "fan_mode_off_action"
 | 
				
			||||||
CONF_FAN_MODE_ON_ACTION = "fan_mode_on_action"
 | 
					CONF_FAN_MODE_ON_ACTION = "fan_mode_on_action"
 | 
				
			||||||
 | 
					CONF_FAN_MODE_QUIET_ACTION = "fan_mode_quiet_action"
 | 
				
			||||||
CONF_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic"
 | 
					CONF_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic"
 | 
				
			||||||
CONF_FAN_ONLY_ACTION = "fan_only_action"
 | 
					CONF_FAN_ONLY_ACTION = "fan_only_action"
 | 
				
			||||||
CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER = "fan_only_action_uses_fan_mode_timer"
 | 
					CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER = "fan_only_action_uses_fan_mode_timer"
 | 
				
			||||||
@@ -548,8 +551,10 @@ CONF_POWER_SAVE_MODE = "power_save_mode"
 | 
				
			|||||||
CONF_POWER_SUPPLY = "power_supply"
 | 
					CONF_POWER_SUPPLY = "power_supply"
 | 
				
			||||||
CONF_PRESET = "preset"
 | 
					CONF_PRESET = "preset"
 | 
				
			||||||
CONF_PRESET_BOOST = "preset_boost"
 | 
					CONF_PRESET_BOOST = "preset_boost"
 | 
				
			||||||
 | 
					CONF_PRESET_COMMAND_TOPIC = "preset_command_topic"
 | 
				
			||||||
CONF_PRESET_ECO = "preset_eco"
 | 
					CONF_PRESET_ECO = "preset_eco"
 | 
				
			||||||
CONF_PRESET_SLEEP = "preset_sleep"
 | 
					CONF_PRESET_SLEEP = "preset_sleep"
 | 
				
			||||||
 | 
					CONF_PRESET_STATE_TOPIC = "preset_state_topic"
 | 
				
			||||||
CONF_PRESSURE = "pressure"
 | 
					CONF_PRESSURE = "pressure"
 | 
				
			||||||
CONF_PRIORITY = "priority"
 | 
					CONF_PRIORITY = "priority"
 | 
				
			||||||
CONF_PROJECT = "project"
 | 
					CONF_PROJECT = "project"
 | 
				
			||||||
@@ -862,6 +867,7 @@ UNIT_AMPERE = "A"
 | 
				
			|||||||
UNIT_BECQUEREL_PER_CUBIC_METER = "Bq/m³"
 | 
					UNIT_BECQUEREL_PER_CUBIC_METER = "Bq/m³"
 | 
				
			||||||
UNIT_BYTES = "B"
 | 
					UNIT_BYTES = "B"
 | 
				
			||||||
UNIT_CELSIUS = "°C"
 | 
					UNIT_CELSIUS = "°C"
 | 
				
			||||||
 | 
					UNIT_CENTIMETER = "cm"
 | 
				
			||||||
UNIT_COUNT_DECILITRE = "/dL"
 | 
					UNIT_COUNT_DECILITRE = "/dL"
 | 
				
			||||||
UNIT_COUNTS_PER_CUBIC_METER = "#/m³"
 | 
					UNIT_COUNTS_PER_CUBIC_METER = "#/m³"
 | 
				
			||||||
UNIT_CUBIC_METER = "m³"
 | 
					UNIT_CUBIC_METER = "m³"
 | 
				
			||||||
@@ -873,6 +879,7 @@ UNIT_EMPTY = ""
 | 
				
			|||||||
UNIT_G = "G"
 | 
					UNIT_G = "G"
 | 
				
			||||||
UNIT_HECTOPASCAL = "hPa"
 | 
					UNIT_HECTOPASCAL = "hPa"
 | 
				
			||||||
UNIT_HERTZ = "Hz"
 | 
					UNIT_HERTZ = "Hz"
 | 
				
			||||||
 | 
					UNIT_HOUR = "h"
 | 
				
			||||||
UNIT_KELVIN = "K"
 | 
					UNIT_KELVIN = "K"
 | 
				
			||||||
UNIT_KILOGRAM = "kg"
 | 
					UNIT_KILOGRAM = "kg"
 | 
				
			||||||
UNIT_KILOMETER = "km"
 | 
					UNIT_KILOMETER = "km"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,6 +55,7 @@ class DashboardSettings:
 | 
				
			|||||||
        self.using_password = False
 | 
					        self.using_password = False
 | 
				
			||||||
        self.on_ha_addon = False
 | 
					        self.on_ha_addon = False
 | 
				
			||||||
        self.cookie_secret = None
 | 
					        self.cookie_secret = None
 | 
				
			||||||
 | 
					        self.absolute_config_dir = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parse_args(self, args):
 | 
					    def parse_args(self, args):
 | 
				
			||||||
        self.on_ha_addon = args.ha_addon
 | 
					        self.on_ha_addon = args.ha_addon
 | 
				
			||||||
@@ -65,6 +66,7 @@ class DashboardSettings:
 | 
				
			|||||||
        if self.using_password:
 | 
					        if self.using_password:
 | 
				
			||||||
            self.password_hash = password_hash(password)
 | 
					            self.password_hash = password_hash(password)
 | 
				
			||||||
        self.config_dir = args.configuration
 | 
					        self.config_dir = args.configuration
 | 
				
			||||||
 | 
					        self.absolute_config_dir = Path(self.config_dir).resolve()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def relative_url(self):
 | 
					    def relative_url(self):
 | 
				
			||||||
@@ -94,7 +96,10 @@ class DashboardSettings:
 | 
				
			|||||||
        return hmac.compare_digest(self.password_hash, password_hash(password))
 | 
					        return hmac.compare_digest(self.password_hash, password_hash(password))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def rel_path(self, *args):
 | 
					    def rel_path(self, *args):
 | 
				
			||||||
        return os.path.join(self.config_dir, *args)
 | 
					        joined_path = os.path.join(self.config_dir, *args)
 | 
				
			||||||
 | 
					        # Raises ValueError if not relative to ESPHome config folder
 | 
				
			||||||
 | 
					        Path(joined_path).resolve().relative_to(self.absolute_config_dir)
 | 
				
			||||||
 | 
					        return joined_path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def list_yaml_files(self):
 | 
					    def list_yaml_files(self):
 | 
				
			||||||
        return util.list_yaml_files([self.config_dir])
 | 
					        return util.list_yaml_files([self.config_dir])
 | 
				
			||||||
@@ -433,6 +438,7 @@ class ImportRequestHandler(BaseHandler):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            name = args["name"]
 | 
					            name = args["name"]
 | 
				
			||||||
            friendly_name = args.get("friendly_name")
 | 
					            friendly_name = args.get("friendly_name")
 | 
				
			||||||
 | 
					            encryption = args.get("encryption", False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            imported_device = next(
 | 
					            imported_device = next(
 | 
				
			||||||
                (res for res in IMPORT_RESULT.values() if res.device_name == name), None
 | 
					                (res for res in IMPORT_RESULT.values() if res.device_name == name), None
 | 
				
			||||||
@@ -452,6 +458,7 @@ class ImportRequestHandler(BaseHandler):
 | 
				
			|||||||
                args["project_name"],
 | 
					                args["project_name"],
 | 
				
			||||||
                args["package_import_url"],
 | 
					                args["package_import_url"],
 | 
				
			||||||
                network,
 | 
					                network,
 | 
				
			||||||
 | 
					                encryption,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        except FileExistsError:
 | 
					        except FileExistsError:
 | 
				
			||||||
            self.set_status(500)
 | 
					            self.set_status(500)
 | 
				
			||||||
@@ -538,38 +545,6 @@ class DownloadBinaryRequestHandler(BaseHandler):
 | 
				
			|||||||
        self.finish()
 | 
					        self.finish()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ManifestRequestHandler(BaseHandler):
 | 
					 | 
				
			||||||
    @authenticated
 | 
					 | 
				
			||||||
    @bind_config
 | 
					 | 
				
			||||||
    def get(self, configuration=None):
 | 
					 | 
				
			||||||
        args = ["esphome", "idedata", settings.rel_path(configuration)]
 | 
					 | 
				
			||||||
        rc, stdout, _ = run_system_command(*args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if rc != 0:
 | 
					 | 
				
			||||||
            self.send_error(404 if rc == 2 else 500)
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        idedata = platformio_api.IDEData(json.loads(stdout))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        firmware_offset = "0x10000" if idedata.extra_flash_images else "0x0"
 | 
					 | 
				
			||||||
        flash_images = [
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                "path": f"./download.bin?configuration={configuration}&type=firmware.bin",
 | 
					 | 
				
			||||||
                "offset": firmware_offset,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        ] + [
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                "path": f"./download.bin?configuration={configuration}&type={os.path.basename(image.path)}",
 | 
					 | 
				
			||||||
                "offset": image.offset,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for image in idedata.extra_flash_images
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.set_header("Content-Type", "application/json")
 | 
					 | 
				
			||||||
        self.write(json.dumps(flash_images))
 | 
					 | 
				
			||||||
        self.finish()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _list_dashboard_entries():
 | 
					def _list_dashboard_entries():
 | 
				
			||||||
    files = settings.list_yaml_files()
 | 
					    files = settings.list_yaml_files()
 | 
				
			||||||
    return [DashboardEntry(file) for file in files]
 | 
					    return [DashboardEntry(file) for file in files]
 | 
				
			||||||
@@ -1147,7 +1122,6 @@ def make_app(debug=get_bool_env(ENV_DEV)):
 | 
				
			|||||||
            (f"{rel}info", InfoRequestHandler),
 | 
					            (f"{rel}info", InfoRequestHandler),
 | 
				
			||||||
            (f"{rel}edit", EditRequestHandler),
 | 
					            (f"{rel}edit", EditRequestHandler),
 | 
				
			||||||
            (f"{rel}download.bin", DownloadBinaryRequestHandler),
 | 
					            (f"{rel}download.bin", DownloadBinaryRequestHandler),
 | 
				
			||||||
            (f"{rel}manifest.json", ManifestRequestHandler),
 | 
					 | 
				
			||||||
            (f"{rel}serial-ports", SerialPortRequestHandler),
 | 
					            (f"{rel}serial-ports", SerialPortRequestHandler),
 | 
				
			||||||
            (f"{rel}ping", PingRequestHandler),
 | 
					            (f"{rel}ping", PingRequestHandler),
 | 
				
			||||||
            (f"{rel}delete", DeleteRequestHandler),
 | 
					            (f"{rel}delete", DeleteRequestHandler),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,11 @@ def _set_mode(value, default_mode):
 | 
				
			|||||||
            CONF_INPUT: True,
 | 
					            CONF_INPUT: True,
 | 
				
			||||||
            CONF_PULLDOWN: True,
 | 
					            CONF_PULLDOWN: True,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "INPUT_OUTPUT_OPEN_DRAIN": {
 | 
				
			||||||
 | 
					            CONF_INPUT: True,
 | 
				
			||||||
 | 
					            CONF_OUTPUT: True,
 | 
				
			||||||
 | 
					            CONF_OPEN_DRAIN: True,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if mode.upper() not in PIN_MODES:
 | 
					    if mode.upper() not in PIN_MODES:
 | 
				
			||||||
        raise cv.Invalid(f"Unknown pin mode {mode}", [CONF_MODE])
 | 
					        raise cv.Invalid(f"Unknown pin mode {mode}", [CONF_MODE])
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ import yaml
 | 
				
			|||||||
import yaml.constructor
 | 
					import yaml.constructor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from esphome import core
 | 
					from esphome import core
 | 
				
			||||||
from esphome.config_helpers import read_config_file
 | 
					from esphome.config_helpers import read_config_file, Extend
 | 
				
			||||||
from esphome.core import (
 | 
					from esphome.core import (
 | 
				
			||||||
    EsphomeError,
 | 
					    EsphomeError,
 | 
				
			||||||
    IPAddress,
 | 
					    IPAddress,
 | 
				
			||||||
@@ -338,6 +338,10 @@ class ESPHomeLoader(yaml.SafeLoader):
 | 
				
			|||||||
        obj = self.construct_scalar(node)
 | 
					        obj = self.construct_scalar(node)
 | 
				
			||||||
        return add_class_to_obj(obj, ESPForceValue)
 | 
					        return add_class_to_obj(obj, ESPForceValue)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @_add_data_ref
 | 
				
			||||||
 | 
					    def construct_extend(self, node):
 | 
				
			||||||
 | 
					        return Extend(str(node.value))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ESPHomeLoader.add_constructor("tag:yaml.org,2002:int", ESPHomeLoader.construct_yaml_int)
 | 
					ESPHomeLoader.add_constructor("tag:yaml.org,2002:int", ESPHomeLoader.construct_yaml_int)
 | 
				
			||||||
ESPHomeLoader.add_constructor(
 | 
					ESPHomeLoader.add_constructor(
 | 
				
			||||||
@@ -369,6 +373,7 @@ ESPHomeLoader.add_constructor(
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda)
 | 
					ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda)
 | 
				
			||||||
ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force)
 | 
					ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force)
 | 
				
			||||||
 | 
					ESPHomeLoader.add_constructor("!extend", ESPHomeLoader.construct_extend)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def load_yaml(fname, clear_secrets=True):
 | 
					def load_yaml(fname, clear_secrets=True):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -157,6 +157,11 @@ class DashboardImportDiscovery:
 | 
				
			|||||||
            return
 | 
					            return
 | 
				
			||||||
        if state_change == ServiceStateChange.Removed:
 | 
					        if state_change == ServiceStateChange.Removed:
 | 
				
			||||||
            self.import_state.pop(name, None)
 | 
					            self.import_state.pop(name, None)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if state_change == ServiceStateChange.Updated and name not in self.import_state:
 | 
				
			||||||
 | 
					            # Ignore updates for devices that are not in the import state
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        info = zeroconf.get_service_info(service_type, name)
 | 
					        info = zeroconf.get_service_info(service_type, name)
 | 
				
			||||||
        _LOGGER.debug("-> resolved info: %s", info)
 | 
					        _LOGGER.debug("-> resolved info: %s", info)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										351
									
								
								tests/component_tests/packages/test_packages.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								tests/component_tests/packages/test_packages.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,351 @@
 | 
				
			|||||||
 | 
					"""Tests for the packages component."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from esphome.const import (
 | 
				
			||||||
 | 
					    CONF_DOMAIN,
 | 
				
			||||||
 | 
					    CONF_ESPHOME,
 | 
				
			||||||
 | 
					    CONF_FILTERS,
 | 
				
			||||||
 | 
					    CONF_ID,
 | 
				
			||||||
 | 
					    CONF_MULTIPLY,
 | 
				
			||||||
 | 
					    CONF_NAME,
 | 
				
			||||||
 | 
					    CONF_OFFSET,
 | 
				
			||||||
 | 
					    CONF_PACKAGES,
 | 
				
			||||||
 | 
					    CONF_PASSWORD,
 | 
				
			||||||
 | 
					    CONF_PLATFORM,
 | 
				
			||||||
 | 
					    CONF_SENSOR,
 | 
				
			||||||
 | 
					    CONF_SSID,
 | 
				
			||||||
 | 
					    CONF_UPDATE_INTERVAL,
 | 
				
			||||||
 | 
					    CONF_WIFI,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from esphome.components.packages import do_packages_pass
 | 
				
			||||||
 | 
					from esphome.config_helpers import Extend
 | 
				
			||||||
 | 
					import esphome.config_validation as cv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test strings
 | 
				
			||||||
 | 
					TEST_DEVICE_NAME = "test_device_name"
 | 
				
			||||||
 | 
					TEST_PLATFORM = "test_platform"
 | 
				
			||||||
 | 
					TEST_WIFI_SSID = "test_wifi_ssid"
 | 
				
			||||||
 | 
					TEST_PACKAGE_WIFI_SSID = "test_package_wifi_ssid"
 | 
				
			||||||
 | 
					TEST_PACKAGE_WIFI_PASSWORD = "test_package_wifi_password"
 | 
				
			||||||
 | 
					TEST_DOMAIN = "test_domain_name"
 | 
				
			||||||
 | 
					TEST_SENSOR_PLATFORM_1 = "test_sensor_platform_1"
 | 
				
			||||||
 | 
					TEST_SENSOR_PLATFORM_2 = "test_sensor_platform_2"
 | 
				
			||||||
 | 
					TEST_SENSOR_NAME_1 = "test_sensor_name_1"
 | 
				
			||||||
 | 
					TEST_SENSOR_NAME_2 = "test_sensor_name_2"
 | 
				
			||||||
 | 
					TEST_SENSOR_ID_1 = "test_sensor_id_1"
 | 
				
			||||||
 | 
					TEST_SENSOR_ID_2 = "test_sensor_id_2"
 | 
				
			||||||
 | 
					TEST_SENSOR_UPDATE_INTERVAL = "test_sensor_update_interval"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture(name="basic_wifi")
 | 
				
			||||||
 | 
					def fixture_basic_wifi():
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        CONF_SSID: TEST_PACKAGE_WIFI_SSID,
 | 
				
			||||||
 | 
					        CONF_PASSWORD: TEST_PACKAGE_WIFI_PASSWORD,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture(name="basic_esphome")
 | 
				
			||||||
 | 
					def fixture_basic_esphome():
 | 
				
			||||||
 | 
					    return {CONF_NAME: TEST_DEVICE_NAME, CONF_PLATFORM: TEST_PLATFORM}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_unused(basic_esphome, basic_wifi):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures do_package_pass does not change a config if packages aren't used.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {CONF_ESPHOME: basic_esphome, CONF_WIFI: basic_wifi}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_invalid_dict(basic_esphome, basic_wifi):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures an error is raised if packages is not valid.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {CONF_ESPHOME: basic_esphome, CONF_PACKAGES: basic_wifi}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with pytest.raises(cv.Invalid):
 | 
				
			||||||
 | 
					        do_packages_pass(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_include(basic_wifi, basic_esphome):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Tests the simple case where an independent config present in a package is added to the top-level config as is.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    In this test, the CONF_WIFI config is expected to be simply added to the top-level config.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_ESPHOME: basic_esphome,
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {"network": {CONF_WIFI: basic_wifi}},
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {CONF_ESPHOME: basic_esphome, CONF_WIFI: basic_wifi}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_append(basic_wifi, basic_esphome):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Tests the case where a key is present in both a package and top-level config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    In this test, CONF_WIFI is defined in a package, and CONF_DOMAIN is added to it at the top level.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_ESPHOME: basic_esphome,
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {"network": {CONF_WIFI: basic_wifi}},
 | 
				
			||||||
 | 
					        CONF_WIFI: {CONF_DOMAIN: TEST_DOMAIN},
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {
 | 
				
			||||||
 | 
					        CONF_ESPHOME: basic_esphome,
 | 
				
			||||||
 | 
					        CONF_WIFI: {
 | 
				
			||||||
 | 
					            CONF_SSID: TEST_PACKAGE_WIFI_SSID,
 | 
				
			||||||
 | 
					            CONF_PASSWORD: TEST_PACKAGE_WIFI_PASSWORD,
 | 
				
			||||||
 | 
					            CONF_DOMAIN: TEST_DOMAIN,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_override(basic_wifi, basic_esphome):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures that the top-level configuration takes precedence over duplicate keys defined in a package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    In this test, CONF_SSID should be overwritten by that defined in the top-level config.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_ESPHOME: basic_esphome,
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {"network": {CONF_WIFI: basic_wifi}},
 | 
				
			||||||
 | 
					        CONF_WIFI: {CONF_SSID: TEST_WIFI_SSID},
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {
 | 
				
			||||||
 | 
					        CONF_ESPHOME: basic_esphome,
 | 
				
			||||||
 | 
					        CONF_WIFI: {
 | 
				
			||||||
 | 
					            CONF_SSID: TEST_WIFI_SSID,
 | 
				
			||||||
 | 
					            CONF_PASSWORD: TEST_PACKAGE_WIFI_PASSWORD,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_multiple_package_order():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures that mutiple packages are merged in order.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {
 | 
				
			||||||
 | 
					            "package1": {
 | 
				
			||||||
 | 
					                "logger": {
 | 
				
			||||||
 | 
					                    "level": "DEBUG",
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "package2": {
 | 
				
			||||||
 | 
					                "logger": {
 | 
				
			||||||
 | 
					                    "level": "VERBOSE",
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {
 | 
				
			||||||
 | 
					        "logger": {
 | 
				
			||||||
 | 
					            "level": "VERBOSE",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_list_merge():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures lists defined in both a package and the top-level config are merged correctly
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {
 | 
				
			||||||
 | 
					            "package_sensors": {
 | 
				
			||||||
 | 
					                CONF_SENSOR: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        CONF_PLATFORM: TEST_SENSOR_PLATFORM_1,
 | 
				
			||||||
 | 
					                        CONF_NAME: TEST_SENSOR_NAME_1,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        CONF_PLATFORM: TEST_SENSOR_PLATFORM_1,
 | 
				
			||||||
 | 
					                        CONF_NAME: TEST_SENSOR_NAME_2,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_2, CONF_NAME: TEST_SENSOR_NAME_1},
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_2, CONF_NAME: TEST_SENSOR_NAME_2},
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_1, CONF_NAME: TEST_SENSOR_NAME_1},
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_1, CONF_NAME: TEST_SENSOR_NAME_2},
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_2, CONF_NAME: TEST_SENSOR_NAME_1},
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_2, CONF_NAME: TEST_SENSOR_NAME_2},
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_list_merge_by_id():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures that components with matching IDs are merged correctly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    In this test, a sensor is defined in a package, and a CONF_UPDATE_INTERVAL is added at the top level,
 | 
				
			||||||
 | 
					    and a sensor name is overridden in another sensor.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {
 | 
				
			||||||
 | 
					            "package_sensors": {
 | 
				
			||||||
 | 
					                CONF_SENSOR: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        CONF_ID: TEST_SENSOR_ID_1,
 | 
				
			||||||
 | 
					                        CONF_PLATFORM: TEST_SENSOR_PLATFORM_1,
 | 
				
			||||||
 | 
					                        CONF_NAME: TEST_SENSOR_NAME_1,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        CONF_ID: TEST_SENSOR_ID_2,
 | 
				
			||||||
 | 
					                        CONF_PLATFORM: TEST_SENSOR_PLATFORM_1,
 | 
				
			||||||
 | 
					                        CONF_NAME: TEST_SENSOR_NAME_2,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "package2": {
 | 
				
			||||||
 | 
					                CONF_SENSOR: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        CONF_ID: Extend(TEST_SENSOR_ID_1),
 | 
				
			||||||
 | 
					                        CONF_DOMAIN: "2",
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "package3": {
 | 
				
			||||||
 | 
					                CONF_SENSOR: [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        CONF_ID: Extend(TEST_SENSOR_ID_1),
 | 
				
			||||||
 | 
					                        CONF_DOMAIN: "3",
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_ID: Extend(TEST_SENSOR_ID_1),
 | 
				
			||||||
 | 
					                CONF_UPDATE_INTERVAL: TEST_SENSOR_UPDATE_INTERVAL,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {CONF_ID: Extend(TEST_SENSOR_ID_2), CONF_NAME: TEST_SENSOR_NAME_1},
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_2, CONF_NAME: TEST_SENSOR_NAME_2},
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_ID: TEST_SENSOR_ID_1,
 | 
				
			||||||
 | 
					                CONF_PLATFORM: TEST_SENSOR_PLATFORM_1,
 | 
				
			||||||
 | 
					                CONF_NAME: TEST_SENSOR_NAME_1,
 | 
				
			||||||
 | 
					                CONF_UPDATE_INTERVAL: TEST_SENSOR_UPDATE_INTERVAL,
 | 
				
			||||||
 | 
					                CONF_DOMAIN: "3",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_ID: TEST_SENSOR_ID_2,
 | 
				
			||||||
 | 
					                CONF_PLATFORM: TEST_SENSOR_PLATFORM_1,
 | 
				
			||||||
 | 
					                CONF_NAME: TEST_SENSOR_NAME_1,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {CONF_PLATFORM: TEST_SENSOR_PLATFORM_2, CONF_NAME: TEST_SENSOR_NAME_2},
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_merge_by_id_with_list():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures that components with matching IDs are merged correctly when their configuration contains lists.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    For example, a sensor with filters defined in both a package and the top level config should be merged.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {
 | 
				
			||||||
 | 
					            "sensors": {
 | 
				
			||||||
 | 
					                CONF_SENSOR: [
 | 
				
			||||||
 | 
					                    {CONF_ID: TEST_SENSOR_ID_1, CONF_FILTERS: [{CONF_MULTIPLY: 42.0}]}
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {CONF_ID: Extend(TEST_SENSOR_ID_1), CONF_FILTERS: [{CONF_OFFSET: 146.0}]}
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_ID: TEST_SENSOR_ID_1,
 | 
				
			||||||
 | 
					                CONF_FILTERS: [{CONF_MULTIPLY: 42.0}, {CONF_OFFSET: 146.0}],
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_package_merge_by_missing_id():
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Ensures that components with missing IDs are not merged.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    config = {
 | 
				
			||||||
 | 
					        CONF_PACKAGES: {
 | 
				
			||||||
 | 
					            "sensors": {
 | 
				
			||||||
 | 
					                CONF_SENSOR: [
 | 
				
			||||||
 | 
					                    {CONF_ID: TEST_SENSOR_ID_1, CONF_FILTERS: [{CONF_MULTIPLY: 42.0}]},
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {CONF_ID: TEST_SENSOR_ID_1, CONF_FILTERS: [{CONF_MULTIPLY: 10.0}]},
 | 
				
			||||||
 | 
					            {CONF_ID: Extend(TEST_SENSOR_ID_2), CONF_FILTERS: [{CONF_OFFSET: 146.0}]},
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expected = {
 | 
				
			||||||
 | 
					        CONF_SENSOR: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_ID: TEST_SENSOR_ID_1,
 | 
				
			||||||
 | 
					                CONF_FILTERS: [{CONF_MULTIPLY: 42.0}],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_ID: TEST_SENSOR_ID_1,
 | 
				
			||||||
 | 
					                CONF_FILTERS: [{CONF_MULTIPLY: 10.0}],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                CONF_ID: Extend(TEST_SENSOR_ID_2),
 | 
				
			||||||
 | 
					                CONF_FILTERS: [{CONF_OFFSET: 146.0}],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    actual = do_packages_pass(config)
 | 
				
			||||||
 | 
					    assert actual == expected
 | 
				
			||||||
@@ -222,6 +222,12 @@ uart:
 | 
				
			|||||||
    rx_pin: GPIO26
 | 
					    rx_pin: GPIO26
 | 
				
			||||||
    baud_rate: 115200
 | 
					    baud_rate: 115200
 | 
				
			||||||
    rx_buffer_size: 1024
 | 
					    rx_buffer_size: 1024
 | 
				
			||||||
 | 
					  - id: ld2410_uart
 | 
				
			||||||
 | 
					    tx_pin: 18
 | 
				
			||||||
 | 
					    rx_pin: 23
 | 
				
			||||||
 | 
					    baud_rate: 256000
 | 
				
			||||||
 | 
					    parity: NONE
 | 
				
			||||||
 | 
					    stop_bits: 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ota:
 | 
					ota:
 | 
				
			||||||
  safe_mode: true
 | 
					  safe_mode: true
 | 
				
			||||||
@@ -1070,6 +1076,8 @@ sensor:
 | 
				
			|||||||
    id: ultrasonic_sensor1
 | 
					    id: ultrasonic_sensor1
 | 
				
			||||||
  - platform: uptime
 | 
					  - platform: uptime
 | 
				
			||||||
    name: Uptime Sensor
 | 
					    name: Uptime Sensor
 | 
				
			||||||
 | 
					  - id: !extend ${devicename}_uptime_pcg
 | 
				
			||||||
 | 
					    unit_of_measurement: s
 | 
				
			||||||
  - platform: wifi_signal
 | 
					  - platform: wifi_signal
 | 
				
			||||||
    name: WiFi Signal Sensor
 | 
					    name: WiFi Signal Sensor
 | 
				
			||||||
    update_interval: 15s
 | 
					    update_interval: 15s
 | 
				
			||||||
@@ -1200,6 +1208,17 @@ sensor:
 | 
				
			|||||||
    pressure:
 | 
					    pressure:
 | 
				
			||||||
      name: "MPL3115A2 Pressure"
 | 
					      name: "MPL3115A2 Pressure"
 | 
				
			||||||
    update_interval: 10s
 | 
					    update_interval: 10s
 | 
				
			||||||
 | 
					  - platform: ld2410
 | 
				
			||||||
 | 
					    moving_distance:
 | 
				
			||||||
 | 
					      name: "Moving distance (cm)"
 | 
				
			||||||
 | 
					    still_distance:
 | 
				
			||||||
 | 
					      name: "Still Distance (cm)"
 | 
				
			||||||
 | 
					    moving_energy:
 | 
				
			||||||
 | 
					      name: "Move Energy"
 | 
				
			||||||
 | 
					    still_energy:
 | 
				
			||||||
 | 
					      name: "Still Energy"
 | 
				
			||||||
 | 
					    detection_distance:
 | 
				
			||||||
 | 
					      name: "Distance Detection"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
esp32_touch:
 | 
					esp32_touch:
 | 
				
			||||||
  setup_mode: false
 | 
					  setup_mode: false
 | 
				
			||||||
@@ -1304,6 +1323,11 @@ binary_sensor:
 | 
				
			|||||||
      number: GPIO9
 | 
					      number: GPIO9
 | 
				
			||||||
      mode: INPUT_PULLUP
 | 
					      mode: INPUT_PULLUP
 | 
				
			||||||
    name: Living Room Window 2
 | 
					    name: Living Room Window 2
 | 
				
			||||||
 | 
					  - platform: gpio
 | 
				
			||||||
 | 
					    pin:
 | 
				
			||||||
 | 
					      number: GPIO9
 | 
				
			||||||
 | 
					      mode: INPUT_OUTPUT_OPEN_DRAIN
 | 
				
			||||||
 | 
					    name: Living Room Button
 | 
				
			||||||
  - platform: status
 | 
					  - platform: status
 | 
				
			||||||
    name: Living Room Status
 | 
					    name: Living Room Status
 | 
				
			||||||
  - platform: esp32_touch
 | 
					  - platform: esp32_touch
 | 
				
			||||||
@@ -1461,6 +1485,13 @@ binary_sensor:
 | 
				
			|||||||
    id: close_sensor
 | 
					    id: close_sensor
 | 
				
			||||||
  - platform: template
 | 
					  - platform: template
 | 
				
			||||||
    id: close_obstacle_sensor
 | 
					    id: close_obstacle_sensor
 | 
				
			||||||
 | 
					  - platform: ld2410
 | 
				
			||||||
 | 
					    has_target:
 | 
				
			||||||
 | 
					      name: presence
 | 
				
			||||||
 | 
					    has_moving_target:
 | 
				
			||||||
 | 
					      name: movement
 | 
				
			||||||
 | 
					    has_still_target:
 | 
				
			||||||
 | 
					      name: still
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pca9685:
 | 
					pca9685:
 | 
				
			||||||
  frequency: 500
 | 
					  frequency: 500
 | 
				
			||||||
@@ -3143,6 +3174,19 @@ button:
 | 
				
			|||||||
    on_press:
 | 
					    on_press:
 | 
				
			||||||
      midea_ac.power_toggle:
 | 
					      midea_ac.power_toggle:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ld2410:
 | 
				
			||||||
 | 
					  id: my_ld2410
 | 
				
			||||||
 | 
					  uart_id: ld2410_uart
 | 
				
			||||||
 | 
					  timeout: 150s
 | 
				
			||||||
 | 
					  max_move_distance: 6m
 | 
				
			||||||
 | 
					  max_still_distance: 0.75m
 | 
				
			||||||
 | 
					  g0_move_threshold: 10
 | 
				
			||||||
 | 
					  g0_still_threshold: 20
 | 
				
			||||||
 | 
					  g2_move_threshold: 20
 | 
				
			||||||
 | 
					  g2_still_threshold: 21
 | 
				
			||||||
 | 
					  g8_move_threshold: 80
 | 
				
			||||||
 | 
					  g8_still_threshold: 81
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lcd_menu:
 | 
					lcd_menu:
 | 
				
			||||||
  display_id: my_lcd_gpio
 | 
					  display_id: my_lcd_gpio
 | 
				
			||||||
  mark_back: 0x5e
 | 
					  mark_back: 0x5e
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -287,6 +287,9 @@ uart:
 | 
				
			|||||||
modbus:
 | 
					modbus:
 | 
				
			||||||
  uart_id: uart1
 | 
					  uart_id: uart1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vbus:
 | 
				
			||||||
 | 
					  uart_id: uart4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ota:
 | 
					ota:
 | 
				
			||||||
  safe_mode: true
 | 
					  safe_mode: true
 | 
				
			||||||
  port: 3286
 | 
					  port: 3286
 | 
				
			||||||
@@ -799,6 +802,11 @@ sensor:
 | 
				
			|||||||
    id: adc128s102_channel_0
 | 
					    id: adc128s102_channel_0
 | 
				
			||||||
    channel: 0
 | 
					    channel: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - platform: vbus
 | 
				
			||||||
 | 
					    model: deltasol c
 | 
				
			||||||
 | 
					    temperature_1:
 | 
				
			||||||
 | 
					      name: Temperature 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
time:
 | 
					time:
 | 
				
			||||||
  - platform: homeassistant
 | 
					  - platform: homeassistant
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -904,6 +912,11 @@ binary_sensor:
 | 
				
			|||||||
      then:
 | 
					      then:
 | 
				
			||||||
        - pzemac.reset_energy: pzemac1
 | 
					        - pzemac.reset_energy: pzemac1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - platform: vbus
 | 
				
			||||||
 | 
					    model: deltasol_bs_plus
 | 
				
			||||||
 | 
					    relay1:
 | 
				
			||||||
 | 
					      name: Relay 1 On
 | 
				
			||||||
 | 
					
 | 
				
			||||||
globals:
 | 
					globals:
 | 
				
			||||||
  - id: my_global_string
 | 
					  - id: my_global_string
 | 
				
			||||||
    type: std::string
 | 
					    type: std::string
 | 
				
			||||||
@@ -1123,14 +1136,16 @@ climate:
 | 
				
			|||||||
      - switch.turn_on: gpio_switch1
 | 
					      - switch.turn_on: gpio_switch1
 | 
				
			||||||
    fan_mode_diffuse_action:
 | 
					    fan_mode_diffuse_action:
 | 
				
			||||||
      - switch.turn_on: gpio_switch2
 | 
					      - switch.turn_on: gpio_switch2
 | 
				
			||||||
 | 
					    fan_mode_quiet_action:
 | 
				
			||||||
 | 
					      - switch.turn_on: gpio_switch1
 | 
				
			||||||
    swing_off_action:
 | 
					    swing_off_action:
 | 
				
			||||||
      - switch.turn_on: gpio_switch1
 | 
					      - switch.turn_on: gpio_switch2
 | 
				
			||||||
    swing_horizontal_action:
 | 
					    swing_horizontal_action:
 | 
				
			||||||
      - switch.turn_on: gpio_switch2
 | 
					 | 
				
			||||||
    swing_vertical_action:
 | 
					 | 
				
			||||||
      - switch.turn_on: gpio_switch1
 | 
					      - switch.turn_on: gpio_switch1
 | 
				
			||||||
    swing_both_action:
 | 
					    swing_vertical_action:
 | 
				
			||||||
      - switch.turn_on: gpio_switch2
 | 
					      - switch.turn_on: gpio_switch2
 | 
				
			||||||
 | 
					    swing_both_action:
 | 
				
			||||||
 | 
					      - switch.turn_on: gpio_switch1
 | 
				
			||||||
    startup_delay: true
 | 
					    startup_delay: true
 | 
				
			||||||
    supplemental_cooling_delta: 2.0
 | 
					    supplemental_cooling_delta: 2.0
 | 
				
			||||||
    cool_deadband: 0.5
 | 
					    cool_deadband: 0.5
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,6 +66,9 @@ mqtt:
 | 
				
			|||||||
          ESP_LOGD("Mqtt Test", "testing/sensor/testing_sensor/state=[%s]", x.c_str());
 | 
					          ESP_LOGD("Mqtt Test", "testing/sensor/testing_sensor/state=[%s]", x.c_str());
 | 
				
			||||||
      # yamllint enable rule:line-length
 | 
					      # yamllint enable rule:line-length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					vbus:
 | 
				
			||||||
 | 
					  - uart_id: uart2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
binary_sensor:
 | 
					binary_sensor:
 | 
				
			||||||
  - platform: gpio
 | 
					  - platform: gpio
 | 
				
			||||||
    pin: GPIO0
 | 
					    pin: GPIO0
 | 
				
			||||||
@@ -183,6 +186,22 @@ binary_sensor:
 | 
				
			|||||||
    id: key1
 | 
					    id: key1
 | 
				
			||||||
    key: 1
 | 
					    key: 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - platform: vbus
 | 
				
			||||||
 | 
					    model: deltasol_bs_plus
 | 
				
			||||||
 | 
					    relay2:
 | 
				
			||||||
 | 
					      name: Relay 2 On
 | 
				
			||||||
 | 
					    sensor1_error:
 | 
				
			||||||
 | 
					      name: Sensor 1 Error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - platform: vbus
 | 
				
			||||||
 | 
					    model: custom
 | 
				
			||||||
 | 
					    command: 0x100
 | 
				
			||||||
 | 
					    source: 0x1234
 | 
				
			||||||
 | 
					    dest: 0x10
 | 
				
			||||||
 | 
					    binary_sensors:
 | 
				
			||||||
 | 
					      - id: vcustom_b
 | 
				
			||||||
 | 
					        name: VBus Custom Binary Sensor
 | 
				
			||||||
 | 
					        lambda: return x[0] & 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tlc5947:
 | 
					tlc5947:
 | 
				
			||||||
  data_pin: GPIO12
 | 
					  data_pin: GPIO12
 | 
				
			||||||
@@ -478,6 +497,27 @@ sensor:
 | 
				
			|||||||
    max_flow_rate:
 | 
					    max_flow_rate:
 | 
				
			||||||
      name: Max Flow Rate
 | 
					      name: Max Flow Rate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - platform: vbus
 | 
				
			||||||
 | 
					    model: deltasol c
 | 
				
			||||||
 | 
					    temperature_3:
 | 
				
			||||||
 | 
					      name: Temperature 3
 | 
				
			||||||
 | 
					    operating_hours_1:
 | 
				
			||||||
 | 
					      name: Operating Hours 1
 | 
				
			||||||
 | 
					    heat_quantity:
 | 
				
			||||||
 | 
					      name: Heat Quantity
 | 
				
			||||||
 | 
					    time:
 | 
				
			||||||
 | 
					      name: System Time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  - platform: vbus
 | 
				
			||||||
 | 
					    model: custom
 | 
				
			||||||
 | 
					    command: 0x100
 | 
				
			||||||
 | 
					    source: 0x1234
 | 
				
			||||||
 | 
					    dest: 0x10
 | 
				
			||||||
 | 
					    sensors:
 | 
				
			||||||
 | 
					      - id: vcustom
 | 
				
			||||||
 | 
					        name: VBus Custom Sensor
 | 
				
			||||||
 | 
					        lambda: return x[0] / 10.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
script:
 | 
					script:
 | 
				
			||||||
  - id: automation_test
 | 
					  - id: automation_test
 | 
				
			||||||
    then:
 | 
					    then:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user