mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 00:51:49 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			2023.2.0b5
			...
			jesserockz
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					751853efdc | 
							
								
								
									
										26
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -138,18 +138,14 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: [deploy-docker]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Trigger Workflow
 | 
			
		||||
        uses: actions/github-script@v6
 | 
			
		||||
        with:
 | 
			
		||||
          github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
			
		||||
          script: |
 | 
			
		||||
            github.rest.actions.createWorkflowDispatch({
 | 
			
		||||
              owner: "esphome",
 | 
			
		||||
              repo: "home-assistant-addon",
 | 
			
		||||
              workflow_id: "bump-version.yml",
 | 
			
		||||
              ref: "main",
 | 
			
		||||
              inputs: {
 | 
			
		||||
                version: "${{ github.event.release.tag_name }}",
 | 
			
		||||
                content: ${{ toJSON(github.event.release.body) }}
 | 
			
		||||
              }
 | 
			
		||||
            })
 | 
			
		||||
      - env:
 | 
			
		||||
          TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
 | 
			
		||||
        # yamllint disable rule:line-length
 | 
			
		||||
        run: |
 | 
			
		||||
          curl \
 | 
			
		||||
            -u ":$TOKEN" \
 | 
			
		||||
            -X POST \
 | 
			
		||||
            -H "Accept: application/vnd.github.v3+json" \
 | 
			
		||||
            https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
 | 
			
		||||
            -d '{"ref":"main","inputs":{"version":"${{ github.event.release.tag_name }}","content":${{ toJSON(github.event.release.body) }}}}'
 | 
			
		||||
        # yamllint enable rule:line-length
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -128,5 +128,3 @@ tests/.esphome/
 | 
			
		||||
 | 
			
		||||
sdkconfig.*
 | 
			
		||||
!sdkconfig.defaults
 | 
			
		||||
 | 
			
		||||
.tests/
 | 
			
		||||
@@ -119,7 +119,6 @@ esphome/components/kalman_combinator/* @Cat-Ion
 | 
			
		||||
esphome/components/key_collector/* @ssieb
 | 
			
		||||
esphome/components/key_provider/* @ssieb
 | 
			
		||||
esphome/components/lcd_menu/* @numo68
 | 
			
		||||
esphome/components/ld2410/* @sebcaps
 | 
			
		||||
esphome/components/ledc/* @OttoWinter
 | 
			
		||||
esphome/components/light/* @esphome/core
 | 
			
		||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
 | 
			
		||||
@@ -275,7 +274,6 @@ esphome/components/uart/* @esphome/core
 | 
			
		||||
esphome/components/ufire_ec/* @pvizeli
 | 
			
		||||
esphome/components/ufire_ise/* @pvizeli
 | 
			
		||||
esphome/components/ultrasonic/* @OttoWinter
 | 
			
		||||
esphome/components/vbus/* @ssieb
 | 
			
		||||
esphome/components/version/* @esphome/core
 | 
			
		||||
esphome/components/wake_on_lan/* @willwill2will54
 | 
			
		||||
esphome/components/web_server_base/* @OttoWinter
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ RUN \
 | 
			
		||||
        python3-cryptography=3.3.2-1 \
 | 
			
		||||
        iputils-ping=3:20210202-1 \
 | 
			
		||||
        git=1:2.30.2-1 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u5 \
 | 
			
		||||
        curl=7.74.0-1.3+deb11u3 \
 | 
			
		||||
        openssh-client=1:8.4p1-5+deb11u1 \
 | 
			
		||||
    && rm -rf \
 | 
			
		||||
        /tmp/* \
 | 
			
		||||
 
 | 
			
		||||
@@ -787,7 +787,6 @@ enum ClimateFanMode {
 | 
			
		||||
  CLIMATE_FAN_MIDDLE = 6;
 | 
			
		||||
  CLIMATE_FAN_FOCUS = 7;
 | 
			
		||||
  CLIMATE_FAN_DIFFUSE = 8;
 | 
			
		||||
  CLIMATE_FAN_QUIET = 9;
 | 
			
		||||
}
 | 
			
		||||
enum ClimateSwingMode {
 | 
			
		||||
  CLIMATE_SWING_OFF = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -235,8 +235,6 @@ template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::Climat
 | 
			
		||||
      return "CLIMATE_FAN_FOCUS";
 | 
			
		||||
    case enums::CLIMATE_FAN_DIFFUSE:
 | 
			
		||||
      return "CLIMATE_FAN_DIFFUSE";
 | 
			
		||||
    case enums::CLIMATE_FAN_QUIET:
 | 
			
		||||
      return "CLIMATE_FAN_QUIET";
 | 
			
		||||
    default:
 | 
			
		||||
      return "UNKNOWN";
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,6 @@ enum ClimateFanMode : uint32_t {
 | 
			
		||||
  CLIMATE_FAN_MIDDLE = 6,
 | 
			
		||||
  CLIMATE_FAN_FOCUS = 7,
 | 
			
		||||
  CLIMATE_FAN_DIFFUSE = 8,
 | 
			
		||||
  CLIMATE_FAN_QUIET = 9,
 | 
			
		||||
};
 | 
			
		||||
enum ClimateSwingMode : uint32_t {
 | 
			
		||||
  CLIMATE_SWING_OFF = 0,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_MODE_STATE_TOPIC,
 | 
			
		||||
    CONF_ON_STATE,
 | 
			
		||||
    CONF_PRESET,
 | 
			
		||||
    CONF_PRESET_COMMAND_TOPIC,
 | 
			
		||||
    CONF_PRESET_STATE_TOPIC,
 | 
			
		||||
    CONF_SWING_MODE,
 | 
			
		||||
    CONF_SWING_MODE_COMMAND_TOPIC,
 | 
			
		||||
    CONF_SWING_MODE_STATE_TOPIC,
 | 
			
		||||
@@ -75,7 +73,6 @@ CLIMATE_FAN_MODES = {
 | 
			
		||||
    "MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE,
 | 
			
		||||
    "FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS,
 | 
			
		||||
    "DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE,
 | 
			
		||||
    "QUIET": ClimateFanMode.CLIMATE_FAN_QUIET,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
 | 
			
		||||
@@ -145,12 +142,6 @@ CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).
 | 
			
		||||
        cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
 | 
			
		||||
            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.requires_component("mqtt"), cv.publish_topic
 | 
			
		||||
        ),
 | 
			
		||||
@@ -225,12 +216,7 @@ async def setup_climate_core_(var, config):
 | 
			
		||||
            cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC]))
 | 
			
		||||
        if CONF_MODE_STATE_TOPIC in config:
 | 
			
		||||
            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:
 | 
			
		||||
            cg.add(
 | 
			
		||||
                mqtt_.set_custom_swing_mode_command_topic(
 | 
			
		||||
 
 | 
			
		||||
@@ -174,8 +174,6 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
 | 
			
		||||
    this->set_fan_mode(CLIMATE_FAN_FOCUS);
 | 
			
		||||
  } else if (str_equals_case_insensitive(fan_mode, "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 {
 | 
			
		||||
    if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) {
 | 
			
		||||
      this->custom_fan_mode_ = fan_mode;
 | 
			
		||||
 
 | 
			
		||||
@@ -62,8 +62,6 @@ const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
 | 
			
		||||
      return LOG_STR("FOCUS");
 | 
			
		||||
    case climate::CLIMATE_FAN_DIFFUSE:
 | 
			
		||||
      return LOG_STR("DIFFUSE");
 | 
			
		||||
    case climate::CLIMATE_FAN_QUIET:
 | 
			
		||||
      return LOG_STR("QUIET");
 | 
			
		||||
    default:
 | 
			
		||||
      return LOG_STR("UNKNOWN");
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -62,8 +62,6 @@ enum ClimateFanMode : uint8_t {
 | 
			
		||||
  CLIMATE_FAN_FOCUS = 7,
 | 
			
		||||
  /// The fan mode is set to Diffuse
 | 
			
		||||
  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
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ namespace climate {
 | 
			
		||||
 *  - supports action - if the climate device supports reporting the active
 | 
			
		||||
 *    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:
 | 
			
		||||
 *    - on, off, auto, high, medium, low, middle, focus, diffuse, quiet
 | 
			
		||||
 *    - on, off, auto, high, medium, low, middle, focus, diffuse
 | 
			
		||||
 *  - supports swing modes - optionally, if it has a swing which can be configured in different ways:
 | 
			
		||||
 *    - off, both, vertical, horizontal
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
import base64
 | 
			
		||||
import secrets
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +73,6 @@ def import_config(
 | 
			
		||||
    project_name: str,
 | 
			
		||||
    import_url: str,
 | 
			
		||||
    network: str = CONF_WIFI,
 | 
			
		||||
    encryption: bool = False,
 | 
			
		||||
) -> None:
 | 
			
		||||
    p = Path(path)
 | 
			
		||||
 | 
			
		||||
@@ -83,21 +80,15 @@ def import_config(
 | 
			
		||||
        raise FileExistsError
 | 
			
		||||
 | 
			
		||||
    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(
 | 
			
		||||
            wizard_file(**kwargs),
 | 
			
		||||
            wizard_file(
 | 
			
		||||
                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",
 | 
			
		||||
        )
 | 
			
		||||
    else:
 | 
			
		||||
@@ -124,11 +115,6 @@ def import_config(
 | 
			
		||||
                "packages": {project_name: import_url},
 | 
			
		||||
                "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)
 | 
			
		||||
 | 
			
		||||
            if network == CONF_WIFI:
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,6 @@ class DemoClimate : public climate::Climate, public Component {
 | 
			
		||||
            climate::CLIMATE_FAN_MIDDLE,
 | 
			
		||||
            climate::CLIMATE_FAN_FOCUS,
 | 
			
		||||
            climate::CLIMATE_FAN_DIFFUSE,
 | 
			
		||||
            climate::CLIMATE_FAN_QUIET,
 | 
			
		||||
        });
 | 
			
		||||
        traits.set_supported_custom_fan_modes({"Auto Low", "Auto High"});
 | 
			
		||||
        traits.set_supported_swing_modes({
 | 
			
		||||
 
 | 
			
		||||
@@ -15,84 +15,6 @@ static const char *const TAG = "display";
 | 
			
		||||
const Color COLOR_OFF(0, 0, 0, 0);
 | 
			
		||||
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) {
 | 
			
		||||
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
 | 
			
		||||
  this->buffer_ = allocator.allocate(buffer_length);
 | 
			
		||||
@@ -102,7 +24,6 @@ void DisplayBuffer::init_internal_(uint32_t buffer_length) {
 | 
			
		||||
  }
 | 
			
		||||
  this->clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayBuffer::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); }
 | 
			
		||||
void DisplayBuffer::clear() { this->fill(COLOR_OFF); }
 | 
			
		||||
int DisplayBuffer::get_width() {
 | 
			
		||||
@@ -129,9 +50,6 @@ int DisplayBuffer::get_height() {
 | 
			
		||||
}
 | 
			
		||||
void DisplayBuffer::set_rotation(DisplayRotation rotation) { this->rotation_ = rotation; }
 | 
			
		||||
void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
 | 
			
		||||
  if (!this->get_clipping().inside(x, y))
 | 
			
		||||
    return;  // NOLINT
 | 
			
		||||
 | 
			
		||||
  switch (this->rotation_) {
 | 
			
		||||
    case DISPLAY_ROTATION_0_DEGREES:
 | 
			
		||||
      break;
 | 
			
		||||
@@ -450,10 +368,6 @@ void DisplayBuffer::do_update_() {
 | 
			
		||||
  } else if (this->writer_.has_value()) {
 | 
			
		||||
    (*this->writer_)(*this);
 | 
			
		||||
  }
 | 
			
		||||
  // remove all not ended clipping regions
 | 
			
		||||
  while (is_clipping()) {
 | 
			
		||||
    end_clipping();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
 | 
			
		||||
  if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
 | 
			
		||||
@@ -478,41 +392,6 @@ void DisplayBuffer::strftime(int x, int y, Font *font, const char *format, time:
 | 
			
		||||
}
 | 
			
		||||
#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 {
 | 
			
		||||
  const int x_data = x - this->glyph_data_->offset_x;
 | 
			
		||||
  const int y_data = y - this->glyph_data_->offset_y;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
#include "esphome/core/defines.h"
 | 
			
		||||
#include "esphome/core/automation.h"
 | 
			
		||||
#include "display_color_utils.h"
 | 
			
		||||
 | 
			
		||||
#include <cstdarg>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -99,32 +100,6 @@ enum DisplayRotation {
 | 
			
		||||
  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 Image;
 | 
			
		||||
class DisplayBuffer;
 | 
			
		||||
@@ -151,7 +126,6 @@ class DisplayBuffer {
 | 
			
		||||
  int get_width();
 | 
			
		||||
  /// Get the height of the image in pixels with rotation applied.
 | 
			
		||||
  int get_height();
 | 
			
		||||
 | 
			
		||||
  /// Set a single pixel at the specified coordinates to the given color.
 | 
			
		||||
  void draw_pixel_at(int x, int y, Color color = COLOR_ON);
 | 
			
		||||
 | 
			
		||||
@@ -400,49 +374,6 @@ class DisplayBuffer {
 | 
			
		||||
   */
 | 
			
		||||
  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:
 | 
			
		||||
  void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);
 | 
			
		||||
 | 
			
		||||
@@ -459,7 +390,6 @@ class DisplayBuffer {
 | 
			
		||||
  DisplayPage *previous_page_{nullptr};
 | 
			
		||||
  std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
 | 
			
		||||
  bool auto_clear_enabled_{true};
 | 
			
		||||
  std::vector<Rect> clipping_rectangle_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DisplayPage {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_TRIGGER_ID,
 | 
			
		||||
    CONF_ON_VALUE,
 | 
			
		||||
    CONF_COMMAND,
 | 
			
		||||
    CONF_CUSTOM,
 | 
			
		||||
    CONF_NUMBER,
 | 
			
		||||
    CONF_FORMAT,
 | 
			
		||||
    CONF_MODE,
 | 
			
		||||
@@ -33,6 +32,7 @@ CONF_BACK = "back"
 | 
			
		||||
CONF_TEXT = "text"
 | 
			
		||||
CONF_SELECT = "select"
 | 
			
		||||
CONF_SWITCH = "switch"
 | 
			
		||||
CONF_CUSTOM = "custom"
 | 
			
		||||
CONF_ITEMS = "items"
 | 
			
		||||
CONF_ON_TEXT = "on_text"
 | 
			
		||||
CONF_OFF_TEXT = "off_text"
 | 
			
		||||
 
 | 
			
		||||
@@ -123,8 +123,11 @@ def validate_gpio_pin(value):
 | 
			
		||||
 | 
			
		||||
def validate_supports(value):
 | 
			
		||||
    mode = value[CONF_MODE]
 | 
			
		||||
    is_input = mode[CONF_INPUT]
 | 
			
		||||
    is_output = mode[CONF_OUTPUT]
 | 
			
		||||
    is_open_drain = mode[CONF_OPEN_DRAIN]
 | 
			
		||||
    is_pullup = mode[CONF_PULLUP]
 | 
			
		||||
    is_pulldown = mode[CONF_PULLDOWN]
 | 
			
		||||
    variant = CORE.data[KEY_ESP32][KEY_VARIANT]
 | 
			
		||||
    if variant not in _esp32_validations:
 | 
			
		||||
        raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
 | 
			
		||||
@@ -135,6 +138,26 @@ def validate_supports(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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -195,7 +195,7 @@ void ESP32ImprovComponent::send_response_(std::vector<uint8_t> &response) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ESP32ImprovComponent::start() {
 | 
			
		||||
  if (this->should_start_ || this->state_ != improv::STATE_STOPPED)
 | 
			
		||||
  if (this->state_ != improv::STATE_STOPPED)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  ESP_LOGD(TAG, "Setting Improv to start");
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,6 @@ ETHERNET_TYPES = {
 | 
			
		||||
    "RTL8201": EthernetType.ETHERNET_TYPE_RTL8201,
 | 
			
		||||
    "DP83848": EthernetType.ETHERNET_TYPE_DP83848,
 | 
			
		||||
    "IP101": EthernetType.ETHERNET_TYPE_IP101,
 | 
			
		||||
    "JL1101": EthernetType.ETHERNET_TYPE_JL1101,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,339 +0,0 @@
 | 
			
		||||
// 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,10 +71,6 @@ void EthernetComponent::setup() {
 | 
			
		||||
      phy = esp_eth_phy_new_ip101(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case ETHERNET_TYPE_JL1101: {
 | 
			
		||||
      phy = esp_eth_phy_new_jl1101(&phy_config);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    default: {
 | 
			
		||||
      this->mark_failed();
 | 
			
		||||
      return;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@ enum EthernetType {
 | 
			
		||||
  ETHERNET_TYPE_RTL8201,
 | 
			
		||||
  ETHERNET_TYPE_DP83848,
 | 
			
		||||
  ETHERNET_TYPE_IP101,
 | 
			
		||||
  ETHERNET_TYPE_JL1101,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ManualIP {
 | 
			
		||||
@@ -83,7 +82,6 @@ class EthernetComponent : public Component {
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
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 esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -1,158 +0,0 @@
 | 
			
		||||
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),
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
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))
 | 
			
		||||
@@ -1,315 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,146 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
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))
 | 
			
		||||
@@ -8,20 +8,6 @@
 | 
			
		||||
#endif
 | 
			
		||||
#include <driver/ledc.h>
 | 
			
		||||
 | 
			
		||||
#define CLOCK_FREQUENCY 80e6f
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
 | 
			
		||||
#undef CLOCK_FREQUENCY
 | 
			
		||||
// starting with ESP32 Arduino 2.0.2, the 40MHz crystal is used as clock by default if supported
 | 
			
		||||
#define CLOCK_FREQUENCY 40e6f
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
#define DEFAULT_CLK LEDC_USE_APB_CLK
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static const uint8_t SETUP_ATTEMPT_COUNT_MAX = 5;
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace ledc {
 | 
			
		||||
 | 
			
		||||
@@ -40,11 +26,11 @@ inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; }
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return CLOCK_FREQUENCY / float(1 << bit_depth); }
 | 
			
		||||
float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return 80e6f / float(1 << bit_depth); }
 | 
			
		||||
 | 
			
		||||
float ledc_min_frequency_for_bit_depth(uint8_t bit_depth, bool low_frequency) {
 | 
			
		||||
  const float max_div_num = ((1 << MAX_RES_BITS) - 1) / (low_frequency ? 32.0f : 256.0f);
 | 
			
		||||
  return CLOCK_FREQUENCY / (max_div_num * float(1 << bit_depth));
 | 
			
		||||
  return 80e6f / (max_div_num * float(1 << bit_depth));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) {
 | 
			
		||||
@@ -60,38 +46,6 @@ optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) {
 | 
			
		||||
  return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_channel_t chan_num,
 | 
			
		||||
                                    uint8_t channel, uint8_t &bit_depth, float frequency) {
 | 
			
		||||
  bit_depth = *ledc_bit_depth_for_frequency(frequency);
 | 
			
		||||
  if (bit_depth < 1) {
 | 
			
		||||
    ESP_LOGE(TAG, "Frequency %f can't be achieved with any bit depth", frequency);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ledc_timer_config_t timer_conf{};
 | 
			
		||||
  timer_conf.speed_mode = speed_mode;
 | 
			
		||||
  timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(bit_depth);
 | 
			
		||||
  timer_conf.timer_num = timer_num;
 | 
			
		||||
  timer_conf.freq_hz = (uint32_t) frequency;
 | 
			
		||||
  timer_conf.clk_cfg = DEFAULT_CLK;
 | 
			
		||||
 | 
			
		||||
  // Configure the time with fallback in case of error
 | 
			
		||||
  int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX;
 | 
			
		||||
  esp_err_t init_result = ESP_FAIL;
 | 
			
		||||
  while (attempt_count_max > 0 && init_result != ESP_OK) {
 | 
			
		||||
    init_result = ledc_timer_config(&timer_conf);
 | 
			
		||||
    if (init_result != ESP_OK) {
 | 
			
		||||
      ESP_LOGW(TAG, "Unable to initialize timer with frequency %.1f and bit depth of %u", frequency, bit_depth);
 | 
			
		||||
      // try again with a lower bit depth
 | 
			
		||||
      timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(--bit_depth);
 | 
			
		||||
    }
 | 
			
		||||
    attempt_count_max--;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return init_result;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void LEDCOutput::write_state(float state) {
 | 
			
		||||
  if (!initialized_) {
 | 
			
		||||
    ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!");
 | 
			
		||||
@@ -107,7 +61,6 @@ void LEDCOutput::write_state(float state) {
 | 
			
		||||
  auto duty = static_cast<uint32_t>(duty_rounded);
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
  ESP_LOGV(TAG, "Setting duty: %u on channel %u", duty, this->channel_);
 | 
			
		||||
  ledcWrite(this->channel_, duty);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
@@ -119,7 +72,6 @@ void LEDCOutput::write_state(float state) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LEDCOutput::setup() {
 | 
			
		||||
  ESP_LOGV(TAG, "Entering setup...");
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
  this->update_frequency(this->frequency_);
 | 
			
		||||
  this->turn_off();
 | 
			
		||||
@@ -131,16 +83,19 @@ void LEDCOutput::setup() {
 | 
			
		||||
  auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2);
 | 
			
		||||
  auto chan_num = static_cast<ledc_channel_t>(channel_ % 8);
 | 
			
		||||
 | 
			
		||||
  esp_err_t timer_init_result =
 | 
			
		||||
      configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_);
 | 
			
		||||
 | 
			
		||||
  if (timer_init_result != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Frequency %f can't be achieved with computed bit depth %u", this->frequency_, this->bit_depth_);
 | 
			
		||||
    this->status_set_error();
 | 
			
		||||
    return;
 | 
			
		||||
  bit_depth_ = *ledc_bit_depth_for_frequency(frequency_);
 | 
			
		||||
  if (bit_depth_ < 1) {
 | 
			
		||||
    ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency_);
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGV(TAG, "Configured frequency %f with a bit depth of %u bits", this->frequency_, this->bit_depth_);
 | 
			
		||||
  ledc_timer_config_t timer_conf{};
 | 
			
		||||
  timer_conf.speed_mode = speed_mode;
 | 
			
		||||
  timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(bit_depth_);
 | 
			
		||||
  timer_conf.timer_num = timer_num;
 | 
			
		||||
  timer_conf.freq_hz = (uint32_t) frequency_;
 | 
			
		||||
  timer_conf.clk_cfg = LEDC_AUTO_CLK;
 | 
			
		||||
  ledc_timer_config(&timer_conf);
 | 
			
		||||
 | 
			
		||||
  ledc_channel_config_t chan_conf{};
 | 
			
		||||
  chan_conf.gpio_num = pin_->get_pin();
 | 
			
		||||
@@ -152,7 +107,6 @@ void LEDCOutput::setup() {
 | 
			
		||||
  chan_conf.hpoint = 0;
 | 
			
		||||
  ledc_channel_config(&chan_conf);
 | 
			
		||||
  initialized_ = true;
 | 
			
		||||
  this->status_clear_error();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -160,80 +114,36 @@ void LEDCOutput::dump_config() {
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "LEDC Output:");
 | 
			
		||||
  LOG_PIN("  Pin ", this->pin_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  LEDC Channel: %u", this->channel_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  PWM Frequency: %.1f Hz", this->frequency_);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Bit depth: %u", this->bit_depth_);
 | 
			
		||||
  ESP_LOGV(TAG, "  Max frequency for bit depth: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_));
 | 
			
		||||
  ESP_LOGV(TAG, "  Min frequency for bit depth: %f",
 | 
			
		||||
           ledc_min_frequency_for_bit_depth(this->bit_depth_, (this->frequency_ < 100)));
 | 
			
		||||
  ESP_LOGV(TAG, "  Max frequency for bit depth-1: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_ - 1));
 | 
			
		||||
  ESP_LOGV(TAG, "  Min frequency for bit depth-1: %f",
 | 
			
		||||
           ledc_min_frequency_for_bit_depth(this->bit_depth_ - 1, (this->frequency_ < 100)));
 | 
			
		||||
  ESP_LOGV(TAG, "  Max frequency for bit depth+1: %f", ledc_max_frequency_for_bit_depth(this->bit_depth_ + 1));
 | 
			
		||||
  ESP_LOGV(TAG, "  Min frequency for bit depth+1: %f",
 | 
			
		||||
           ledc_min_frequency_for_bit_depth(this->bit_depth_ + 1, (this->frequency_ < 100)));
 | 
			
		||||
  ESP_LOGV(TAG, "  Max res bits: %d", MAX_RES_BITS);
 | 
			
		||||
  ESP_LOGV(TAG, "  Clock frequency: %f", CLOCK_FREQUENCY);
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Frequency: %.1f Hz", this->frequency_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LEDCOutput::update_frequency(float frequency) {
 | 
			
		||||
  auto bit_depth_opt = ledc_bit_depth_for_frequency(frequency);
 | 
			
		||||
  if (!bit_depth_opt.has_value()) {
 | 
			
		||||
    ESP_LOGE(TAG, "Frequency %f can't be achieved with any bit depth", this->frequency_);
 | 
			
		||||
    this->status_set_error();
 | 
			
		||||
    ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency);
 | 
			
		||||
    this->status_set_warning();
 | 
			
		||||
  }
 | 
			
		||||
  this->bit_depth_ = bit_depth_opt.value_or(8);
 | 
			
		||||
  this->frequency_ = frequency;
 | 
			
		||||
#ifdef USE_ARDUINO
 | 
			
		||||
  ESP_LOGV(TAG, "Using Arduino API - Trying to define channel, frequency and bit depth...");
 | 
			
		||||
  u_int32_t configured_frequency = 0;
 | 
			
		||||
 | 
			
		||||
  // Configure LEDC channel, frequency and bit depth with fallback
 | 
			
		||||
  int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX;
 | 
			
		||||
  while (attempt_count_max > 0 && configured_frequency == 0) {
 | 
			
		||||
    ESP_LOGV(TAG, "Trying initialize channel %u with frequency %.1f and bit depth of %u...", this->channel_,
 | 
			
		||||
             this->frequency_, this->bit_depth_);
 | 
			
		||||
    configured_frequency = ledcSetup(this->channel_, frequency, this->bit_depth_);
 | 
			
		||||
    if (configured_frequency != 0) {
 | 
			
		||||
      initialized_ = true;
 | 
			
		||||
      this->status_clear_error();
 | 
			
		||||
      ESP_LOGV(TAG, "Configured frequency: %u with bit depth: %u", configured_frequency, this->bit_depth_);
 | 
			
		||||
    } else {
 | 
			
		||||
      ESP_LOGW(TAG, "Unable to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_,
 | 
			
		||||
               this->frequency_, this->bit_depth_);
 | 
			
		||||
      // try again with a lower bit depth
 | 
			
		||||
      this->bit_depth_--;
 | 
			
		||||
    }
 | 
			
		||||
    attempt_count_max--;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (configured_frequency == 0) {
 | 
			
		||||
    ESP_LOGE(TAG, "Permanently failed to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_,
 | 
			
		||||
             this->frequency_, this->bit_depth_);
 | 
			
		||||
    this->status_set_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ledcSetup(this->channel_, frequency, this->bit_depth_);
 | 
			
		||||
  initialized_ = true;
 | 
			
		||||
#endif  // USE_ARDUINO
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
  if (!initialized_) {
 | 
			
		||||
    ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto speed_mode = get_speed_mode(channel_);
 | 
			
		||||
  auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2);
 | 
			
		||||
  auto chan_num = static_cast<ledc_channel_t>(channel_ % 8);
 | 
			
		||||
 | 
			
		||||
  esp_err_t timer_init_result =
 | 
			
		||||
      configure_timer_frequency(speed_mode, timer_num, chan_num, this->channel_, this->bit_depth_, this->frequency_);
 | 
			
		||||
 | 
			
		||||
  if (timer_init_result != ESP_OK) {
 | 
			
		||||
    ESP_LOGE(TAG, "Frequency %f can't be achieved with computed bit depth %u", this->frequency_, this->bit_depth_);
 | 
			
		||||
    this->status_set_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  this->status_clear_error();
 | 
			
		||||
  ledc_timer_config_t timer_conf{};
 | 
			
		||||
  timer_conf.speed_mode = speed_mode;
 | 
			
		||||
  timer_conf.duty_resolution = static_cast<ledc_timer_bit_t>(bit_depth_);
 | 
			
		||||
  timer_conf.timer_num = timer_num;
 | 
			
		||||
  timer_conf.freq_hz = (uint32_t) frequency_;
 | 
			
		||||
  timer_conf.clk_cfg = LEDC_AUTO_CLK;
 | 
			
		||||
  ledc_timer_config(&timer_conf);
 | 
			
		||||
#endif
 | 
			
		||||
  // re-apply duty
 | 
			
		||||
  this->write_state(this->duty_);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,6 @@ class MDNSComponent : public Component {
 | 
			
		||||
 | 
			
		||||
  void add_extra_service(MDNSService service) { services_extra_.push_back(std::move(service)); }
 | 
			
		||||
 | 
			
		||||
  void on_shutdown() override;
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  std::vector<MDNSService> services_extra_{};
 | 
			
		||||
  std::vector<MDNSService> services_{};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								esphome/components/mdns/mdns_esp32_arduino.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								esphome/components/mdns/mdns_esp32_arduino.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#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,11 +1,10 @@
 | 
			
		||||
#if defined(USE_ESP8266) && defined(USE_ARDUINO)
 | 
			
		||||
 | 
			
		||||
#include <ESP8266mDNS.h>
 | 
			
		||||
#include "mdns_component.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "esphome/components/network/ip_address.h"
 | 
			
		||||
#include "esphome/components/network/util.h"
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "mdns_component.h"
 | 
			
		||||
#include <ESP8266mDNS.h>
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mdns {
 | 
			
		||||
@@ -38,11 +37,6 @@ void MDNSComponent::setup() {
 | 
			
		||||
 | 
			
		||||
void MDNSComponent::loop() { MDNS.update(); }
 | 
			
		||||
 | 
			
		||||
void MDNSComponent::on_shutdown() {
 | 
			
		||||
  MDNS.close();
 | 
			
		||||
  delay(10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace mdns
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,9 @@
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
#ifdef USE_ESP_IDF
 | 
			
		||||
 | 
			
		||||
#include "mdns_component.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include <mdns.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "esphome/core/hal.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
#include "mdns_component.h"
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace mdns {
 | 
			
		||||
@@ -48,12 +47,7 @@ void MDNSComponent::setup() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MDNSComponent::on_shutdown() {
 | 
			
		||||
  mdns_free();
 | 
			
		||||
  delay(40);  // Allow the mdns packets announcing service removal to be sent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace mdns
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
#endif  // USE_ESP32
 | 
			
		||||
#endif
 | 
			
		||||
@@ -38,11 +38,6 @@ void MDNSComponent::setup() {
 | 
			
		||||
 | 
			
		||||
void MDNSComponent::loop() { MDNS.update(); }
 | 
			
		||||
 | 
			
		||||
void MDNSComponent::on_shutdown() {
 | 
			
		||||
  MDNS.close();
 | 
			
		||||
  delay(40);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace mdns
 | 
			
		||||
}  // namespace esphome
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ void MICS4514Component::setup() {
 | 
			
		||||
  uint8_t power_mode;
 | 
			
		||||
  this->read_register(POWER_MODE_REGISTER, &power_mode, 1);
 | 
			
		||||
  if (power_mode == 0x00) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "Waking up MICS 4514, sensors will have data after 3 minutes...");
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "Waking up MICS 4514");
 | 
			
		||||
    power_mode = 0x01;
 | 
			
		||||
    this->write_register(POWER_MODE_REGISTER, &power_mode, 1);
 | 
			
		||||
    delay(100);  // NOLINT
 | 
			
		||||
 
 | 
			
		||||
@@ -84,18 +84,18 @@ ClimateTraits AirConditioner::traits() {
 | 
			
		||||
  traits.set_supported_custom_presets(this->supported_custom_presets_);
 | 
			
		||||
  traits.set_supported_custom_fan_modes(this->supported_custom_fan_modes_);
 | 
			
		||||
  /* + MINIMAL SET OF CAPABILITIES */
 | 
			
		||||
  traits.add_supported_mode(ClimateMode::CLIMATE_MODE_OFF);
 | 
			
		||||
  traits.add_supported_mode(ClimateMode::CLIMATE_MODE_FAN_ONLY);
 | 
			
		||||
  traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_AUTO);
 | 
			
		||||
  traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_LOW);
 | 
			
		||||
  traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_MEDIUM);
 | 
			
		||||
  traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_HIGH);
 | 
			
		||||
  traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_OFF);
 | 
			
		||||
  traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_VERTICAL);
 | 
			
		||||
  traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_NONE);
 | 
			
		||||
  traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_SLEEP);
 | 
			
		||||
  if (this->base_.getAutoconfStatus() == dudanov::midea::AUTOCONF_OK)
 | 
			
		||||
    Converters::to_climate_traits(traits, this->base_.getCapabilities());
 | 
			
		||||
  if (!traits.get_supported_modes().empty())
 | 
			
		||||
    traits.add_supported_mode(ClimateMode::CLIMATE_MODE_OFF);
 | 
			
		||||
  if (!traits.get_supported_swing_modes().empty())
 | 
			
		||||
    traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_OFF);
 | 
			
		||||
  if (!traits.get_supported_presets().empty())
 | 
			
		||||
    traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_NONE);
 | 
			
		||||
  return traits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,6 @@ bool MopekaProCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
 | 
			
		||||
  // Now parse the data - See Datasheet for definition
 | 
			
		||||
 | 
			
		||||
  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) {
 | 
			
		||||
    ESP_LOGE(TAG, "Unsupported Sensor Type (0x%X)", manu_data.data[0]);
 | 
			
		||||
    return false;
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ enum SensorType {
 | 
			
		||||
  STANDARD_BOTTOM_UP = 0x03,
 | 
			
		||||
  TOP_DOWN_AIR_ABOVE = 0x04,
 | 
			
		||||
  BOTTOM_UP_WATER = 0x05,
 | 
			
		||||
  LIPPERT_BOTTOM_UP = 0x06,
 | 
			
		||||
  PLUS_BOTTOM_UP = 0x08
 | 
			
		||||
  // all other values are reserved
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -66,42 +66,18 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
 | 
			
		||||
  // temperature units are always coerced to Celsius internally
 | 
			
		||||
  root[MQTT_TEMPERATURE_UNIT] = "C";
 | 
			
		||||
 | 
			
		||||
  if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
 | 
			
		||||
    // preset_mode_command_topic
 | 
			
		||||
    root[MQTT_PRESET_MODE_COMMAND_TOPIC] = this->get_preset_command_topic();
 | 
			
		||||
    // preset_mode_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.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();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (traits.get_supports_action()) {
 | 
			
		||||
    // action_topic
 | 
			
		||||
    root[MQTT_ACTION_TOPIC] = this->get_action_state_topic();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (traits.get_supports_fan_modes()) {
 | 
			
		||||
  if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
 | 
			
		||||
    // fan_mode_command_topic
 | 
			
		||||
    root[MQTT_FAN_MODE_COMMAND_TOPIC] = this->get_fan_mode_command_topic();
 | 
			
		||||
    // fan_mode_state_topic
 | 
			
		||||
@@ -126,8 +102,6 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
 | 
			
		||||
      fan_modes.add("focus");
 | 
			
		||||
    if (traits.supports_fan_mode(CLIMATE_FAN_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())
 | 
			
		||||
      fan_modes.add(fan_mode);
 | 
			
		||||
  }
 | 
			
		||||
@@ -220,14 +194,6 @@ 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()) {
 | 
			
		||||
    this->subscribe(this->get_fan_mode_command_topic(), [this](const std::string &topic, const std::string &payload) {
 | 
			
		||||
      auto call = this->device_->make_call();
 | 
			
		||||
@@ -305,42 +271,6 @@ bool MQTTClimateComponent::publish_state_() {
 | 
			
		||||
    if (!this->publish(this->get_away_state_topic(), payload))
 | 
			
		||||
      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()) {
 | 
			
		||||
    const char *payload = "unknown";
 | 
			
		||||
    switch (this->device_->action) {
 | 
			
		||||
@@ -398,9 +328,6 @@ bool MQTTClimateComponent::publish_state_() {
 | 
			
		||||
        case CLIMATE_FAN_DIFFUSE:
 | 
			
		||||
          payload = "diffuse";
 | 
			
		||||
          break;
 | 
			
		||||
        case CLIMATE_FAN_QUIET:
 | 
			
		||||
          payload = "quiet";
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (this->device_->custom_fan_mode.has_value())
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,6 @@ class MQTTClimateComponent : public mqtt::MQTTComponent {
 | 
			
		||||
  MQTT_COMPONENT_CUSTOM_TOPIC(fan_mode, command)
 | 
			
		||||
  MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, state)
 | 
			
		||||
  MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, command)
 | 
			
		||||
  MQTT_COMPONENT_CUSTOM_TOPIC(preset, state)
 | 
			
		||||
  MQTT_COMPONENT_CUSTOM_TOPIC(preset, command)
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  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"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        for package_name, package_config in reversed(packages.items()):
 | 
			
		||||
        for package_name, package_config in packages.items():
 | 
			
		||||
            with cv.prepend_path(package_name):
 | 
			
		||||
                recursive_package = package_config
 | 
			
		||||
                if CONF_URL in package_config:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
#include "pid_autotuner.h"
 | 
			
		||||
#include "esphome/core/log.h"
 | 
			
		||||
 | 
			
		||||
#ifndef M_PI
 | 
			
		||||
#define M_PI 3.1415926535897932384626433
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace esphome {
 | 
			
		||||
namespace pid {
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +73,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!std::isnan(this->setpoint_) && this->setpoint_ != setpoint) {
 | 
			
		||||
    ESP_LOGW(TAG, "%s: Setpoint changed during autotune! The result will not be accurate!", this->id_.c_str());
 | 
			
		||||
    ESP_LOGW(TAG, "Setpoint changed during autotune! The result will not be accurate!");
 | 
			
		||||
  }
 | 
			
		||||
  this->setpoint_ = setpoint;
 | 
			
		||||
 | 
			
		||||
@@ -91,7 +87,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
			
		||||
 | 
			
		||||
  if (!this->frequency_detector_.has_enough_data() || !this->amplitude_detector_.has_enough_data()) {
 | 
			
		||||
    // not enough data for calculation yet
 | 
			
		||||
    ESP_LOGV(TAG, "%s:   Not enough data yet for autotuner", this->id_.c_str());
 | 
			
		||||
    ESP_LOGV(TAG, "  Not enough data yet for aututuner");
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -101,13 +97,12 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
			
		||||
    // The frequency/amplitude is not fully accurate yet, try to wait
 | 
			
		||||
    // until the fault clears, or terminate after a while anyway
 | 
			
		||||
    if (zc_symmetrical) {
 | 
			
		||||
      ESP_LOGVV(TAG, "%s:   ZC is not symmetrical", this->id_.c_str());
 | 
			
		||||
      ESP_LOGVV(TAG, "  ZC is not symmetrical");
 | 
			
		||||
    }
 | 
			
		||||
    if (amplitude_convergent) {
 | 
			
		||||
      ESP_LOGVV(TAG, "%s:   Amplitude is not convergent", this->id_.c_str());
 | 
			
		||||
      ESP_LOGVV(TAG, "  Amplitude is not convergent");
 | 
			
		||||
    }
 | 
			
		||||
    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_);
 | 
			
		||||
 | 
			
		||||
    if (this->enough_data_phase_ == 0) {
 | 
			
		||||
@@ -121,7 +116,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ESP_LOGI(TAG, "%s: PID Autotune finished!", this->id_.c_str());
 | 
			
		||||
  ESP_LOGI(TAG, "PID Autotune finished!");
 | 
			
		||||
 | 
			
		||||
  float osc_ampl = this->amplitude_detector_.get_mean_oscillation_amplitude();
 | 
			
		||||
  float d = (this->relay_function_.output_positive - this->relay_function_.output_negative) / 2.0f;
 | 
			
		||||
@@ -136,12 +131,12 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
void PIDAutotuner::dump_config() {
 | 
			
		||||
  ESP_LOGI(TAG, "PID Autotune:");
 | 
			
		||||
  if (this->state_ == AUTOTUNE_SUCCEEDED) {
 | 
			
		||||
    ESP_LOGI(TAG, "%s: PID Autotune:", this->id_.c_str());
 | 
			
		||||
    ESP_LOGI(TAG, "  State: Succeeded!");
 | 
			
		||||
    bool has_issue = false;
 | 
			
		||||
    if (!this->amplitude_detector_.is_amplitude_convergent()) {
 | 
			
		||||
      ESP_LOGW(TAG, "  Could not reliably determine oscillation amplitude, PID parameters may be inaccurate!");
 | 
			
		||||
      ESP_LOGW(TAG, "  Could not reliable determine oscillation amplitude, PID parameters may be inaccurate!");
 | 
			
		||||
      ESP_LOGW(TAG, "    Please make sure you eliminate all outside influences on the measured temperature.");
 | 
			
		||||
      has_issue = true;
 | 
			
		||||
    }
 | 
			
		||||
@@ -178,12 +173,10 @@ void PIDAutotuner::dump_config() {
 | 
			
		||||
    print_rule_("Pessen Integral PID", 0.7f, 1.75f, 0.105f);
 | 
			
		||||
    print_rule_("Some Overshoot PID", 0.333f, 0.667f, 0.111f);
 | 
			
		||||
    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) {
 | 
			
		||||
    ESP_LOGD(TAG, "%s: PID Autotune:", this->id_.c_str());
 | 
			
		||||
    ESP_LOGD(TAG, "  Autotune is still running!");
 | 
			
		||||
    ESP_LOGI(TAG, "  Autotune is still running!");
 | 
			
		||||
    ESP_LOGD(TAG, "  Status: Trying to reach %.2f °C", setpoint_ - relay_function_.current_target_error());
 | 
			
		||||
    ESP_LOGD(TAG, "  Stats so far:");
 | 
			
		||||
    ESP_LOGD(TAG, "    Phases: %u", relay_function_.phase_count);
 | 
			
		||||
@@ -228,6 +221,7 @@ float PIDAutotuner::RelayFunction::update(float error) {
 | 
			
		||||
  float output = state == RELAY_FUNCTION_POSITIVE ? output_positive : output_negative;
 | 
			
		||||
  if (change) {
 | 
			
		||||
    this->phase_count++;
 | 
			
		||||
    ESP_LOGV(TAG, "Autotune: Turning output to %.1f%%", output * 100);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return output;
 | 
			
		||||
@@ -251,8 +245,10 @@ void PIDAutotuner::OscillationFrequencyDetector::update(uint32_t now, float erro
 | 
			
		||||
 | 
			
		||||
  if (had_crossing) {
 | 
			
		||||
    // Had crossing above hysteresis threshold, record
 | 
			
		||||
    ESP_LOGV(TAG, "Autotune: Detected Zero-Cross at %u", now);
 | 
			
		||||
    if (this->last_zerocross != 0) {
 | 
			
		||||
      uint32_t dt = now - this->last_zerocross;
 | 
			
		||||
      ESP_LOGV(TAG, "  dt: %u", dt);
 | 
			
		||||
      this->zerocrossing_intervals.push_back(dt);
 | 
			
		||||
    }
 | 
			
		||||
    this->last_zerocross = now;
 | 
			
		||||
@@ -301,11 +297,13 @@ void PIDAutotuner::OscillationAmplitudeDetector::update(float error,
 | 
			
		||||
      // The positive error peak must have been in previous segment (180° shifted)
 | 
			
		||||
      // record 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) {
 | 
			
		||||
      // Transitioned from negative error to positive error.
 | 
			
		||||
      // The negative error peak must have been in previous segment (180° shifted)
 | 
			
		||||
      // record phase_min
 | 
			
		||||
      this->phase_mins.push_back(phase_min);
 | 
			
		||||
      ESP_LOGV(TAG, "Autotune: Phase Min: %f", phase_min);
 | 
			
		||||
    }
 | 
			
		||||
    // reset phase values for next phase
 | 
			
		||||
    this->phase_min = error;
 | 
			
		||||
 
 | 
			
		||||
@@ -31,8 +31,6 @@ class PIDAutotuner {
 | 
			
		||||
 | 
			
		||||
  void dump_config();
 | 
			
		||||
 | 
			
		||||
  void set_autotuner_id(std::string id) { this->id_ = std::move(id); }
 | 
			
		||||
 | 
			
		||||
  void set_noiseband(float noiseband) {
 | 
			
		||||
    relay_function_.noiseband = noiseband;
 | 
			
		||||
    // ZC detector uses 1/4 the noiseband of relay function (noise suppression)
 | 
			
		||||
@@ -108,7 +106,6 @@ class PIDAutotuner {
 | 
			
		||||
  } state_ = AUTOTUNE_RUNNING;
 | 
			
		||||
  float ku_;
 | 
			
		||||
  float pu_;
 | 
			
		||||
  std::string id_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace pid
 | 
			
		||||
 
 | 
			
		||||
@@ -130,6 +130,9 @@ void PIDClimate::update_pid_() {
 | 
			
		||||
        // keep autotuner instance so that subsequent dump_configs will print the long result message.
 | 
			
		||||
      } else {
 | 
			
		||||
        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!");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -148,24 +151,10 @@ void PIDClimate::start_autotune(std::unique_ptr<PIDAutotuner> &&autotune) {
 | 
			
		||||
  float min_value = this->supports_cool_() ? -1.0f : 0.0f;
 | 
			
		||||
  float max_value = this->supports_heat_() ? 1.0f : 0.0f;
 | 
			
		||||
  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]() {
 | 
			
		||||
    if (this->autotuner_ != nullptr && !this->autotuner_->is_finished())
 | 
			
		||||
      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(); }
 | 
			
		||||
 
 | 
			
		||||
@@ -237,107 +237,6 @@ async def build_dumpers(config):
 | 
			
		||||
    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
 | 
			
		||||
(
 | 
			
		||||
    CoolixData,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,108 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
#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,27 +202,5 @@ bool SCD30Component::is_data_ready_() {
 | 
			
		||||
  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 esphome
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,6 @@ class SCD30Component : public Component, public sensirion_common::SensirionI2CDe
 | 
			
		||||
  }
 | 
			
		||||
  void set_temperature_offset(float offset) { temperature_offset_ = offset; }
 | 
			
		||||
  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 update();
 | 
			
		||||
@@ -35,7 +33,6 @@ class SCD30Component : public Component, public sensirion_common::SensirionI2CDe
 | 
			
		||||
    COMMUNICATION_FAILED,
 | 
			
		||||
    FIRMWARE_IDENTIFICATION_FAILED,
 | 
			
		||||
    MEASUREMENT_INIT_FAILED,
 | 
			
		||||
    FORCE_RECALIBRATION_FAILED,
 | 
			
		||||
    UNKNOWN
 | 
			
		||||
  } error_code_{UNKNOWN};
 | 
			
		||||
  bool enable_asc_{true};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
from esphome import automation, core
 | 
			
		||||
from esphome import core
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.components import i2c, sensor
 | 
			
		||||
@@ -9,7 +9,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_TEMPERATURE,
 | 
			
		||||
    CONF_CO2,
 | 
			
		||||
    CONF_UPDATE_INTERVAL,
 | 
			
		||||
    CONF_VALUE,
 | 
			
		||||
    DEVICE_CLASS_HUMIDITY,
 | 
			
		||||
    DEVICE_CLASS_TEMPERATURE,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
@@ -27,11 +26,6 @@ SCD30Component = scd30_ns.class_(
 | 
			
		||||
    "SCD30Component", cg.Component, sensirion_common.SensirionI2CDevice
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Actions
 | 
			
		||||
ForceRecalibrationWithReference = scd30_ns.class_(
 | 
			
		||||
    "ForceRecalibrationWithReference", automation.Action
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration"
 | 
			
		||||
CONF_ALTITUDE_COMPENSATION = "altitude_compensation"
 | 
			
		||||
CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation"
 | 
			
		||||
@@ -112,26 +106,3 @@ async def to_code(config):
 | 
			
		||||
    if CONF_TEMPERATURE in config:
 | 
			
		||||
        sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
 | 
			
		||||
        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,7 +6,8 @@ from esphome.const import (
 | 
			
		||||
    CONF_STORE_BASELINE,
 | 
			
		||||
    CONF_TEMPERATURE_SOURCE,
 | 
			
		||||
    ICON_RADIATOR,
 | 
			
		||||
    DEVICE_CLASS_AQI,
 | 
			
		||||
    DEVICE_CLASS_NITROUS_OXIDE,
 | 
			
		||||
    DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
    STATE_CLASS_MEASUREMENT,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -66,13 +67,13 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_VOC): sensor.sensor_schema(
 | 
			
		||||
                icon=ICON_RADIATOR,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_AQI,
 | 
			
		||||
                device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(GAS_SENSOR),
 | 
			
		||||
            cv.Optional(CONF_NOX): sensor.sensor_schema(
 | 
			
		||||
                icon=ICON_RADIATOR,
 | 
			
		||||
                accuracy_decimals=0,
 | 
			
		||||
                device_class=DEVICE_CLASS_AQI,
 | 
			
		||||
                device_class=DEVICE_CLASS_NITROUS_OXIDE,
 | 
			
		||||
                state_class=STATE_CLASS_MEASUREMENT,
 | 
			
		||||
            ).extend(GAS_SENSOR),
 | 
			
		||||
            cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ from esphome.const import (
 | 
			
		||||
    UNIT_WATT,
 | 
			
		||||
    DEVICE_CLASS_POWER,
 | 
			
		||||
    DEVICE_CLASS_VOLTAGE,
 | 
			
		||||
    DEVICE_CLASS_CURRENT,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import HexInt, CORE
 | 
			
		||||
 | 
			
		||||
@@ -170,7 +169,7 @@ CONFIG_SCHEMA = (
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_CURRENT): sensor.sensor_schema(
 | 
			
		||||
                unit_of_measurement=UNIT_AMPERE,
 | 
			
		||||
                device_class=DEVICE_CLASS_CURRENT,
 | 
			
		||||
                device_class=DEVICE_CLASS_POWER,
 | 
			
		||||
                accuracy_decimals=2,
 | 
			
		||||
            ),
 | 
			
		||||
            # Change the default gamma_correct setting.
 | 
			
		||||
 
 | 
			
		||||
@@ -156,7 +156,6 @@ void Sim800LComponent::parse_cmd_(std::string message) {
 | 
			
		||||
    case STATE_SEND_USSD1:
 | 
			
		||||
      this->send_cmd_("AT+CUSD=1, \"" + this->ussd_ + "\"");
 | 
			
		||||
      this->state_ = STATE_SEND_USSD2;
 | 
			
		||||
      this->expect_ack_ = true;
 | 
			
		||||
      break;
 | 
			
		||||
    case STATE_SEND_USSD2:
 | 
			
		||||
      ESP_LOGD(TAG, "SendUssd2: '%s'", message.c_str());
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@ from esphome.const import (
 | 
			
		||||
    CONF_FAN_MODE_MIDDLE_ACTION,
 | 
			
		||||
    CONF_FAN_MODE_FOCUS_ACTION,
 | 
			
		||||
    CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
			
		||||
    CONF_FAN_MODE_QUIET_ACTION,
 | 
			
		||||
    CONF_FAN_ONLY_ACTION,
 | 
			
		||||
    CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER,
 | 
			
		||||
    CONF_FAN_ONLY_COOLING,
 | 
			
		||||
@@ -274,7 +273,6 @@ def validate_thermostat(config):
 | 
			
		||||
            CONF_FAN_MODE_MIDDLE_ACTION,
 | 
			
		||||
            CONF_FAN_MODE_FOCUS_ACTION,
 | 
			
		||||
            CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
			
		||||
            CONF_FAN_MODE_QUIET_ACTION,
 | 
			
		||||
        ],
 | 
			
		||||
    }
 | 
			
		||||
    for req_config_item, config_triggers in requirements.items():
 | 
			
		||||
@@ -415,7 +413,6 @@ def validate_thermostat(config):
 | 
			
		||||
            "MIDDLE": [CONF_FAN_MODE_MIDDLE_ACTION],
 | 
			
		||||
            "FOCUS": [CONF_FAN_MODE_FOCUS_ACTION],
 | 
			
		||||
            "DIFFUSE": [CONF_FAN_MODE_DIFFUSE_ACTION],
 | 
			
		||||
            "QUIET": [CONF_FAN_MODE_QUIET_ACTION],
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for preset_config in config[CONF_PRESET]:
 | 
			
		||||
@@ -503,13 +500,12 @@ def validate_thermostat(config):
 | 
			
		||||
            CONF_FAN_MODE_MIDDLE_ACTION,
 | 
			
		||||
            CONF_FAN_MODE_FOCUS_ACTION,
 | 
			
		||||
            CONF_FAN_MODE_DIFFUSE_ACTION,
 | 
			
		||||
            CONF_FAN_MODE_QUIET_ACTION,
 | 
			
		||||
        ]
 | 
			
		||||
        for config_req_action in requirements:
 | 
			
		||||
            if config_req_action in config:
 | 
			
		||||
                return config
 | 
			
		||||
        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}, {CONF_FAN_MODE_QUIET_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} must be defined to use {CONF_MIN_FAN_MODE_SWITCHING_TIME}"
 | 
			
		||||
        )
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
@@ -567,9 +563,6 @@ CONFIG_SCHEMA = cv.All(
 | 
			
		||||
            cv.Optional(CONF_FAN_MODE_DIFFUSE_ACTION): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_FAN_MODE_QUIET_ACTION): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
            cv.Optional(CONF_SWING_BOTH_ACTION): automation.validate_automation(
 | 
			
		||||
                single=True
 | 
			
		||||
            ),
 | 
			
		||||
@@ -843,11 +836,6 @@ async def to_code(config):
 | 
			
		||||
            var.get_fan_mode_diffuse_trigger(), [], config[CONF_FAN_MODE_DIFFUSE_ACTION]
 | 
			
		||||
        )
 | 
			
		||||
        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:
 | 
			
		||||
        await automation.build_automation(
 | 
			
		||||
            var.get_swing_mode_both_trigger(), [], config[CONF_SWING_BOTH_ACTION]
 | 
			
		||||
 
 | 
			
		||||
@@ -247,8 +247,6 @@ climate::ClimateTraits ThermostatClimate::traits() {
 | 
			
		||||
    traits.add_supported_fan_mode(climate::CLIMATE_FAN_FOCUS);
 | 
			
		||||
  if (supports_fan_mode_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_)
 | 
			
		||||
    traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
 | 
			
		||||
@@ -596,10 +594,6 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bo
 | 
			
		||||
        trig = this->fan_mode_diffuse_trigger_;
 | 
			
		||||
        ESP_LOGVV(TAG, "Switching to FAN_DIFFUSE mode");
 | 
			
		||||
        break;
 | 
			
		||||
      case climate::CLIMATE_FAN_QUIET:
 | 
			
		||||
        trig = this->fan_mode_quiet_trigger_;
 | 
			
		||||
        ESP_LOGVV(TAG, "Switching to FAN_QUIET mode");
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        // we cannot report an invalid mode back to HA (even if it asked for one)
 | 
			
		||||
        //  and must assume some valid value
 | 
			
		||||
@@ -1099,7 +1093,6 @@ ThermostatClimate::ThermostatClimate()
 | 
			
		||||
      fan_mode_middle_trigger_(new Trigger<>()),
 | 
			
		||||
      fan_mode_focus_trigger_(new Trigger<>()),
 | 
			
		||||
      fan_mode_diffuse_trigger_(new Trigger<>()),
 | 
			
		||||
      fan_mode_quiet_trigger_(new Trigger<>()),
 | 
			
		||||
      swing_mode_both_trigger_(new Trigger<>()),
 | 
			
		||||
      swing_mode_off_trigger_(new Trigger<>()),
 | 
			
		||||
      swing_mode_horizontal_trigger_(new Trigger<>()),
 | 
			
		||||
@@ -1215,9 +1208,6 @@ void ThermostatClimate::set_supports_fan_mode_focus(bool supports_fan_mode_focus
 | 
			
		||||
void ThermostatClimate::set_supports_fan_mode_diffuse(bool 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) {
 | 
			
		||||
  this->supports_swing_mode_both_ = supports_swing_mode_both;
 | 
			
		||||
}
 | 
			
		||||
@@ -1260,7 +1250,6 @@ 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_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_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_off_trigger() const { return this->swing_mode_off_trigger_; }
 | 
			
		||||
Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; }
 | 
			
		||||
@@ -1305,8 +1294,7 @@ void ThermostatClimate::dump_config() {
 | 
			
		||||
  }
 | 
			
		||||
  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_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_ ||
 | 
			
		||||
      this->supports_fan_mode_quiet_) {
 | 
			
		||||
      this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_) {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "  Minimum Fan Mode Switching Time: %us",
 | 
			
		||||
                  this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000);
 | 
			
		||||
  }
 | 
			
		||||
@@ -1335,7 +1323,6 @@ void ThermostatClimate::dump_config() {
 | 
			
		||||
  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 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 OFF: %s", YESNO(this->supports_swing_mode_off_));
 | 
			
		||||
  ESP_LOGCONFIG(TAG, "  Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_));
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,6 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
			
		||||
  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_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_horizontal(bool supports_swing_mode_horizontal);
 | 
			
		||||
  void set_supports_swing_mode_off(bool supports_swing_mode_off);
 | 
			
		||||
@@ -133,7 +132,6 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
			
		||||
  Trigger<> *get_fan_mode_middle_trigger() const;
 | 
			
		||||
  Trigger<> *get_fan_mode_focus_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_horizontal_trigger() const;
 | 
			
		||||
  Trigger<> *get_swing_mode_off_trigger() const;
 | 
			
		||||
@@ -279,7 +277,6 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
			
		||||
  bool supports_fan_mode_middle_{false};
 | 
			
		||||
  bool supports_fan_mode_focus_{false};
 | 
			
		||||
  bool supports_fan_mode_diffuse_{false};
 | 
			
		||||
  bool supports_fan_mode_quiet_{false};
 | 
			
		||||
 | 
			
		||||
  /// Whether the controller supports various swing modes.
 | 
			
		||||
  ///
 | 
			
		||||
@@ -375,9 +372,6 @@ class ThermostatClimate : public climate::Climate, public Component {
 | 
			
		||||
  /// The trigger to call when the controller should switch the fan to "diffuse" position.
 | 
			
		||||
  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".
 | 
			
		||||
  Trigger<> *swing_mode_both_trigger_{nullptr};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
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)
 | 
			
		||||
@@ -1,296 +0,0 @@
 | 
			
		||||
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))
 | 
			
		||||
@@ -1,142 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,115 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,568 +0,0 @@
 | 
			
		||||
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))
 | 
			
		||||
@@ -1,208 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,151 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,124 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
#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,7 +14,6 @@ from esphome import core, yaml_util, loader
 | 
			
		||||
import esphome.core.config as core_config
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ESPHOME,
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_PLATFORM,
 | 
			
		||||
    CONF_PACKAGES,
 | 
			
		||||
    CONF_SUBSTITUTIONS,
 | 
			
		||||
@@ -25,7 +24,6 @@ from esphome.core import CORE, EsphomeError
 | 
			
		||||
from esphome.helpers import indent
 | 
			
		||||
from esphome.util import safe_print, OrderedDict
 | 
			
		||||
 | 
			
		||||
from esphome.config_helpers import Extend
 | 
			
		||||
from esphome.loader import get_component, get_platform, ComponentManifest
 | 
			
		||||
from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue
 | 
			
		||||
from esphome.voluptuous_schema import ExtraKeysInvalid
 | 
			
		||||
@@ -336,13 +334,6 @@ class LoadValidationStep(ConfigValidationStep):
 | 
			
		||||
                continue
 | 
			
		||||
            p_name = p_config.get("platform")
 | 
			
		||||
            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)
 | 
			
		||||
                continue
 | 
			
		||||
            # Remove temp output path and construct new one
 | 
			
		||||
@@ -663,7 +654,7 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
			
		||||
    loader.clear_component_meta_finders()
 | 
			
		||||
    loader.install_custom_components_meta_finder()
 | 
			
		||||
 | 
			
		||||
    # 0. Load packages
 | 
			
		||||
    # Load packages
 | 
			
		||||
    if CONF_PACKAGES in config:
 | 
			
		||||
        from esphome.components.packages import do_packages_pass
 | 
			
		||||
 | 
			
		||||
@@ -677,7 +668,7 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
			
		||||
 | 
			
		||||
    CORE.raw_config = config
 | 
			
		||||
 | 
			
		||||
    # 1. Load substitutions
 | 
			
		||||
    # Load substitutions
 | 
			
		||||
    if CONF_SUBSTITUTIONS in config:
 | 
			
		||||
        from esphome.components import substitutions
 | 
			
		||||
 | 
			
		||||
@@ -695,13 +686,7 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
			
		||||
 | 
			
		||||
    CORE.raw_config = config
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
    # Load external_components
 | 
			
		||||
    if CONF_EXTERNAL_COMPONENTS in config:
 | 
			
		||||
        from esphome.components.external_components import do_external_components_pass
 | 
			
		||||
 | 
			
		||||
@@ -728,7 +713,7 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
			
		||||
        )
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    # 2. Load partial core config
 | 
			
		||||
    # Load partial core config
 | 
			
		||||
    result[CONF_ESPHOME] = config[CONF_ESPHOME]
 | 
			
		||||
    result.add_output_path([CONF_ESPHOME], CONF_ESPHOME)
 | 
			
		||||
    try:
 | 
			
		||||
@@ -749,6 +734,12 @@ def validate_config(config, command_line_substitutions) -> Config:
 | 
			
		||||
        # do not try to validate further as we don't know what the target is
 | 
			
		||||
        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():
 | 
			
		||||
        result.add_validation_step(LoadValidationStep(domain, conf))
 | 
			
		||||
    result.add_validation_step(IDPassValidationStep())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,10 @@
 | 
			
		||||
import json
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from esphome.const import CONF_ID
 | 
			
		||||
from esphome.core import CORE
 | 
			
		||||
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:
 | 
			
		||||
    if CORE.vscode and (
 | 
			
		||||
        not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)
 | 
			
		||||
@@ -53,25 +36,7 @@ def merge_config(full_old, full_new):
 | 
			
		||||
        if isinstance(new, list):
 | 
			
		||||
            if not isinstance(old, list):
 | 
			
		||||
                return 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
 | 
			
		||||
            return old + new
 | 
			
		||||
        if new is None:
 | 
			
		||||
            return old
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ import voluptuous as vol
 | 
			
		||||
 | 
			
		||||
from esphome import core
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.config_helpers import Extend
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    ALLOWED_NAME_CHARS,
 | 
			
		||||
    CONF_AVAILABILITY,
 | 
			
		||||
@@ -491,8 +490,6 @@ def declare_id(type):
 | 
			
		||||
        if value is None:
 | 
			
		||||
            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 validator
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
"""Constants used by esphome."""
 | 
			
		||||
 | 
			
		||||
__version__ = "2023.2.0b5"
 | 
			
		||||
__version__ = "2023.2.0-dev"
 | 
			
		||||
 | 
			
		||||
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"}
 | 
			
		||||
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
 | 
			
		||||
SECRETS_FILES = ("secrets.yaml", "secrets.yml")
 | 
			
		||||
SECRETS_FILES = {"secrets.yaml", "secrets.yml"}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONF_ABOVE = "above"
 | 
			
		||||
@@ -141,7 +141,6 @@ CONF_CURRENT = "current"
 | 
			
		||||
CONF_CURRENT_OPERATION = "current_operation"
 | 
			
		||||
CONF_CURRENT_RESISTOR = "current_resistor"
 | 
			
		||||
CONF_CURRENT_TEMPERATURE_STATE_TOPIC = "current_temperature_state_topic"
 | 
			
		||||
CONF_CUSTOM = "custom"
 | 
			
		||||
CONF_CUSTOM_FAN_MODE = "custom_fan_mode"
 | 
			
		||||
CONF_CUSTOM_FAN_MODES = "custom_fan_modes"
 | 
			
		||||
CONF_CUSTOM_PRESET = "custom_preset"
 | 
			
		||||
@@ -168,7 +167,6 @@ CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length"
 | 
			
		||||
CONF_DELAY = "delay"
 | 
			
		||||
CONF_DELIMITER = "delimiter"
 | 
			
		||||
CONF_DELTA = "delta"
 | 
			
		||||
CONF_DEST = "dest"
 | 
			
		||||
CONF_DEVICE = "device"
 | 
			
		||||
CONF_DEVICE_CLASS = "device_class"
 | 
			
		||||
CONF_DEVICE_FACTOR = "device_factor"
 | 
			
		||||
@@ -237,7 +235,6 @@ CONF_FAN_MODE_MEDIUM_ACTION = "fan_mode_medium_action"
 | 
			
		||||
CONF_FAN_MODE_MIDDLE_ACTION = "fan_mode_middle_action"
 | 
			
		||||
CONF_FAN_MODE_OFF_ACTION = "fan_mode_off_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_ONLY_ACTION = "fan_only_action"
 | 
			
		||||
CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER = "fan_only_action_uses_fan_mode_timer"
 | 
			
		||||
@@ -551,10 +548,8 @@ CONF_POWER_SAVE_MODE = "power_save_mode"
 | 
			
		||||
CONF_POWER_SUPPLY = "power_supply"
 | 
			
		||||
CONF_PRESET = "preset"
 | 
			
		||||
CONF_PRESET_BOOST = "preset_boost"
 | 
			
		||||
CONF_PRESET_COMMAND_TOPIC = "preset_command_topic"
 | 
			
		||||
CONF_PRESET_ECO = "preset_eco"
 | 
			
		||||
CONF_PRESET_SLEEP = "preset_sleep"
 | 
			
		||||
CONF_PRESET_STATE_TOPIC = "preset_state_topic"
 | 
			
		||||
CONF_PRESSURE = "pressure"
 | 
			
		||||
CONF_PRIORITY = "priority"
 | 
			
		||||
CONF_PROJECT = "project"
 | 
			
		||||
@@ -867,7 +862,6 @@ UNIT_AMPERE = "A"
 | 
			
		||||
UNIT_BECQUEREL_PER_CUBIC_METER = "Bq/m³"
 | 
			
		||||
UNIT_BYTES = "B"
 | 
			
		||||
UNIT_CELSIUS = "°C"
 | 
			
		||||
UNIT_CENTIMETER = "cm"
 | 
			
		||||
UNIT_COUNT_DECILITRE = "/dL"
 | 
			
		||||
UNIT_COUNTS_PER_CUBIC_METER = "#/m³"
 | 
			
		||||
UNIT_CUBIC_METER = "m³"
 | 
			
		||||
@@ -879,7 +873,6 @@ UNIT_EMPTY = ""
 | 
			
		||||
UNIT_G = "G"
 | 
			
		||||
UNIT_HECTOPASCAL = "hPa"
 | 
			
		||||
UNIT_HERTZ = "Hz"
 | 
			
		||||
UNIT_HOUR = "h"
 | 
			
		||||
UNIT_KELVIN = "K"
 | 
			
		||||
UNIT_KILOGRAM = "kg"
 | 
			
		||||
UNIT_KILOMETER = "km"
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,6 @@ class DashboardSettings:
 | 
			
		||||
        self.using_password = False
 | 
			
		||||
        self.on_ha_addon = False
 | 
			
		||||
        self.cookie_secret = None
 | 
			
		||||
        self.absolute_config_dir = None
 | 
			
		||||
 | 
			
		||||
    def parse_args(self, args):
 | 
			
		||||
        self.on_ha_addon = args.ha_addon
 | 
			
		||||
@@ -66,7 +65,6 @@ class DashboardSettings:
 | 
			
		||||
        if self.using_password:
 | 
			
		||||
            self.password_hash = password_hash(password)
 | 
			
		||||
        self.config_dir = args.configuration
 | 
			
		||||
        self.absolute_config_dir = Path(self.config_dir).resolve()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def relative_url(self):
 | 
			
		||||
@@ -96,10 +94,7 @@ class DashboardSettings:
 | 
			
		||||
        return hmac.compare_digest(self.password_hash, password_hash(password))
 | 
			
		||||
 | 
			
		||||
    def rel_path(self, *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
 | 
			
		||||
        return os.path.join(self.config_dir, *args)
 | 
			
		||||
 | 
			
		||||
    def list_yaml_files(self):
 | 
			
		||||
        return util.list_yaml_files([self.config_dir])
 | 
			
		||||
@@ -438,7 +433,6 @@ class ImportRequestHandler(BaseHandler):
 | 
			
		||||
        try:
 | 
			
		||||
            name = args["name"]
 | 
			
		||||
            friendly_name = args.get("friendly_name")
 | 
			
		||||
            encryption = args.get("encryption", False)
 | 
			
		||||
 | 
			
		||||
            imported_device = next(
 | 
			
		||||
                (res for res in IMPORT_RESULT.values() if res.device_name == name), None
 | 
			
		||||
@@ -458,7 +452,6 @@ class ImportRequestHandler(BaseHandler):
 | 
			
		||||
                args["project_name"],
 | 
			
		||||
                args["package_import_url"],
 | 
			
		||||
                network,
 | 
			
		||||
                encryption,
 | 
			
		||||
            )
 | 
			
		||||
        except FileExistsError:
 | 
			
		||||
            self.set_status(500)
 | 
			
		||||
@@ -545,11 +538,35 @@ class DownloadBinaryRequestHandler(BaseHandler):
 | 
			
		||||
        self.finish()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EsphomeVersionHandler(BaseHandler):
 | 
			
		||||
class ManifestRequestHandler(BaseHandler):
 | 
			
		||||
    @authenticated
 | 
			
		||||
    def get(self):
 | 
			
		||||
    @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({"version": const.__version__}))
 | 
			
		||||
        self.write(json.dumps(flash_images))
 | 
			
		||||
        self.finish()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1008,14 +1025,8 @@ class SafeLoaderIgnoreUnknown(yaml.SafeLoader):
 | 
			
		||||
    def ignore_unknown(self, node):
 | 
			
		||||
        return f"{node.tag} {node.value}"
 | 
			
		||||
 | 
			
		||||
    def construct_yaml_binary(self, node) -> str:
 | 
			
		||||
        return super().construct_yaml_binary(node).decode("ascii")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SafeLoaderIgnoreUnknown.add_constructor(None, SafeLoaderIgnoreUnknown.ignore_unknown)
 | 
			
		||||
SafeLoaderIgnoreUnknown.add_constructor(
 | 
			
		||||
    "tag:yaml.org,2002:binary", SafeLoaderIgnoreUnknown.construct_yaml_binary
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class JsonConfigRequestHandler(BaseHandler):
 | 
			
		||||
@@ -1136,6 +1147,7 @@ def make_app(debug=get_bool_env(ENV_DEV)):
 | 
			
		||||
            (f"{rel}info", InfoRequestHandler),
 | 
			
		||||
            (f"{rel}edit", EditRequestHandler),
 | 
			
		||||
            (f"{rel}download.bin", DownloadBinaryRequestHandler),
 | 
			
		||||
            (f"{rel}manifest.json", ManifestRequestHandler),
 | 
			
		||||
            (f"{rel}serial-ports", SerialPortRequestHandler),
 | 
			
		||||
            (f"{rel}ping", PingRequestHandler),
 | 
			
		||||
            (f"{rel}delete", DeleteRequestHandler),
 | 
			
		||||
@@ -1149,7 +1161,6 @@ def make_app(debug=get_bool_env(ENV_DEV)):
 | 
			
		||||
            (f"{rel}rename", EsphomeRenameHandler),
 | 
			
		||||
            (f"{rel}prometheus-sd", PrometheusServiceDiscoveryHandler),
 | 
			
		||||
            (f"{rel}boards/([a-z0-9]+)", BoardsRequestHandler),
 | 
			
		||||
            (f"{rel}version", EsphomeVersionHandler),
 | 
			
		||||
        ],
 | 
			
		||||
        **app_settings,
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
@@ -49,11 +49,6 @@ def _set_mode(value, default_mode):
 | 
			
		||||
            CONF_INPUT: 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:
 | 
			
		||||
        raise cv.Invalid(f"Unknown pin mode {mode}", [CONF_MODE])
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ import yaml
 | 
			
		||||
import yaml.constructor
 | 
			
		||||
 | 
			
		||||
from esphome import core
 | 
			
		||||
from esphome.config_helpers import read_config_file, Extend
 | 
			
		||||
from esphome.config_helpers import read_config_file
 | 
			
		||||
from esphome.core import (
 | 
			
		||||
    EsphomeError,
 | 
			
		||||
    IPAddress,
 | 
			
		||||
@@ -338,10 +338,6 @@ class ESPHomeLoader(yaml.SafeLoader):
 | 
			
		||||
        obj = self.construct_scalar(node)
 | 
			
		||||
        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(
 | 
			
		||||
@@ -373,7 +369,6 @@ ESPHomeLoader.add_constructor(
 | 
			
		||||
)
 | 
			
		||||
ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda)
 | 
			
		||||
ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force)
 | 
			
		||||
ESPHomeLoader.add_constructor("!extend", ESPHomeLoader.construct_extend)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_yaml(fname, clear_secrets=True):
 | 
			
		||||
 
 | 
			
		||||
@@ -157,11 +157,6 @@ class DashboardImportDiscovery:
 | 
			
		||||
            return
 | 
			
		||||
        if state_change == ServiceStateChange.Removed:
 | 
			
		||||
            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)
 | 
			
		||||
        _LOGGER.debug("-> resolved info: %s", info)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ pyserial==3.5
 | 
			
		||||
platformio==6.1.5  # When updating platformio, also update Dockerfile
 | 
			
		||||
esptool==4.4
 | 
			
		||||
click==8.1.3
 | 
			
		||||
esphome-dashboard==20230214.0
 | 
			
		||||
esphome-dashboard==20230120.0
 | 
			
		||||
aioesphomeapi==13.1.0
 | 
			
		||||
zeroconf==0.47.1
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,351 +0,0 @@
 | 
			
		||||
"""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,12 +222,6 @@ uart:
 | 
			
		||||
    rx_pin: GPIO26
 | 
			
		||||
    baud_rate: 115200
 | 
			
		||||
    rx_buffer_size: 1024
 | 
			
		||||
  - id: ld2410_uart
 | 
			
		||||
    tx_pin: 18
 | 
			
		||||
    rx_pin: 23
 | 
			
		||||
    baud_rate: 256000
 | 
			
		||||
    parity: NONE
 | 
			
		||||
    stop_bits: 1
 | 
			
		||||
 | 
			
		||||
ota:
 | 
			
		||||
  safe_mode: true
 | 
			
		||||
@@ -1076,8 +1070,6 @@ sensor:
 | 
			
		||||
    id: ultrasonic_sensor1
 | 
			
		||||
  - platform: uptime
 | 
			
		||||
    name: Uptime Sensor
 | 
			
		||||
  - id: !extend ${devicename}_uptime_pcg
 | 
			
		||||
    unit_of_measurement: s
 | 
			
		||||
  - platform: wifi_signal
 | 
			
		||||
    name: WiFi Signal Sensor
 | 
			
		||||
    update_interval: 15s
 | 
			
		||||
@@ -1208,17 +1200,6 @@ sensor:
 | 
			
		||||
    pressure:
 | 
			
		||||
      name: "MPL3115A2 Pressure"
 | 
			
		||||
    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:
 | 
			
		||||
  setup_mode: false
 | 
			
		||||
@@ -1323,11 +1304,6 @@ binary_sensor:
 | 
			
		||||
      number: GPIO9
 | 
			
		||||
      mode: INPUT_PULLUP
 | 
			
		||||
    name: Living Room Window 2
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    pin:
 | 
			
		||||
      number: GPIO9
 | 
			
		||||
      mode: INPUT_OUTPUT_OPEN_DRAIN
 | 
			
		||||
    name: Living Room Button
 | 
			
		||||
  - platform: status
 | 
			
		||||
    name: Living Room Status
 | 
			
		||||
  - platform: esp32_touch
 | 
			
		||||
@@ -1485,13 +1461,6 @@ binary_sensor:
 | 
			
		||||
    id: close_sensor
 | 
			
		||||
  - platform: template
 | 
			
		||||
    id: close_obstacle_sensor
 | 
			
		||||
  - platform: ld2410
 | 
			
		||||
    has_target:
 | 
			
		||||
      name: presence
 | 
			
		||||
    has_moving_target:
 | 
			
		||||
      name: movement
 | 
			
		||||
    has_still_target:
 | 
			
		||||
      name: still
 | 
			
		||||
 | 
			
		||||
pca9685:
 | 
			
		||||
  frequency: 500
 | 
			
		||||
@@ -3174,19 +3143,6 @@ button:
 | 
			
		||||
    on_press:
 | 
			
		||||
      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:
 | 
			
		||||
  display_id: my_lcd_gpio
 | 
			
		||||
  mark_back: 0x5e
 | 
			
		||||
 
 | 
			
		||||
@@ -287,9 +287,6 @@ uart:
 | 
			
		||||
modbus:
 | 
			
		||||
  uart_id: uart1
 | 
			
		||||
 | 
			
		||||
vbus:
 | 
			
		||||
  uart_id: uart4
 | 
			
		||||
 | 
			
		||||
ota:
 | 
			
		||||
  safe_mode: true
 | 
			
		||||
  port: 3286
 | 
			
		||||
@@ -802,11 +799,6 @@ sensor:
 | 
			
		||||
    id: adc128s102_channel_0
 | 
			
		||||
    channel: 0
 | 
			
		||||
 | 
			
		||||
  - platform: vbus
 | 
			
		||||
    model: deltasol c
 | 
			
		||||
    temperature_1:
 | 
			
		||||
      name: Temperature 1
 | 
			
		||||
 | 
			
		||||
time:
 | 
			
		||||
  - platform: homeassistant
 | 
			
		||||
 | 
			
		||||
@@ -912,11 +904,6 @@ binary_sensor:
 | 
			
		||||
      then:
 | 
			
		||||
        - pzemac.reset_energy: pzemac1
 | 
			
		||||
 | 
			
		||||
  - platform: vbus
 | 
			
		||||
    model: deltasol_bs_plus
 | 
			
		||||
    relay1:
 | 
			
		||||
      name: Relay 1 On
 | 
			
		||||
 | 
			
		||||
globals:
 | 
			
		||||
  - id: my_global_string
 | 
			
		||||
    type: std::string
 | 
			
		||||
@@ -1136,16 +1123,14 @@ climate:
 | 
			
		||||
      - switch.turn_on: gpio_switch1
 | 
			
		||||
    fan_mode_diffuse_action:
 | 
			
		||||
      - switch.turn_on: gpio_switch2
 | 
			
		||||
    fan_mode_quiet_action:
 | 
			
		||||
      - switch.turn_on: gpio_switch1
 | 
			
		||||
    swing_off_action:
 | 
			
		||||
      - switch.turn_on: gpio_switch2
 | 
			
		||||
      - switch.turn_on: gpio_switch1
 | 
			
		||||
    swing_horizontal_action:
 | 
			
		||||
      - switch.turn_on: gpio_switch1
 | 
			
		||||
    swing_vertical_action:
 | 
			
		||||
      - switch.turn_on: gpio_switch2
 | 
			
		||||
    swing_both_action:
 | 
			
		||||
    swing_vertical_action:
 | 
			
		||||
      - switch.turn_on: gpio_switch1
 | 
			
		||||
    swing_both_action:
 | 
			
		||||
      - switch.turn_on: gpio_switch2
 | 
			
		||||
    startup_delay: true
 | 
			
		||||
    supplemental_cooling_delta: 2.0
 | 
			
		||||
    cool_deadband: 0.5
 | 
			
		||||
 
 | 
			
		||||
@@ -66,9 +66,6 @@ mqtt:
 | 
			
		||||
          ESP_LOGD("Mqtt Test", "testing/sensor/testing_sensor/state=[%s]", x.c_str());
 | 
			
		||||
      # yamllint enable rule:line-length
 | 
			
		||||
 | 
			
		||||
vbus:
 | 
			
		||||
  - uart_id: uart2
 | 
			
		||||
 | 
			
		||||
binary_sensor:
 | 
			
		||||
  - platform: gpio
 | 
			
		||||
    pin: GPIO0
 | 
			
		||||
@@ -186,22 +183,6 @@ binary_sensor:
 | 
			
		||||
    id: key1
 | 
			
		||||
    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:
 | 
			
		||||
  data_pin: GPIO12
 | 
			
		||||
@@ -497,27 +478,6 @@ sensor:
 | 
			
		||||
    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:
 | 
			
		||||
  - id: automation_test
 | 
			
		||||
    then:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user